/* This file is part of Nori, a simple educational ray tracer Copyright (c) 2015 by Wenzel Jakob Nori is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Nori 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma once #include NORI_NAMESPACE_BEGIN /** * \brief Generic n-dimensional bounding box data structure * * Maintains a minimum and maximum position along each dimension and provides * various convenience functions for querying and modifying them. * * This class is parameterized by the underlying point data structure, * which permits the use of different scalar types and dimensionalities, e.g. * \code * TBoundingBox integerBBox(Point3i(0, 1, 3), Point3i(4, 5, 6)); * TBoundingBox doubleBBox(Point2d(0.0, 1.0), Point2d(4.0, 5.0)); * \endcode * * \tparam T The underlying point data type (e.g. \c Point2d) * \ingroup libcore */ template struct TBoundingBox { enum { Dimension = _PointType::Dimension }; typedef _PointType PointType; typedef typename PointType::Scalar Scalar; typedef typename PointType::VectorType VectorType; /** * \brief Create a new invalid bounding box * * Initializes the components of the minimum * and maximum position to \f$\infty\f$ and \f$-\infty\f$, * respectively. */ TBoundingBox() { reset(); } /// Create a collapsed bounding box from a single point TBoundingBox(const PointType &p) : min(p), max(p) { } /// Create a bounding box from two positions TBoundingBox(const PointType &min, const PointType &max) : min(min), max(max) { } /// Test for equality against another bounding box bool operator==(const TBoundingBox &bbox) const { return min == bbox.min && max == bbox.max; } /// Test for inequality against another bounding box bool operator!=(const TBoundingBox &bbox) const { return min != bbox.min || max != bbox.max; } /// Calculate the n-dimensional volume of the bounding box Scalar getVolume() const { return (max - min).prod(); } /// Calculate the n-1 dimensional volume of the boundary float getSurfaceArea() const { VectorType d = max - min; float result = 0.0f; for (int i=0; i min.array()).all() && (p.array() < max.array()).all(); } else { return (p.array() >= min.array()).all() && (p.array() <= max.array()).all(); } } /** * \brief Check whether a specified bounding box lies \a on or \a within * the current bounding box * * Note that by definition, an 'invalid' bounding box (where min=\f$\infty\f$ * and max=\f$-\infty\f$) does not cover any space. Hence, this method will always * return \a true when given such an argument. * * \param strict Set this parameter to \c true if the bounding * box boundary should be excluded in the test */ bool contains(const TBoundingBox &bbox, bool strict = false) const { if (strict) { return (bbox.min.array() > min.array()).all() && (bbox.max.array() < max.array()).all(); } else { return (bbox.min.array() >= min.array()).all() && (bbox.max.array() <= max.array()).all(); } } /** * \brief Check two axis-aligned bounding boxes for possible overlap. * * \param strict Set this parameter to \c true if the bounding * box boundary should be excluded in the test * * \return \c true If overlap was detected. */ bool overlaps(const TBoundingBox &bbox, bool strict = false) const { if (strict) { return (bbox.min.array() < max.array()).all() && (bbox.max.array() > min.array()).all(); } else { return (bbox.min.array() <= max.array()).all() && (bbox.max.array() >= min.array()).all(); } } /** * \brief Calculate the smallest squared distance between * the axis-aligned bounding box and the point \c p. */ Scalar squaredDistanceTo(const PointType &p) const { Scalar result = 0; for (int i=0; i max[i]) value = p[i] - max[i]; result += value*value; } return result; } /** * \brief Calculate the smallest distance between * the axis-aligned bounding box and the point \c p. */ Scalar distanceTo(const PointType &p) const { return std::sqrt(squaredDistanceTo(p)); } /** * \brief Calculate the smallest square distance between * the axis-aligned bounding box and \c bbox. */ Scalar squaredDistanceTo(const TBoundingBox &bbox) const { Scalar result = 0; for (int i=0; i max[i]) value = bbox.min[i] - max[i]; result += value*value; } return result; } /** * \brief Calculate the smallest distance between * the axis-aligned bounding box and \c bbox. */ Scalar distanceTo(const TBoundingBox &bbox) const { return std::sqrt(squaredDistanceTo(bbox)); } /** * \brief Check whether this is a valid bounding box * * A bounding box \c bbox is valid when * \code * bbox.min[dim] <= bbox.max[dim] * \endcode * holds along each dimension \c dim. */ bool isValid() const { return (max.array() >= min.array()).all(); } /// Check whether this bounding box has collapsed to a single point bool isPoint() const { return (max.array() == min.array()).all(); } /// Check whether this bounding box has any associated volume bool hasVolume() const { return (max.array() > min.array()).all(); } /// Return the dimension index with the largest associated side length int getMajorAxis() const { VectorType d = max - min; int largest = 0; for (int i=1; i d[largest]) largest = i; return largest; } /// Return the dimension index with the shortest associated side length int getMinorAxis() const { VectorType d = max - min; int shortest = 0; for (int i=1; i::infinity()); max.setConstant(-std::numeric_limits::infinity()); } /// Expand the bounding box to contain another point void expandBy(const PointType &p) { min = min.cwiseMin(p); max = max.cwiseMax(p); } /// Expand the bounding box to contain another bounding box void expandBy(const TBoundingBox &bbox) { min = min.cwiseMin(bbox.min); max = max.cwiseMax(bbox.max); } /// Merge two bounding boxes static TBoundingBox merge(const TBoundingBox &bbox1, const TBoundingBox &bbox2) { return TBoundingBox( bbox1.min.cwiseMin(bbox2.min), bbox1.max.cwiseMax(bbox2.max) ); } /// Return the index of the largest axis int getLargestAxis() const { VectorType extents = max-min; if (extents[0] >= extents[1] && extents[0] >= extents[2]) return 0; else if (extents[1] >= extents[0] && extents[1] >= extents[2]) return 1; else return 2; } /// Return the position of a bounding box corner PointType getCorner(int index) const { PointType result; for (int i=0; i::infinity(); float farT = std::numeric_limits::infinity(); for (int i=0; i<3; i++) { float origin = ray.o[i]; float minVal = min[i], maxVal = max[i]; if (ray.d[i] == 0) { if (origin < minVal || origin > maxVal) return false; } else { float t1 = (minVal - origin) * ray.dRcp[i]; float t2 = (maxVal - origin) * ray.dRcp[i]; if (t1 > t2) std::swap(t1, t2); nearT = std::max(t1, nearT); farT = std::min(t2, farT); if (!(nearT <= farT)) return false; } } return ray.mint <= farT && nearT <= ray.maxt; } /// Return the overlapping region of the bounding box and an unbounded ray bool rayIntersect(const Ray3f &ray, float &nearT, float &farT) const { nearT = -std::numeric_limits::infinity(); farT = std::numeric_limits::infinity(); for (int i=0; i<3; i++) { float origin = ray.o[i]; float minVal = min[i], maxVal = max[i]; if (ray.d[i] == 0) { if (origin < minVal || origin > maxVal) return false; } else { float t1 = (minVal - origin) * ray.dRcp[i]; float t2 = (maxVal - origin) * ray.dRcp[i]; if (t1 > t2) std::swap(t1, t2); nearT = std::max(t1, nearT); farT = std::min(t2, farT); if (!(nearT <= farT)) return false; } } return true; } PointType min; ///< Component-wise minimum PointType max; ///< Component-wise maximum }; NORI_NAMESPACE_END