10

I am trying to draw the line formed by the intersections of two planes in 3D, but I am having trouble understanding the math, which has been explained here and here.

I tried to figure it out myself, but the closest that I got to a solution was a vector pointing along the same direction as the intersection line, by using the cross product of the normals of the planes. I have no idea how to find a point on the intersection line, any point would do. I think that this method is a dead end. Here is a screenshot of this attempt: planes and their normals and cross product of their normals

I tried to use the solution mentioned in this question, but it has a dead link to the original explanation, and the equation didn't work for me (it has unbalanced parentheses, which I tried to correct below).

var planeA = new THREE.Plane((new THREE.Vector3(0, 0, 1)).normalize(), 100);
var planeB = new THREE.Plane((new THREE.Vector3(1, 1, 1)).normalize(), -100);

var x1 = planeA.normal.x,
    y1 = planeA.normal.y,
    z1 = planeA.normal.z,
    d1 = planeA.constant;

var x2 = planeB.normal.x,
    y2 = planeB.normal.y,
    z2 = planeB.normal.z,
    d2 = planeB.constant;

var point1 = new THREE.Vector3();
point1.x = 0;
point1.z = (y2 / y1) * (d1 - d2) / (z2 - z1 * y2 / y1);
point1.y = (-z1 * point1.z - d1) / y1;

var point2 = new THREE.Vector3();
point2.x = 1;
point2.z = (y2 / y1) * (x1 * point2.x + d1) - (x2 * point2.x - d2) / (z2 - z1 * y2 / y1);
point2.y = (-z1 * point2.z - x1 * point2.x - d1) / y1;

console.log(point1, point2);

output:

THREE.Vector3 {x: -1, y: NaN, z: NaN, …}
THREE.Vector3 {x: 1, y: Infinity, z: -Infinity, …}

expected output:

  • A point along the intersection where x = 0, and
  • Another point on the same line where x = 1

If someone could point me to a good explanation of how this is supposed to work, or an example of a plane-plane intersection algorithm, I would be grateful.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dan Ross
  • 3,596
  • 4
  • 31
  • 60
  • If you just let `x = 0` in both plane equations you can find `y` and `z` corresponding to that `x`. Repeat this for `x = 1`. –  Apr 17 '13 at 15:11

4 Answers4

7

Here is an implementation of a solution for plane-plane intersections described at http://geomalgorithms.com/a05-_intersect-1.html . Essentially, you first use the cross product of the normals of the planes to find the direction of a line in both planes. Secondly, you use some algebra on the implicit equation of the planes (P . n + d = 0 where P is some point on the plane, n is the normal and d is the plane constant) to solve for a point which is on the intersection of the planes and also on one of the x=0, y=0 or z=0 planes. The solution is then the line described by a point and a vector. I was using three.js version 79

/*

Algorithm taken from http://geomalgorithms.com/a05-_intersect-1.html. See the
section 'Intersection of 2 Planes' and specifically the subsection
(A) Direct Linear Equation

*/
function intersectPlanes(p1, p2) {

  // the cross product gives us the direction of the line at the intersection
  // of the two planes, and gives us an easy way to check if the two planes
  // are parallel - the cross product will have zero magnitude
  var direction = new THREE.Vector3().crossVectors(p1.normal, p2.normal)
  var magnitude = direction.distanceTo(new THREE.Vector3(0, 0, 0))
  if (magnitude === 0) {
    return null
  }

  // now find a point on the intersection. We use the 'Direct Linear Equation'
  // method described in the linked page, and we choose which coordinate
  // to set as zero by seeing which has the largest absolute value in the
  // directional vector

  var X = Math.abs(direction.x)
  var Y = Math.abs(direction.y)
  var Z = Math.abs(direction.z)

  var point

  if (Z >= X && Z >= Y) {
    point = solveIntersectingPoint('z', 'x', 'y', p1, p2)
  } else if (Y >= Z && Y >= X){
    point = solveIntersectingPoint('y', 'z', 'x', p1, p2)
  } else {
    point = solveIntersectingPoint('x', 'y', 'z', p1, p2)
  }

  return [point, direction]
}


/*

This method helps finding a point on the intersection between two planes.
Depending on the orientation of the planes, the problem could solve for the
zero point on either the x, y or z axis

*/
function solveIntersectingPoint(zeroCoord, A, B, p1, p2){
    var a1 = p1.normal[A]
    var b1 = p1.normal[B]
    var d1 = p1.constant

    var a2 = p2.normal[A]
    var b2 = p2.normal[B]
    var d2 = p2.constant

    var A0 = ((b2 * d1) - (b1 * d2)) / ((a1 * b2 - a2 * b1))
    var B0 = ((a1 * d2) - (a2 * d1)) / ((a1 * b2 - a2 * b1))

    var point = new THREE.Vector3()
    point[zeroCoord] = 0
    point[A] = A0
    point[B] = B0

    return point
}


var planeA = new THREE.Plane((new THREE.Vector3(0, 0, 1)).normalize(), 100)
var planeB = new THREE.Plane((new THREE.Vector3(1, 1, 1)).normalize(), -100)

var [point, direction] = intersectPlanes(planeA, planeB)
user2013483
  • 71
  • 2
  • 2
  • This code is not working! i posted it in a jsfiddle and dra the planes and a vector along the returned intersect, its completly somewhere else: https://jsfiddle.net/ksfoLp1d/6/ How did this get 7 upvotes? –  Apr 03 '21 at 11:21
5

When I have problems like this, I usually let a symbolic algebra package (Mathematica in this case) deal with it. After typing

In[1]:= n1={x1,y1,z1};n2={x2,y2,z2};p={x,y,z};

In[2]:= Solve[n1.p==d1&&n2.p==d2,p]    

and simplifying and substituting x=0 and x=1, I get

                d2 z1 - d1 z2        d2 y1 - d1 y2
Out[5]= {{{y -> -------------, z -> ----------------}}, 
                y2 z1 - y1 z2       -(y2 z1) + y1 z2

            d2 z1 - x2 z1 - d1 z2 + x1 z2
>    {{y -> -----------------------------, 
                    y2 z1 - y1 z2

            d2 y1 - x2 y1 + (-d1 + x1) y2
>      z -> -----------------------------}}}
                  -(y2 z1) + y1 z2
David Eisenstat
  • 64,237
  • 7
  • 60
  • 120
  • That sounds like a great tool for problems like this, but I am unfamiliar with symbolic algebra packages. To tell you the truth, I am even having a hard time understanding the output when you did it for me :p I checked out Octave, and decided that it would take about as much time to learn the tool as it would to research a solution to my problem the old fashioned way. I would love to explore Octave more in the future though, maybe while taking taking an online course in linear algebra. I hope Khan has lessons! – Dan Ross Apr 16 '13 at 20:54
  • Thank you for showing me a great tool for future geometry problem solving. I am new to 3D programming, and I can see that there will be a lot more problems like this in my future. – Dan Ross Apr 16 '13 at 20:55
  • @DanRoss Unless Octave has features I don't know about, you want something more like [Sage](http://www.sagemath.org/). – David Eisenstat Apr 16 '13 at 20:59
  • Also, you might want to learn about projective geometry to reduce the number of degenerate cases you have to deal with (e.g., line is parallel to the x-axis). – David Eisenstat Apr 16 '13 at 21:01
  • Yes, cases. An [article](http://geomalgorithms.com/a05-_intersect-1.html) recommended choosing the axis with the largest absolute value in the cross product of the plane normals. I chose the x axis arbitrarily. Projective geometry you call it, I will have to google that. – Dan Ross Apr 16 '13 at 21:21
4

It is easy to let three.js solve this for you.

If you were to express your problem in matrix notation

m * x = v

Then the solution for x is

x = inverse( m ) * v

We'll use a 4x4 matrix for m, because three.js has an inverse() method for the Matrix4 class.

var x1 = 0,
    y1 = 0,
    z1 = 1,
    d1 = 100;

var x2 = 1,
    y2 = 1,
    z2 = 1,
    d2 = -100;

var c = 0; // the desired value for the x-coordinate

var v = new THREE.Vector4( d1, d2, c, 1 );

var m = new THREE.Matrix4( x1, y1, z1, 0, 
                           x2, y2, z2, 0,
                           1,  0,  0,  0,
                           0,  0,  0,  1
                         );

var minv = new THREE.Matrix4().getInverse( m );

v.applyMatrix4( minv );

console.log( v );

The x-component of v will be equal to c, as desired, and the y- and z-components will contain the values you are looking for. The w-component is irrelevalent.

Now, repeat for the next value of c, c = 1.

three.js r.58

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • executing this code returns { x: 100, y: -100, z: 0, w: 1 }, so x is not equal to c. And when i play around this always seems to return in x and y what is in d1 and d2 and in z whats in c and never changes no matter how i change the vectors of the plane creation. How is this the accepted answer? Here is a jsfiddle where i was trying this out: https://jsfiddle.net/0vzbjeco/ –  Apr 03 '21 at 16:36
3

Prerequisites


Recall that to represent a line we need a vector describing its direction and a point through which this line goes. This is called parameterized form:

line_point(t) = t * (point_2 - point_1) + point_1

where point_1 and point_2 are arbitrary points through which the line goes, and t is a scalar which parameterizes our line. Now we can find any point line_point(t) on the line if we put arbitrary t into the equation above.

NOTE: The term (point_2 - point_1) is nothing, but a vector describing the direction of our line, and the term point_1 is nothing, but a point through which our line goes (of course point_2) would also be fine to use too.

The Algorithm


  1. Find the direction direction of the intersection line by taking cross product of plane normals, i.e. direction = cross(normal_1, normal_2).

  2. Take any plane, for example the first one, and find any 2 distinct points on this plane: point_1 and point_2. If we assume that the plane equation is of the form a1 * x + b1 * y + c1 * z + d1 = 0, then to find 2 distinct points we could do the following:

    y1 = 1
    z1 = 0
    x1 = -(b1 + d1) / a1
    
    y2 = 0
    z2 = 1
    x2 = -(c1 + d1) / a1
    

    where point_1 = (x1, y1, z1) and point_2 = (x2, y2, z2).

  3. Now that we have 2 points, we can construct the parameterized representation of the line lying on this first plane: line_point(t) = t * (point_2 - point_1) + point_1, where line_point(t) describes any point on this line, and t is just an input scalar (frequently called parameter).

  4. Find the intersection point intersection_point of the line line_point(t) and the second plane a2 * x + b2 * y + c2 * z + d2 = 0 by using the standard line-plane intersection algorithm (pay attention to the Algebraic form section as this is all you need to implement line-plane intersection, if you haven't done so already).

  5. Our intersection line is now found and can be constructed in parameterized form as usual: intersection_line_point(s) = s * direction + intersection_point, where intersection_line_point(s) describes any point on this intersection line, and s is parameter.

NOTE: I didn't read this algorithm anywhere, I've just devised it from the top of my head based on my knowledge of linear algebra. That doesn't mean that it doesn't work, but it might be possible that this algorithm can be optimized further.

Conditioning


When 2 normal vectors normal_1 and normal_2 are almost collinear this problem gets extremely ill-conditioned. Geometrically it means that the 2 planes are almost parallel to each other and determining the intersection line with acceptable precision becomes impossible in finite-precision arithmetic which is floating-point arithmetic in this case.

Alexander Shukaev
  • 16,674
  • 8
  • 70
  • 85
  • Well, I got to Step 2, and found a problem: `point_1` (red ball) and `point_2` (green ball) were not on the first plane, they were on either side of it [pic](http://imgur.com/jwch42f). It is quite possible that I did not properly understand your explanation, 3D math is a new subject for me, and I didn't do much linear algebra in school. I did finally find a good [article](http://geomalgorithms.com/a05-_intersect-1.html) about intersection algorithms, and it seems that I have made a [working](http://bit.ly/XEU1jO) [script](http://bit.ly/13d1gUH) from it's "Direct Linear Equation" solution. – Dan Ross Apr 16 '13 at 21:15
  • You're obviously doing it wrong. Once again, on the step 2 we want to choose **any plain** of these 2 (because it simply does not matter which) and find **any line** that lies on that plane. To determine a line all we have to do is simple take **2 arbitrary points** and that plane (again doesn't matter which points exactly, it only matters that they are on that plane). So we pick a simple choice to do little computations, and when these 2 points are computed - our line (lying on the chosen plane) can be fully determined by the `line_point(t)` equation from the step 3. – Alexander Shukaev Apr 16 '13 at 21:43