///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002-2010, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// *       Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// *       Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// *       Neither the name of Industrial Light & Magic nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////



#ifndef INCLUDED_IMATHBOXALGO_H
#define INCLUDED_IMATHBOXALGO_H


//---------------------------------------------------------------------------
//
//	This file contains algorithms applied to or in conjunction
//	with bounding boxes (Imath::Box). These algorithms require
//	more headers to compile. The assumption made is that these
//	functions are called much less often than the basic box
//	functions or these functions require more support classes.
//
//	Contains:
//
//	T clip<T>(const T& in, const Box<T>& box)
//
//	Vec3<T> closestPointOnBox(const Vec3<T>&, const Box<Vec3<T>>& )
//
//	Vec3<T> closestPointInBox(const Vec3<T>&, const Box<Vec3<T>>& )
//
//	Box< Vec3<S> > transform(const Box<Vec3<S>>&, const Matrix44<T>&)
//	Box< Vec3<S> > affineTransform(const Box<Vec3<S>>&, const Matrix44<T>&)
//
//	void transform(const Box<Vec3<S>>&, const Matrix44<T>&, Box<V3ec3<S>>&)
//	void affineTransform(const Box<Vec3<S>>&,
//                           const Matrix44<T>&,
//                           Box<V3ec3<S>>&)
//
//	bool findEntryAndExitPoints(const Line<T> &line,
//				    const Box< Vec3<T> > &box,
//				    Vec3<T> &enterPoint,
//				    Vec3<T> &exitPoint)
//
//	bool intersects(const Box<Vec3<T>> &box,
//			const Line3<T> &ray,
//			Vec3<T> intersectionPoint)
//
//	bool intersects(const Box<Vec3<T>> &box, const Line3<T> &ray)
//
//---------------------------------------------------------------------------

#include "ImathBox.h"
#include "ImathMatrix.h"
#include "ImathLineAlgo.h"
#include "ImathPlane.h"

namespace Imath {


template <class T>
inline T
clip (const T &p, const Box<T> &box)
{
    //
    // Clip the coordinates of a point, p, against a box.
    // The result, q, is the closest point to p that is inside the box.
    //

    T q;

    for (int i = 0; i < int (box.min.dimensions()); i++)
    {
    if (p[i] < box.min[i])
        q[i] = box.min[i];
    else if (p[i] > box.max[i])
        q[i] = box.max[i];
    else
        q[i] = p[i];
    }

    return q;
}


template <class T>
inline T
closestPointInBox (const T &p, const Box<T> &box)
{
    return clip (p, box);
}


template <class T>
Vec3<T>
closestPointOnBox (const Vec3<T> &p, const Box< Vec3<T> > &box)
{
    //
    // Find the point, q, on the surface of
    // the box, that is closest to point p.
    //
    // If the box is empty, return p.
    //

    if (box.isEmpty())
    return p;

    Vec3<T> q = closestPointInBox (p, box);

    if (q == p)
    {
    Vec3<T> d1 = p - box.min;
    Vec3<T> d2 = box.max - p;

    Vec3<T> d ((d1.x < d2.x)? d1.x: d2.x,
           (d1.y < d2.y)? d1.y: d2.y,
           (d1.z < d2.z)? d1.z: d2.z);

    if (d.x < d.y && d.x < d.z)
    {
        q.x = (d1.x < d2.x)? box.min.x: box.max.x;
    }
    else if (d.y < d.z)
    {
        q.y = (d1.y < d2.y)? box.min.y: box.max.y;
    }
    else
    {
        q.z = (d1.z < d2.z)? box.min.z: box.max.z;
    }
    }

    return q;
}


template <class S, class T>
Box< Vec3<S> >
transform (const Box< Vec3<S> > &box, const Matrix44<T> &m)
{
    //
    // Transform a 3D box by a matrix, and compute a new box that
    // tightly encloses the transformed box.
    //
    // If m is an affine transform, then we use James Arvo's fast
    // method as described in "Graphics Gems", Academic Press, 1990,
    // pp. 548-550.
    //

    //
    // A transformed empty box is still empty, and a transformed infinite box
    // is still infinite
    //

    if (box.isEmpty() || box.isInfinite())
    return box;

    //
    // If the last column of m is (0 0 0 1) then m is an affine
    // transform, and we use the fast Graphics Gems trick.
    //

    if (m[0][3] == 0 && m[1][3] == 0 && m[2][3] == 0 && m[3][3] == 1)
    {
    Box< Vec3<S> > newBox;

    for (int i = 0; i < 3; i++)
        {
        newBox.min[i] = newBox.max[i] = (S) m[3][i];

        for (int j = 0; j < 3; j++)
            {
        S a, b;

        a = (S) m[j][i] * box.min[j];
        b = (S) m[j][i] * box.max[j];

        if (a < b)
                {
            newBox.min[i] += a;
            newBox.max[i] += b;
        }
        else
                {
            newBox.min[i] += b;
            newBox.max[i] += a;
        }
        }
    }

    return newBox;
    }

    //
    // M is a projection matrix.  Do things the naive way:
    // Transform the eight corners of the box, and find an
    // axis-parallel box that encloses the transformed corners.
    //

    Vec3<S> points[8];

    points[0][0] = points[1][0] = points[2][0] = points[3][0] = box.min[0];
    points[4][0] = points[5][0] = points[6][0] = points[7][0] = box.max[0];

    points[0][1] = points[1][1] = points[4][1] = points[5][1] = box.min[1];
    points[2][1] = points[3][1] = points[6][1] = points[7][1] = box.max[1];

    points[0][2] = points[2][2] = points[4][2] = points[6][2] = box.min[2];
    points[1][2] = points[3][2] = points[5][2] = points[7][2] = box.max[2];

    Box< Vec3<S> > newBox;

    for (int i = 0; i < 8; i++)
    newBox.extendBy (points[i] * m);

    return newBox;
}

template <class S, class T>
void
transform (const Box< Vec3<S> > &box,
           const Matrix44<T>    &m,
           Box< Vec3<S> >       &result)
{
    //
    // Transform a 3D box by a matrix, and compute a new box that
    // tightly encloses the transformed box.
    //
    // If m is an affine transform, then we use James Arvo's fast
    // method as described in "Graphics Gems", Academic Press, 1990,
    // pp. 548-550.
    //

    //
    // A transformed empty box is still empty, and a transformed infinite
    // box is still infinite
    //

    if (box.isEmpty() || box.isInfinite())
    {
    return;
    }

    //
    // If the last column of m is (0 0 0 1) then m is an affine
    // transform, and we use the fast Graphics Gems trick.
    //

    if (m[0][3] == 0 && m[1][3] == 0 && m[2][3] == 0 && m[3][3] == 1)
    {
    for (int i = 0; i < 3; i++)
        {
        result.min[i] = result.max[i] = (S) m[3][i];

        for (int j = 0; j < 3; j++)
            {
        S a, b;

        a = (S) m[j][i] * box.min[j];
        b = (S) m[j][i] * box.max[j];

        if (a < b)
                {
            result.min[i] += a;
            result.max[i] += b;
        }
        else
                {
            result.min[i] += b;
            result.max[i] += a;
        }
        }
    }

    return;
    }

    //
    // M is a projection matrix.  Do things the naive way:
    // Transform the eight corners of the box, and find an
    // axis-parallel box that encloses the transformed corners.
    //

    Vec3<S> points[8];

    points[0][0] = points[1][0] = points[2][0] = points[3][0] = box.min[0];
    points[4][0] = points[5][0] = points[6][0] = points[7][0] = box.max[0];

    points[0][1] = points[1][1] = points[4][1] = points[5][1] = box.min[1];
    points[2][1] = points[3][1] = points[6][1] = points[7][1] = box.max[1];

    points[0][2] = points[2][2] = points[4][2] = points[6][2] = box.min[2];
    points[1][2] = points[3][2] = points[5][2] = points[7][2] = box.max[2];

    for (int i = 0; i < 8; i++)
    result.extendBy (points[i] * m);
}


template <class S, class T>
Box< Vec3<S> >
affineTransform (const Box< Vec3<S> > &box, const Matrix44<T> &m)
{
    //
    // Transform a 3D box by a matrix whose rightmost column
    // is (0 0 0 1), and compute a new box that tightly encloses
    // the transformed box.
    //
    // As in the transform() function, above, we use James Arvo's
    // fast method.
    //

    if (box.isEmpty() || box.isInfinite())
    {
    //
    // A transformed empty or infinite box is still empty or infinite
    //

    return box;
    }

    Box< Vec3<S> > newBox;

    for (int i = 0; i < 3; i++)
    {
    newBox.min[i] = newBox.max[i] = (S) m[3][i];

    for (int j = 0; j < 3; j++)
    {
        S a, b;

        a = (S) m[j][i] * box.min[j];
        b = (S) m[j][i] * box.max[j];

        if (a < b)
        {
        newBox.min[i] += a;
        newBox.max[i] += b;
        }
        else
        {
        newBox.min[i] += b;
        newBox.max[i] += a;
        }
    }
    }

    return newBox;
}

template <class S, class T>
void
affineTransform (const Box< Vec3<S> > &box,
                 const Matrix44<T>    &m,
                 Box<Vec3<S> >        &result)
{
    //
    // Transform a 3D box by a matrix whose rightmost column
    // is (0 0 0 1), and compute a new box that tightly encloses
    // the transformed box.
    //
    // As in the transform() function, above, we use James Arvo's
    // fast method.
    //

    if (box.isEmpty())
    {
    //
    // A transformed empty box is still empty
    //
        result.makeEmpty();
    return;
    }

    if (box.isInfinite())
    {
    //
    // A transformed infinite box is still infinite
    //
        result.makeInfinite();
    return;
    }

    for (int i = 0; i < 3; i++)
    {
    result.min[i] = result.max[i] = (S) m[3][i];

    for (int j = 0; j < 3; j++)
    {
        S a, b;

        a = (S) m[j][i] * box.min[j];
        b = (S) m[j][i] * box.max[j];

        if (a < b)
        {
        result.min[i] += a;
        result.max[i] += b;
        }
        else
        {
        result.min[i] += b;
        result.max[i] += a;
        }
    }
    }
}


template <class T>
bool
findEntryAndExitPoints (const Line3<T> &r,
            const Box<Vec3<T> > &b,
            Vec3<T> &entry,
            Vec3<T> &exit)
{
    //
    // Compute the points where a ray, r, enters and exits a box, b:
    //
    // findEntryAndExitPoints() returns
    //
    //     - true if the ray starts inside the box or if the
    //       ray starts outside and intersects the box
    //
    //	   - false otherwise (that is, if the ray does not
    //       intersect the box)
    //
    // The entry and exit points are
    //
    //     - points on two of the faces of the box when
    //       findEntryAndExitPoints() returns true
    //       (The entry end exit points may be on either
    //       side of the ray's origin)
    //
    //     - undefined when findEntryAndExitPoints()
    //       returns false
    //

    if (b.isEmpty())
    {
    //
    // No ray intersects an empty box
    //

    return false;
    }

    //
    // The following description assumes that the ray's origin is outside
    // the box, but the code below works even if the origin is inside the
    // box:
    //
    // Between one and three "frontfacing" sides of the box are oriented
    // towards the ray's origin, and between one and three "backfacing"
    // sides are oriented away from the ray's origin.
    // We intersect the ray with the planes that contain the sides of the
    // box, and compare the distances between the ray's origin and the
    // ray-plane intersections.  The ray intersects the box if the most
    // distant frontfacing intersection is nearer than the nearest
    // backfacing intersection.  If the ray does intersect the box, then
    // the most distant frontfacing ray-plane intersection is the entry
    // point and the nearest backfacing ray-plane intersection is the
    // exit point.
    //

    const T TMAX = limits<T>::max();

    T tFrontMax = -TMAX;
    T tBackMin = TMAX;

    //
    // Minimum and maximum X sides.
    //

    if (r.dir.x >= 0)
    {
    T d1 = b.max.x - r.pos.x;
    T d2 = b.min.x - r.pos.x;

    if (r.dir.x > 1 ||
        (abs (d1) < TMAX * r.dir.x &&
         abs (d2) < TMAX * r.dir.x))
    {
        T t1 = d1 / r.dir.x;
        T t2 = d2 / r.dir.x;

        if (tBackMin > t1)
        {
        tBackMin = t1;

        exit.x = b.max.x;
        exit.y = clamp (r.pos.y + t1 * r.dir.y, b.min.y, b.max.y);
        exit.z = clamp (r.pos.z + t1 * r.dir.z, b.min.z, b.max.z);
        }

        if (tFrontMax < t2)
        {
        tFrontMax = t2;

        entry.x = b.min.x;
        entry.y = clamp (r.pos.y + t2 * r.dir.y, b.min.y, b.max.y);
        entry.z = clamp (r.pos.z + t2 * r.dir.z, b.min.z, b.max.z);
        }
    }
    else if (r.pos.x < b.min.x || r.pos.x > b.max.x)
    {
        return false;
    }
    }
    else // r.dir.x < 0
    {
    T d1 = b.min.x - r.pos.x;
    T d2 = b.max.x - r.pos.x;

    if (r.dir.x < -1 ||
        (abs (d1) < -TMAX * r.dir.x &&
         abs (d2) < -TMAX * r.dir.x))
    {
        T t1 = d1 / r.dir.x;
        T t2 = d2 / r.dir.x;

        if (tBackMin > t1)
        {
        tBackMin = t1;

        exit.x = b.min.x;
        exit.y = clamp (r.pos.y + t1 * r.dir.y, b.min.y, b.max.y);
        exit.z = clamp (r.pos.z + t1 * r.dir.z, b.min.z, b.max.z);
        }

        if (tFrontMax < t2)
        {
        tFrontMax = t2;

        entry.x = b.max.x;
        entry.y = clamp (r.pos.y + t2 * r.dir.y, b.min.y, b.max.y);
        entry.z = clamp (r.pos.z + t2 * r.dir.z, b.min.z, b.max.z);
        }
    }
    else if (r.pos.x < b.min.x || r.pos.x > b.max.x)
    {
        return false;
    }
    }

    //
    // Minimum and maximum Y sides.
    //

    if (r.dir.y >= 0)
    {
    T d1 = b.max.y - r.pos.y;
    T d2 = b.min.y - r.pos.y;

    if (r.dir.y > 1 ||
        (abs (d1) < TMAX * r.dir.y &&
         abs (d2) < TMAX * r.dir.y))
    {
        T t1 = d1 / r.dir.y;
        T t2 = d2 / r.dir.y;

        if (tBackMin > t1)
        {
        tBackMin = t1;

        exit.x = clamp (r.pos.x + t1 * r.dir.x, b.min.x, b.max.x);
        exit.y = b.max.y;
        exit.z = clamp (r.pos.z + t1 * r.dir.z, b.min.z, b.max.z);
        }

        if (tFrontMax < t2)
        {
        tFrontMax = t2;

        entry.x = clamp (r.pos.x + t2 * r.dir.x, b.min.x, b.max.x);
        entry.y = b.min.y;
        entry.z = clamp (r.pos.z + t2 * r.dir.z, b.min.z, b.max.z);
        }
    }
    else if (r.pos.y < b.min.y || r.pos.y > b.max.y)
    {
        return false;
    }
    }
    else // r.dir.y < 0
    {
    T d1 = b.min.y - r.pos.y;
    T d2 = b.max.y - r.pos.y;

    if (r.dir.y < -1 ||
        (abs (d1) < -TMAX * r.dir.y &&
         abs (d2) < -TMAX * r.dir.y))
    {
        T t1 = d1 / r.dir.y;
        T t2 = d2 / r.dir.y;

        if (tBackMin > t1)
        {
        tBackMin = t1;

        exit.x = clamp (r.pos.x + t1 * r.dir.x, b.min.x, b.max.x);
        exit.y = b.min.y;
        exit.z = clamp (r.pos.z + t1 * r.dir.z, b.min.z, b.max.z);
        }

        if (tFrontMax < t2)
        {
        tFrontMax = t2;

        entry.x = clamp (r.pos.x + t2 * r.dir.x, b.min.x, b.max.x);
        entry.y = b.max.y;
        entry.z = clamp (r.pos.z + t2 * r.dir.z, b.min.z, b.max.z);
        }
    }
    else if (r.pos.y < b.min.y || r.pos.y > b.max.y)
    {
        return false;
    }
    }

    //
    // Minimum and maximum Z sides.
    //

    if (r.dir.z >= 0)
    {
    T d1 = b.max.z - r.pos.z;
    T d2 = b.min.z - r.pos.z;

    if (r.dir.z > 1 ||
        (abs (d1) < TMAX * r.dir.z &&
         abs (d2) < TMAX * r.dir.z))
    {
        T t1 = d1 / r.dir.z;
        T t2 = d2 / r.dir.z;

        if (tBackMin > t1)
        {
        tBackMin = t1;

        exit.x = clamp (r.pos.x + t1 * r.dir.x, b.min.x, b.max.x);
        exit.y = clamp (r.pos.y + t1 * r.dir.y, b.min.y, b.max.y);
        exit.z = b.max.z;
        }

        if (tFrontMax < t2)
        {
        tFrontMax = t2;

        entry.x = clamp (r.pos.x + t2 * r.dir.x, b.min.x, b.max.x);
        entry.y = clamp (r.pos.y + t2 * r.dir.y, b.min.y, b.max.y);
        entry.z = b.min.z;
        }
    }
    else if (r.pos.z < b.min.z || r.pos.z > b.max.z)
    {
        return false;
    }
    }
    else // r.dir.z < 0
    {
    T d1 = b.min.z - r.pos.z;
    T d2 = b.max.z - r.pos.z;

    if (r.dir.z < -1 ||
        (abs (d1) < -TMAX * r.dir.z &&
         abs (d2) < -TMAX * r.dir.z))
    {
        T t1 = d1 / r.dir.z;
        T t2 = d2 / r.dir.z;

        if (tBackMin > t1)
        {
        tBackMin = t1;

        exit.x = clamp (r.pos.x + t1 * r.dir.x, b.min.x, b.max.x);
        exit.y = clamp (r.pos.y + t1 * r.dir.y, b.min.y, b.max.y);
        exit.z = b.min.z;
        }

        if (tFrontMax < t2)
        {
        tFrontMax = t2;

        entry.x = clamp (r.pos.x + t2 * r.dir.x, b.min.x, b.max.x);
        entry.y = clamp (r.pos.y + t2 * r.dir.y, b.min.y, b.max.y);
        entry.z = b.max.z;
        }
    }
    else if (r.pos.z < b.min.z || r.pos.z > b.max.z)
    {
        return false;
    }
    }

    return tFrontMax <= tBackMin;
}


template<class T>
bool
intersects (const Box< Vec3<T> > &b, const Line3<T> &r, Vec3<T> &ip)
{
    //
    // Intersect a ray, r, with a box, b, and compute the intersection
    // point, ip:
    //
    // intersect() returns
    //
    //     - true if the ray starts inside the box or if the
    //       ray starts outside and intersects the box
    //
    //     - false if the ray starts outside the box and intersects it,
    //       but the intersection is behind the ray's origin.
    //
    //     - false if the ray starts outside and does not intersect it
    //
    // The intersection point is
    //
    //     - the ray's origin if the ray starts inside the box
    //
    //     - a point on one of the faces of the box if the ray
    //       starts outside the box
    //
    //     - undefined when intersect() returns false
    //

    if (b.isEmpty())
    {
    //
    // No ray intersects an empty box
    //

    return false;
    }

    if (b.intersects (r.pos))
    {
    //
    // The ray starts inside the box
    //

    ip = r.pos;
    return true;
    }

    //
    // The ray starts outside the box.  Between one and three "frontfacing"
    // sides of the box are oriented towards the ray, and between one and
    // three "backfacing" sides are oriented away from the ray.
    // We intersect the ray with the planes that contain the sides of the
    // box, and compare the distances between ray's origin and the ray-plane
    // intersections.
    // The ray intersects the box if the most distant frontfacing intersection
    // is nearer than the nearest backfacing intersection.  If the ray does
    // intersect the box, then the most distant frontfacing ray-plane
    // intersection is the ray-box intersection.
    //

    const T TMAX = limits<T>::max();

    T tFrontMax = -1;
    T tBackMin = TMAX;

    //
    // Minimum and maximum X sides.
    //

    if (r.dir.x > 0)
    {
    if (r.pos.x > b.max.x)
        return false;

    T d = b.max.x - r.pos.x;

    if (r.dir.x > 1 || d < TMAX * r.dir.x)
    {
        T t = d / r.dir.x;

        if (tBackMin > t)
        tBackMin = t;
    }

    if (r.pos.x <= b.min.x)
    {
        T d = b.min.x - r.pos.x;
        T t = (r.dir.x > 1 || d < TMAX * r.dir.x)? d / r.dir.x: TMAX;

        if (tFrontMax < t)
        {
        tFrontMax = t;

        ip.x = b.min.x;
        ip.y = clamp (r.pos.y + t * r.dir.y, b.min.y, b.max.y);
        ip.z = clamp (r.pos.z + t * r.dir.z, b.min.z, b.max.z);
        }
    }
    }
    else if (r.dir.x < 0)
    {
    if (r.pos.x < b.min.x)
        return false;

    T d = b.min.x - r.pos.x;

    if (r.dir.x < -1 || d > TMAX * r.dir.x)
    {
        T t = d / r.dir.x;

        if (tBackMin > t)
        tBackMin = t;
    }

    if (r.pos.x >= b.max.x)
    {
        T d = b.max.x - r.pos.x;
        T t = (r.dir.x < -1 || d > TMAX * r.dir.x)? d / r.dir.x: TMAX;

        if (tFrontMax < t)
        {
        tFrontMax = t;

        ip.x = b.max.x;
        ip.y = clamp (r.pos.y + t * r.dir.y, b.min.y, b.max.y);
        ip.z = clamp (r.pos.z + t * r.dir.z, b.min.z, b.max.z);
        }
    }
    }
    else // r.dir.x == 0
    {
    if (r.pos.x < b.min.x || r.pos.x > b.max.x)
        return false;
    }

    //
    // Minimum and maximum Y sides.
    //

    if (r.dir.y > 0)
    {
    if (r.pos.y > b.max.y)
        return false;

    T d = b.max.y - r.pos.y;

    if (r.dir.y > 1 || d < TMAX * r.dir.y)
    {
        T t = d / r.dir.y;

        if (tBackMin > t)
        tBackMin = t;
    }

    if (r.pos.y <= b.min.y)
    {
        T d = b.min.y - r.pos.y;
        T t = (r.dir.y > 1 || d < TMAX * r.dir.y)? d / r.dir.y: TMAX;

        if (tFrontMax < t)
        {
        tFrontMax = t;

        ip.x = clamp (r.pos.x + t * r.dir.x, b.min.x, b.max.x);
        ip.y = b.min.y;
        ip.z = clamp (r.pos.z + t * r.dir.z, b.min.z, b.max.z);
        }
    }
    }
    else if (r.dir.y < 0)
    {
    if (r.pos.y < b.min.y)
        return false;

    T d = b.min.y - r.pos.y;

    if (r.dir.y < -1 || d > TMAX * r.dir.y)
    {
        T t = d / r.dir.y;

        if (tBackMin > t)
        tBackMin = t;
    }

    if (r.pos.y >= b.max.y)
    {
        T d = b.max.y - r.pos.y;
        T t = (r.dir.y < -1 || d > TMAX * r.dir.y)? d / r.dir.y: TMAX;

        if (tFrontMax < t)
        {
        tFrontMax = t;

        ip.x = clamp (r.pos.x + t * r.dir.x, b.min.x, b.max.x);
        ip.y = b.max.y;
        ip.z = clamp (r.pos.z + t * r.dir.z, b.min.z, b.max.z);
        }
    }
    }
    else // r.dir.y == 0
    {
    if (r.pos.y < b.min.y || r.pos.y > b.max.y)
        return false;
    }

    //
    // Minimum and maximum Z sides.
    //

    if (r.dir.z > 0)
    {
    if (r.pos.z > b.max.z)
        return false;

    T d = b.max.z - r.pos.z;

    if (r.dir.z > 1 || d < TMAX * r.dir.z)
    {
        T t = d / r.dir.z;

        if (tBackMin > t)
        tBackMin = t;
    }

    if (r.pos.z <= b.min.z)
    {
        T d = b.min.z - r.pos.z;
        T t = (r.dir.z > 1 || d < TMAX * r.dir.z)? d / r.dir.z: TMAX;

        if (tFrontMax < t)
        {
        tFrontMax = t;

        ip.x = clamp (r.pos.x + t * r.dir.x, b.min.x, b.max.x);
        ip.y = clamp (r.pos.y + t * r.dir.y, b.min.y, b.max.y);
        ip.z = b.min.z;
        }
    }
    }
    else if (r.dir.z < 0)
    {
    if (r.pos.z < b.min.z)
        return false;

    T d = b.min.z - r.pos.z;

    if (r.dir.z < -1 || d > TMAX * r.dir.z)
    {
        T t = d / r.dir.z;

        if (tBackMin > t)
        tBackMin = t;
    }

    if (r.pos.z >= b.max.z)
    {
        T d = b.max.z - r.pos.z;
        T t = (r.dir.z < -1 || d > TMAX * r.dir.z)? d / r.dir.z: TMAX;

        if (tFrontMax < t)
        {
        tFrontMax = t;

        ip.x = clamp (r.pos.x + t * r.dir.x, b.min.x, b.max.x);
        ip.y = clamp (r.pos.y + t * r.dir.y, b.min.y, b.max.y);
        ip.z = b.max.z;
        }
    }
    }
    else // r.dir.z == 0
    {
    if (r.pos.z < b.min.z || r.pos.z > b.max.z)
        return false;
    }

    return tFrontMax <= tBackMin;
}


template<class T>
bool
intersects (const Box< Vec3<T> > &box, const Line3<T> &ray)
{
    Vec3<T> ignored;
    return intersects (box, ray, ignored);
}


} // namespace Imath

#endif
