diff --git a/fractional/src/continuous.rs b/fractional/src/continuous.rs index b720679..673410a 100644 --- a/fractional/src/continuous.rs +++ b/fractional/src/continuous.rs @@ -39,20 +39,55 @@ impl Continuous { v[0] = an; // The convergence criteria „an_1 == 2 * a0“ is not good for // very small x thus I decided to break the iteration at constant - // time. Which is the 10 below. + // time. Which is the 5 below. if v.len() > 1 { inner(&mut v[1..], x, a0, mn_1, dn_1, an_1); } } - let mut v :Vec = vec!(0; 10); + let mut v :Vec = vec!(0; 5); inner(&mut v, x, a0, 0, 1, a0); Continuous(v) } - pub fn into_prec(&self, prec :usize) -> Fractional { + // general continous fraction form of a fractional... + pub fn from_prec(f :&Fractional, prec :Option) -> Self { + fn inner(v :&mut Vec, f :Fractional, prec :Option) { + let mut process = |prec :Option| { + let Fractional(n, d) = f; + let a = n / d; + let Fractional(_n, _d) = f.noreduce_sub(a.into()); + + v.push(a); + match _n { + 1 => v.push(_d), + 0 => {}, + _ => inner(v, Fractional(_d, _n), prec), + } + }; + + match prec { + Some(0) => {}, + None => process(None), + Some(p) => process(Some(p - 1)), + } + } + + let mut v = match prec { + None => Vec::with_capacity(100), + Some(p) => Vec::with_capacity(p + 1), + }; + + inner(&mut v, *f, prec); + Continuous(v) + } + + pub fn into_prec(&self, prec :Option) -> Fractional { let Continuous(c) = self; - let p = if prec <= c.len() { prec } else { c.len() }; + let p = match prec { + Some(p) => if p <= c.len() { p } else { c.len() }, + None => c.len(), + }; let to_frac = |acc :Fractional, x :&i64| { let Fractional(an, ad) = acc.noreduce_add((*x).into()); @@ -68,33 +103,13 @@ impl Continuous { } impl From<&Fractional> for Continuous { - // general continous fraction form of a fractional... fn from(x :&Fractional) -> Self { - fn inner(mut v :Vec, f :Fractional) -> Vec { - let Fractional(n, d) = f; - let a = n / d; - let Fractional(_n, _d) = f.noreduce_sub(a.into()); - - v.push(a); - match _n { - 1 => { v.push(_d); v }, - 0 => v, - _ => inner(v, Fractional(_d, _n)), - } - } - - Continuous(inner(Vec::new(), *x)) + Self::from_prec(x, None) } } impl Into for &Continuous { fn into(self) -> Fractional { - let Continuous(c) = self; - let Fractional(n, d) = c.iter().rev().fold( Fractional(0, 1) - , |acc, x| { - let Fractional(an, ad) = acc + (*x).into(); - Fractional(ad, an) - }); - Fractional(d, n) + (&self).into_prec(None) } } diff --git a/fractional/src/easel.rs b/fractional/src/easel.rs index f78b855..6a2ea27 100644 --- a/fractional/src/easel.rs +++ b/fractional/src/easel.rs @@ -1,5 +1,7 @@ // // This is an abstraction over a drawing environment. +// Future note: z-Buffer is described here: +// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes // // Georg Hopp // diff --git a/fractional/src/fractional.rs b/fractional/src/fractional.rs index 42a0e38..0f8376d 100644 --- a/fractional/src/fractional.rs +++ b/fractional/src/fractional.rs @@ -69,10 +69,13 @@ impl Fractional { Self(1, _n / _d) } } else { - //Self(n / hcf(n, d), d / hcf(n, d)) - let regular_reduced = self; - let cont :Continuous = (®ular_reduced).into(); - cont.into_prec(5) + // Self(n / hcf(n, d), d / hcf(n, d)) + // The above reduces prcisely but results in very large numerator + // or denominator occasionally. The below is less precise but + // keeps the numbers small… the bad point is, that it is not very + // fast. + let cont = Continuous::from_prec(&self, Some(5)); + (&cont).into() } } @@ -171,10 +174,7 @@ impl Add for Fractional { type Output = Self; fn add(self, other: Self) -> Self { - let Fractional(n1, d1) = self; - let Fractional(n2, d2) = other; - let n = n1 * (self.gcd(other) / d1) + n2 * (self.gcd(other) / d2); - Self(n, self.gcd(other)).reduce() + self.noreduce_add(other).reduce() } } @@ -191,7 +191,7 @@ impl Neg for Fractional { fn neg(self) -> Self { let Fractional(n, d) = self; - Self(-n, d).reduce() + Self(-n, d) } } diff --git a/fractional/src/trigonometry.rs b/fractional/src/trigonometry.rs index 8b41d3a..cd010b1 100644 --- a/fractional/src/trigonometry.rs +++ b/fractional/src/trigonometry.rs @@ -171,7 +171,7 @@ impl Trig for Fractional { match d { 0 => Fractional(0, 1), 90 => Fractional(1, 1), - _ => reduce(d, PRECISION, &f64::sin), + _ => generate(d, PRECISION, &f64::sin), } } @@ -193,7 +193,7 @@ impl Trig for Fractional { 45 => Fractional(1, 1), 90 => Fractional(1, 0), // although they are both inf and -inf. 135 => -Fractional(1, 1), - _ => reduce(d, PRECISION, &f64::tan), + _ => generate(d, PRECISION, &f64::tan), } } @@ -201,6 +201,22 @@ impl Trig for Fractional { } } +// search for a fraction with a denominator less than MAX_DENOMINATOR that +// provides the minimal PRECISION criteria. +// !! With f = &f64::tan and d close to the inf boundarys of tan +// we get very large numerators because the numerator becomes a +// multiple of the denominator. +fn generate(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional { + // This is undefined behaviour for very large f64, but our f64 + // is always between 0.0 and 1000000.0 which should be fine. + let s = (f((d as f64).to_radians()) * p as f64).round() as i64; + let Fractional(n, dn) = Fractional(s, p).reduce(); + match dn.abs().cmp(&MAX_DENOMINATOR) { + Ordering::Less => Fractional(n, dn), + _ => generate(d, p + 1, f), + } +} + impl Trig for f64 { fn pi() -> Self { std::f64::consts::PI @@ -262,19 +278,3 @@ impl Trig for f64 { TANTAB.to_vec() } } - -// search for a fraction with a denominator less than MAX_DENOMINATOR that -// provides the minimal PRECISION criteria. -// !! With f = &f64::tan and d close to the inf boundarys of tan -// we get very large numerators because the numerator becomes a -// multiple of the denominator. -fn reduce(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional { - // This is undefined behaviour for very large f64, but our f64 - // is always between 0.0 and 1000000.0 which should be fine. - let s = (f((d as f64).to_radians()) * p as f64).round() as i64; - let Fractional(n, dn) = Fractional(s, p).reduce(); - match dn.abs().cmp(&MAX_DENOMINATOR) { - Ordering::Less => Fractional(n, dn), - _ => reduce(d, p + 1, f), - } -}