[FEAT] Update cyclinder
This commit is contained in:
@@ -212,8 +212,8 @@ int main(void)
|
|||||||
|
|
||||||
// Configure the camera.
|
// Configure the camera.
|
||||||
// the_camera = Camera(100, 50, std::numbers::pi / 10);
|
// the_camera = Camera(100, 50, std::numbers::pi / 10);
|
||||||
the_camera = Camera(320, 200, 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(640, 480, std::numbers::pi / 10);
|
||||||
the_camera.show_progress_bar();
|
the_camera.show_progress_bar();
|
||||||
the_camera.set_transform(
|
the_camera.set_transform(
|
||||||
Matrix::view_transform(Tuple::Point(8, 3.5, -9), Tuple::Point(0, 0.3, 0), Tuple::Vector(0, 1, 0)));
|
Matrix::view_transform(Tuple::Point(8, 3.5, -9), Tuple::Point(0, 0.3, 0), Tuple::Vector(0, 1, 0)));
|
||||||
|
|||||||
@@ -34,11 +34,17 @@
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
bool Raytracer::double_equal(double a, double b)
|
bool Raytracer::double_equal(double the_a, double the_b)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
double the_fabs = std::fabs(a - b);
|
double the_fabs = std::fabs(a - b);
|
||||||
bool the_test = the_fabs < kEpsilon;
|
bool the_test = the_fabs < kEpsilon;
|
||||||
#endif
|
#endif
|
||||||
return std::fabs(a - b) < kEpsilon;
|
if (the_a == std::numeric_limits<double>::infinity() && the_b == std::numeric_limits<double>::infinity())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (the_a == -std::numeric_limits<double>::infinity() && the_b == -std::numeric_limits<double>::infinity())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return std::fabs(the_a - the_b) < kEpsilon;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ using namespace Raytracer;
|
|||||||
|
|
||||||
Intersections Cylinder::local_intersect(const Ray &a_ray)
|
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_t0, the_t1;
|
||||||
double the_y0, the_y1;
|
double the_y0, the_y1;
|
||||||
Intersections the_intersections;
|
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);
|
the_a = std::pow(the_ray_direction.x(), 2) + std::pow(the_ray_direction.z(), 2);
|
||||||
|
|
||||||
// Ray is parallel to the y axis
|
// 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() +
|
// Caps
|
||||||
2 * the_ray_origin.z() * the_ray_direction.z();
|
intersect_caps(a_ray, the_intersections);
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return the_intersections;
|
return the_intersections;
|
||||||
}
|
}
|
||||||
@@ -123,3 +125,60 @@ void Cylinder::set_maximum(double a_value)
|
|||||||
{
|
{
|
||||||
m_maximum = 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,9 +49,17 @@ namespace Raytracer
|
|||||||
double maximum(void);
|
double maximum(void);
|
||||||
void set_maximum(double a_value);
|
void set_maximum(double a_value);
|
||||||
|
|
||||||
|
bool closed(void);
|
||||||
|
void set_closed(bool a_state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double m_minimum = std::numeric_limits<double>::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<double>::infinity();
|
||||||
double m_maximum = std::numeric_limits<double>::infinity();
|
double m_maximum = std::numeric_limits<double>::infinity();
|
||||||
|
bool m_closed = false;
|
||||||
};
|
};
|
||||||
}; // namespace Raytracer
|
}; // namespace Raytracer
|
||||||
|
|
||||||
|
|||||||
@@ -185,9 +185,9 @@ SCENARIO("The default minimum and maximum for a cylinder", "[features/cylinders.
|
|||||||
Cylinder cyl;
|
Cylinder cyl;
|
||||||
THEN("cym.minimum = -infinity")
|
THEN("cym.minimum = -infinity")
|
||||||
{
|
{
|
||||||
REQUIRE(cyl.minimum() == std::numeric_limits<double>::infinity());
|
REQUIRE(cyl.minimum() == -std::numeric_limits<double>::infinity());
|
||||||
}
|
}
|
||||||
AND_THEN("cym.maximum = -infinity")
|
AND_THEN("cym.maximum = infinity")
|
||||||
{
|
{
|
||||||
REQUIRE(cyl.maximum() == std::numeric_limits<double>::infinity());
|
REQUIRE(cyl.maximum() == std::numeric_limits<double>::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(<direction>)")
|
||||||
|
{
|
||||||
|
AND_GIVEN("r <- ray(<point>, 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 = <count>")
|
||||||
|
{
|
||||||
|
REQUIRE(xs.count() == the_test[i].count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user