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