3D线平面相交

| 如果给定一条直线(由矢量或直线上的两个点表示),如何找到直线与平面相交的点?我已经找到了很多资源,但是我不明白那里的方程式(它们似乎不是标准的代数)。我想要一个可由标准编程语言(我使用Java)解释的方程式(无论时间长短)。     
已邀请:
这是Java中的一种方法,用于查找线和平面之间的交点。没有包含向量方法,但是它们的功能很容易说明。
/**
 * Determines the point of intersection between a plane defined by a point and a normal vector and a line defined by a point and a direction vector.
 *
 * @param planePoint    A point on the plane.
 * @param planeNormal   The normal vector of the plane.
 * @param linePoint     A point on the line.
 * @param lineDirection The direction vector of the line.
 * @return The point of intersection between the line and the plane, null if the line is parallel to the plane.
 */
public static Vector lineIntersection(Vector planePoint, Vector planeNormal, Vector linePoint, Vector lineDirection) {
    if (planeNormal.dot(lineDirection.normalize()) == 0) {
        return null;
    }

    double t = (planeNormal.dot(planePoint) - planeNormal.dot(linePoint)) / planeNormal.dot(lineDirection.normalize());
    return linePoint.plus(lineDirection.normalize().scale(t));
}
    
这是一个Python示例,该示例查找线和平面的交点。 平面可以是点和法线,也可以是4d向量(法线形式), 在下面的示例中(提供了两者的代码)。 还要注意,此函数会计算一个值,该值表示该点在直线上的位置(在下面的代码中称为“1ѭ”)。您可能也想返回此值,因为从0到1的值与线段相交-这可能对调用方有用。 代码注释中指出了其他细节。 注意:此示例使用无任何依赖的纯函数-使其易于迁移到其他语言。使用
Vector
数据类型和运算符重载,它可以更简洁(包括在下面的示例中)。
# intersection function
def isect_line_plane_v3(p0, p1, p_co, p_no, epsilon=1e-6):
    \"\"\"
    p0, p1: Define the line.
    p_co, p_no: define the plane:
        p_co Is a point on the plane (plane coordinate).
        p_no Is a normal vector defining the plane direction;
             (does not need to be normalized).

    Return a Vector or None (when the intersection can\'t be found).
    \"\"\"

    u = sub_v3v3(p1, p0)
    dot = dot_v3v3(p_no, u)

    if abs(dot) > epsilon:
        # The factor of the point between p0 -> p1 (0 - 1)
        # if \'fac\' is between (0 - 1) the point intersects with the segment.
        # Otherwise:
        #  < 0.0: behind p0.
        #  > 1.0: infront of p1.
        w = sub_v3v3(p0, p_co)
        fac = -dot_v3v3(p_no, w) / dot
        u = mul_v3_fl(u, fac)
        return add_v3v3(p0, u)
    else:
        # The segment is parallel to plane.
        return None

# ----------------------
# generic math functions

def add_v3v3(v0, v1):
    return (
        v0[0] + v1[0],
        v0[1] + v1[1],
        v0[2] + v1[2],
        )


def sub_v3v3(v0, v1):
    return (
        v0[0] - v1[0],
        v0[1] - v1[1],
        v0[2] - v1[2],
        )


def dot_v3v3(v0, v1):
    return (
        (v0[0] * v1[0]) +
        (v0[1] * v1[1]) +
        (v0[2] * v1[2])
        )


def len_squared_v3(v0):
    return dot_v3v3(v0, v0)


def mul_v3_fl(v0, f):
    return (
        v0[0] * f,
        v0[1] * f,
        v0[2] * f,
        )
如果将平面定义为4d向量(法线形式),则需要在平面上找到一个点,然后像以前一样计算交点(请参见
p_co
赋值)。
def isect_line_plane_v3_4d(p0, p1, plane, epsilon=1e-6):
    u = sub_v3v3(p1, p0)
    dot = dot_v3v3(plane, u)

    if abs(dot) > epsilon:
        # Calculate a point on the plane
        # (divide can be omitted for unit hessian-normal form).
        p_co = mul_v3_fl(plane, -plane[3] / len_squared_v3(plane))

        w = sub_v3v3(p0, p_co)
        fac = -dot_v3v3(plane, w) / dot
        u = mul_v3_fl(u, fac)
        return add_v3v3(p0, u)
    else:
        return None
为了进一步参考,它取自Blender并适用于Python。 math_geom.c中的
isect_line_plane_v3()
为了清楚起见,以下是使用mathutils API的版本(可以为其他带有运算符重载的数学库进行修改)。
# point-normal plane
def isect_line_plane_v3(p0, p1, p_co, p_no, epsilon=1e-6):
    u = p1 - p0
    dot = p_no * u
    if abs(dot) > epsilon:
        w = p0 - p_co
        fac = -(plane * w) / dot
        return p0 + (u * fac)
    else:
        return None


# normal-form plane
def isect_line_plane_v3_4d(p0, p1, plane, epsilon=1e-6):
    u = p1 - p0
    dot = plane.xyz * u
    if abs(dot) > epsilon:
        p_co = plane.xyz * (-plane[3] / plane.xyz.length_squared)

        w = p0 - p_co
        fac = -(plane * w) / dot
        return p0 + (u * fac)
    else:
        return None
    
您需要考虑以下三种情况: 平面与直线平行,且直线不在平面内(无相交) 平面不平行于线(一个交点) 平面包含线(线在其上的每个点相交) 您可以用参数化的形式表示行,如下所示: http://answers.yahoo.com/question/index?qid=20080830195656AA3aEBr 本讲座的前几页对飞机进行相同操作: http://math.mit.edu/classes/18.02/notes/lecture5compl-09.pdf 如果平面的法线垂直于沿线的方向,则说明您有一个边缘盒,需要查看它是否相交或位于平面内。 否则,您只有一个相交点,并且可以解决。 我知道这不是代码,但是要获得一个可靠的解决方案,您可能希望将其放在应用程序的上下文中。 编辑:这是一个示例,该示例恰好有一个相交点。假设您从第一个链接中的参数化方程式开始:
x = 5 - 13t
y = 5 - 11t
z = 5 - 8t
参数“ 9”可以是任何值。满足这些方程式的所有
(x, y, z)
的(无限)组构成直线。然后,如果您有一个平面方程,请说:
x + 2y + 2z = 5
(从此处获取)您可以将上面的
x
y
z
的等式替换为平面的等式,现在仅在参数
t
中。解决
t
。这是平面上那条线的
t
特定值。然后,您可以返回到线方程并用
t
代替来求解solve12ѭ,
y
z
。     
使用numpy和python:
#Based on http://geomalgorithms.com/a05-_intersect-1.html
from __future__ import print_function
import numpy as np

epsilon=1e-6

#Define plane
planeNormal = np.array([0, 0, 1])
planePoint = np.array([0, 0, 5]) #Any point on the plane

#Define ray
rayDirection = np.array([0, -1, -1])
rayPoint = np.array([0, 0, 10]) #Any point along the ray

ndotu = planeNormal.dot(rayDirection) 

if abs(ndotu) < epsilon:
    print (\"no intersection or line is within plane\")

w = rayPoint - planePoint
si = -planeNormal.dot(w) / ndotu
Psi = w + si * rayDirection + planePoint

print (\"intersection at\", Psi)
    
在Python中基于此Matlab代码(减去交叉检查)
# n: normal vector of the Plane 
# V0: any point that belongs to the Plane 
# P0: end point 1 of the segment P0P1
# P1:  end point 2 of the segment P0P1
n = np.array([1., 1., 1.])
V0 = np.array([1., 1., -5.])
P0 = np.array([-5., 1., -1.])
P1 = np.array([1., 2., 3.])

w = P0 - V0;
u = P1-P0;
N = -np.dot(n,w);
D = np.dot(n,u)
sI = N / D
I = P0+ sI*u
print I
结果
[-3.90909091  1.18181818 -0.27272727]
我以图形方式检查了它似乎可行, 我相信这是之前共享的链接的更强大的实现     
如果您有两个点p和q定义一条线,并且平面的坐标为一般笛卡尔形式ax + by + cz + d = 0,则可以使用参数方法。 如果您出于编码目的需要此代码,请参见以下JavaScript代码段:
/**
* findLinePlaneIntersectionCoords (to avoid requiring unnecessary instantiation)
* Given points p with px py pz and q that define a line, and the plane
* of formula ax+by+cz+d = 0, returns the intersection point or null if none.
*/
function findLinePlaneIntersectionCoords(px, py, pz, qx, qy, qz, a, b, c, d) {
    var tDenom = a*(qx-px) + b*(qy-py) + c*(qz-pz);
    if (tDenom == 0) return null;

    var t = - ( a*px + b*py + c*pz + d ) / tDenom;

    return {
        x: (px+t*(qx-px)),
        y: (py+t*(qy-py)),
        z: (pz+t*(qz-pz))
    };
}

// Example (plane at y = 10  and perpendicular line from the origin)
console.log(JSON.stringify(findLinePlaneIntersectionCoords(0,0,0,0,1,0,0,1,0,-10)));

// Example (no intersection, plane and line are parallel)
console.log(JSON.stringify(findLinePlaneIntersectionCoords(0,0,0,0,0,1,0,1,0,-10)));
这个问题很老,但是由于有这么方便的解决方案,我认为这可能会对某人有所帮助。 平面和直线相交以均等坐标表示时非常优雅,但让我们假设您只想要解决方案: 有一个向量4x1 p描述平面,使得平面上任何齐次点的p ^ T * x = 0。接下来计算直线L = ab ^ T-ba ^ T的拔钉器坐标,其中a = {point_1; 1},b = {point_2; 1},都为4x1 计算:x = L * p = {x0,x1,x2,x3} x_intersect =({x0,x1,x2} / x3)其中,如果x3为零,则在欧几里得意义上没有交集。     
只是为了扩展ZGorlock的答案,我已经完成了点积,3D矢量的正反缩放。这些计算的参考是“点积”,“添加两个3D向量”和“缩放”。注意:Vec3D只是一个具有点的自定义类:x,y和z。
/**
 * Determines the point of intersection between a plane defined by a point and a normal vector and a line defined by a point and a direction vector.
 *
 * @param planePoint    A point on the plane.
 * @param planeNormal   The normal vector of the plane.
 * @param linePoint     A point on the line.
 * @param lineDirection The direction vector of the line.
 * @return The point of intersection between the line and the plane, null if the line is parallel to the plane.
 */
public static Vec3D lineIntersection(Vec3D planePoint, Vec3D planeNormal, Vec3D linePoint, Vec3D lineDirection) {
    //ax × bx + ay × by
    int dot = (int) (planeNormal.x * lineDirection.x + planeNormal.y * lineDirection.y);
    if (dot == 0) {
        return null;
    }

    // Ref for dot product calculation: https://www.mathsisfun.com/algebra/vectors-dot-product.html
    int dot2 = (int) (planeNormal.x * planePoint.x + planeNormal.y * planePoint.y);
    int dot3 = (int) (planeNormal.x * linePoint.x + planeNormal.y * linePoint.y);
    int dot4 = (int) (planeNormal.x * lineDirection.x + planeNormal.y * lineDirection.y);

    double t = (dot2 - dot3) / dot4;

    float xs = (float) (lineDirection.x * t);
    float ys = (float) (lineDirection.y * t);
    float zs = (float) (lineDirection.z * t);
    Vec3D lineDirectionScale = new Vec3D( xs, ys, zs);

    float xa = (linePoint.x + lineDirectionScale.x);
    float ya = (linePoint.y + lineDirectionScale.y);
    float za = (linePoint.z + lineDirectionScale.z);

    return new Vec3D(xa, ya, za);
}
    

要回复问题请先登录注册