Showing
7 changed files
with
424 additions
and
275 deletions
fractional/src/continuous.rs
0 → 100644
| 1 | +// | ||
| 2 | +// A «continued fraction» is a representation of a fraction as a vector | ||
| 3 | +// of integrals… Irrational fractions will result in infinite most of the | ||
| 4 | +// time repetitive vectors. They can be used to get a resonable approximation | ||
| 5 | +// for sqrt on fractionals. | ||
| 6 | +// | ||
| 7 | +// Georg Hopp <georg@steffers.org> | ||
| 8 | +// | ||
| 9 | +// Copyright © 2019 Georg Hopp | ||
| 10 | +// | ||
| 11 | +// This program is free software: you can redistribute it and/or modify | ||
| 12 | +// it under the terms of the GNU General Public License as published by | ||
| 13 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 14 | +// (at your option) any later version. | ||
| 15 | +// | ||
| 16 | +// This program is distributed in the hope that it will be useful, | ||
| 17 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | +// GNU General Public License for more details. | ||
| 20 | +// | ||
| 21 | +// You should have received a copy of the GNU General Public License | ||
| 22 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 23 | +// | ||
| 24 | +use crate::Fractional; | ||
| 25 | + | ||
| 26 | +#[derive(Debug)] | ||
| 27 | +pub struct Continuous (Vec<i64>); | ||
| 28 | + | ||
| 29 | +impl Continuous { | ||
| 30 | + // calculate a sqrt as continued fraction sequence. Taken from: | ||
| 31 | + // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots# | ||
| 32 | + // Continued_fraction_expansion | ||
| 33 | + pub fn sqrt(x :i64, a0 :i64) -> Self { | ||
| 34 | + fn inner(mut v :Vec<i64>, | ||
| 35 | + x :i64, | ||
| 36 | + a0 :i64, | ||
| 37 | + mn :i64, | ||
| 38 | + dn :i64, | ||
| 39 | + an :i64) -> Vec<i64> { | ||
| 40 | + let mn_1 = dn * an - mn; | ||
| 41 | + let dn_1 = (x - mn_1 * mn_1) / dn; | ||
| 42 | + let an_1 = (a0 + mn_1) / dn_1; | ||
| 43 | + | ||
| 44 | + v.push(an); | ||
| 45 | + | ||
| 46 | + // The convergence criteria „an_1 == 2 * a0“ is not good for | ||
| 47 | + // very small x thus I decided to break the iteration at constant | ||
| 48 | + // time. Which is the 10 below. | ||
| 49 | + match v.len() { | ||
| 50 | + 10 => v, | ||
| 51 | + _ => inner(v, x, a0, mn_1, dn_1, an_1), | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + Continuous(inner(Vec::new(), x, a0, 0, 1, a0)) | ||
| 56 | + } | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +impl From<&Fractional> for Continuous { | ||
| 60 | + // general continous fraction form of a fractional... | ||
| 61 | + fn from(x :&Fractional) -> Self { | ||
| 62 | + fn inner(mut v :Vec<i64>, f :Fractional) -> Vec<i64> { | ||
| 63 | + let Fractional(n, d) = f; | ||
| 64 | + let a = n / d; | ||
| 65 | + let Fractional(_n, _d) = f - a.into(); | ||
| 66 | + | ||
| 67 | + v.push(a); | ||
| 68 | + match _n { | ||
| 69 | + 1 => { v.push(_d); v }, | ||
| 70 | + _ => inner(v, Fractional(_d, _n)), | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + Continuous(inner(Vec::new(), *x)) | ||
| 75 | + } | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +impl Into<Fractional> for &Continuous { | ||
| 79 | + fn into(self) -> Fractional { | ||
| 80 | + let Continuous(c) = self; | ||
| 81 | + let Fractional(n, d) = c.iter().rev().fold( Fractional(0, 1) | ||
| 82 | + , |acc, x| { | ||
| 83 | + let Fractional(an, ad) = acc + (*x).into(); | ||
| 84 | + Fractional(ad, an) | ||
| 85 | + }); | ||
| 86 | + Fractional(d, n) | ||
| 87 | + } | ||
| 88 | +} |
| @@ -31,10 +31,6 @@ use std::num::TryFromIntError; | @@ -31,10 +31,6 @@ use std::num::TryFromIntError; | ||
| 31 | #[derive(Debug, Eq, Clone, Copy)] | 31 | #[derive(Debug, Eq, Clone, Copy)] |
| 32 | pub struct Fractional (pub i64, pub i64); | 32 | pub struct Fractional (pub i64, pub i64); |
| 33 | 33 | ||
| 34 | -pub type Continuous = Vec<i64>; | ||
| 35 | - | ||
| 36 | -pub type Error = &'static str; | ||
| 37 | - | ||
| 38 | #[inline] | 34 | #[inline] |
| 39 | fn hcf(x :i64, y :i64) -> i64 { | 35 | fn hcf(x :i64, y :i64) -> i64 { |
| 40 | match y { | 36 | match y { |
| @@ -43,36 +39,8 @@ fn hcf(x :i64, y :i64) -> i64 { | @@ -43,36 +39,8 @@ fn hcf(x :i64, y :i64) -> i64 { | ||
| 43 | } | 39 | } |
| 44 | } | 40 | } |
| 45 | 41 | ||
| 46 | -// calculate a sqrt as continued fraction sequence. Taken from: | ||
| 47 | -// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Continued_fraction_expansion | ||
| 48 | -fn sqrt_cfrac(x :i64, a0 :i64) -> Continuous { | ||
| 49 | - let v :Continuous = Vec::new(); | ||
| 50 | - | ||
| 51 | - fn inner(mut v :Continuous, | ||
| 52 | - x :i64, | ||
| 53 | - a0 :i64, | ||
| 54 | - mn :i64, | ||
| 55 | - dn :i64, | ||
| 56 | - an :i64) -> Continuous { | ||
| 57 | - let mn_1 = dn * an - mn; | ||
| 58 | - let dn_1 = (x - mn_1 * mn_1) / dn; | ||
| 59 | - let an_1 = (a0 + mn_1) / dn_1; | ||
| 60 | - | ||
| 61 | - v.push(an); | ||
| 62 | - match v.len() { | ||
| 63 | - 10 => v, | ||
| 64 | - _ => inner(v, x, a0, mn_1, dn_1, an_1), | ||
| 65 | - } | ||
| 66 | - | ||
| 67 | - // This convergence criterium is not good for very small x | ||
| 68 | - // thus I decided to break the iteration at constant time. | ||
| 69 | -// match an_1 == 2 * a0 { | ||
| 70 | -// true => v, | ||
| 71 | -// _ => inner(v, x, a0, mn_1, dn_1, an_1), | ||
| 72 | -// } | ||
| 73 | - } | ||
| 74 | - | ||
| 75 | - inner(v, x, a0, 0, 1, a0) | 42 | +pub fn from_vector(xs: &Vec<i64>) -> Vec<Fractional> { |
| 43 | + xs.iter().map(|x| Fractional(*x, 1)).collect() | ||
| 76 | } | 44 | } |
| 77 | 45 | ||
| 78 | impl Fractional { | 46 | impl Fractional { |
| @@ -98,76 +66,6 @@ impl Fractional { | @@ -98,76 +66,6 @@ impl Fractional { | ||
| 98 | Self(n / hcf(n, d), d / hcf(n, d)) | 66 | Self(n / hcf(n, d), d / hcf(n, d)) |
| 99 | } | 67 | } |
| 100 | } | 68 | } |
| 101 | - | ||
| 102 | - #[inline] | ||
| 103 | - pub fn numerator(self) -> i64 { | ||
| 104 | - self.0 | ||
| 105 | - } | ||
| 106 | - | ||
| 107 | - #[inline] | ||
| 108 | - pub fn denominator(self) -> i64 { | ||
| 109 | - self.1 | ||
| 110 | - } | ||
| 111 | - | ||
| 112 | - // This is a really bad approximation of sqrt for a fractional... | ||
| 113 | - // for (9/3) it will result 3 which if way to far from the truth, | ||
| 114 | - // which is ~1.7320508075 | ||
| 115 | - // BUT we can use this value as starting guess for creating a | ||
| 116 | - // continous fraction for the sqrt... and create a much better | ||
| 117 | - // fractional representation of the sqrt. | ||
| 118 | - // So, if inner converges, but is not a perfect square (does not | ||
| 119 | - // end up in an Ordering::Equal - which is the l > h case) | ||
| 120 | - // we use the l - 1 as starting guess for sqrt_cfrac. | ||
| 121 | - // taken from: | ||
| 122 | - // https://www.geeksforgeeks.org/square-root-of-an-integer/ | ||
| 123 | - pub fn sqrt(self) -> Result<Self, Error> { | ||
| 124 | - // find the sqrt of x in O(log x/2). | ||
| 125 | - // This stops if a perfect sqare was found. Else it passes | ||
| 126 | - // the found value as starting guess to the continous fraction | ||
| 127 | - // sqrt function. | ||
| 128 | - fn floor_sqrt(x :i64) -> Fractional { | ||
| 129 | - fn inner(l :i64, h :i64, x :i64) -> Fractional { | ||
| 130 | - if l > h { | ||
| 131 | - (&sqrt_cfrac(x, l - 1)).into() | ||
| 132 | - } else { | ||
| 133 | - let m = (l + h) / 2; | ||
| 134 | - match x.cmp(&(m * m)) { | ||
| 135 | - Ordering::Equal => m.into(), | ||
| 136 | - Ordering::Less => inner(l, m - 1, x), | ||
| 137 | - Ordering::Greater => inner(m + 1, h, x), | ||
| 138 | - } | ||
| 139 | - } | ||
| 140 | - } | ||
| 141 | - | ||
| 142 | - match x { | ||
| 143 | - 0 => 0.into(), | ||
| 144 | - 1 => 1.into(), | ||
| 145 | - _ => inner(1, x / 2, x), | ||
| 146 | - } | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - let Fractional(n, d) = self; | ||
| 150 | - | ||
| 151 | - let n = match n.cmp(&0) { | ||
| 152 | - Ordering::Equal => 0.into(), | ||
| 153 | - Ordering::Less => return Err("sqrt on negative undefined"), | ||
| 154 | - Ordering::Greater => floor_sqrt(n), | ||
| 155 | - }; | ||
| 156 | - | ||
| 157 | - let d = match d.cmp(&0) { | ||
| 158 | - Ordering::Equal => 0.into(), | ||
| 159 | - Ordering::Less => return Err("sqrt on negative undefined"), | ||
| 160 | - Ordering::Greater => floor_sqrt(d), | ||
| 161 | - }; | ||
| 162 | - | ||
| 163 | - Ok(n / d) | ||
| 164 | - } | ||
| 165 | -} | ||
| 166 | - | ||
| 167 | -impl fmt::Display for Fractional { | ||
| 168 | - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| 169 | - write!(f, "({}/{})", self.0, self.1) | ||
| 170 | - } | ||
| 171 | } | 69 | } |
| 172 | 70 | ||
| 173 | impl From<i64> for Fractional { | 71 | impl From<i64> for Fractional { |
| @@ -176,6 +74,12 @@ impl From<i64> for Fractional { | @@ -176,6 +74,12 @@ impl From<i64> for Fractional { | ||
| 176 | } | 74 | } |
| 177 | } | 75 | } |
| 178 | 76 | ||
| 77 | +impl From<i32> for Fractional { | ||
| 78 | + fn from(x: i32) -> Self { | ||
| 79 | + Self(x as i64, 1) | ||
| 80 | + } | ||
| 81 | +} | ||
| 82 | + | ||
| 179 | impl TryFrom<usize> for Fractional { | 83 | impl TryFrom<usize> for Fractional { |
| 180 | type Error = &'static str; | 84 | type Error = &'static str; |
| 181 | 85 | ||
| @@ -188,10 +92,6 @@ impl TryFrom<usize> for Fractional { | @@ -188,10 +92,6 @@ impl TryFrom<usize> for Fractional { | ||
| 188 | } | 92 | } |
| 189 | } | 93 | } |
| 190 | 94 | ||
| 191 | -pub fn from_vector(xs: &Vec<i64>) -> Vec<Fractional> { | ||
| 192 | - xs.iter().map(|x| Fractional(*x, 1)).collect() | ||
| 193 | -} | ||
| 194 | - | ||
| 195 | impl TryInto<f64> for Fractional { | 95 | impl TryInto<f64> for Fractional { |
| 196 | type Error = TryFromIntError; | 96 | type Error = TryFromIntError; |
| 197 | 97 | ||
| @@ -212,34 +112,9 @@ impl TryInto<(i32, i32)> for Fractional { | @@ -212,34 +112,9 @@ impl TryInto<(i32, i32)> for Fractional { | ||
| 212 | } | 112 | } |
| 213 | } | 113 | } |
| 214 | 114 | ||
| 215 | -impl Into<Continuous> for Fractional { | ||
| 216 | - // general continous fraction form of a fractional... | ||
| 217 | - fn into(self) -> Continuous { | ||
| 218 | - let v :Continuous = Vec::new(); | ||
| 219 | - | ||
| 220 | - fn inner(mut v :Continuous, f :Fractional) -> Continuous { | ||
| 221 | - let Fractional(n, d) = f; | ||
| 222 | - let a = n / d; | ||
| 223 | - let Fractional(_n, _d) = f - a.into(); | ||
| 224 | - | ||
| 225 | - v.push(a); | ||
| 226 | - match _n { | ||
| 227 | - 1 => { v.push(_d); v }, | ||
| 228 | - _ => inner(v, Fractional(_d, _n)), | ||
| 229 | - } | ||
| 230 | - } | ||
| 231 | - | ||
| 232 | - inner(v, self) | ||
| 233 | - } | ||
| 234 | -} | ||
| 235 | - | ||
| 236 | -impl From<&Continuous> for Fractional { | ||
| 237 | - fn from(x :&Continuous) -> Self { | ||
| 238 | - let Self(n, d) = x.iter().rev().fold(Fractional(0, 1), |acc, x| { | ||
| 239 | - let Self(an, ad) = acc + (*x).into(); | ||
| 240 | - Self(ad, an) | ||
| 241 | - }); | ||
| 242 | - Self(d, n) | 115 | +impl fmt::Display for Fractional { |
| 116 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| 117 | + write!(f, "({}/{})", self.0, self.1) | ||
| 243 | } | 118 | } |
| 244 | } | 119 | } |
| 245 | 120 |
| @@ -20,11 +20,13 @@ | @@ -20,11 +20,13 @@ | ||
| 20 | // | 20 | // |
| 21 | extern crate lazy_static; | 21 | extern crate lazy_static; |
| 22 | 22 | ||
| 23 | +pub type Error = &'static str; | ||
| 24 | + | ||
| 25 | +pub mod continuous; | ||
| 23 | pub mod fractional; | 26 | pub mod fractional; |
| 27 | +pub mod transform; | ||
| 24 | pub mod trigonometry; | 28 | pub mod trigonometry; |
| 25 | pub mod vector; | 29 | pub mod vector; |
| 26 | -pub mod transform; | ||
| 27 | 30 | ||
| 28 | -use fractional::{Fractional}; | ||
| 29 | -use trigonometry::{sin, cos}; | ||
| 30 | -use vector::{Vector}; | 31 | +use fractional::Fractional; |
| 32 | +use vector::Vector; |
| @@ -22,8 +22,9 @@ use std::convert::{TryFrom, TryInto, Into}; | @@ -22,8 +22,9 @@ use std::convert::{TryFrom, TryInto, Into}; | ||
| 22 | use std::num::TryFromIntError; | 22 | use std::num::TryFromIntError; |
| 23 | use std::f64::consts::PI as FPI; | 23 | use std::f64::consts::PI as FPI; |
| 24 | 24 | ||
| 25 | -use fractional::fractional::{Fractional, from_vector, Continuous}; | ||
| 26 | -use fractional::trigonometry::{sin, cos, tan, PI}; | 25 | +use fractional::continuous::Continuous; |
| 26 | +use fractional::fractional::{Fractional, from_vector}; | ||
| 27 | +use fractional::trigonometry::Trig; | ||
| 27 | use fractional::vector::{Vector}; | 28 | use fractional::vector::{Vector}; |
| 28 | use fractional::transform::{translate, rotate_x, rotate_y, rotate_z, rotate_v}; | 29 | use fractional::transform::{translate, rotate_x, rotate_y, rotate_z, rotate_v}; |
| 29 | 30 | ||
| @@ -51,8 +52,8 @@ fn continuous() { | @@ -51,8 +52,8 @@ fn continuous() { | ||
| 51 | let d = Fractional(45, 16); | 52 | let d = Fractional(45, 16); |
| 52 | let e = Fractional(16, 45); | 53 | let e = Fractional(16, 45); |
| 53 | 54 | ||
| 54 | - let dc :Continuous = d.into(); | ||
| 55 | - let ec :Continuous = e.into(); | 55 | + let dc :Continuous = (&d).into(); |
| 56 | + let ec :Continuous = (&e).into(); | ||
| 56 | 57 | ||
| 57 | println!("cont frac of d : {} => {:?}", d, dc); | 58 | println!("cont frac of d : {} => {:?}", d, dc); |
| 58 | println!("cont frac of e : {} => {:?}", e, ec); | 59 | println!("cont frac of e : {} => {:?}", e, ec); |
| @@ -82,21 +83,22 @@ fn sqrt() { | @@ -82,21 +83,22 @@ fn sqrt() { | ||
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | fn pi() { | 85 | fn pi() { |
| 85 | - let pir :f64 = PI.try_into().unwrap(); | ||
| 86 | - let pit :(i32, i32) = PI.try_into().unwrap(); | ||
| 87 | - let pi2r :f64 = (PI * PI).try_into().unwrap(); | 86 | + let pi = Fractional::pi(); |
| 87 | + let pir :f64 = pi.try_into().unwrap(); | ||
| 88 | + let pit :(i32, i32) = pi.try_into().unwrap(); | ||
| 89 | + let pi2r :f64 = (pi * pi).try_into().unwrap(); | ||
| 88 | 90 | ||
| 89 | println!(" Rust π : {}" , FPI); | 91 | println!(" Rust π : {}" , FPI); |
| 90 | - println!(" π : {} {}" , PI, pir); | 92 | + println!(" π : {} {}" , pi, pir); |
| 91 | println!(" π as tuple : {:?}" , pit); | 93 | println!(" π as tuple : {:?}" , pit); |
| 92 | println!(" Rust π² : {}" , FPI * FPI); | 94 | println!(" Rust π² : {}" , FPI * FPI); |
| 93 | - println!(" π² : {} {}" , PI * PI, pi2r); | 95 | + println!(" π² : {} {}" , pi * pi, pi2r); |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | fn _sin() { | 98 | fn _sin() { |
| 97 | for d in [ 0, 45, 90, 135, 180, 225, 270, 315 | 99 | for d in [ 0, 45, 90, 135, 180, 225, 270, 315 |
| 98 | , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { | 100 | , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { |
| 99 | - let s = sin(*d as i32); | 101 | + let s = Fractional::sin(*d as i32); |
| 100 | let sr :f64 = s.try_into().unwrap(); | 102 | let sr :f64 = s.try_into().unwrap(); |
| 101 | let _s = f64::sin(*d as f64 * FPI / 180.0); | 103 | let _s = f64::sin(*d as f64 * FPI / 180.0); |
| 102 | 104 | ||
| @@ -107,22 +109,22 @@ fn _sin() { | @@ -107,22 +109,22 @@ fn _sin() { | ||
| 107 | fn _tan() { | 109 | fn _tan() { |
| 108 | for d in [ 0, 45, 90, 135, 180, 225, 270, 315 | 110 | for d in [ 0, 45, 90, 135, 180, 225, 270, 315 |
| 109 | , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { | 111 | , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { |
| 110 | - let s = tan(*d as i32); | ||
| 111 | - let sr :f64 = s.try_into().unwrap(); | ||
| 112 | - let _s = f64::tan(*d as f64 * FPI / 180.0); | 112 | + let t = Fractional::tan(*d as i32); |
| 113 | + let tr :f64 = t.try_into().unwrap(); | ||
| 114 | + let _t = f64::tan(*d as f64 * FPI / 180.0); | ||
| 113 | 115 | ||
| 114 | - println!("{:>14} : {} {} / {}", format!("tan {}", d), s, sr, _s); | 116 | + println!("{:>14} : {} {} / {}", format!("tan {}", d), t, tr, _t); |
| 115 | } | 117 | } |
| 116 | } | 118 | } |
| 117 | 119 | ||
| 118 | fn _cos() { | 120 | fn _cos() { |
| 119 | for d in [ 0, 45, 90, 135, 180, 225, 270, 315 | 121 | for d in [ 0, 45, 90, 135, 180, 225, 270, 315 |
| 120 | , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { | 122 | , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { |
| 121 | - let s = cos(*d as i32); | ||
| 122 | - let sr :f64 = s.try_into().unwrap(); | ||
| 123 | - let _s = f64::cos(*d as f64 * FPI / 180.0); | 123 | + let c = Fractional::cos(*d as i32); |
| 124 | + let cr :f64 = c.try_into().unwrap(); | ||
| 125 | + let _c = f64::cos(*d as f64 * FPI / 180.0); | ||
| 124 | 126 | ||
| 125 | - println!("{:>14} : {} {} / {}", format!("cos {}", d), s, sr, _s); | 127 | + println!("{:>14} : {} {} / {}", format!("cos {}", d), c, cr, _c); |
| 126 | } | 128 | } |
| 127 | } | 129 | } |
| 128 | 130 | ||
| @@ -152,8 +154,8 @@ fn _vector() { | @@ -152,8 +154,8 @@ fn _vector() { | ||
| 152 | } | 154 | } |
| 153 | 155 | ||
| 154 | fn _transform() { | 156 | fn _transform() { |
| 155 | - let v = Vector(1.into(), 1.into(), 1.into()); | ||
| 156 | - let v1 = Vector(1.into(), 2.into(), 3.into()); | 157 | + let v = Vector(Fractional(1,1), Fractional(1,1), Fractional(1,1)); |
| 158 | + let v1 = Vector(Fractional(1,1), Fractional(2,1), Fractional(3,1)); | ||
| 157 | let mt = translate(v); | 159 | let mt = translate(v); |
| 158 | 160 | ||
| 159 | println!("{:>14} : {}", "Vector v1", v1); | 161 | println!("{:>14} : {}", "Vector v1", v1); |
| @@ -184,7 +186,7 @@ fn _transform() { | @@ -184,7 +186,7 @@ fn _transform() { | ||
| 184 | } | 186 | } |
| 185 | println!(); | 187 | println!(); |
| 186 | 188 | ||
| 187 | - let v3 = Vector(1.into(), 0.into(), 1.into()); | 189 | + let v3 = Vector(Fractional(1,1), Fractional(0,1), Fractional(1,1)); |
| 188 | println!("{:>14} : {}", "Vector v3", v3); | 190 | println!("{:>14} : {}", "Vector v3", v3); |
| 189 | for d in [ 30, 45, 60, 90, 120, 135, 150, 180 | 191 | for d in [ 30, 45, 60, 90, 120, 135, 150, 180 |
| 190 | , 210, 225, 240, 270, 300, 315, 330 ].iter() { | 192 | , 210, 225, 240, 270, 300, 315, 330 ].iter() { |
| @@ -18,15 +18,19 @@ | @@ -18,15 +18,19 @@ | ||
| 18 | // You should have received a copy of the GNU General Public License | 18 | // You should have received a copy of the GNU General Public License |
| 19 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 20 | // | 20 | // |
| 21 | -use std::ops::{Mul}; | ||
| 22 | -use crate::{Fractional, cos, sin, Vector}; | ||
| 23 | - | ||
| 24 | -pub struct TMatrix( (Fractional, Fractional, Fractional, Fractional) | ||
| 25 | - , (Fractional, Fractional, Fractional, Fractional) | ||
| 26 | - , (Fractional, Fractional, Fractional, Fractional) | ||
| 27 | - , (Fractional, Fractional, Fractional, Fractional) ); | ||
| 28 | - | ||
| 29 | -pub fn translate(v :Vector) -> TMatrix { | 21 | +use std::ops::{Add, Sub, Neg, Mul, Div}; |
| 22 | +use crate::Vector; | ||
| 23 | +use crate::trigonometry::Trig; | ||
| 24 | + | ||
| 25 | +pub struct TMatrix<T>( (T, T, T, T) | ||
| 26 | + , (T, T, T, T) | ||
| 27 | + , (T, T, T, T) | ||
| 28 | + , (T, T, T, T) ) | ||
| 29 | + where T: Add + Sub + Neg + Mul + Div + Trig + From<i32> + Copy; | ||
| 30 | + | ||
| 31 | +pub fn translate<T>(v :Vector<T>) -> TMatrix<T> | ||
| 32 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 33 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 30 | let Vector(x, y, z) = v; | 34 | let Vector(x, y, z) = v; |
| 31 | 35 | ||
| 32 | TMatrix( (1.into(), 0.into(), 0.into(), 0.into()) | 36 | TMatrix( (1.into(), 0.into(), 0.into(), 0.into()) |
| @@ -35,49 +39,71 @@ pub fn translate(v :Vector) -> TMatrix { | @@ -35,49 +39,71 @@ pub fn translate(v :Vector) -> TMatrix { | ||
| 35 | , ( x, y, z, 1.into()) ) | 39 | , ( x, y, z, 1.into()) ) |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | -pub fn rotate_x(a :i32) -> TMatrix { | 42 | +pub fn rotate_x<T>(a :i32) -> TMatrix<T> |
| 43 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 44 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 45 | + let sin :T = Trig::sin(a); | ||
| 46 | + let cos :T = Trig::cos(a); | ||
| 47 | + | ||
| 39 | TMatrix( (1.into(), 0.into(), 0.into(), 0.into()) | 48 | TMatrix( (1.into(), 0.into(), 0.into(), 0.into()) |
| 40 | - , (0.into(), cos(a), -sin(a), 0.into()) | ||
| 41 | - , (0.into(), sin(a), cos(a), 0.into()) | 49 | + , (0.into(), cos , -sin , 0.into()) |
| 50 | + , (0.into(), sin , cos , 0.into()) | ||
| 42 | , (0.into(), 0.into(), 0.into(), 1.into()) ) | 51 | , (0.into(), 0.into(), 0.into(), 1.into()) ) |
| 43 | } | 52 | } |
| 44 | 53 | ||
| 45 | -pub fn rotate_y(a :i32) -> TMatrix { | ||
| 46 | - TMatrix( ( cos(a), 0.into(), sin(a), 0.into()) | 54 | +pub fn rotate_y<T>(a :i32) -> TMatrix<T> |
| 55 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 56 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 57 | + let sin :T = Trig::sin(a); | ||
| 58 | + let cos :T = Trig::cos(a); | ||
| 59 | + | ||
| 60 | + TMatrix( (cos , 0.into(), sin , 0.into()) | ||
| 47 | , (0.into(), 1.into(), 0.into(), 0.into()) | 61 | , (0.into(), 1.into(), 0.into(), 0.into()) |
| 48 | - , ( -sin(a), 0.into(), cos(a), 0.into()) | 62 | + , (-sin , 0.into(), cos , 0.into()) |
| 49 | , (0.into(), 0.into(), 0.into(), 1.into()) ) | 63 | , (0.into(), 0.into(), 0.into(), 1.into()) ) |
| 50 | } | 64 | } |
| 51 | 65 | ||
| 52 | -pub fn rotate_z(a :i32) -> TMatrix { | ||
| 53 | - TMatrix( ( cos(a), -sin(a), 0.into(), 0.into()) | ||
| 54 | - , ( sin(a), cos(a), 0.into(), 0.into()) | 66 | +pub fn rotate_z<T>(a :i32) -> TMatrix<T> |
| 67 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 68 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 69 | + let sin :T = Trig::sin(a); | ||
| 70 | + let cos :T = Trig::cos(a); | ||
| 71 | + | ||
| 72 | + TMatrix( (cos , -sin , 0.into(), 0.into()) | ||
| 73 | + , (sin , cos , 0.into(), 0.into()) | ||
| 55 | , (0.into(), 0.into(), 1.into(), 0.into()) | 74 | , (0.into(), 0.into(), 1.into(), 0.into()) |
| 56 | , (0.into(), 0.into(), 0.into(), 1.into()) ) | 75 | , (0.into(), 0.into(), 0.into(), 1.into()) ) |
| 57 | } | 76 | } |
| 58 | 77 | ||
| 59 | -pub fn rotate_v(v :&Vector, a :i32) -> TMatrix { | 78 | +pub fn rotate_v<T>(v :&Vector<T>, a :i32) -> TMatrix<T> |
| 79 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 80 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 60 | let Vector(x, y, z) = *v; | 81 | let Vector(x, y, z) = *v; |
| 61 | 82 | ||
| 62 | - let zero :Fractional = 0.into(); | ||
| 63 | - let one :Fractional = 1.into(); | 83 | + let sin :T = Trig::sin(a); |
| 84 | + let cos :T = Trig::cos(a); | ||
| 85 | + | ||
| 86 | + let zero :T = 0.into(); | ||
| 87 | + let one :T = 1.into(); | ||
| 64 | 88 | ||
| 65 | - TMatrix( ( (one - cos(a)) * x * x + cos(a) | ||
| 66 | - , (one - cos(a)) * x * y - sin(a) * z | ||
| 67 | - , (one - cos(a)) * x * z + sin(a) * y | 89 | + TMatrix( ( (one - cos) * x * x + cos |
| 90 | + , (one - cos) * x * y - sin * z | ||
| 91 | + , (one - cos) * x * z + sin * y | ||
| 68 | , zero ) | 92 | , zero ) |
| 69 | - , ( (one - cos(a)) * x * y + sin(a) * z | ||
| 70 | - , (one - cos(a)) * y * y + cos(a) | ||
| 71 | - , (one - cos(a)) * y * z - sin(a) * x | 93 | + , ( (one - cos) * x * y + sin * z |
| 94 | + , (one - cos) * y * y + cos | ||
| 95 | + , (one - cos) * y * z - sin * x | ||
| 72 | , zero ) | 96 | , zero ) |
| 73 | - , ( (one - cos(a)) * x * z - sin(a) * y | ||
| 74 | - , (one - cos(a)) * y * z + sin(a) * x | ||
| 75 | - , (one - cos(a)) * z * z + cos(a) | 97 | + , ( (one - cos) * x * z - sin * y |
| 98 | + , (one - cos) * y * z + sin * x | ||
| 99 | + , (one - cos) * z * z + cos | ||
| 76 | , zero ) | 100 | , zero ) |
| 77 | , (0.into(), 0.into(), 0.into(), 1.into()) ) | 101 | , (0.into(), 0.into(), 0.into(), 1.into()) ) |
| 78 | } | 102 | } |
| 79 | 103 | ||
| 80 | -pub fn scale(v :Vector) -> TMatrix { | 104 | +pub fn scale<T>(v :Vector<T>) -> TMatrix<T> |
| 105 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 106 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 81 | let Vector(x, y, z) = v; | 107 | let Vector(x, y, z) = v; |
| 82 | 108 | ||
| 83 | TMatrix( ( x, 0.into(), 0.into(), 0.into()) | 109 | TMatrix( ( x, 0.into(), 0.into(), 0.into()) |
| @@ -86,8 +112,10 @@ pub fn scale(v :Vector) -> TMatrix { | @@ -86,8 +112,10 @@ pub fn scale(v :Vector) -> TMatrix { | ||
| 86 | , (0.into(), 0.into(), 0.into(), 1.into()) ) | 112 | , (0.into(), 0.into(), 0.into(), 1.into()) ) |
| 87 | } | 113 | } |
| 88 | 114 | ||
| 89 | -impl TMatrix { | ||
| 90 | - pub fn apply(&self, v :&Vector) -> Vector { | 115 | +impl<T> TMatrix<T> |
| 116 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 117 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 118 | + pub fn apply(&self, v :&Vector<T>) -> Vector<T> { | ||
| 91 | let TMatrix( (a11, a12, a13, a14) | 119 | let TMatrix( (a11, a12, a13, a14) |
| 92 | , (a21, a22, a23, a24) | 120 | , (a21, a22, a23, a24) |
| 93 | , (a31, a32, a33, a34) | 121 | , (a31, a32, a33, a34) |
| @@ -97,13 +125,15 @@ impl TMatrix { | @@ -97,13 +125,15 @@ impl TMatrix { | ||
| 97 | let v = Vector( a11 * x + a21 * y + a31 * z + a41 | 125 | let v = Vector( a11 * x + a21 * y + a31 * z + a41 |
| 98 | , a12 * x + a22 * y + a32 * z + a42 | 126 | , a12 * x + a22 * y + a32 * z + a42 |
| 99 | , a13 * x + a23 * y + a33 * z + a43 ); | 127 | , a13 * x + a23 * y + a33 * z + a43 ); |
| 100 | - let Fractional(wn, wd) = a14 * x + a24 * y + a34 * z + a44; | 128 | + let w = a14 * x + a24 * y + a34 * z + a44; |
| 101 | 129 | ||
| 102 | - v.mul(&Fractional(wd, wn)) | 130 | + v.mul(&w.recip()) |
| 103 | } | 131 | } |
| 104 | } | 132 | } |
| 105 | 133 | ||
| 106 | -impl Mul for TMatrix { | 134 | +impl<T> Mul for TMatrix<T> |
| 135 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 136 | + + Mul<Output = T> + Div<Output = T> + Trig + From<i32> + Copy { | ||
| 107 | type Output = Self; | 137 | type Output = Self; |
| 108 | 138 | ||
| 109 | // ATTENTION: This is not commutative, nor assoziative. | 139 | // ATTENTION: This is not commutative, nor assoziative. |
| @@ -26,90 +26,230 @@ | @@ -26,90 +26,230 @@ | ||
| 26 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | 26 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | // | 27 | // |
| 28 | use std::cmp::Ordering; | 28 | use std::cmp::Ordering; |
| 29 | -use crate::{Fractional}; | 29 | +use std::ops::Neg; |
| 30 | +use std::marker::Sized; | ||
| 31 | +use crate::{Fractional, Error}; | ||
| 32 | +use crate::continuous::Continuous; | ||
| 30 | 33 | ||
| 31 | -pub const PI :Fractional = Fractional(355, 113); // This is a really close | ||
| 32 | - // fractional approximation | ||
| 33 | - // for pi | 34 | +pub trait Trig { |
| 35 | + fn pi() -> Self; | ||
| 36 | + fn recip(self) -> Self; | ||
| 37 | + fn sqrt(self) -> Result<Self, Error> where Self: Sized; | ||
| 38 | + fn sintab() -> Vec<Self> where Self: Sized; | ||
| 39 | + fn tantab() -> Vec<Self> where Self: Sized; | ||
| 40 | + | ||
| 41 | + fn sin(d :i32) -> Self | ||
| 42 | + where Self: Sized + Neg<Output = Self> + Copy { | ||
| 43 | + match d { | ||
| 44 | + 0 ..=90 => Self::sintab()[d as usize], | ||
| 45 | + 91 ..=180 => Self::sintab()[180 - d as usize], | ||
| 46 | + 181..=270 => -Self::sintab()[d as usize - 180], | ||
| 47 | + 271..=359 => -Self::sintab()[360 - d as usize], | ||
| 48 | + _ => Self::sin(d % 360), | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + fn cos(d :i32) -> Self | ||
| 53 | + where Self: Sized + Neg<Output = Self> + Copy { | ||
| 54 | + match d { | ||
| 55 | + 0 ..=90 => Self::sintab()[90 - d as usize], | ||
| 56 | + 91 ..=180 => -Self::sintab()[90 - (180 - d as usize)], | ||
| 57 | + 181..=270 => -Self::sintab()[90 - (d as usize - 180)], | ||
| 58 | + 271..=359 => Self::sintab()[90 - (360 - d as usize)], | ||
| 59 | + _ => Self::cos(d % 360), | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + fn tan(d :i32) -> Self where Self: Sized + Copy { | ||
| 64 | + match d { | ||
| 65 | + 0 ..=179 => Self::tantab()[d as usize], | ||
| 66 | + 180..=359 => Self::tantab()[d as usize - 180], | ||
| 67 | + _ => Self::tan(d % 360), | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | +} | ||
| 34 | 71 | ||
| 35 | // Try to keep precision as high as possible while having a denominator | 72 | // Try to keep precision as high as possible while having a denominator |
| 36 | -// as small as possible. | ||
| 37 | -// The values are taken by triel and error. | 73 | +// as small as possible. The values are taken by try and error. |
| 38 | const PRECISION :i64 = 1000000; | 74 | const PRECISION :i64 = 1000000; |
| 39 | const MAX_DENOMINATOR :i64 = 7000; | 75 | const MAX_DENOMINATOR :i64 = 7000; |
| 40 | 76 | ||
| 41 | -pub fn sin(d :i32) -> Fractional { | ||
| 42 | - match d { | ||
| 43 | - 0 ..=90 => SINTAB[d as usize], | ||
| 44 | - 91 ..=180 => SINTAB[180 - d as usize], | ||
| 45 | - 181..=270 => -SINTAB[d as usize - 180], | ||
| 46 | - 271..=359 => -SINTAB[360 - d as usize], | ||
| 47 | - _ => sin(d % 360), | 77 | +// This is a really close fractional approximation for pi. |
| 78 | +impl Trig for Fractional { | ||
| 79 | + fn pi() -> Self { | ||
| 80 | + Fractional(355, 113) | ||
| 48 | } | 81 | } |
| 49 | -} | ||
| 50 | 82 | ||
| 51 | -pub fn cos(d :i32) -> Fractional { | ||
| 52 | - match d { | ||
| 53 | - 0 ..=90 => SINTAB[90 - d as usize], | ||
| 54 | - 91 ..=180 => -SINTAB[90 - (180 - d as usize)], | ||
| 55 | - 181..=270 => -SINTAB[90 - (d as usize - 180)], | ||
| 56 | - 271..=359 => SINTAB[90 - (360 - d as usize)], | ||
| 57 | - _ => cos(d % 360), | 83 | + fn recip(self) -> Self { |
| 84 | + let Fractional(n, d) = self; | ||
| 85 | + Fractional(d, n) | ||
| 58 | } | 86 | } |
| 59 | -} | ||
| 60 | 87 | ||
| 61 | -pub fn tan(d :i32) -> Fractional { | ||
| 62 | - match d { | ||
| 63 | - 0 ..=179 => TANTAB[d as usize], | ||
| 64 | - 180..=359 => TANTAB[d as usize - 180], | ||
| 65 | - _ => tan(d % 360), | 88 | + // This is a really bad approximation of sqrt for a fractional... |
| 89 | + // for (9/3) it will result 3 which if way to far from the truth, | ||
| 90 | + // which is ~1.7320508075 | ||
| 91 | + // BUT we can use this value as starting guess for creating a | ||
| 92 | + // continous fraction for the sqrt... and create a much better | ||
| 93 | + // fractional representation of the sqrt. | ||
| 94 | + // So, if inner converges, but is not a perfect square (does not | ||
| 95 | + // end up in an Ordering::Equal - which is the l > h case) | ||
| 96 | + // we use the l - 1 as starting guess for sqrt_cfrac. | ||
| 97 | + // taken from: | ||
| 98 | + // https://www.geeksforgeeks.org/square-root-of-an-integer/ | ||
| 99 | + fn sqrt(self) -> Result<Self, Error> { | ||
| 100 | + // find the sqrt of x in O(log x/2). | ||
| 101 | + // This stops if a perfect sqare was found. Else it passes | ||
| 102 | + // the found value as starting guess to the continous fraction | ||
| 103 | + // sqrt function. | ||
| 104 | + fn floor_sqrt(x :i64) -> Fractional { | ||
| 105 | + fn inner(l :i64, h :i64, x :i64) -> Fractional { | ||
| 106 | + if l > h { | ||
| 107 | + (&Continuous::sqrt(x, l - 1)).into() | ||
| 108 | + } else { | ||
| 109 | + let m = (l + h) / 2; | ||
| 110 | + match x.cmp(&(m * m)) { | ||
| 111 | + Ordering::Equal => m.into(), | ||
| 112 | + Ordering::Less => inner(l, m - 1, x), | ||
| 113 | + Ordering::Greater => inner(m + 1, h, x), | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + match x { | ||
| 119 | + 0 => 0.into(), | ||
| 120 | + 1 => 1.into(), | ||
| 121 | + _ => inner(1, x / 2, x), | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + let Fractional(n, d) = self; | ||
| 126 | + | ||
| 127 | + let n = match n.cmp(&0) { | ||
| 128 | + Ordering::Equal => 0.into(), | ||
| 129 | + Ordering::Less => return Err("sqrt on negative undefined"), | ||
| 130 | + Ordering::Greater => floor_sqrt(n), | ||
| 131 | + }; | ||
| 132 | + | ||
| 133 | + let d = match d.cmp(&0) { | ||
| 134 | + Ordering::Equal => 0.into(), | ||
| 135 | + Ordering::Less => return Err("sqrt on negative undefined"), | ||
| 136 | + Ordering::Greater => floor_sqrt(d), | ||
| 137 | + }; | ||
| 138 | + | ||
| 139 | + Ok(n / d) | ||
| 66 | } | 140 | } |
| 67 | -} | ||
| 68 | 141 | ||
| 69 | -// hold sin Fractionals from 0 to 89 ... | ||
| 70 | -// luckily with a bit of index tweeking this can also be used for | ||
| 71 | -// cosine values. | ||
| 72 | -lazy_static::lazy_static! { | ||
| 73 | - static ref SINTAB :Vec<Fractional> = | ||
| 74 | - (0..=90).map(|x| _sin(x)).collect(); | ||
| 75 | -} | 142 | + fn sintab() -> Vec<Self> { |
| 143 | + // hold sin Fractionals from 0 to 89 ... | ||
| 144 | + // luckily with a bit of index tweeking this can also be used for | ||
| 145 | + // cosine values. | ||
| 146 | + lazy_static::lazy_static! { | ||
| 147 | + static ref SINTAB :Vec<Fractional> = | ||
| 148 | + (0..=90).map(|x| _sin(x)).collect(); | ||
| 149 | + } | ||
| 76 | 150 | ||
| 77 | -// This table exists only because the sin(α) / cos(α) method | ||
| 78 | -// yields very large unreducable denominators in a lot of cases. | ||
| 79 | -lazy_static::lazy_static! { | ||
| 80 | - static ref TANTAB :Vec<Fractional> = | ||
| 81 | - (0..180).map(|x| _tan(x)).collect(); | ||
| 82 | -} | 151 | + // fractional sin from f64 sin. (From 0° to 90°) |
| 152 | + fn _sin(d: u32) -> Fractional { | ||
| 153 | + match d { | ||
| 154 | + 0 => Fractional(0, 1), | ||
| 155 | + 90 => Fractional(1, 1), | ||
| 156 | + _ => reduce(d, PRECISION, &f64::sin), | ||
| 157 | + } | ||
| 158 | + } | ||
| 83 | 159 | ||
| 84 | -// fractional sin from f64 sin. (From 0° to 90°) | ||
| 85 | -fn _sin(d: u32) -> Fractional { | ||
| 86 | - match d { | ||
| 87 | - 0 => Fractional(0, 1), | ||
| 88 | - 90 => Fractional(1, 1), | ||
| 89 | - _ => reduce(d, PRECISION, &f64::sin), | 160 | + SINTAB.to_vec() |
| 161 | + } | ||
| 162 | + | ||
| 163 | + fn tantab() -> Vec<Self> { | ||
| 164 | + // This table exists only because the sin(α) / cos(α) method | ||
| 165 | + // yields very large unreducable denominators in a lot of cases. | ||
| 166 | + lazy_static::lazy_static! { | ||
| 167 | + static ref TANTAB :Vec<Fractional> = | ||
| 168 | + (0..180).map(|x| _tan(x)).collect(); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + // fractional tan from f64 tan. (From 0° to 179°) | ||
| 172 | + fn _tan(d: u32) -> Fractional { | ||
| 173 | + match d { | ||
| 174 | + 0 => Fractional(0, 1), | ||
| 175 | + 45 => Fractional(1, 1), | ||
| 176 | + 90 => Fractional(1, 0), // although they are both inf and -inf. | ||
| 177 | + 135 => -Fractional(1, 1), | ||
| 178 | + _ => reduce(d, PRECISION, &f64::tan), | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + TANTAB.to_vec() | ||
| 90 | } | 183 | } |
| 91 | } | 184 | } |
| 92 | 185 | ||
| 93 | -// fractional tan from f64 tan. (From 0° to 179°) | ||
| 94 | -fn _tan(d: u32) -> Fractional { | ||
| 95 | - match d { | ||
| 96 | - 0 => Fractional(0, 1), | ||
| 97 | - 45 => Fractional(1, 1), | ||
| 98 | - 90 => Fractional(1, 0), // although they are both inf and -inf. | ||
| 99 | - 135 => -Fractional(1, 1), | ||
| 100 | - _ => reduce(d, PRECISION, &f64::tan), | 186 | +impl Trig for f64 { |
| 187 | + fn pi() -> Self { | ||
| 188 | + std::f64::consts::PI | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + fn recip(self) -> Self { | ||
| 192 | + self.recip() | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + fn sqrt(self) -> Result<Self, Error> { | ||
| 196 | + let x = self.sqrt(); | ||
| 197 | + match x.is_nan() { | ||
| 198 | + true => Err("sqrt on negative undefined"), | ||
| 199 | + false => Ok(x), | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + fn sintab() -> Vec<Self> { | ||
| 204 | + lazy_static::lazy_static! { | ||
| 205 | + static ref SINTAB :Vec<f64> = | ||
| 206 | + (0..=90).map(|x| _sin(x)).collect(); | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + // f64 sin. (From 0° to 90°) | ||
| 210 | + fn _sin(d: u32) -> f64 { | ||
| 211 | + match d { | ||
| 212 | + 0 => 0.0, | ||
| 213 | + 90 => 1.0, | ||
| 214 | + _ => (d as f64).to_radians().sin(), | ||
| 215 | + } | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + SINTAB.to_vec() | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + fn tantab() -> Vec<Self> { | ||
| 222 | + // This table exists only because the sin(α) / cos(α) method | ||
| 223 | + // yields very large unreducable denominators in a lot of cases. | ||
| 224 | + lazy_static::lazy_static! { | ||
| 225 | + static ref TANTAB :Vec<f64> = | ||
| 226 | + (0..180).map(|x| _tan(x)).collect(); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + // fractional tan from f64 tan. (From 0° to 179°) | ||
| 230 | + fn _tan(d: u32) -> f64 { | ||
| 231 | + match d { | ||
| 232 | + 0 => 0.0, | ||
| 233 | + 45 => 1.0, | ||
| 234 | + 90 => std::f64::INFINITY, | ||
| 235 | + 135 => -1.0, | ||
| 236 | + _ => (d as f64).to_radians().tan(), | ||
| 237 | + } | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + TANTAB.to_vec() | ||
| 101 | } | 241 | } |
| 102 | } | 242 | } |
| 103 | 243 | ||
| 104 | -// search for a fraction with a denominator less than 10000 that | ||
| 105 | -// provides the minimal precision criteria. | 244 | +// search for a fraction with a denominator less than MAX_DENOMINATOR that |
| 245 | +// provides the minimal PRECISION criteria. | ||
| 106 | // !! With f = &f64::tan and d close to the inf boundarys of tan | 246 | // !! With f = &f64::tan and d close to the inf boundarys of tan |
| 107 | // we get very large numerators because the numerator becomes a | 247 | // we get very large numerators because the numerator becomes a |
| 108 | // multiple of the denominator. | 248 | // multiple of the denominator. |
| 109 | fn reduce(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional { | 249 | fn reduce(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional { |
| 110 | // This is undefined behaviour for very large f64, but our f64 | 250 | // This is undefined behaviour for very large f64, but our f64 |
| 111 | - // is always between 0.0 and 10000000.0 which should be fine. | ||
| 112 | - let s = (f(f64::to_radians(d as f64)) * p as f64).round() as i64; | 251 | + // is always between 0.0 and 1000000.0 which should be fine. |
| 252 | + let s = (f((d as f64).to_radians()) * p as f64).round() as i64; | ||
| 113 | let Fractional(n, dn) = Fractional(s, p).reduce(); | 253 | let Fractional(n, dn) = Fractional(s, p).reduce(); |
| 114 | match dn.abs().cmp(&MAX_DENOMINATOR) { | 254 | match dn.abs().cmp(&MAX_DENOMINATOR) { |
| 115 | Ordering::Less => Fractional(n, dn), | 255 | Ordering::Less => Fractional(n, dn), |
| @@ -18,29 +18,32 @@ | @@ -18,29 +18,32 @@ | ||
| 18 | // You should have received a copy of the GNU General Public License | 18 | // You should have received a copy of the GNU General Public License |
| 19 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 20 | // | 20 | // |
| 21 | -use std::ops::{Add, Sub, Neg, Mul}; | 21 | +use std::ops::{Add, Sub, Neg, Mul, Div}; |
| 22 | use std::fmt; | 22 | use std::fmt; |
| 23 | -use crate::{Fractional}; | 23 | +use crate::trigonometry::Trig; |
| 24 | 24 | ||
| 25 | #[derive(Debug, Eq, Clone, Copy)] | 25 | #[derive(Debug, Eq, Clone, Copy)] |
| 26 | -pub struct Vector(pub Fractional, pub Fractional, pub Fractional); | 26 | +pub struct Vector<T>(pub T, pub T, pub T) |
| 27 | + where T: Add + Sub + Neg + Mul + Div + Trig + Copy; | ||
| 27 | 28 | ||
| 28 | -impl Vector { | ||
| 29 | - pub fn x(self) -> Fractional { self.0 } | ||
| 30 | - pub fn y(self) -> Fractional { self.1 } | ||
| 31 | - pub fn z(self) -> Fractional { self.2 } | 29 | +impl<T> Vector<T> |
| 30 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 31 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
| 32 | + pub fn x(self) -> T { self.0 } | ||
| 33 | + pub fn y(self) -> T { self.1 } | ||
| 34 | + pub fn z(self) -> T { self.2 } | ||
| 32 | 35 | ||
| 33 | - pub fn abs(self) -> Fractional { | 36 | + pub fn abs(self) -> T { |
| 34 | let Vector(x, y, z) = self; | 37 | let Vector(x, y, z) = self; |
| 35 | (x * x + y * y + z * z).sqrt().unwrap() | 38 | (x * x + y * y + z * z).sqrt().unwrap() |
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | - pub fn mul(self, s :&Fractional) -> Self { | 41 | + pub fn mul(self, s :&T) -> Self { |
| 39 | let Vector(x, y, z) = self; | 42 | let Vector(x, y, z) = self; |
| 40 | Vector(x * *s, y * *s, z * *s) | 43 | Vector(x * *s, y * *s, z * *s) |
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | - pub fn dot(self, other :Self) -> Fractional { | 46 | + pub fn dot(self, other :Self) -> T { |
| 44 | let Vector(x1, y1, z1) = self; | 47 | let Vector(x1, y1, z1) = self; |
| 45 | let Vector(x2, y2, z2) = other; | 48 | let Vector(x2, y2, z2) = other; |
| 46 | 49 | ||
| @@ -48,24 +51,25 @@ impl Vector { | @@ -48,24 +51,25 @@ impl Vector { | ||
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | pub fn norm(self) -> Self { | 53 | pub fn norm(self) -> Self { |
| 51 | - let Fractional(n, d) = self.abs(); | ||
| 52 | // TODO This can result in 0 or inf Vectors… | 54 | // TODO This can result in 0 or inf Vectors… |
| 53 | // Maybe we need to handle zero and inf abs here… | 55 | // Maybe we need to handle zero and inf abs here… |
| 54 | - self.mul(&Fractional(d, n)) | 56 | + self.mul(&self.abs()) |
| 55 | } | 57 | } |
| 56 | 58 | ||
| 57 | - pub fn distance(self, other :Self) -> Fractional { | 59 | + pub fn distance(self, other :Self) -> T { |
| 58 | (self - other).abs() | 60 | (self - other).abs() |
| 59 | } | 61 | } |
| 60 | } | 62 | } |
| 61 | 63 | ||
| 62 | -impl fmt::Display for Vector { | 64 | +impl<T> fmt::Display for Vector<T> |
| 65 | +where T: Add + Sub + Neg + Mul + Div + Trig + fmt::Display + Copy { | ||
| 63 | fn fmt(&self, f :&mut fmt::Formatter<'_>) -> fmt::Result { | 66 | fn fmt(&self, f :&mut fmt::Formatter<'_>) -> fmt::Result { |
| 64 | write!(f, "({}, {}, {})", self.0, self.1, self.2) | 67 | write!(f, "({}, {}, {})", self.0, self.1, self.2) |
| 65 | } | 68 | } |
| 66 | } | 69 | } |
| 67 | 70 | ||
| 68 | -impl PartialEq for Vector { | 71 | +impl<T> PartialEq for Vector<T> |
| 72 | +where T: Add + Sub + Neg + Mul + Div + Trig + PartialEq + Copy { | ||
| 69 | fn eq(&self, other :&Self) -> bool { | 73 | fn eq(&self, other :&Self) -> bool { |
| 70 | let Vector(x1, y1, z1) = self; | 74 | let Vector(x1, y1, z1) = self; |
| 71 | let Vector(x2, y2, z2) = other; | 75 | let Vector(x2, y2, z2) = other; |
| @@ -73,7 +77,9 @@ impl PartialEq for Vector { | @@ -73,7 +77,9 @@ impl PartialEq for Vector { | ||
| 73 | } | 77 | } |
| 74 | } | 78 | } |
| 75 | 79 | ||
| 76 | -impl Add for Vector { | 80 | +impl<T> Add for Vector<T> |
| 81 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 82 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
| 77 | type Output = Self; | 83 | type Output = Self; |
| 78 | 84 | ||
| 79 | fn add(self, other :Self) -> Self { | 85 | fn add(self, other :Self) -> Self { |
| @@ -83,7 +89,9 @@ impl Add for Vector { | @@ -83,7 +89,9 @@ impl Add for Vector { | ||
| 83 | } | 89 | } |
| 84 | } | 90 | } |
| 85 | 91 | ||
| 86 | -impl Sub for Vector { | 92 | +impl<T> Sub for Vector<T> |
| 93 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 94 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
| 87 | type Output = Self; | 95 | type Output = Self; |
| 88 | 96 | ||
| 89 | fn sub(self, other :Self) -> Self { | 97 | fn sub(self, other :Self) -> Self { |
| @@ -91,7 +99,9 @@ impl Sub for Vector { | @@ -91,7 +99,9 @@ impl Sub for Vector { | ||
| 91 | } | 99 | } |
| 92 | } | 100 | } |
| 93 | 101 | ||
| 94 | -impl Neg for Vector { | 102 | +impl<T> Neg for Vector<T> |
| 103 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 104 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
| 95 | type Output = Self; | 105 | type Output = Self; |
| 96 | 106 | ||
| 97 | fn neg(self) -> Self { | 107 | fn neg(self) -> Self { |
| @@ -100,7 +110,9 @@ impl Neg for Vector { | @@ -100,7 +110,9 @@ impl Neg for Vector { | ||
| 100 | } | 110 | } |
| 101 | } | 111 | } |
| 102 | 112 | ||
| 103 | -impl Mul for Vector { | 113 | +impl<T> Mul for Vector<T> |
| 114 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 115 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
| 104 | type Output = Self; | 116 | type Output = Self; |
| 105 | 117 | ||
| 106 | fn mul(self, other :Self) -> Self { | 118 | fn mul(self, other :Self) -> Self { |
Please
register
or
login
to post a comment