From 38107165a7a0ab34ecc157863159392bb33f346b Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Tue, 19 Mar 2024 18:42:19 +0100 Subject: [PATCH] [FEAT] Update cyclinder --- apps/chapter_13.cpp | 4 +- raytracing/src/core/common.cpp | 10 ++- raytracing/src/shapes/cylinder.cpp | 123 +++++++++++++++++++++-------- raytracing/src/shapes/cylinder.h | 10 ++- tests/13_cylinders.cpp | 72 ++++++++++++++++- 5 files changed, 180 insertions(+), 39 deletions(-) diff --git a/apps/chapter_13.cpp b/apps/chapter_13.cpp index c6ec9d9..a2f8435 100644 --- a/apps/chapter_13.cpp +++ b/apps/chapter_13.cpp @@ -212,8 +212,8 @@ int main(void) // Configure the camera. // the_camera = Camera(100, 50, std::numbers::pi / 10); - the_camera = Camera(320, 200, std::numbers::pi / 10); - // the_camera = Camera(640, 480, std::numbers::pi / 10); + // the_camera = Camera(320, 200, std::numbers::pi / 10); + the_camera = Camera(640, 480, std::numbers::pi / 10); the_camera.show_progress_bar(); the_camera.set_transform( Matrix::view_transform(Tuple::Point(8, 3.5, -9), Tuple::Point(0, 0.3, 0), Tuple::Vector(0, 1, 0))); diff --git a/raytracing/src/core/common.cpp b/raytracing/src/core/common.cpp index f3dc629..a15edbf 100644 --- a/raytracing/src/core/common.cpp +++ b/raytracing/src/core/common.cpp @@ -34,11 +34,17 @@ /* ------------------------------------------------------------------------- */ -bool Raytracer::double_equal(double a, double b) +bool Raytracer::double_equal(double the_a, double the_b) { #if 0 double the_fabs = std::fabs(a - b); bool the_test = the_fabs < kEpsilon; #endif - return std::fabs(a - b) < kEpsilon; + if (the_a == std::numeric_limits::infinity() && the_b == std::numeric_limits::infinity()) + return true; + + if (the_a == -std::numeric_limits::infinity() && the_b == -std::numeric_limits::infinity()) + return true; + + return std::fabs(the_a - the_b) < kEpsilon; } diff --git a/raytracing/src/shapes/cylinder.cpp b/raytracing/src/shapes/cylinder.cpp index 05bb868..5511bc1 100644 --- a/raytracing/src/shapes/cylinder.cpp +++ b/raytracing/src/shapes/cylinder.cpp @@ -41,7 +41,7 @@ using namespace Raytracer; Intersections Cylinder::local_intersect(const Ray &a_ray) { - double the_a, the_b, the_c, the_disc; + double the_a, the_b, the_c, the_discriminant; double the_t0, the_t1; double the_y0, the_y1; Intersections the_intersections; @@ -51,40 +51,42 @@ Intersections Cylinder::local_intersect(const Ray &a_ray) the_a = std::pow(the_ray_direction.x(), 2) + std::pow(the_ray_direction.z(), 2); // Ray is parallel to the y axis - if (double_equal(the_a, 0)) + if (double_equal(the_a, 0) == false) { - return the_intersections; + + the_b = 2 * the_ray_origin.x() * the_ray_direction.x() + + 2 * the_ray_origin.z() * the_ray_direction.z(); + the_c = std::pow(the_ray_origin.x(), 2) + std::pow(the_ray_origin.z(), 2) - 1; + + the_discriminant = std::pow(the_b, 2) - 4 * the_a * the_c; + + if (the_discriminant < 0) + { + return the_intersections; + } + + the_t0 = (-the_b - std::sqrt(the_discriminant)) / (2 * the_a); + the_t1 = (-the_b + std::sqrt(the_discriminant)) / (2 * the_a); + if (the_t0 > the_t1) + { + std::swap(the_t0, the_t1); + } + + the_y0 = the_ray_origin.y() + the_t0 * the_ray_direction.y(); + if ((m_minimum < the_y0) && (the_y0 < m_maximum)) + { + the_intersections.add(Intersection(the_t0, this)); + } + + the_y1 = the_ray_origin.y() + the_t1 * the_ray_direction.y(); + if ((m_minimum < the_y1) && (the_y1 < m_maximum)) + { + the_intersections.add(Intersection(the_t1, this)); + } } - the_b = 2 * the_ray_origin.x() * the_ray_direction.x() + - 2 * the_ray_origin.z() * the_ray_direction.z(); - the_c = std::pow(the_ray_origin.x(), 2) + std::pow(the_ray_origin.z(), 2) - 1; - - the_disc = std::pow(the_b, 2) - 4 * the_a * the_c; - - if (the_disc < 0) - { - return the_intersections; - } - - the_t0 = (-the_b - std::sqrt(the_disc)) / (2 * the_a); - the_t1 = (-the_b + std::sqrt(the_disc)) / (2 * the_a); - if (the_t0 > the_t1) - { - std::swap(the_t0, the_t1); - } - - the_y0 = the_ray_origin.y() + the_t0 * the_ray_direction.y(); - if ((m_minimum < the_y0) && (the_y0 < m_maximum)) - { - the_intersections.add(Intersection(the_t0, this)); - } - - the_y1 = the_ray_origin.y() + the_t1 * the_ray_direction.y(); - if ((m_minimum < the_y1) && (the_y1 < m_maximum)) - { - the_intersections.add(Intersection(the_t1, this)); - } + // Caps + intersect_caps(a_ray, the_intersections); return the_intersections; } @@ -123,3 +125,60 @@ void Cylinder::set_maximum(double a_value) { m_maximum = a_value; } + +/* ------------------------------------------------------------------------- */ + +bool Cylinder::closed(void) +{ + return m_closed; +} + +/* ------------------------------------------------------------------------- */ + +void Cylinder::set_closed(bool a_state) +{ + m_closed = a_state; +} + +/* ------------------------------------------------------------------------- */ + +bool Cylinder::check_cap(const Ray &a_ray, double a_distance_t) +{ + double the_x, the_z; + + const Tuple &the_ray_direction = a_ray.direction(); + const Tuple &the_ray_origin = a_ray.origin(); + + the_x = the_ray_origin.x() + a_distance_t * the_ray_direction.x(); + the_z = the_ray_origin.z() + a_distance_t * the_ray_direction.z(); + + return (std::pow(the_x, 2) + std::pow(the_z, 2)) <= 1; +} + +/* ------------------------------------------------------------------------- */ + +void Cylinder::intersect_caps(const Ray &a_ray, Intersections &an_xs) +{ + double the_distance_t; + // Caps only matter if the cylinder is closed. and might possibility be intersected the ray. + if ((m_closed == false) or (double_equal(a_ray.direction().y(), 0))) + { + return; + } + + // Check for an intersection with the lower end cap by intersecting + // the ray with the plane at y = cyl.minimum + the_distance_t = (m_minimum - a_ray.origin().y()) / a_ray.direction().y(); + if (check_cap(a_ray, the_distance_t)) + { + an_xs.add(Intersection(the_distance_t, this)); + } + + // Check for an intersection with the upper end cap by intersecting + // the ray with the plane at y = cyl.maximum + the_distance_t = (m_maximum - a_ray.origin().y()) / a_ray.direction().y(); + if (check_cap(a_ray, the_distance_t)) + { + an_xs.add(Intersection(the_distance_t, this)); + } +} diff --git a/raytracing/src/shapes/cylinder.h b/raytracing/src/shapes/cylinder.h index 40313e9..5222c0f 100644 --- a/raytracing/src/shapes/cylinder.h +++ b/raytracing/src/shapes/cylinder.h @@ -49,9 +49,17 @@ namespace Raytracer double maximum(void); void set_maximum(double a_value); + bool closed(void); + void set_closed(bool a_state); + private: - double m_minimum = std::numeric_limits::infinity(); + bool check_cap(const Ray &a_ray, double a_distance_t); + void intersect_caps(const Ray &a_ray, Intersections &an_xs); + + private: + double m_minimum = -std::numeric_limits::infinity(); double m_maximum = std::numeric_limits::infinity(); + bool m_closed = false; }; }; // namespace Raytracer diff --git a/tests/13_cylinders.cpp b/tests/13_cylinders.cpp index f8625f5..6d36627 100644 --- a/tests/13_cylinders.cpp +++ b/tests/13_cylinders.cpp @@ -185,9 +185,9 @@ SCENARIO("The default minimum and maximum for a cylinder", "[features/cylinders. Cylinder cyl; THEN("cym.minimum = -infinity") { - REQUIRE(cyl.minimum() == std::numeric_limits::infinity()); + REQUIRE(cyl.minimum() == -std::numeric_limits::infinity()); } - AND_THEN("cym.maximum = -infinity") + AND_THEN("cym.maximum = infinity") { REQUIRE(cyl.maximum() == std::numeric_limits::infinity()); } @@ -245,3 +245,71 @@ SCENARIO("Intersecting a constrained cylinder", "[features/cylinders.feature]") } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The default closed value for a cylinder", "[features/cylinders.feature]") +{ + GIVEN("cyl <- cylinder()") + { + Cylinder cyl; + THEN("cyl.closed = false") + { + REQUIRE(cyl.closed() == false); + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Intersecting the caps of a closed cylinder", "[features/cylinders.feature]") +{ + // | | point | direction | count | + // | 1 | point(0, 3, 0) | vector(0, -1, 0) | 2 | + // | 2 | point(0, 3, -2) | vector(0, -1, 2) | 2 | + // | 3 | point(0, 4, -2) | vector(0, -1, 1) | 2 | # corner case + // | 4 | point(0, 0, -2) | vector(0, 1, 2) | 2 | + // | 5 | point(0, -1, -2) | vector(0, 1, 1) | 2 | # corner case + CylinderTestConstrained the_test[] = { + {Tuple::Point(0, 3, 0), Tuple::Vector(0, -1, 0), 2}, + {Tuple::Point(0, 3, -2), Tuple::Vector(0, -1, 2), 2}, + {Tuple::Point(0, 4, -2), Tuple::Vector(0, -1, 1), 2}, + {Tuple::Point(0, 0, -2), Tuple::Vector(0, 1, 2), 2}, + {Tuple::Point(0, -1, -2), Tuple::Vector(0, 1, 1), 2} + }; + GIVEN("cyl <- cylinder()") + { + Cylinder cyl; + AND_GIVEN("cyl.minimum <- 1") + { + cyl.set_minimum(1); + AND_GIVEN("cyl.maximum <- 2") + { + cyl.set_maximum(2); + AND_GIVEN("cyl.closed <- true") + { + cyl.set_closed(true); + AND_GIVEN("direction <- normalize()") + { + AND_GIVEN("r <- ray(, direction)") + { + WHEN("xs <- local_intersect(cyl,r)") + { + for (int i = 0; i < 5; i++) + { + Tuple direction = the_test[i].direction.normalize(); + Ray r(the_test[i].point, direction); + Intersections xs = cyl.local_intersect(r); + THEN("xs.count = ") + { + REQUIRE(xs.count() == the_test[i].count); + } + } + } + } + } + } + } + } + } +}