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
+ |
+
+
+
+ Git
+ |
+
+
+ Github
+ |
+
+
+ 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);
+}