C#中线段与轴对齐框的交点

时间:2010-06-24 01:23:24

标签: c# 3d

我正在寻找一种算法,用于确定线段和轴对齐框之间的近交叉点和远交点。

这是我的方法定义:

public static Point3D[] IntersectionOfLineSegmentWithAxisAlignedBox(
    Point3D rayBegin, Point3D rayEnd, Point3D boxCenter, Size3D boxSize)

如果线段与框不相交,则该方法应返回一个空的Point3D数组。

从我迄今为止的研究中,我发现了一些高度优化算法的研究论文,但它们似乎都是用C ++编写的,需要将多个长类文件转换为C#。出于我的目的,一些相当有效的东西,易于理解的人获得点产品和交叉产品,以及简单/简短的东西将是首选。

4 个答案:

答案 0 :(得分:3)

以下是我最终使用的内容:

public static List<Point3D> IntersectionOfLineSegmentWithAxisAlignedBox(
    Point3D segmentBegin, Point3D segmentEnd, Point3D boxCenter, Size3D boxSize)
{
    var beginToEnd = segmentEnd - segmentBegin;
    var minToMax = new Vector3D(boxSize.X, boxSize.Y, boxSize.Z);
    var min = boxCenter - minToMax / 2;
    var max = boxCenter + minToMax / 2;
    var beginToMin = min - segmentBegin;
    var beginToMax = max - segmentBegin;
    var tNear = double.MinValue;
    var tFar = double.MaxValue;
    var intersections = new List<Point3D>();
    foreach (Axis axis in Enum.GetValues(typeof(Axis)))
    {
        if (beginToEnd.GetCoordinate(axis) == 0) // parallel
        {
            if (beginToMin.GetCoordinate(axis) > 0 || beginToMax.GetCoordinate(axis) < 0)
                return intersections; // segment is not between planes
        }
        else
        {
            var t1 = beginToMin.GetCoordinate(axis) / beginToEnd.GetCoordinate(axis);
            var t2 = beginToMax.GetCoordinate(axis) / beginToEnd.GetCoordinate(axis);
            var tMin = Math.Min(t1, t2);
            var tMax = Math.Max(t1, t2);
            if (tMin > tNear) tNear = tMin;
            if (tMax < tFar) tFar = tMax;
            if (tNear > tFar || tFar < 0) return intersections;

        }
    }
    if (tNear >= 0 && tNear <= 1) intersections.Add(segmentBegin + beginToEnd * tNear);
    if (tFar >= 0 && tFar <= 1) intersections.Add(segmentBegin + beginToEnd * tFar);
    return intersections;
}

public enum Axis
{
    X,
    Y,
    Z
}

public static double GetCoordinate(this Point3D point, Axis axis)
{
    switch (axis)
    {
        case Axis.X:
            return point.X;
        case Axis.Y:
            return point.Y;
        case Axis.Z:
            return point.Z;
        default:
            throw new ArgumentException();
    }
}

public static double GetCoordinate(this Vector3D vector, Axis axis)
{
    switch (axis)
    {
        case Axis.X:
            return vector.X;
        case Axis.Y:
            return vector.Y;
        case Axis.Z:
            return vector.Z;
        default:
            throw new ArgumentException();
    }
}

答案 1 :(得分:2)

嗯,对于一个轴对齐的框,它非常简单:你必须找到你的光线与6个平面的交点(由方框面定义),然后检查你发现的点对照顶点坐标限制。

答案 2 :(得分:0)

这是另一个高效且优雅的解决方案。

它是用C ++编写的,但是可以轻松地翻译成Python(根据从其他SO问题到该线程的链接)或C#(根据原始PO)。它假定已访问3D向量类/结构(此处为Vector3f),并且定义了基本的代数运算符/运算符。在C ++中,可以使用Eigen::Vector3f之类的东西;在Python中,可以使用简单的NumPy array;在C#中,可以使用Vector3

该例程经过优化,可以检查是否有多个网格对齐的框。

使用简单的基于端点的构造函数定义细分类:

class Segment {
public:
    Segment(const Vector3f& startPoint, const Vector3f& endPoint) :
            origin(startPoint), direction(endPoint - startPoint),
            inverseDirection(Vector3f(1.0) / direction),
            sign{(inverseDirection.x < 0),(inverseDirection.y < 0),(inverseDirection.z < 0)}
            {}

    float length(){
        return sqrtf(direction.x * direction.x + direction.y * direction.y +
                     direction.z * direction.z);
    }
    Vector3f origin, endpoint, direction;
    Vector3f inverseDirection;
    int sign[3];
};

执行检查的实际例程:

bool SegmentIntersectsGridAlignedBox3D(Segment segment, Vector3f boxMin, Vector3f boxMax){
    float tmin, tmax, tymin, tymax, tzmin, tzmax;
    Vector3f bounds[] = {boxMin, boxMax};

    tmin = (bounds[segment.sign[0]].x - segment.origin.x) * segment.inverseDirection.x;
    tmax = (bounds[1 - segment.sign[0]].x - segment.origin.x) * segment.inverseDirection.x;
    tymin = (bounds[segment.sign[1]].y - segment.origin.y) * segment.inverseDirection.y;
    tymax = (bounds[1 - segment.sign[1]].y - segment.origin.y) * segment.inverseDirection.y;

    if ((tmin > tymax) || (tymin > tmax)){
        return false;
    }
    if (tymin > tmin) {
        tmin = tymin;
    }
    if (tymax < tmax){
        tmax = tymax;
    }

    tzmin = (bounds[segment.sign[2]].z - segment.origin.z) * segment.inverseDirection.z;
    tzmax = (bounds[1 - segment.sign[2]].z - segment.origin.z) * segment.inverseDirection.z;

    if ((tmin > tzmax) || (tzmin > tmax)){
        return false;
    }
    if (tzmin > tmin){
        tmin = tzmin;
    }
    if (tzmax < tmax){
        tmax = tzmax;
    }
    // this last check is different from the 'ray' case in below references: 
    // we need to check that the segment is on the span of the line
    // that intersects the box
    return !(tmax < 0.0f || tmin > 1.0f); 
}

该答案的信用主要归于scratchpixel.com和this tutorial的作者,该作者基于:

威廉姆斯,艾米,史蒂夫·巴鲁斯,R。基思·莫利和彼得·雪莉。 “一种有效且强大的射线盒相交算法。” Journal of graphics tools 10,否。 1(2005):49-54 link

您可以在本教程中找到有关代码的非常详细的说明。

我所做的只是稍微修改了代码以解决沿线的问题,而不是射线相交的问题。

答案 3 :(得分:0)

答案的优化版本。没有理由进行分配或查找。

public struct Ray3
{
  public Vec3 origin, direction;

        public bool IntersectRayBox(Box3 box, out Vec3 point1, out Vec3 point2)
        {
            var min = (box.center - (box.size / 2)) - origin;
            var max = (box.center + (box.size / 2)) - origin;
            float near = float.MinValue;
            float far = float.MaxValue;

            // X
            float t1 = min.x / direction.x;
            float t2 = max.x / direction.x;
            float tMin = Math.Min(t1, t2);
            float tMax = Math.Max(t1, t2);
            if (tMin > near) near = tMin;
            if (tMax < far) far = tMax;
            if (near > far || far < 0)
            {
                point1 = Vec3.zero;
                point2 = Vec3.zero;
                return false;
            }

            // Y
            t1 = min.y / direction.y;
            t2 = max.y / direction.y;
            tMin = Math.Min(t1, t2);
            tMax = Math.Max(t1, t2);
            if (tMin > near) near = tMin;
            if (tMax < far) far = tMax;
            if (near > far || far < 0)
            {
                point1 = Vec3.zero;
                point2 = Vec3.zero;
                return false;
            }

            // Z
            t1 = min.z / direction.z;
            t2 = max.z / direction.z;
            tMin = Math.Min(t1, t2);
            tMax = Math.Max(t1, t2);
            if (tMin > near) near = tMin;
            if (tMax < far) far = tMax;
            if (near > far || far < 0)
            {
                point1 = Vec3.zero;
                point2 = Vec3.zero;
                return false;
            }

            point1 = origin + direction * near;
            point2 = origin + direction * far;
            return true;
        }
}