intersect_line_alignedbox.hpp

Back to Intersection between shapes

pastel/geometry/intersect/

#ifndef PASTELGEOMETRY_INTERSECT_LINE_ALIGNEDBOX_HPP
#define PASTELGEOMETRY_INTERSECT_LINE_ALIGNEDBOX_HPP

#include "pastel/geometry/intersect/intersect_line_alignedbox.h"

#include "pastel/sys/math/constants.h"
#include "pastel/geometry/shape/line.h"
#include "pastel/geometry/shape/alignedbox.h"
#include "pastel/sys/vector.h"

namespace Pastel
{

    template <typename Real, integer N>
    bool intersect(
        const Line<Real, N>& line,
        const AlignedBox<Real, N>& alignedBox,
        Vector<Real, 2>& hitList)
    {
        PENSURE_OP(line.n(), ==, alignedBox.n());

        Real tMin = -Infinity();
        Real tMax = Infinity();

        AlignedBox<Real, N> testAlignedBox = 
            alignedBox - line.position();

        integer dimension = line.n();

        for (integer i = 0;i < dimension;++i)
        {
            if (zero(line.direction()[i]))
            {
                // There are some algorithms that
                // do not check the direction for zero
                // to get infinities in the inverse
                // directions. This works
                // in the rest of the algorithm
                // almost in every case.
                // However, there are singular cases
                // that don't work correctly.
                // For an example, let:

                // f == -infinity &&
                // testAlignedBox.min()[i] == 0 &&
                // testAlignedBox.max()[i] >= 0
                // Then
                // t0 = testAlignedBox[i].min()[i] * f = 0
                // t1 = testAlignedBox[i].max()[i] * f = -infinity
                // This yields incorrect results.
                // There are 3 such cases more (+/- infinity,
                // whether min or max is zero).

                if (positive(testAlignedBox.min()[i]) ||
                    negative(testAlignedBox.max()[i]))
                {
                    return false;
                }
            }
            else
            {
                Real f = line.inverseDirection()[i];

                Real t0 = testAlignedBox.min()[i] * f;
                Real t1 = testAlignedBox.max()[i] * f;

                if (t0 > t1)
                {
                    std::swap(t0, t1);
                }

                if (t0 > tMin)
                {
                    tMin = t0;
                }
                if (t1 < tMax)
                {
                    tMax = t1;
                }
                if (tMin > tMax)
                {
                    return false;
                }
            }
        }

        hitList.set(tMin, tMax);

        return true;
    }

}

#endif