200 lines
5.9 KiB
C++
200 lines
5.9 KiB
C++
/*!
|
|
* cylinder.cpp
|
|
*
|
|
* Copyright (c) 2024, NADAL Jean-Baptiste. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*
|
|
* @Author: NADAL Jean-Baptiste
|
|
* @Date: 18/03/2024
|
|
*
|
|
*/
|
|
|
|
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
|
|
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#include <cmath>
|
|
|
|
#include "core/common.h"
|
|
#include "core/intersections.h"
|
|
|
|
#include "cylinder.h"
|
|
|
|
using namespace Raytracer;
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
Intersections Cylinder::local_intersect(const Ray &a_ray)
|
|
{
|
|
double the_a, the_b, the_c, the_discriminant;
|
|
double the_t0, the_t1;
|
|
double the_y0, the_y1;
|
|
Intersections the_intersections;
|
|
const Tuple &the_ray_direction = a_ray.direction();
|
|
const Tuple &the_ray_origin = a_ray.origin();
|
|
|
|
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) == false)
|
|
{
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
// Caps
|
|
intersect_caps(a_ray, the_intersections);
|
|
|
|
return the_intersections;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
Tuple Cylinder::local_normal_at(const Tuple &a_local_point) const
|
|
{
|
|
|
|
double the_distance;
|
|
// Compute the square of the distance from the y axis
|
|
the_distance = std::pow(a_local_point.x(), 2) + std::pow(a_local_point.z(), 2);
|
|
if ((the_distance < 1) && (a_local_point.y() >= m_maximum - kEpsilon))
|
|
{
|
|
return Tuple::Vector(0, 1, 0);
|
|
}
|
|
else if ((the_distance < 1) && (a_local_point.y() <= m_minimum + kEpsilon))
|
|
{
|
|
return Tuple::Vector(0, -1, 0);
|
|
}
|
|
|
|
return Tuple::Vector(a_local_point.x(), 0, a_local_point.z());
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
double Cylinder::minimum(void)
|
|
{
|
|
return m_minimum;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
void Cylinder::set_minimum(double a_value)
|
|
{
|
|
m_minimum = a_value;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
double Cylinder::maximum(void)
|
|
{
|
|
return m_maximum;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
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;
|
|
const Tuple &the_ray_direction = a_ray.direction();
|
|
|
|
// Caps only matter if the cylinder is closed. and might possibility be intersected the ray.
|
|
if ((m_closed == false) or (double_equal(the_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()) / the_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()) / the_ray_direction.y();
|
|
if (check_cap(a_ray, the_distance_t))
|
|
{
|
|
an_xs.add(Intersection(the_distance_t, this));
|
|
}
|
|
}
|