How to project a point onto a plane in 3D? - c++

I have a 3D point (point_x,point_y,point_z) and I want to project it onto a 2D plane in 3D space which (the plane) is defined by a point coordinates (orig_x,orig_y,orig_z) and a unary perpendicular vector (normal_dx,normal_dy,normal_dz).
How should I handle this?

Make a vector from your orig point to the point of interest:
v = point-orig (in each dimension);
Take the dot product of that vector with the unit normal vector n:
dist = vx*nx + vy*ny + vz*nz; dist = scalar distance from point to plane along the normal
Multiply the unit normal vector by the distance, and subtract that vector from your point.
projected_point = point - dist*normal;
Edit with picture:
I've modified your picture a bit. Red is v. dist is the length of blue and green, equal to v dot normal. Blue is normal*dist. Green is the same vector as blue, they're just plotted in different places. To find planar_xyz, start from point and subtract the green vector.

This is really easy, all you have to do is find the perpendicular (abbr here |_) distance from the point P to the plane, then translate P back by the perpendicular distance in the direction of the plane normal. The result is the translated P sits in the plane.
Taking an easy example (that we can verify by inspection) :
Set n=(0,1,0), and P=(10,20,-5).
The projected point should be (10,10,-5). You can see by inspection that Pproj is 10 units perpendicular from the plane, and if it were in the plane, it would have y=10.
So how do we find this analytically?
The plane equation is Ax+By+Cz+d=0. What this equation means is "in order for a point (x,y,z) to be in the plane, it must satisfy Ax+By+Cz+d=0".
What is the Ax+By+Cz+d=0 equation for the plane drawn above?
The plane has normal n=(0,1,0). The d is found simply by using a test point already in the plane:
(0)x + (1)y + (0)z + d = 0
The point (0,10,0) is in the plane. Plugging in above, we find, d=-10. The plane equation is then 0x + 1y + 0z - 10 = 0 (if you simplify, you get y=10).
A nice interpretation of d is it speaks of the perpendicular distance you would need to translate the plane along its normal to have the plane pass through the origin.
Anyway, once we have d, we can find the |_ distance of any point to the plane by the following equation:
There are 3 possible classes of results for |_ distance to plane:
0: ON PLANE EXACTLY (almost never happens with floating point inaccuracy issues)
+1: >0: IN FRONT of plane (on normal side)
-1: <0: BEHIND plane (ON OPPOSITE SIDE OF NORMAL)
Anyway,
Which you can verify as correct by inspection in the diagram above

This answer is an addition to two existing answers.
I aim to show how the explanations by #tmpearce and #bobobobo boil down to the same thing, while at the same time providing quick answers to those who are merely interested in copying the equation best suited for their situation.
Method for planes defined by normal n and point o
This method was explained in the answer by #tmpearce.
Given a point-normal definition of a plane with normal n and point o on the plane, a point p', being the point on the plane closest to the given point p, can be found by:
p' = p - (n ⋅ (p - o)) × n
Method for planes defined by normal n and scalar d
This method was explained in the answer by #bobobobo.
Given a plane defined by normal n and scalar d, a point p', being the point on the plane closest to the given point p, can be found by:
p' = p - (n ⋅ p + d) × n
If instead you've got a point-normal definition of a plane (the plane is defined by normal n and point o on the plane) #bobobobo suggests to find d:
d = -n ⋅ o
and insert this into equation 2. This yields:
p' = p - (n ⋅ p - n ⋅ o) × n
A note about the difference
Take a closer look at equations 1 and 4. By comparing them you'll see that equation 1 uses n ⋅ (p - o) where equation 2 uses n ⋅ p - n ⋅ o. That's actually two ways of writing down the same thing:
n ⋅ (p - o) = n ⋅ p - n ⋅ o = n ⋅ p + d
One may thus choose to interpret the scalar d as if it were a 'pre-calculation'. I'll explain: if a plane's n and o are known, but o is only used to calculate n ⋅ (p - o),
we may as well define the plane by n and d and calculate n ⋅ p + d instead, because we've just seen that that's the same thing.
Additionally for programming using d has two advantages:
Finding p' now is a simpler calculation, especially for computers. Compare:
using n and o: 3 subtractions + 3 multiplications + 2 additions
using n and d: 0 subtractions + 3 multiplications + 3 additions.
Using d limits the definition of a plane to only 4 real numbers (3 for n + 1 for d), instead of 6 (3 for n + 3 for o). This saves ⅓ memory.

It's not sufficient to provide only the plane origin and the normal vector. This does define the 3d plane, however this does not define the coordinate system on the plane.
Think that you may rotate your plane around the normal vector with regard to its origin (i.e. put the normal vector at the origin and "rotate").
You may however find the distance of the projected point to the origin (which is obviously invariant to rotation).
Subtract the origin from the 3d point. Then do a cross product with the normal direction. If your normal vector is normalized - the resulting vector's length equals to the needed value.
EDIT
A complete answer would need an extra parameter. Say, you supply also the vector that denotes the x-axis on your plane.
So we have vectors n and x. Assume they're normalized.
The origin is denoted by O, your 3D point is p.
Then your point is projected by the following:
x = (p - O) dot x
y = (p - O) dot (n cross x)

Let V = (orig_x,orig_y,orig_z) - (point_x,point_y,point_z)
N = (normal_dx,normal_dy,normal_dz)
Let d = V.dotproduct(N);
Projected point P = V + d.N

I think you should slightly change the way you describe the plane. Indeed, the best way to describe the plane is via a vector n and a scalar c
(x, n) = c
The (absolute value of the) constant c is the distance of the plane from the origin, and is equal to (P, n), where P is any point on the plane.
So, let P be your orig point and A' be the projection of a new point A onto the plane. What you need to do is find a such that A' = A - a*n satisfies the equation of the plane, that is
(A - a*n, n) = (P, n)
Solving for a, you find that
a = (A, n) - (P, n) = (A, n) - c
which gives
A' = A - [(A, n) - c]n
Using your names, this reads
c = orig_x*normal_dx + orig_y*normal_dy+orig_z*normal_dz;
a = point_x*normal_dx + point_y*normal_dy + point_z*normal_dz - c;
planar_x = point_x - a*normal_dx;
planar_y = point_y - a*normal_dy;
planar_z = point_z - a*normal_dz;
Note: your code would save one scalar product if instead of the orig point P you store c=(P, n), which means basically 25% less flops for each projection (in case this routine is used many times in your code).

Let r be the point to project and p be the result of the projection. Let c be any point on the plane and let n be a normal to the plane (not necessarily normalised). Write p = r + m d for some scalar m which will be seen to be indeterminate if their is no solution.
Since (p - c).n = 0 because all points on the plane satisfy this restriction one has (r - c).n + m(d . n) = 0 and so m = [(c - r).n]/[d.n] where the dot product (.) is used. But if d.n = 0 there is no solution. For example if d and n are perpendicular to one another no solution is available.

Related

How do reverse perspective with OpenGL?

I'm looking for a way to do reverse perspective with OpenGL and C++. For now, I'm using glFrustum to have a classic perspective, but I would like to know if a reverse pespective as presented here (https://en.wikipedia.org/wiki/Reverse_perspective and below) is possible ? If not, is there any other way with OpenGL to do this ?
This question really intrigued me. I'm not entirely convinced that what I'm going to refer to now as 'Byzantine perspective' can be accommodated by a transform analogous to that provided by (pre-core profile) glFrustum. I have done some work on it though, inspired by derivations in Computer Graphics: Principles and Practice (2nd Ed), and the formulation for the OpenGL CCS / NDCS.
Unfortunately, the original S.O. site doesn't allow for embedded LaTeX, so the matrices will not be pretty. Consider this answer a work in progress
So far, I've derived the matrix transform resulting in what is sometimes referred to as the 'normalized frustum'. The far plane at Z = -1, the near plane at Z = - N / F, and the R, L, T, B planes having unit slope. (this would be very clear given a good diagram)
[ 2N / (R - L) 0 (R + L) / (R - L) 0 ]
[ 0 2N / (T - B) (T + B) / (T - B) 0 ]
[ 0 0 1 0 ]
[ 0 0 0 F ]
Call this matrix: [F.p]. For any point: P = (x, y, - N, 1)^T on the near plane, it's easy to demonstrate that the transformed homogeneous point lies on the Z = - N / F plane. (Note: ^T is the 'transpose' operator to make it clear that this is in fact a column vector.)
Likewise, given a point: P = (x, y, - F, 1)^T on the far plane, the transformed homogeneous point lines on the Z = -1 plane.
A Byzantine perspective requires another constraint - we'll use the variable D, where Z = - D is a point analogous to the 'eye' at Z = 0, or the 'projection reference point' (PRP).
As you have already gathered from the image you've provided, parallel lines converge at Z = - D, rather than at the 'eye'. You don't want the image from the the point of convergence however. You want to visualise the effect from the 'front'. The question is - can we construct an OpenGL matrix similar to that provided by glFrustum which yields a Byzantine perspective? And can it be made to fit within a GL pipeline?
What I've derived so far, is the 'normalized Byzantine frustum'. Again, the far plane is at Z = -1, the near plane at Z = - N / F, and R, L, T, B planes have unit slope - albeit the negative slope of the regular frustum. (again a clear picture would be worth a thousand words here)
[ 2N / (R - L) 0 (R + L) / (R - L) 0 ]
[ 0 2N / (T - B) (T + B) / (T - B) 0 ]
[ 0 0 N / F 0 ]
[ 0 0 0 N ]
Call this matrix: [F.b]. The X,Y coordinate transforms are identical, but the Z,W component transforms differ. This is somewhat intuitive given that this is, in a sense, a 'reverse' of the orthodox view volume.
Again, given a point: P = (x, y, - N, 1)^T on the near plane, the transformed homogeneous point lies on the Z = - N / F plane, and the homogeneous transform of a point: P = (x, y, - F, 1)^T on the far plane lies on the Z = -1 plane.
Given the similarities in the matrices, and the fact that only simple perspective transforms (and a few trivial scaling and translation matrices) are required to yield parallel projection matrices that conform to OpenGL clip coordinate space (CCS), and its NDCS projection, it seems likely that an OpenGL 'Byzantine' projection could be made to work. I just need more time to work on it...

Numerically stable Angle bisector algorithm

Is there any numerically stable angle bisector algorithm?
The problem is the following:
Given three vectors (2 dimensional) A,B,C
Find the bisector of angle B (angle between AB and BC)
Actually I'm computing it in the following way:
Normalize AB
Normalize BC
Find (AB+CD)/2f (Mid Point)
The bisector is line passing between B and the Mid Point.
The problem with my approach is that when the angle is almost 180° (AB almost parallel to BC) the bisector is very inaccurate (of course because mid point is almost coincident with B). The current algorithm is so inaccurate that sometimes the resulting bisector is almost parallel to one of the other 2 segments.
And yes there are no "cast" problems, all computations are done in single precision floating point.
You could use that the angle bisector remains the same if you rotate BA by +90° and BC by -90°.
So use the original formula if the situation is stable, that is, if the dot product of BA and BC is positive.
If it is negative, apply the rotations, for BA (x,y) -> (-y,x) and for BC (x,y) -> (y,-x), which also renders the dot product positive. Proceed as before with the new vectors.
If you try this out you will note that the jump in direction of the bisector now occurs for the angle -90° between the vectors. It is not possible to avoid this jump, as a continuous bisector will only be the same after two turns (fixing BA and moving C).
It’s not trivial. Let’s say the two edge vectors are a and b:
float2 a = A - B;
float2 b = C - B;
Compute the dot product float dp = dot( a, b )
Normalize both vectors:
float2 a_norm = normalize( a );
float2 b_norm = normalize( b );
Check the sign bit of the dot product. When the dp is non-negative,
return normalize( a_norm + b_norm ); and you’re done.
When the dot product is negative, you have obtuse angle between input vectors.
Applying a naïve formula in this case would screw up the numerical precision.
Need another way.
float2 c = normalize( a_norm - b_norm );
float dir = dot( a, rotate90( b ) );
return ( dir < 0 ) ? rotate90( c ) : rotate270( c );
Note - instead of the +, this is what gives the precision win. When the angle between a and b is greater than 90°, the angle between a and -b is less than 90°, and the length of a_norm - b_norm is large enough to give accurate direction. We just need to rotate it by 90° afterwards, in the correct direction.
P.S. Rotating 2D vectors by multiples of 90° is lossless operation.
Here’s pseudocode for rotate90 and rotate270 functions:
float2 rotate90( float2 vec )
{
return float2( vec.y, -vec.x );
}
float2 rotate270( float2 vec )
{
return float2( -vec.y, vec.x );
}
A simple enough way to do this follows in two formats (but the content is otherwise identical):
Pseudocode
// Move A and C to the origin for easier rotation calculations
Aprime=A-B;
Cprime=C-B;
// The counter-clockwise angle between the positive X axis to A'
angle_a = arctan(Aprime.y, Aprimet.x);
// ditto for C'
angle_c = arctan(Cprime.y, Cprime.x);
// The counter-clockwise angle from A' to C'
angle_ac = angle_c - angle_a;
// The counter-clockwise angle from the positive X axis to M'
angle_m = angle_ac/2 + angle_a;
// Construct M' which, like A' and C', is relative to the origin.
Mprime=(cos(angle_m), sin(angle_m));
// Construct M which is relative to B rather than relative to the origin.
M=Mprime+B
In English
Move the vectors to the origin by
A'=A-B
B'=B
C'=C-B
Get the angle from the positive X axis to A' as angle_a = arctan(A_y, A_x).
Get the angle from the positive X axis to C' as angle_c = arctan(C_y, C_x).
Get the counter-clockwise angle from A' to C' as angle_ac = angle_c - angle_a.
Get the angle from the positive X axis to M' as angle_m = angle_ac/2 + angle_a.
Construct M' from this angle as M' = (cos(angle_m), sin(angle_m)).
Construct M as M = M' + B.
The vector BM bisects the angle ABC.
Since there is arbitrary division, there are no difficulties with this method. Here's a graphing calculator to encourage intuition with the solution: https://www.desmos.com/calculator/xwbno717da
You can find the bisecting vector quite simply with:
∥BC∥ * BA + ∥BA∥ * BC
But that also won't be numerically stable with ABC collinear or nearly so. What might work better would be to find the angle between AB and BC, via the dot product.
cos θ = (BA · BC) / (∥BC∥ * ∥BA∥)
That will produce the correct angle even in the collinear case.
Definition: If A and B are points, vector(A,B) is the vector from point A to B.
Lets say that point O is the point of origin for our coordinate system.
The coordinates of point A are the same as of radius-vector(O,A).
Let point M be the middle point for the bisector,so you need to:
-normalize vector(B,A)
-normalize vector(B,C)
-vector(B,M) = vector(B,A)+vector(B,C) //vector from B to middle point
-(optionally) You can multiply vector(B,M) with a scalar to get a longer vector / increase distance between B and M
-vector(O,M) = vector(O,B) + vector(B,M)//radius-vector from O to M
Now middle point M has the same coordinates as radius-vector(O,M).

OpenGL sutherland-hodgman polygon clipping algorithm in homogeneous coordinates (4D, CCS)

I have two questions. (I marked 1, 2 below)
In OpenGl, the clipping is done by sutherland-hodgman.
However, I wonder how to work sutherland-hodgman algorithm in homogeneous system (4D)
I made a situation.
In VCS, there is a line, R= (0, 3, -2, 1), S = (0, 0, 1, 1) (End points of the line)
And a frustum is right = 1, left = -1, near = 1, far = 3, top = 4, bottom = -4
Therefore, the projection matrix P is
1 0 0 0
0 1/4 0 0
0 0 -2 -3
0 0 -1 0
If we calculate the line with the P, then the each end points is like that
R' = (0, 3/4, 1, 2), S' = (0, 0, -5, -1)
I know that perspective division should not be done now, because if we do perspective division, the clipping result is not correct.
Here I am curious. What makes a correct clipping because we did not just do perspective division. What mathematical properties are here?
How to calculate the clipping result in above situation?
(The fact that two intersections occur in the w-y coordinate system makes me confused. I thought the result line is one, not divided two parts)
I'm not quite sure whether you understood the sutherland-hodgman algorithm correctly (or at least I didn't get your example). Thus I will prove here, that it doesn't make any difference whether clipping happens before or after the perspective divide. The proof is only shown for one plane (clipping has to be done against all 6 planes), since applying multiple such clipping operations after each other makes not difference here.
Let's assume we have two points (as you described) R' and S' in clip space. And we have a clipping plane P given in hessian normal form [n, p] (if we take the left plane this is [1,0,0,1]).
If we would be calculating in pure 3d space (R^3), then checking whether a line crosses this plane would be done by calculating the signed distance of both points to the plane and checking if the sign is different. The signed distance for a point X = [x/w,y/w,z/w] is given by
D = dot(n, X) + p
Let's write down the actual equation we have (including the perspective divide):
d = n_x * x/w + n_y * y/w + n_z * z/w + p
In order to find the exact intersection point, we would, again in R^3 space, calculate for both points (A = R'/R'w, B = S'/S'w) the distance to the plane (da, db) and perform a linear interpolation (I will only write the equations for the x-coordinate here since y and z are working similar):
x = A_x * (1 - da/(da - db)) + A_y * (da/(da-db))
x = R'x/R'w * (1 - da/(da - db)) + S'x/S'w * (da/(da-db))
And w = 1 (since we interpolate between two points both having w = 1)
Now we already know from the previous discussion, that clipping has to happen before the perspective divide, thus we have to adapt this equation. This means, that for each point, the clipping cube has a different scaling w. Lt's see what happens when we try to perform the the same operations in P^3 (before the perspective divide):
First, we "revert" the perspective divide to get to X=[x,y,z,w] for which the distance to the plane is given by
d = n_x * x/w + n_y * y/w + n_z * z/w + p
d = (n_x * x + n_y * y + n_z * z) / w + p
d * w = n_x * x + n_y * y + n_z * z + p * w
d * w = dot([n, p], [x,y,z,w])
d * w = dot(P, X)
Since we are only interested in the sign of the whole calculation, which we haven't changed by our operations, we can compare the D*ws and get the same inside-out result as in R^3.
For the two points R' and S', the calculated distances in P^3 are dr = da * R'w and ds = db * S'w. When we now use the same interpolation equation as above but for R' and S' we get for x:
x' = R'x * (1 - (da * R'w)/(da * R'w - db * S'w)) + S'x * (da * R'w)/(da * R'w - db * S'w)
On the first view this looks rather different from the result we got in R^3, but since we are still in P^3 (thus x'), we still have to do the perspective divide on the result (this is allowed here, since the interpolated point will always be at the border of the view-frustum and thus dividing by w will not introduce any problems). The interpolated w component is given as:
w' = R'w * (1 - (da * R'w)/(da * R'w - db * S'w)) + S'w * (da * R'w)/(da * R'w - db * S'w)
And when calculating x/w we get
x = x' / w';
x = R'x/R'w * (1 - da/(da - db)) + S'x/S'w * (da/(da-db))
which is exactly the same result as when calculating everything in R^3.
Conclusion: The interpolation gives the same result, no matter if we perform the perspective divide first and interpolation afterwards or interpolating first and dividing then. But with the second variant we avoid the problem with points flipping from behind the viewer to the front since we are only dividing points that are guaranteed to be inside (or on the border) of the viewing frustum.
You speak of polygon clipping in a homogeneous system (4D) but from your question I assume that you actually mean homogeneous coordinates, which makes a lot more sense. (There are many possible homogenous systems.)
Ok, so you want to use "4D" coordinates, which are really "3D coordinates and a w term". The w term represents (projection transformations) the projective term that partially relates the screen-space coordinate to the original world space position. Assuming that you are NOT interested in projective space clipping, this term is not relevant.
I'm assuming this because the clipping box you describe is axis-aligned on planes in 3D. Even if it was rotated or scaled in 3D space, each of the planes would still be a 3D plane, the 4th coordinate always being '1'.
So how to clip:
clip line segment L against each of the planes of the clipping box, i.e. 6 clipping planes in total (you describe the normals of each clipping plane aptly), and see if any intersection point v is shared by the line and the tested plane P so that
v lies on the line segment (i.e. a t between 0 and 1)
v lies within the bounds of the plane P (i.e. the coordinate should not lie beyond any of the adjacent planes. Since you are using axis-aligned clipping planes, this is easy to check.)
Any of these intersections between a (3D + w) line and one of the 3D planes occurs in 3D, and intersection points have to be a 3D coordinates. You can extend each of these coordinates with a 4th w coordinate into a "4D" coordinate so that you can further transform them using 4x4 matrices for view and projection processing.

How to fit a plane to a 3D point cloud?

I want to fit a plane to a 3D point cloud. I use a RANSAC approach, where I sample several points from the point cloud, calculate the plane, and store the plane with the smallest error. The error is the distance between the points and the plane. I want to do this in C++, using Eigen.
So far, I sample points from the point cloud and center the data. Now, I need to fit the plane to the samples points. I know I need to solve Mx = 0, but how do I do this? So far I have M (my samples), I want to know x (the plane) and this fit needs to be as close to 0 as possible.
I have no idea where to continue from here. All I have are my sampled points and I need more data.
From you question I assume that you are familiar with the Ransac algorithm, so I will spare you of lengthy talks.
In a first step, you sample three random points. You can use the Random class for that but picking them not truly random usually gives better results. To those points, you can simply fit a plane using Hyperplane::Through.
In the second step, you repetitively cross out some points with large Hyperplane::absDistance and perform a least-squares fit on the remaining ones. It may look like this:
Vector3f mu = mean(points);
Matrix3f covar = covariance(points, mu);
Vector3 normal = smallest_eigenvector(covar);
JacobiSVD<Matrix3f> svd(covariance, ComputeFullU);
Vector3f normal = svd.matrixU().col(2);
Hyperplane<float, 3> result(normal, mu);
Unfortunately, the functions mean and covariance are not built-in, but they are rather straightforward to code.
Recall that the equation for a plane passing through origin is Ax + By + Cz = 0, where (x, y, z) can be any point on the plane and (A, B, C) is the normal vector perpendicular to this plane.
The equation for a general plane (that may or may not pass through origin) is Ax + By + Cz + D = 0, where the additional coefficient D represents how far the plane is away from the origin, along the direction of the normal vector of the plane. [Note that in this equation (A, B, C) forms a unit normal vector.]
Now, we can apply a trick here and fit the plane using only provided point coordinates. Divide both sides by D and rearrange this term to the right-hand side. This leads to A/D x + B/D y + C/D z = -1. [Note that in this equation (A/D, B/D, C/D) forms a normal vector with length 1/D.]
We can set up a system of linear equations accordingly, and then solve it by an Eigen solver as follows.
// Example for 5 points
Eigen::Matrix<double, 5, 3> matA; // row: 5 points; column: xyz coordinates
Eigen::Matrix<double, 5, 1> matB = -1 * Eigen::Matrix<double, 5, 1>::Ones();
// Find the plane normal
Eigen::Vector3d normal = matA.colPivHouseholderQr().solve(matB);
// Check if the fitting is healthy
double D = 1 / normal.norm();
normal.normalize(); // normal is a unit vector from now on
bool planeValid = true;
for (int i = 0; i < 5; ++i) { // compare Ax + By + Cz + D with 0.2 (ideally Ax + By + Cz + D = 0)
if ( fabs( normal(0)*matA(i, 0) + normal(1)*matA(i, 1) + normal(2)*matA(i, 2) + D) > 0.2) {
planeValid = false; // 0.2 is an experimental threshold; can be tuned
break;
}
}
This method is equivalent to the typical SVD-based method, but much faster. It is suitable for use when points are known to be roughly in a plane shape. However, the SVD-based method is more numerically stable (when the plane is far far away from origin) and robust to outliers.

calculating perpendicular and angular distance between line segments in 3d

I am working on implementing a clustering algorithm in C++. Specifically, this algorithm: http://www.cs.uiuc.edu/~hanj/pdf/sigmod07_jglee.pdf
At one point in the algorithm (sec 3.2 p4-5), I am to calculate perpendicular and angular distance (d┴ and dθ) between two line segments: p1 to p2, p1 to p3.
It has been a while since I had a math class, I am kinda shaky on what these actually are conceptually and how to calculate them. Can anyone help?
To get the perpendicular distance of a point Q to a line defined by two points P_1 and P_2 calculate this:
d = DOT(Q, CROSS(P_1, P_2) )/MAG(P_2 - P_1)
where DOT is the dot product, CROSS is the vector cross product, and MAG is the magnitude (sqrt(X*X+Y*Y+..))
Using Fig 5. You calculate d_1 the distance from sj to line (si->ei) and d_2 the distance from ej to the same line.
I would establish a coordinate system based on three points, two (P_1, P_2) for a line and the third Q for either the start or the end of the other line segment. The three axis of the coordinate system can be defined as such:
e = UNIT(P_2 - P_1) // axis along the line from P_1 to P_2
k = UNIT( CROSS(e, Q) ) // axis normal to plane defined by P_1, P_2, Q
n = UNIT( CROSS(k, e) ) // axis normal to line towards Q
where UNIT() is function to return a unit vector (with magnitude=1).
Then you can establish all your projected lengths with simple dot products. So considering the line si-ei and the point sj in Fig 5, the lengths are:
(l || 1) = DOT(e, sj-si);
(l |_ 1) = DOT(n, sj-si);
ps = si + e * (l || 1) //projected point
And with the end of the second segment ej, new coordinate axes (e,k,n) need to be computed
(l || 2) = DOT(e, ei-ej);
(l |_ 1) = DOT(n, ej-ei);
pe = ei - e * (l || 1) //projected point
Eventually the angle distance is
(d th) = ATAN( ((l |_ 2)-(L |_ 1))/MAG(pe-ps) )
PS. You might want to post this at Math.SO where you can get better answers.
Look at figure 5 on page 3. It draws out what d┴ and dθ are.
EDIT: The "Lehmer mean" is defined using Lp-space conventions. So in 3 dimensions, you would use p = 3. Let's say that the (Euclidean) distance between the two start points is d1, and between the ends is d2. Then d┴(L1, L2) = (d1^3 + d2^3) / (d1^2 + d2^2).
To find the angle between two vectors, you can use their dot product. The norm (denoted ||x||) is computed like this.