// // Test our fractional crate / module... // // Georg Hopp // // Copyright © 2019 Georg Hopp // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // use std::convert::{TryFrom, TryInto, Into}; use std::f64::consts::PI as FPI; use std::fmt::Display; use std::num::TryFromIntError; use std::ops::{Add,Sub,Neg,Mul,Div}; use std::sync::mpsc; use std::thread; use std::time::{Duration, Instant}; use fractional::continuous::Continuous; use fractional::easel::{ Coordinate, Coordinates, Drawable, Line, Polyline , Polygon, Rectangle}; use fractional::fractional::{Fractional, from_vector}; use fractional::trigonometry::Trig; use fractional::vector::{Vector}; use fractional::transform::{TMatrix, translate, rotate_x, rotate_y, rotate_z, rotate_v}; use fractional::xcb::XcbEasel; use fractional::easel::Canvas; fn mean(v: &Vec) -> Result { let r = v.iter().fold(0, |acc, x| acc + x); let l = i64::try_from(v.len())?; Ok(Fractional(r, l)) } fn common_fractional() { let a = vec![3, 6, 1, 9]; let b = from_vector(&a); let c = mean(&a).unwrap(); // This might fail if the len of the // vector (usize) does not fit into i32. let cr :f64 = c.try_into().unwrap(); println!(" [i32] : {:?}", a); println!(" [Fractional] : {:?}", b); println!(" mean of [i32] : {}" , c); println!(" as f64 : {}" , cr); println!(" again as f64 : {}" , TryInto::::try_into(c).unwrap()); } fn continuous() { let d = Fractional(45, 16); let e = Fractional(16, 45); let dc :Continuous = (&d).into(); let ec :Continuous = (&e).into(); println!("cont frac of d : {} => {:?}", d, dc); println!("cont frac of e : {} => {:?}", e, ec); println!(" reverted dc : {:?} {}", dc, Into::::into(&dc)); println!(" reverted ec : {:?} {}", ec, Into::::into(&ec)); } fn sqrt() { let f = Fractional(-9, 4); let fr :f64 = f.try_into().unwrap(); let sq = f.sqrt(); let _sq = fr.sqrt(); println!("{:>14} : {:?} / {}", format!("sqrt {}", f), sq, _sq); for f in [ Fractional(9, 4) , Fractional(45, 16) , Fractional(16, 45) , Fractional(9, 3) ].iter() { let fr :f64 = (*f).try_into().unwrap(); let sq = f.sqrt().unwrap(); let sqr :f64 = sq.try_into().unwrap(); let _sqr = fr.sqrt(); println!("{:>14} : {} {} / {}", format!("sqrt {}", f), sq, sqr, _sqr); } } fn pi() { let pi = Fractional::pi(); let pir :f64 = pi.try_into().unwrap(); let pit :(i32, i32) = pi.try_into().unwrap(); let pi2r :f64 = (pi * pi).try_into().unwrap(); println!(" Rust π : {}" , FPI); println!(" π : {} {}" , pi, pir); println!(" π as tuple : {:?}" , pit); println!(" Rust π² : {}" , FPI * FPI); println!(" π² : {} {}" , pi * pi, pi2r); } fn _sin() { for d in [ 0, 30, 45, 90, 135, 180, 225, 270, 315 , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { let s = Fractional::sin(*d as i32); let sr :f64 = s.try_into().unwrap(); let _s = f64::sin(*d as f64 * FPI / 180.0); println!("{:>14} : {} {} / {}", format!("sin {}", d), s, sr, _s); } } fn _tan() { for d in [ 0, 30, 45, 90, 135, 180, 225, 270, 315 , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { let t = Fractional::tan(*d as i32); let tr :f64 = t.try_into().unwrap(); let _t = f64::tan(*d as f64 * FPI / 180.0); println!("{:>14} : {} {} / {}", format!("tan {}", d), t, tr, _t); } } fn _cos() { for d in [ 0, 30, 45, 90, 135, 180, 225, 270, 315 , 9, 17, 31, 73, 89, 123, 213, 312, 876 ].iter() { let c = Fractional::cos(*d as i32); let cr :f64 = c.try_into().unwrap(); let _c = f64::cos(*d as f64 * FPI / 180.0); println!("{:>14} : {} {} / {}", format!("cos {}", d), c, cr, _c); } } fn _vector1() { let v1 = Vector(1.into(), 2.into(), 3.into()); let v2 = Vector(2.into(), 2.into(), 3.into()); let s :Fractional = 3.into(); _vector(v1, v2, s); } fn _vector2() { let v1 = Vector(1.0, 2.0, 3.0); let v2 = Vector(2.0, 2.0, 3.0); let s = 3.0; _vector(v1, v2, s); } fn _vector(v1 :Vector, v2 :Vector, s :T) where T: Add + Sub + Neg + Mul + Div + Trig + Copy + Display { println!("{:>14} : {}", "Vector v1", v1); println!("{:>14} : {}", "Vector v2", v2); println!("{:>14} : {}", "abs v1", v1.abs()); println!("{:>14} : {}", "-v1", -v1); println!("{:>14} : {}", "v1 + v1", v1 + v1); println!("{:>14} : {}", "v1 - v1", v1 - v1); println!("{:>14} : {}", "v2 - v1", v2 - v1); println!("{:>14} : {}", format!("v1 * {}", s), v1.mul(&s)); println!("{:>14} : {}", "norm v1", v1.norm()); println!("{:>14} : {}", "abs norm v1", v1.norm().abs()); println!("{:>14} : {}", "abs v1", v1.abs()); println!("{:>14} : {}", "norm * abs", v1.norm().mul(&v1.abs())); println!("{:>14} : {}", "distance v1 v2", v1.distance(v2)); println!("{:>14} : {}", "distance v2 v1", v2.distance(v1)); println!("{:>14} : {}", "v1 dot v2", v1.dot(v2)); println!("{:>14} : {}", "v2 dot v1", v2.dot(v1)); println!("{:>14} : {}", "v1 * v2", v1 * v2); println!("{:>14} : {}", "v2 * v1", v2 * v1); } fn _transform1() { let v = Vector(Fractional(1,1), Fractional(1,1), Fractional(1,1)); let v1 = Vector(Fractional(1,1), Fractional(2,1), Fractional(3,1)); let v2 = Vector(Fractional(1,1), Fractional(1,1), Fractional(0,1)); let v3 = Vector(Fractional(1,1), Fractional(0,1), Fractional(1,1)); _transform(v, v1, v2, v3); } fn _transform2() { let v = Vector(1.0, 1.0, 1.0); let v1 = Vector(1.0, 2.0, 3.0); let v2 = Vector(1.0, 1.0, 0.0); let v3 = Vector(1.0, 0.0, 1.0); _transform(v, v1, v2, v3); } fn _transform(v :Vector, v1 :Vector, v2 :Vector, v3 :Vector) where T: Add + Sub + Neg + Mul + Div + Trig + From + Copy + Display { println!("{:>14} : {}", "Vector v1", v1); println!("{:>14} : {}", "translate v1", translate(v).apply(&v1)); println!(); fn _rot( o :&str , n :&str , v :&Vector , fs :&[&dyn Fn(i32) -> TMatrix] ) where T: Add + Sub + Neg + Mul + Div + Trig + From + Copy + Display { for d in [ 30, 45, 60, 90, 120, 135, 150, 180 , 210, 225, 240, 270, 300, 315, 330 ].iter() { let mi = fs.iter().map(|f| f(*d as i32)); println!( "{:>14} : {}" , format!("{} {} {}", o, d, n) , TMatrix::combine(mi).apply(v) ); } } println!("{:>14} : {}", "Vector v2", v2); _rot("rot_x", "v2", &v2, &[&rotate_x]); println!(); _rot("rot_y", "v2", &v2, &[&rotate_y]); println!(); _rot("rot_xy", "v2", &v2, &[&rotate_x, &rotate_y]); println!(); println!("{:>14} : {}", "Vector v3", v3); _rot("rot_z", "v3", &v3, &[&rotate_z]); println!(); for d in [ 30, 45, 60, 90, 120, 135, 150, 180 , 210, 225, 240, 270, 300, 315, 330 ].iter() { println!( "{:>14} : {}" , format!("rot_v {} v2", d) , rotate_v(&v, *d as i32).apply(&v2)); } } fn _line() { let a = (Coordinate(0, 1), Coordinate(6, 4)); let b = (Coordinate(0, 4), Coordinate(6, 1)); let c = (Coordinate(1, 0), Coordinate(6, 8)); let d = (Coordinate(1, 8), Coordinate(6, 0)); for i in [a, b, c, d].iter() { println!("{:>14} : {}", Line(i.0, i.1), Line(i.0, i.1).plot()); println!("{:>14} : {}", Line(i.1, i.0), Line(i.1, i.0).plot()); } println!(); let r = Rectangle(Coordinate(1, 1), Coordinate(10, 5)); println!("{:>14} : {}", r, r.plot()); println!(); let pl = Polyline( Coordinates(vec!(a.0, a.1, b.0, b.1, c.0, c.1, d.0, d.1))); println!("{:>14} : {}", pl, pl.plot()); println!(); let pg = Polygon( Coordinates(vec!( Coordinate( 0, -10) , Coordinate( 10, 10) , Coordinate(-10, 10) ))); println!("{:>14} : {}", pg, pg.plot()); let i = Vector(Fractional( 0,1), Fractional(-30,1), Fractional(0,1)); let j = Vector(Fractional( 30,1), Fractional( 30,1), Fractional(0,1)); let k = Vector(Fractional(-30,1), Fractional( 30,1), Fractional(0,1)); let rot :TMatrix = rotate_z(20); let Vector(ix, iy, _) = rot.apply(&i); let Vector(jx, jy, _) = rot.apply(&j); let Vector(kx, ky, _) = rot.apply(&k); fn to_i32(x :Fractional) -> i32 { let Fractional(n, d) = x; (n / d + if (n % d).abs() < (n / 2).abs() { 0 } else { 1 }) as i32 } println!(); let pg = Polygon( Coordinates(vec!( Coordinate(to_i32(ix) + 100, to_i32(iy) + 100) , Coordinate(to_i32(jx) + 100, to_i32(jy) + 100) , Coordinate(to_i32(kx) + 100, to_i32(ky) + 100) ))); println!("{:>14} : {}", pg, pg.plot()); let i = Vector( 0.0, -30.0, 0.0); let j = Vector( 30.0, 30.0, 0.0); let k = Vector(-30.0, 30.0, 0.0); let rot :TMatrix = rotate_z(20); let Vector(ix, iy, _) = rot.apply(&i); let Vector(jx, jy, _) = rot.apply(&j); let Vector(kx, ky, _) = rot.apply(&k); fn to_i32_2(x :f64) -> i32 { x.round() as i32 } println!(); let pg = Polygon( Coordinates(vec!( Coordinate(to_i32_2(ix) + 100, to_i32_2(iy) + 100) , Coordinate(to_i32_2(jx) + 100, to_i32_2(jy) + 100) , Coordinate(to_i32_2(kx) + 100, to_i32_2(ky) + 100) ))); println!("{:>14} : {}", pg, pg.plot()); } fn main() { common_fractional(); println!(); continuous(); println!(); sqrt(); println!(); pi(); println!(); _sin(); println!(); _cos(); println!(); _tan(); println!(); _vector1(); println!(); _vector2(); println!(); _transform1(); println!(); _transform2(); println!(); _line(); let xcb = XcbEasel::new().unwrap(); let mut canvas = xcb.canvas(151, 151).unwrap(); canvas.set_title("Something..."); canvas.init_events(); let (tx, rx) = mpsc::channel(); // TODO I ran into overflow issues using fractionals so for now // use floating point values. // https://rechneronline.de/pi/tetrahedron.php // yi = a / 12 * √6 let yi = 60.0 / 12.0 * 6.0.sqrt().unwrap(); // yc = a / 4 * √6 let yc = 60.0 / 4.0 * 6.0.sqrt().unwrap(); // zi = √3 / 6 * a let zi = 3.0.sqrt().unwrap() / 6.0 * 60.0; // zc = √3 / 3 * a let zc = 3.0.sqrt().unwrap() / 3.0 * 60.0; let i = Vector( 0.0, yc, 0.0); let j = Vector(-30.0, -yi, -zi); let k = Vector( 30.0, -yi, -zi); let l = Vector( 0.0, -yi, zc); let cf1 = Vector(-30.0, 30.0, -30.0); let cf2 = Vector(-30.0, -30.0, -30.0); let cf3 = Vector( 30.0, -30.0, -30.0); let cf4 = Vector( 30.0, 30.0, -30.0); let cb1 = Vector(-30.0, 30.0, 30.0); let cb2 = Vector(-30.0, -30.0, 30.0); let cb3 = Vector( 30.0, -30.0, 30.0); let cb4 = Vector( 30.0, 30.0, 30.0); fn to_screen(c: &dyn Canvas, v :Vector) -> Coordinate { // TODO .. these are in fact constants that should be stored once // somewhere… Rust doesn't let me make this static here. // In a way they are part of the canvas and they should change as the // canvas is changing… let fovx :f64 = 1.0 / ::tan(50); let fovy :f64 = c.width() as f64 / c.height() as f64 * fovx; let xs = ( v.x() / v.z() * fovx * c.width() as f64 ).round() as i32 + c.width() as i32 / 2; let ys = ( -v.y() / v.z() * fovy * c.height() as f64 ).round() as i32 + c.height() as i32 / 2; Coordinate(xs, ys) } canvas.start_events(tx); let start = Instant::now(); let step = Duration::from_millis(25); let mut last = Instant::now(); thread::spawn(move || { //const DWC :f64 = 10.0; loop { let deg = ((start.elapsed() / 20).as_millis() % 360) as i32; let rot1 :TMatrix = rotate_z(deg) * rotate_x(-deg*2) * translate(Vector(0.0, 0.0, 150.0)); let rot2 :TMatrix = rotate_z(deg) * rotate_y(-deg*2) * translate(Vector(0.0, 0.0, 150.0)); let ia = rot1.apply(&i); let ja = rot1.apply(&j); let ka = rot1.apply(&k); let la = rot1.apply(&l); let cf1a = rot2.apply(&cf1); let cf2a = rot2.apply(&cf2); let cf3a = rot2.apply(&cf3); let cf4a = rot2.apply(&cf4); let cb1a = rot2.apply(&cb1); let cb2a = rot2.apply(&cb2); let cb3a = rot2.apply(&cb3); let cb4a = rot2.apply(&cb4); let pg1 = Polygon(Coordinates(vec!( to_screen(&canvas, ja) , to_screen(&canvas, ka) , to_screen(&canvas, la) ))); let pg2 = Polygon(Coordinates(vec!( to_screen(&canvas, ja) , to_screen(&canvas, ia) , to_screen(&canvas, ka) ))); let pg3 = Polygon(Coordinates(vec!( to_screen(&canvas, la) , to_screen(&canvas, ia) , to_screen(&canvas, ja) ))); let pg4 = Polygon(Coordinates(vec!( to_screen(&canvas, ka) , to_screen(&canvas, ia) , to_screen(&canvas, la) ))); // front: cf1 cf2 cf3 cf4 let cf = Polygon(Coordinates(vec!( to_screen(&canvas, cf1a) , to_screen(&canvas, cf2a) , to_screen(&canvas, cf3a) , to_screen(&canvas, cf4a) ))); // back: cb4 cb3 cb2 cb1 let cb = Polygon(Coordinates(vec!( to_screen(&canvas, cb4a) , to_screen(&canvas, cb3a) , to_screen(&canvas, cb2a) , to_screen(&canvas, cb1a) ))); // top: cf2 cb2 cb3 cf3 let ct = Polygon(Coordinates(vec!( to_screen(&canvas, cf2a) , to_screen(&canvas, cb2a) , to_screen(&canvas, cb3a) , to_screen(&canvas, cf3a) ))); // bottom: cf1 cf4 cb4 cb1 let co = Polygon(Coordinates(vec!( to_screen(&canvas, cf1a) , to_screen(&canvas, cf4a) , to_screen(&canvas, cb4a) , to_screen(&canvas, cb1a) ))); // left: cf1 cb1 cb2 cf2 let cl = Polygon(Coordinates(vec!( to_screen(&canvas, cf1a) , to_screen(&canvas, cb1a) , to_screen(&canvas, cb2a) , to_screen(&canvas, cf2a) ))); // right: cf3 cb3 cb4 cf4 let cr = Polygon(Coordinates(vec!( to_screen(&canvas, cf3a) , to_screen(&canvas, cb3a) , to_screen(&canvas, cb4a) , to_screen(&canvas, cf4a) ))); canvas.clear(); canvas.draw(&pg1, Coordinate(0,0), 0xFFFF00); canvas.draw(&pg2, Coordinate(0,0), 0xFFFF00); canvas.draw(&pg3, Coordinate(0,0), 0xFFFF00); canvas.draw(&pg4, Coordinate(0,0), 0xFFFF00); canvas.draw( &cf, Coordinate(0,0), 0x0000FF); canvas.draw( &cb, Coordinate(0,0), 0x0000FF); canvas.draw( &ct, Coordinate(0,0), 0x0000FF); canvas.draw( &co, Coordinate(0,0), 0x0000FF); canvas.draw( &cl, Coordinate(0,0), 0x0000FF); canvas.draw( &cr, Coordinate(0,0), 0x0000FF); let passed = Instant::now() - last; let f = (passed.as_nanos() / step.as_nanos()) as u32; if f > 1 { println!("!!! Detected frame drop"); } last = last + step*(f + 1); canvas.put_text( Coordinate(10, 15) , &format!( "sleep: {:?}" , last - Instant::now() )); canvas.show(); thread::sleep(last - Instant::now()); } }); for x in rx { match x { 1 => break, _ => {}, } } }