You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
5.5 KiB

3 months ago
// David Eberly, Geometric Tools, Redmond WA 98052
// Copyright (c) 1998-2021
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
// Version: 4.0.2019.08.13
#pragma once
#include <Mathematics/DistLineSegment.h>
#include <Mathematics/Rectangle.h>
#include <Mathematics/Vector3.h>
namespace gte
{
template <typename Real>
class DCPQuery<Real, Line3<Real>, Rectangle3<Real>>
{
public:
struct Result
{
Real distance, sqrDistance;
Real lineParameter, rectangleParameter[2];
Vector3<Real> closestPoint[2];
};
Result operator()(Line3<Real> const& line, Rectangle3<Real> const& rectangle)
{
Result result;
// Test if line intersects rectangle. If so, the squared distance
// is zero.
Vector3<Real> N = Cross(rectangle.axis[0], rectangle.axis[1]);
Real NdD = Dot(N, line.direction);
if (std::fabs(NdD) > (Real)0)
{
// The line and rectangle are not parallel, so the line
// intersects the plane of the rectangle.
Vector3<Real> diff = line.origin - rectangle.center;
Vector3<Real> basis[3]; // {D, U, V}
basis[0] = line.direction;
ComputeOrthogonalComplement<Real>(1, basis);
Real UdD0 = Dot(basis[1], rectangle.axis[0]);
Real UdD1 = Dot(basis[1], rectangle.axis[1]);
Real UdPmC = Dot(basis[1], diff);
Real VdD0 = Dot(basis[2], rectangle.axis[0]);
Real VdD1 = Dot(basis[2], rectangle.axis[1]);
Real VdPmC = Dot(basis[2], diff);
Real invDet = ((Real)1) / (UdD0 * VdD1 - UdD1 * VdD0);
// Rectangle coordinates for the point of intersection.
Real s0 = (VdD1 * UdPmC - UdD1 * VdPmC) * invDet;
Real s1 = (UdD0 * VdPmC - VdD0 * UdPmC) * invDet;
if (std::fabs(s0) <= rectangle.extent[0] && std::fabs(s1) <= rectangle.extent[1])
{
// Line parameter for the point of intersection.
Real DdD0 = Dot(line.direction, rectangle.axis[0]);
Real DdD1 = Dot(line.direction, rectangle.axis[1]);
Real DdDiff = Dot(line.direction, diff);
result.lineParameter = s0 * DdD0 + s1 * DdD1 - DdDiff;
// Rectangle coordinates for the point of intersection.
result.rectangleParameter[0] = s0;
result.rectangleParameter[1] = s1;
// The intersection point is inside or on the rectangle.
result.closestPoint[0] =
line.origin + result.lineParameter * line.direction;
result.closestPoint[1] =
rectangle.center + s0 * rectangle.axis[0] + s1 * rectangle.axis[1];
result.distance = (Real)0;
result.sqrDistance = (Real)0;
return result;
}
}
// Either (1) the line is not parallel to the rectangle and the
// point of intersection of the line and the plane of the
// rectangle is outside the rectangle or (2) the line and
// rectangle are parallel. Regardless, the closest point on
// the rectangle is on an edge of the rectangle. Compare the
// line to all four edges of the rectangle.
result.distance = std::numeric_limits<Real>::max();
result.sqrDistance = std::numeric_limits<Real>::max();
Vector3<Real> scaledDir[2] =
{
rectangle.extent[0] * rectangle.axis[0],
rectangle.extent[1] * rectangle.axis[1]
};
for (int i1 = 0, omi1 = 1; i1 <= 1; ++i1, --omi1)
{
for (int i0 = -1; i0 <= 1; i0 += 2)
{
Vector3<Real> segCenter = rectangle.center + scaledDir[i1] * (Real)i0;
Vector3<Real> segDirection = rectangle.axis[omi1];
Real segExtent = rectangle.extent[omi1];
Segment3<Real> segment(segCenter, segDirection, segExtent);
DCPQuery<Real, Line3<Real>, Segment3<Real>> query;
auto lsResult = query(line, segment);
if (lsResult.sqrDistance < result.sqrDistance)
{
result.sqrDistance = lsResult.sqrDistance;
result.distance = lsResult.distance;
result.lineParameter = lsResult.parameter[0];
// ratio is in [-1,1]
Real ratio = lsResult.parameter[1] / segExtent;
result.rectangleParameter[0] =
rectangle.extent[0] * (omi1 * i0 + i1 * ratio);
result.rectangleParameter[1] =
rectangle.extent[1] * (i1 * i0 + omi1 * ratio);
result.closestPoint[0] = lsResult.closestPoint[0];
result.closestPoint[1] = lsResult.closestPoint[1];
}
}
}
return result;
}
};
// Template alias for convenience.
template <typename Real>
using DCPLine3Rectangle3 = DCPQuery<Real, Line3<Real>, Rectangle3<Real>>;
}