diff --git a/.gitignore b/.gitignore index 582a8b6..797e2e1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.[Vv]scode *.ppm *.png +backup_code.rs \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 242eafc..428bfa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lib" version = "0.1.0" dependencies = [ "rand 0.3.23", "rayon", + "serde_json", ] [[package]] @@ -53,6 +60,30 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.3.23" @@ -96,8 +127,6 @@ name = "ray_tracer" version = "0.1.0" dependencies = [ "lib", - "rand 0.3.23", - "rayon", ] [[package]] @@ -129,6 +158,61 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 9c59a72..3314735 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,18 +7,25 @@ authors = ["Sen"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Your dependencies here lib ={ path = "lib"} # needed for the tests to work -rand="0.3.14" -rayon = "1.10.0" + +[dev-dependencies] +# Dependencies needed only for testing +# mockall = "0.13.0" [[bin]] name = "bin" path = "src/main.rs" +[[test]] +name = "test" +path = "tests/common_config.rs" + # Check https://rust-classes.com/chapter_4_3 [workspace] members = [ - "lib", + "lib" # DONOT include the tests directory here and DONOT create a Cargo.toml file in the tests directory ] resolver = "2" diff --git a/README.md b/README.md index 636acbb..0a50329 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,34 @@ -# RustRayTracer +# RustyRayTracer + + + + + + + + +
+ + Rust + +
Rust +
+ + Git + +
Git +
+ GitHub +
Github +
+ webpack +
Linux +
## About This is a simple ray-tracer implementation in Rust of [Peter Shirley's "Ray Tracing In One Weekend"](https://raytracing.github.io/books/RayTracingInOneWeekend.html) book. This is the first of the series: @@ -29,4 +59,3 @@ Every commit in the code implements a particular chapter. In this way it's easy ## License This project is licensed under the GNU GENERAL PUBLIC license. See the LICENSE file for more details. - diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 09cfd87..a131c21 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -7,4 +7,5 @@ authors = ["Sen"] [dependencies] rand="0.3.14" -rayon = "1.10.0" \ No newline at end of file +rayon = "1.10.0" +serde_json = "1.0.125" \ No newline at end of file diff --git a/lib/src/utilities/camera.rs b/lib/src/utilities/camera.rs index f79c003..5608289 100644 --- a/lib/src/utilities/camera.rs +++ b/lib/src/utilities/camera.rs @@ -1,6 +1,5 @@ -use rayon::prelude::*; - use rand::Rng; +use rayon::prelude::*; use super::{ color::Color, @@ -58,7 +57,7 @@ impl Camera { // Render and write to file let file_path = "image_test.ppm"; - let mut file = File::create(&file_path).unwrap(); + let mut file = File::create(file_path).unwrap(); writeln!(file, "P3\n{} {}\n255", self.image_width, self.image_height).unwrap(); let pixel_color_vec: Vec = (0..self.image_height) @@ -75,7 +74,7 @@ impl Camera { Self::ray_color(ray_sent, self.max_depth, &world[..]) }) .sum(); // need to implement sum trait for Color - return pixel_color; // Return the Color from the map closure + pixel_color // Return the Color from the map closure }) .collect::>() // Collect the inner Vec }) @@ -83,9 +82,8 @@ impl Camera { for pixel in pixel_color_vec.iter() { let write_res = (*pixel * self.pixel_samples_scale).write_color(&mut file); - match write_res { - Err(e) => println!("Error in writing result to file: {}", e), - _ => (), // The () is just the unit value, so nothing will happen + if let Err(e) = write_res { + println!("Error in writing result to file: {}", e) } } @@ -137,7 +135,7 @@ impl Camera { return Color::default(); } - if let Some(hit) = world.hit(ray, Interval::new(0.001, std::f64::INFINITY)) { + if let Some(hit) = world.hit(ray, Interval::new(0.001, f64::INFINITY)) { if let Some(scatter) = hit.material.scatter(ray, &hit) { let Scatter { scattered_ray, diff --git a/lib/src/utilities/color.rs b/lib/src/utilities/color.rs index 83825e9..c1ae676 100644 --- a/lib/src/utilities/color.rs +++ b/lib/src/utilities/color.rs @@ -51,7 +51,7 @@ impl Color { if linear_component > 0.0 { return linear_component.sqrt(); } - return 0.0; + 0.0 } } diff --git a/lib/src/utilities/hit_record.rs b/lib/src/utilities/hit_record.rs index 52778b8..84b677e 100644 --- a/lib/src/utilities/hit_record.rs +++ b/lib/src/utilities/hit_record.rs @@ -25,6 +25,8 @@ impl<'a> HitRecord<'a> { material, } } + /// Sets the hit record normal vector. + /// The parameter `outward_normal` is assumed to have unit length pub fn set_face_normal( ray: Ray, outward_normal: Vector3, @@ -32,8 +34,6 @@ impl<'a> HitRecord<'a> { material: &'a dyn Material, parameter: f64, ) -> Self { - // Sets the hit record normal vector. - // NOTE: the parameter `outward_normal` is assumed to have unit length let is_face_front = ray.get_direction().dot_prod(outward_normal) < 0.0; let normal = match is_face_front { true => outward_normal, diff --git a/lib/src/utilities/interval.rs b/lib/src/utilities/interval.rs index 2ce3d47..0b6e72e 100644 --- a/lib/src/utilities/interval.rs +++ b/lib/src/utilities/interval.rs @@ -5,11 +5,7 @@ pub struct Interval { impl Interval { // https://stackoverflow.com/questions/26549480/how-do-i-declare-a-static-field-in-a-struct-in-rust - // const EMPTY: Interval = Interval::new(std::f64::INFINITY, std::f64::NEG_INFINITY); - // const UNIVERSE: Interval = Interval::new(std::f64::NEG_INFINITY, std::f64::INFINITY); - pub const fn new(min: f64, max: f64) -> Self { - // Needs to add it as const fn, since EMPTY const needs a const function Self { min, max } } @@ -29,8 +25,8 @@ impl Interval { impl Default for Interval { fn default() -> Self { Self { - min: std::f64::INFINITY, - max: std::f64::NEG_INFINITY, + min: f64::INFINITY, + max: f64::NEG_INFINITY, } } } diff --git a/lib/src/utilities/mod.rs b/lib/src/utilities/mod.rs index 5e4a8ef..67098f6 100644 --- a/lib/src/utilities/mod.rs +++ b/lib/src/utilities/mod.rs @@ -6,4 +6,5 @@ pub mod interval; pub mod material; pub mod point; pub mod ray; +pub mod scenes; pub mod vector3; diff --git a/lib/src/utilities/point.rs b/lib/src/utilities/point.rs index 1807972..c7ad5be 100644 --- a/lib/src/utilities/point.rs +++ b/lib/src/utilities/point.rs @@ -1,8 +1,6 @@ -use super::vector3::Vector3; use std::ops::{Add, Neg, Sub}; -/// The Point3 Class -/// +use super::vector3::Vector3; #[derive(Clone, Copy)] pub struct Point3 { diff --git a/lib/src/utilities/ray.rs b/lib/src/utilities/ray.rs index e3dbddd..1075802 100644 --- a/lib/src/utilities/ray.rs +++ b/lib/src/utilities/ray.rs @@ -1,9 +1,6 @@ use super::{point::Point3, vector3::Vector3}; -/// The Ray Class -/// - -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct Ray { origin: Point3, direction: Vector3, @@ -26,12 +23,3 @@ impl Ray { self.direction } } - -impl Default for Ray { - fn default() -> Self { - Self { - origin: Point3::default(), - direction: Vector3::default(), - } - } -} diff --git a/lib/src/utilities/scenes.rs b/lib/src/utilities/scenes.rs new file mode 100644 index 0000000..d4682c2 --- /dev/null +++ b/lib/src/utilities/scenes.rs @@ -0,0 +1,168 @@ +use rand::Rng; +use rayon::prelude::*; +use std::{collections::HashMap, fs}; + +use super::{ + color::Color, + geometry::{Hittable, Sphere}, + material::{Dielectric, Lambertian, Metal}, + point::Point3, +}; + +const NUMBER_BALLS: i32 = 7; +const SCENE_FILE_PATH: &str = "scene_data.json"; + +fn translate_color_to_scale(color_component: f64) -> f64 { + color_component.clamp(0.0, 256.0) / 256.0 +} + +pub fn generate_scene(world: &mut Vec>) { + // World + + // Scene - ground + let material_ground = Box::new(Lambertian::new(Color::new(0.8, 0.8, 0.8))); + world.push(Box::new(Sphere::new( + Point3::new(0.0, -1000.0, 0.0), + 1000.0, + material_ground, + ))); + + // Scene - small balls (random) + let spheres_scene: Vec> = (-NUMBER_BALLS..NUMBER_BALLS) + .into_par_iter() + .flat_map(|x_index| { + (-NUMBER_BALLS..NUMBER_BALLS) + .into_par_iter() + .map(|y_index| { + let choose_material_random: f64 = rand::thread_rng().r#gen::(); + let center = Point3::new( + (x_index as f64) + (0.9 * rand::thread_rng().r#gen::()), + 0.2, + (y_index as f64) + (0.9 * rand::thread_rng().r#gen::()), + ); + if choose_material_random < 0.4 { + // Lambertian + let material_lambertian = Box::new(Lambertian::new(Color::new( + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + ))); + Box::new(Sphere::new(center, 0.2, material_lambertian)) as Box + } else if choose_material_random < 0.8 { + // Metal + let material_metal = Box::new(Metal::new( + Color::new( + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + ), + rand::thread_rng().r#gen::(), + )); + Box::new(Sphere::new(center, 0.2, material_metal)) as Box + } else { + // Glass + let material_glass = Box::new(Dielectric::new(1.33)); + Box::new(Sphere::new(center, 0.2, material_glass)) as Box + } + }) + .collect::>>() + }) + .collect(); + + world.extend(spheres_scene); + + // Scene - big balls with Glass material + let material_glass = Box::new(Dielectric::new(1.0 / 1.55)); + world.push(Box::new(Sphere::new( + Point3::new(8.0, 1.0, 0.0), + 1.0, + material_glass, + ))); + let material_bubble = Box::new(Dielectric::new(1.55)); + world.push(Box::new(Sphere::new( + Point3::new(0.0, 1.0, 0.0), + 1.0, + material_bubble, + ))); + + // Scene - big ball with Lambertian material + let material_lambertian = Box::new(Lambertian::new(Color::new(0.4, 0.2, 0.1))); + world.push(Box::new(Sphere::new( + Point3::new(-4.0, 1.0, 0.0), + 1.0, + material_lambertian, + ))); + + // Scene - big ball with Metal material + let material_metal = Box::new(Metal::new(Color::new(0.7, 0.6, 0.5), 0.2)); + world.push(Box::new(Sphere::new( + Point3::new(4.0, 1.0, 0.0), + 1.0, + material_metal, + ))); + + // -------------------------FROM JSON----------------------------- + + let scenes_file_path = SCENE_FILE_PATH.to_owned(); + let file = fs::File::open(scenes_file_path).expect("Could not open file"); + let json_data: HashMap>> = + serde_json::from_reader(file).expect("File is not proper JSON"); + let json_parse_ball = json_data.get("Ball").expect("Can't read Ball data"); + + for ball in json_parse_ball.iter() { + { + if let Some(material_str) = ball["material"]["type"].as_str() { + if material_str == "lambertian" { + let color_obj = Color::new( + translate_color_to_scale(ball["color"]["r"].as_f64().unwrap_or_default()), + translate_color_to_scale(ball["color"]["g"].as_f64().unwrap_or_default()), + translate_color_to_scale(ball["color"]["b"].as_f64().unwrap_or_default()), + ); + let material_obj = Box::new(Lambertian::new(color_obj)); + + let center_obj = Point3::new( + ball["center"]["x"].as_f64().unwrap_or_default(), + ball["center"]["y"].as_f64().unwrap_or_default(), + ball["center"]["z"].as_f64().unwrap_or_default(), + ); + let radius_obj = ball["radius"].as_f64().unwrap_or_default(); + + world.push(Box::new(Sphere::new(center_obj, radius_obj, material_obj))); + } else if material_str == "metal" { + let color_obj = Color::new( + translate_color_to_scale(ball["color"]["r"].as_f64().unwrap_or_default()), + translate_color_to_scale(ball["color"]["g"].as_f64().unwrap_or_default()), + translate_color_to_scale(ball["color"]["b"].as_f64().unwrap_or_default()), + ); + let fuzz_obj = ball["material"]["fuzz"].as_f64().unwrap_or_default(); + let material_obj = Box::new(Metal::new(color_obj, fuzz_obj)); + + let center_obj = Point3::new( + ball["center"]["x"].as_f64().unwrap_or_default(), + ball["center"]["y"].as_f64().unwrap_or_default(), + ball["center"]["z"].as_f64().unwrap_or_default(), + ); + let radius_obj = ball["radius"].as_f64().unwrap_or_default(); + + world.push(Box::new(Sphere::new(center_obj, radius_obj, material_obj))); + } else if material_str == "dielectric" { + let rf_index_obj = ball["material"]["ref_idx"].as_f64().unwrap_or_default(); + let material_obj = Box::new(Dielectric::new(rf_index_obj)); + + let center_obj = Point3::new( + ball["center"]["x"].as_f64().unwrap_or_default(), + ball["center"]["y"].as_f64().unwrap_or_default(), + ball["center"]["z"].as_f64().unwrap_or_default(), + ); + let radius_obj = ball["radius"].as_f64().unwrap_or_default(); + + world.push(Box::new(Sphere::new(center_obj, radius_obj, material_obj))); + } else { + println!("Wrong Material"); + } + } + } + } + + // ------------------------------------------------------ +} diff --git a/lib/src/utilities/vector3.rs b/lib/src/utilities/vector3.rs index a4048c6..9ce6242 100644 --- a/lib/src/utilities/vector3.rs +++ b/lib/src/utilities/vector3.rs @@ -1,9 +1,5 @@ -use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; - use rand::Rng; - -/// The vec3 Class -/// +use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; #[derive(Clone, Copy)] pub struct Vector3 { @@ -97,7 +93,7 @@ impl Vector3 { } pub fn reflection(&self, normal_vec: &Self) -> Self { - return *self - ((*normal_vec * (self.dot_prod(*normal_vec))) * 2.0); + *self - ((*normal_vec * (self.dot_prod(*normal_vec))) * 2.0) } pub fn refraction(&self, normal_vec: &Self, ratio_refractive_index: f64) -> Self { diff --git a/scene_data.json b/scene_data.json new file mode 100644 index 0000000..1d8effd --- /dev/null +++ b/scene_data.json @@ -0,0 +1,29 @@ +{ + "Ball": [ + { + "color": {"r": 255.0, "g": 100.0, "b": 0.0}, + "center": {"x": -6.0, "y": 1.0, "z": 1.5}, + "radius": 1.0, + "material": { + "type": "lambertian" + } + }, + { + "color": {"r": 0.0, "g": 100.0, "b": 255.0}, + "center": {"x": -8.0, "y": 1.0, "z": 3.0}, + "radius": 1.0, + "material": { + "type": "metal", + "fuzz": 0.5 + } + }, + { + "center": {"x": -10.0, "y": 1.0, "z": 2.0}, + "radius": 1.7, + "material": { + "type": "dielectric", + "ref_idx": 1.5 + } + } + ] +} diff --git a/src/main.rs b/src/main.rs index 5ea8ba2..ccae8ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,116 +1,15 @@ -use rand::Rng; - -use lib::utilities::{ - camera::Camera, - color::Color, - geometry::{Hittable, Sphere}, - material::{Dielectric, Lambertian, Metal}, - point::Point3, - vector3::Vector3, -}; -use rayon::prelude::*; - -const NUMBER_BALLS: i32 = 5; +use lib::utilities::{camera::Camera, geometry::Hittable, point::Point3, scenes, vector3::Vector3}; const ASPECT_RATIO: f64 = 16.0 / 9.0; -const IMAGE_WIDTH: i32 = 800; +const IMAGE_WIDTH: i32 = 1600; const SAMPLES_PER_PIXEL: i32 = 200; -const MAX_DEPTH: i32 = 50; +const MAX_DEPTH: i32 = 80; const VERTICAL_FOV: f64 = 40.0; fn main() { // https://raytracing.github.io/books/RayTracingInOneWeekend.html - - // World let mut world: Vec> = Vec::new(); - - // Scene - ground - let material_ground = Box::new(Lambertian::new(Color::new(0.8, 0.8, 0.8))); - world.push(Box::new(Sphere::new( - Point3::new(0.0, -1000.0, 0.0), - 1000.0, - material_ground, - ))); - - // Scene - small balls (random) - let spheres_scene: Vec> = (-NUMBER_BALLS..NUMBER_BALLS) - .into_par_iter() - .flat_map(|x_index| { - (-NUMBER_BALLS..NUMBER_BALLS) - .into_par_iter() - .map(|y_index| { - let choose_material_random: f64 = rand::thread_rng().r#gen::(); - let center = Point3::new( - (x_index as f64) + (0.9 * rand::thread_rng().r#gen::()), - 0.2, - (y_index as f64) + (0.9 * rand::thread_rng().r#gen::()), - ); - if choose_material_random < 0.4 { - // Lambertian - let material_lambertian = Box::new(Lambertian::new(Color::new( - rand::thread_rng().r#gen::(), - rand::thread_rng().r#gen::(), - rand::thread_rng().r#gen::(), - ))); - return Box::new(Sphere::new(center, 0.2, material_lambertian)) - as Box; - } else if choose_material_random < 0.8 { - // Metal - let material_metal = Box::new(Metal::new( - Color::new( - rand::thread_rng().r#gen::(), - rand::thread_rng().r#gen::(), - rand::thread_rng().r#gen::(), - ), - rand::thread_rng().r#gen::(), - )); - return Box::new(Sphere::new(center, 0.2, material_metal)) - as Box; - } else { - // Glass - let material_glass = Box::new(Dielectric::new(1.33)); - return Box::new(Sphere::new(center, 0.2, material_glass)) - as Box; - } - }) - .collect::>>() - }) - .collect(); - - world.extend(spheres_scene); - // If you don't need to use spheres_scene after pushing its contents to world, you can move the entire vector. - // Explanation: world.extend(spheres_scene) moves the contents of spheres_scene into world. The extend method consumes the vector, avoiding the need for cloning or referencing, and effectively transfers ownership of each Box to world. - // Use moving (via extend) if you want to transfer ownership and don't need to use spheres_scene afterward, which is more efficient because it avoids unnecessary duplication. - - // Scene - big balls with Glass material - let material_glass = Box::new(Dielectric::new(1.0 / 1.55)); - world.push(Box::new(Sphere::new( - Point3::new(8.0, 1.0, 0.0), - 1.0, - material_glass, - ))); - let material_bubble = Box::new(Dielectric::new(1.55)); - world.push(Box::new(Sphere::new( - Point3::new(0.0, 1.0, 0.0), - 1.0, - material_bubble, - ))); - - // Scene - big ball with Lambertian material - let material_lambertian = Box::new(Lambertian::new(Color::new(0.4, 0.2, 0.1))); - world.push(Box::new(Sphere::new( - Point3::new(-4.0, 1.0, 0.0), - 1.0, - material_lambertian, - ))); - - // Scene - big ball with Metal material - let material_metal = Box::new(Metal::new(Color::new(0.7, 0.6, 0.5), 0.2)); - world.push(Box::new(Sphere::new( - Point3::new(4.0, 1.0, 0.0), - 1.0, - material_metal, - ))); + scenes::generate_scene(&mut world); // Camera let mut cam: Camera = Camera::new(); diff --git a/tests/color_tests.rs b/tests/color_tests.rs new file mode 100644 index 0000000..6df9ec7 --- /dev/null +++ b/tests/color_tests.rs @@ -0,0 +1,32 @@ +use lib::utilities::color::{Color, Cross}; + +mod common_config; + +#[test] +fn add_assign_test() { + let mut color_test = Color::new(1.0, 2.0, 3.0); + color_test += Color::new(4.0, 0.0, 6.0); + assert_eq!(color_test.get_r(), 5.0); + assert_eq!(color_test.get_g(), 2.0); + assert_eq!(color_test.get_b(), 9.0); +} + +#[test] +fn multiply_assign_test() { + let mut color_test = Color::new(10.0, 20.0, 30.0); + color_test *= Color::new(4.0, 0.0, 6.0); + assert_eq!(color_test.get_r(), 40.0); + assert_eq!(color_test.get_g(), 0.0); + assert_eq!(color_test.get_b(), 180.0); +} + +#[test] +fn cross_product_test() { + let lhs_color = Color::new(5.0, 7.0, 1.0); + let rhs_color = Color::new(4.0, 4.0, 3.0); + let cross_prod_col = lhs_color.cross_prod(rhs_color); + + assert_eq!(cross_prod_col.get_r(), 17.0); + assert_eq!(cross_prod_col.get_g(), -11.0); + assert_eq!(cross_prod_col.get_b(), -8.0); +} diff --git a/tests/common_config.rs b/tests/common_config.rs new file mode 100644 index 0000000..d8f6c51 --- /dev/null +++ b/tests/common_config.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +mod common_config {} diff --git a/tests/vector3_tests.rs b/tests/vector3_tests.rs new file mode 100644 index 0000000..066222a --- /dev/null +++ b/tests/vector3_tests.rs @@ -0,0 +1,64 @@ +use lib::utilities::vector3::{Cross, Vector3}; + +mod common_config; + +#[test] +fn length_squared_test() { + let vec_test = Vector3::new(1.0, 2.0, 3.0); + assert_eq!(vec_test.length_squared(), 14.0); +} + +#[test] +fn cross_product_test() { + let lhs_vec = Vector3::new(5.0, 7.0, 1.0); + let rhs_vec = Vector3::new(4.0, 4.0, 3.0); + let cross_prod_vec = lhs_vec.cross_prod(rhs_vec); + + assert_eq!(cross_prod_vec.get_x(), 17.0); + assert_eq!(cross_prod_vec.get_y(), -11.0); + assert_eq!(cross_prod_vec.get_z(), -8.0); +} + +#[test] +fn dot_product_test() { + let lhs_vec = Vector3::new(5.0, 7.0, 1.0); + let rhs_vec = Vector3::new(4.0, 4.0, 3.0); + let dot_prod = lhs_vec.dot_prod(rhs_vec); + + assert_eq!(dot_prod, 51.0); +} + +#[test] +fn reflection_test() { + let ray_vec = Vector3::new(5.0, 7.0, 1.0); + let normal_vec = Vector3::new(1.0, 1.0, 1.0); + let reflected_ray = ray_vec.reflection(&normal_vec); + + assert_eq!(reflected_ray.get_x(), -21.0); + assert_eq!(reflected_ray.get_y(), -19.0); + assert_eq!(reflected_ray.get_z(), -25.0); +} + +#[test] +fn refraction_high_refractiveindex_test() { + let ray_vec = Vector3::new(5.0, 7.0, 1.0); + let normal_vec = Vector3::new(1.0, 1.0, 1.0); + let ratio_refractive_index = 2.0; + let refracted_ray = ray_vec.refraction(&normal_vec, ratio_refractive_index); + + assert_eq!(refracted_ray.get_x().trunc(), -47.0); + assert_eq!(refracted_ray.get_y().trunc(), -43.0); + assert_eq!(refracted_ray.get_z().trunc(), -55.0); +} + +#[test] +fn refraction_low_refractiveindex_test() { + let ray_vec = Vector3::new(5.0, 7.0, 1.0); + let normal_vec = Vector3::new(1.0, 1.0, 1.0); + let ratio_refractive_index = 1.0 / 2.0; + let refracted_ray = ray_vec.refraction(&normal_vec, ratio_refractive_index); + + assert_eq!(refracted_ray.get_x().trunc(), -11.0); + assert_eq!(refracted_ray.get_y().trunc(), -10.0); + assert_eq!(refracted_ray.get_z().trunc(), -13.0); +}