web-dev-qa-db-fra.com

Intersection de plans de lignes 3D

Si on me donne une ligne (représentée par un vecteur ou deux points sur la ligne), comment trouver le point auquel la ligne coupe un plan? J'ai trouvé beaucoup de ressources à ce sujet mais je ne peux pas comprendre les équations là-bas (elles ne semblent pas être algébriques standard). Je voudrais une équation (peu importe la durée) qui peut être interprétée par un langage de programmation standard (j'utilise Java).

28
jt78

Voici une méthode en Java qui trouve l'intersection entre une ligne et un plan. Il existe des méthodes vectorielles qui ne sont pas incluses mais leurs fonctions sont assez explicites.

/**
 * 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));
}
12
ZGorlock

Voici un exemple Python qui trouve l'intersection d'une ligne et d'un plan.

Où l'avion peut être soit un point et un normal, soit un vecteur 4d (forme normale), dans les exemples ci-dessous (le code pour les deux est fourni) .

Notez également que cette fonction calcule une valeur représentant l'emplacement du point sur la ligne (appelée fac dans le code ci-dessous). Vous voudrez peut-être également le renvoyer, car les valeurs de 0 à 1 coupent le segment de ligne - ce qui peut être utile pour l'appelant.

Autres détails notés dans les commentaires de code.


Remarque: Cet exemple utilise des fonctions pures, sans aucune dépendance - pour faciliter le passage à d'autres langues. Avec un type de données Vector et une surcharge d'opérateur, il peut être plus concis (inclus dans l'exemple ci-dessous).

# 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,
        )

Si le plan est défini comme un vecteur 4d (forme normale) , nous devons trouver un point sur le plan, puis calculer l'intersection comme précédemment (voir p_co Affectation).

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

Pour plus de référence, cela a été tiré de Blender et adapté à Python. isect_line_plane_v3() dans math_geom.c


Pour plus de clarté, voici les versions utilisant l'API mathutils (qui peut être modifiée pour d'autres bibliothèques mathématiques avec surcharge d'opérateur) .

# 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
31
ideasman42

Vous devrez considérer trois cas:

  • Le plan est parallèle à la ligne et la ligne ne se trouve pas dans le plan (pas d'intersection)
  • Le plan n'est pas parallèle à la ligne (un point d'intersection)
  • Le plan contient la ligne (la ligne se coupe en tout point)

Vous pouvez exprimer la ligne sous forme paramétrisée, comme ici:

http://answers.yahoo.com/question/index?qid=20080830195656AA3aEBr

Les premières pages de cette conférence font de même pour l'avion:

http://math.mit.edu/classes/18.02/notes/lecture5compl-09.pdf

Si la normale au plan est perpendiculaire à la direction le long de la ligne, vous avez un cas Edge et vous devez voir s'il se coupe ou s'il se trouve dans le plan.

Sinon, vous avez un point d'intersection et pouvez le résoudre.

Je sais que ce n'est pas du code, mais pour obtenir une solution robuste, vous voudrez probablement mettre cela dans le contexte de votre application.

EDIT: Voici un exemple pour lequel il y a exactement un point d'intersection. Disons que vous commencez avec les équations paramétrées dans le premier lien:

x = 5 - 13t
y = 5 - 11t
z = 5 - 8t

Le paramètre t peut être n'importe quoi. L'ensemble (infini) de tous les (x, y, z) qui satisfont ces équations constituent la droite. Ensuite, si vous avez l'équation pour un avion, dites:

x + 2y + 2z = 5

(tiré de ici ) vous pouvez remplacer les équations par x, y et z ci-dessus dans l'équation pour l'avion, qui est maintenant uniquement dans le paramètre t. Résolvez pour t. Il s'agit de la valeur particulière de t pour cette ligne située dans le plan. Ensuite, vous pouvez résoudre pour x, y et z en remontant aux équations de ligne et en remplaçant t.

18
John

Utilisation de numpy et 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)
11
TimSC

Basé sur this Code Matlab (moins les contrôles d'intersection), en Python

# 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

Résultat

[-3.90909091  1.18181818 -0.27272727]

Je l'ai vérifié graphiquement, cela semble fonctionner,

enter image description here

Je pense que c'est une implémentation plus robuste du lien partagé avant

3
BBDynSys

Si vous avez deux points p et q qui définissent une ligne et un plan sous la forme cartésienne générale ax + by + cz + d = 0, vous pouvez utiliser la méthode paramétrique.

Si vous en aviez besoin à des fins de codage, voici un extrait 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)));
3
DNax

Cette question est ancienne mais comme il existe une solution beaucoup plus pratique, j'ai pensé que cela pourrait aider quelqu'un.

Les intersections de plan et de ligne sont assez élégantes lorsqu'elles sont exprimées en coordonnées homogènes, mais supposons que vous vouliez juste la solution:

Il existe un vecteur 4x1 p qui décrit le plan tel que p ^ T * x = 0 pour tout point homogène du plan. Calculez ensuite les coordonnées du plumeur pour la ligne L = ab ^ T - ba ^ T où a = {point_1; 1}, b = {point_2; 1}, les deux 4x1 sur la ligne

calculer: x = L * p = {x0, x1, x2, x3}

x_intersect = ({x0, x1, x2}/x3) où si x3 est nul il n'y a pas d'intersection au sens euclidien.

2
midjji

Juste pour développer ZGorlock's réponse, j'ai fait le produit scalaire, plus et l'échelle des vecteurs 3D. Les références pour ces calculs sont produit scalaire , ajouter deux vecteurs 3D et mise à l'échelle . Remarque: Vec3D est juste une classe personnalisée qui a des points: x, y et 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);
}
0
Muhammad Qumail