Positioning objects parallel with a mesh - math

I'm trying to align multiple line objects along a human body circumference depending on the orientation of the triangles from the mesh. I would like to put the lines parallel to the mesh. I correctly assign the position for the lines along the circumference, but I also need to add the rotation of the lines such that to be parallel with the body.
The body is a mesh formed by multiple triangles and every line is "linked" with a triangle.
All I have is:
3 points for the closest triangle from the mesh for every line
The normal of the triangle
The positions for the instantiated lines (2 points, start and end)
I need to calculate the angle for every X, Y, Z axes for the line such that the normal of the triangle is perpendicular with the line mesh. I don't know how to get the desired angle. I really appreciate if someone would like to help me.
input:
FVector TrianglePoints[3];
FVector Triangle_Normal; //Calculated as (B-A)^(C-A), where A,B,C are the points of the triangle
FVector linePosition; //I also have the start line and the endLine position if that helps
ouput:
//FRotator rotation(x,y,z), such that the triangle normal and the line object to be perpendicular.
An overview of the circumference line construction. Now the rotation is calculated using the Start position and End position for each line. When we cross some irregular parts of the mesh we want to rotate the lines correctly. Now the rotation is fixed, depending just on the line start and end position.

If I have understood correctly your goal, here is some related vector geometry:
A,B,C are the vertices of the triangle:
A = [xA, yA, zA],
B = [xB, yB, zB]
C = [xC, yC, zC]
K,L are the endpoints of the line-segment:
K = [xK, yK, zK]
L = [xL, yL, zL]
vectors are interpreted as row-vectors
by . I denote matrix multiplication
by x I denote cross product of 3D vectors
by t() I denote the transpose of a matrix
by | | I denote the norm (magnitude) of a vector
Goal: find the rotation matrix and rotation transformation of segment KL
around its midpoint, so that after rotation KL is parallel to the plane ABC
also, the rotation is the "minimal" angle rotation by witch we need to
rotate KL in order to make it parallel to ABC
AB = B - A
AC = C - A
KL = L - K
n = AB x AC
n = n / |n|
u = KL x n
u = u / |u|
v = n x u
cos = ( KL . t(v) ) / |KL|
sin = ( KL . t(n) ) / |KL|
U = [[ u[0], u[1], u[2] ],
[ v[0], v[1], v[2] ],
[ n[0], n[1], n[2] ],
R = [[1, 0, 0],
[0, cos, sin],
[0, -sin, cos]]
ROT = t(U).R.U
then, one can rotate the segment KL around its midpoint
M = (K + L)/2
Y = M + ROT (X - M)
Here is a python script version
A = np.array([0,0,0])
B = np.array([3,0,0])
C = np.array([2,3,0])
K = np.array([ -1,0,1])
L = np.array([ 2,2,2])
KL = L-K
U = np.empty((3,3), dtype=float)
U[2,:] = np.cross(B-A, C-A)
U[2,:] = U[2,:] / np.linalg.norm(U[2,:])
U[0,:] = np.cross(KL, U[2,:])
U[0,:] = U[0,:] / np.linalg.norm(U[0,:])
U[1,:] = np.cross(U[2,:], U[0,:])
norm_KL = np.linalg.norm(KL)
cos_ = KL.dot(U[1,:]) / norm_KL
sin_ = KL.dot(U[2,:]) / norm_KL
R = np.array([[1, 0, 0],
[0, cos_, sin_],
[0,-sin_, cos_]])
ROT = (U.T).dot(R.dot(U))
M = (K+L) / 2
K_rot = M + ROT.dot( K - M )
L_rot = M + ROT.dot( L - M )
print(L_rot)
print(K_rot)
print(L_rot-K_rot)
print((L_rot-K_rot).dot(U[2,:]))

A more inspired solution was to use a procedural mesh, generated at runtime, that have all the requirements that I need:
Continuously along multiple vertices
Easy to apply a UV map for texture tiling
Can be updated at runtime
Isn't hard to compute/work with it

Related

Extrude 2d vertices/vectors

I have a set of vertices/vectors and I need to extrude them inside object boundaries to give that object a thickness
as an example:
I need to turn something like this:
to something like this:
how can I achieve this?
(I'm using C++, OpenGL & GLM)
-- Update --
thanks to #Futurologist answer, I was able to resolve the issue and hey, It works like a charm!
Sorry for the python, but it's faster and easier for me to write it like that, plus it maybe reveals some the geometric concepts in the background.
Is this what you are after?
'''
angle bisectors and offsetting a polygon
'''
def bisectors(P, thickness):
#P is n x 2 matrix, row P[j,:] is a vertex of a polygon in the plane,
#P is the ordered set of vertices of the polygon
n = P.shape[0];
B = np.zeros((n,2), dtype=float);
for j in range(n):
if j == 0:
v_in = P[0,:] - P[n-1,:];
v_out = P[1,:] - P[0,:];
elif j == n-1:
v_in = P[n-1,:] - P[n-2,:];
v_out = P[0,:] - P[n-1,:];
else:
v_in = P[j,:] - P[j-1,:];
v_out =P[j+1,:] - P[j,:];
v_in = v_in / math.sqrt(v_in.dot(v_in)); #normalize edge-vector
v_out = v_out / math.sqrt(v_out.dot(v_out)); #normalize edge-vector
# bisector of the complementary angle at the vertex j,
# pointing counter clockwise and displacing the vertex so that
# the resulting polygon is "thickness" units inwards in normal direction:
bisector = v_in + v_out;
bisector = bisector / abs(bisector.dot(v_in));
bisector = thickness * bisector
# 90 degree counter clockwise rotation of complementary bisector:
B[j,0] = - bisector[1];
B[j,1] = bisector[0];
return B
def offset_vertices(Polygon, thickness):
Polygon_off = Polygon + bisectors(Polygon, thickness)
return Polygon_off
P = np.array([[0,0],[2,0],[3,1],[1,3]])
P_off = offset_vertices(P, 0.1)
# Plotting
P = np.vstack((P, P[0,:] ))
P_off = np.vstack((P_off, P_off[0,:] ))
fig, axs = plt.subplots(1)
axs.plot(P[:,0], P[:,1], 'bo')
axs.plot(P_off[:,0], P_off[:,1], 'ro')
axs.plot(P[:,0], P[:,1])
axs.plot(P_off[:,0], P_off[:,1])
axs.set_aspect('equal')
plt.grid()
plt.show()

Finding if a circle is fully contained within multiple triangles?

In a game, an area is defined by triangles that never overlap, and characters are defined by circles.
How can I know whether the full character's collision circle is contained within these triangles?
Example image:
Here, the red parts are outside triangles, so the circle isn't contained within them. Is there an algorithm that can detect this?
I've only came up with "non-perfect" solutions, like sampling points at the border of the circle, then testing if each is inside a triangle.
So basically, the triangles form a domain with polygonal boundary and you want to check if a disk, defined by a center point and a radius is contained inside the domain. So if you start with the triangles, you have to find a way to extract the polygonal boundary of your domain and represent it as a 2D array (matrix) of shape n rows and two columns so that every row is the two coordinates of a vertex point of the polygonal boundary line and the points are ordered so that they are consecutive order along the boundary in a counterclockwise position, i.e. when you walk in a direction from point of index i to the next point i+1 the domain stays on your left. For example, here is the representation of a polygonal boundary of a domain like yours:
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
Observe that the first and the last points are the same.
In such a scenario, maybe you can try the following algorithm:
import numpy as np
import math
def angle_and_dist(p1, p2, o):
p12 = p2 - p1
op1 = p1 - o
op2 = p2 - o
norm_p12 = math.sqrt(p12[0]**2 + p12[1]**2)
norm_op1 = math.sqrt(op1[0]**2 + op1[1]**2)
norm_op2 = math.sqrt(op2[0]**2 + op2[1]**2)
p12_perp = np.array([ - p12[1], p12[0] ])
h = - op1.dot(p12_perp)
theta12 = op1.dot(op2) / (norm_op1*norm_op2)
theta12 = math.acos( theta12 )
if h < 0:
theta12 = - theta12
if op1.dot(p12) > 0:
return theta12, norm_op1
elif op2.dot(p12) < 0:
return theta12, norm_op2
else:
return theta12, h/norm_p12
def is_in_polygon(p, disk):
o, r = disk
n_p = len(p)-1
index_o = 0
h_min = 400
for i in range(n_p):
theta, h = angle_and_dist(p[i,:], p[i+1,:], o)
index_o = index_o + theta
if 0 <= h and h < h_min:
h_min = h
if theta <= math.pi/100:
return 'center of disc is not inside polygon'
elif theta > math.pi/100:
if h_min > r:
return 'disc is inside polygon'
else:
return 'center of disc is inside polygon but disc is not'
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
# A test example:
#disc = (np.array([3*a/4, 2]), a/4-0.001)
disc = (np.array([3*a/4, 2]), math.sqrt(3)*a/8 - 0.0001)
print(is_in_polygon(Pgon, disc))

Find line that is tangent to 2 given circles

I've got a situation in which I have 2 circles (C1 and C2)
and i need to find the line equation for the line that is tangent to both of these circles.
So as far as i'm aware, given a single point (P1) and C2's point and radius it is possible to quite easily get 2 possible points of tangency for C2 and P1 to make 2 line equations. But as i don't have P1, only the knowledge that the point will be one of a possible 2 points on C1, i'm not sure how to calculate this.
I assume it will be something along the lines of getting the 2 tangent line equations of C1 that are equal to the same of C2.
Both circles can have any radius, they could be the same or they could be hugely different. They will also never overlap (they can still touch though). And I'm looking for the 2 possible internal tangents.
Oh, and also, visuals would be very helpful haha :)
Let O be the intersection point between the line through the centers and the tangent.
Let d be the distance between the centers and h1, h2 be the distances between O and the centers. By similarity, these are proportional to the radii.
Hence,
h1 / h2 = r1 / r2 = m,
h1 + h2 = d,
giving
h1 = m d / (1 + m),
h2 = d / (1 + m).
Then the coordinates of O are found by interpolating between the centers
xo = (h2.x1 + h1.x2) / d
yo = (h2.y1 + h1.y2) / d
and the angle of the tangent is that of the line through the centers plus or minus the angle between this line and the tangent,
a = arctan((y2 - y1)/(x2 - x1)) +/- arcsin(r1 / h1).
You can write the implicit equation of the tangent as
cos(a).y - sin(a).x = cos(a).yo - sin(a).xo.
(source: imag.fr)
So we are going to use a homothetic transformation. If the circles C and C' have respectively centres O and O', and radius r and r', then we know there exists a unique homothetic transformation with centre J and ratio a, such that :
a = |JO|/|JO'| = r/r'
Noting AB is the vector from A to B, and |z| the norm of a vector z.
Hence you get J, knowing that it is between O and O' which we both already know.
Then with u the projection of JR on JO', and v the decomposition on its orthogonal, and considering the sine s and cosine c of the angle formed by O'JR, we have
|u| = |JR| * c
|v| = |JR| * s
c^2 + s^2 = 1
And finally because the triangle JRO' is right-angled in R :
s = r' / |JO|'
Putting all of this together, we get :
J = O + OO' / |OO'| * a / (a+1)
if |OJ| == r and |O'J| == r' then
return the orthogonal line to (OO') passing through J
|JR| = √( |JO'|^ - r'^2 )
s = r' / |JO'|
c = √( 1 - s^2 )
u = c * |JR| * OO' / |OO'|
w = (-u.y, u.x) % any orthogonal vector to u
v = s * |JR| * w / |w|
return lines corresponding to parametric equations J+t*(u+v) and J+t*(u-v)

How can I convert coordinates on a circle to coordinates on a square?

I'm currently working on a game in LBP2 that has modify the way a controller gives input. This question:
How can I convert coordinates on a square to coordinates on a circle?
Has helped me quite a lot with what I am doing, but I do have one problem. I need the inverse function of the one they give. They go from square -> circle, and I've tried searching all over for how to map a circle to a square.
The function given in the previous question is:
xCircle = xSquare * sqrt(1 - 0.5*ySquare^2)
yCircle = ySquare * sqrt(1 - 0.5*xSquare^2)
From Mapping a Square to a Circle
My question is given xCircle and yCircle... how do I find xSquare and ySquare?
I've tried all of the algebra I know, filled up two pages of notes, tried to get wolfram alpha to get the inverse functions, but this problem is beyond my abilities.
Thank you for taking a look.
x = ½ √( 2 + u² - v² + 2u√2 ) - ½ √( 2 + u² - v² - 2u√2 )
y = ½ √( 2 - u² + v² + 2v√2 ) - ½ √( 2 - u² + v² - 2v√2 )
Note on notation: I'm using x = xSquare , y = ySquare, u = xCircle and v = yCircle;
i.e. (u,v) are circular disc coordinates and (x,y) are square coordinates.
For a C++ implementation of the equations, go to
http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html
See http://squircular.blogspot.com
for more example images.
Also, see http://arxiv.org/abs/1509.06344 for the proof/derivation
This mapping is the inverse of
u = x √( 1 - ½ y² )
v = y √( 1 - ½ x² )
P.S. The mapping is not unique. There are other mappings out there. The picture below illustrates the non-uniqueness of the mapping.
if you have xCircle and yCircle that means that you're on a circle with radius R = sqrt(xCircle^2 + yCircle^2). Now you need to extend that circle to a square with half-side = R,
if (xCircle < yCircle)
ySquare = R, xSquare = xCircle * R/yCircle
else
xSquare = R, ySquare = yCircle * R/xCircle
this is for the first quadrant, for others you need some trivial tweaking with the signs
There are many ways you could do this; here's one simple way.
Imagine a circle of radius R centred on the origin, and a square of side 2R centred on the origin, we want to map all of the points within and on the boundary of the circle (with coordinates (x,y)) to points within and on the boundary of the square. Note that we can also describe points within the circle using polar coordinates (r, ø) (that's supposed to be a phi), where
x = r cos ø,
y = r sin ø
(ie r^2 = x^2 + y^2 and r <= 1). Then imagine other coordinates x' = a(ø) x = a(ø) r cos ø, and y' = a(ø) y (ie, we decide that a won't depend on r).
In order to map the boundary of the circle (r = 1) to the boundary of the square (x' = R), we must have, for ø < 45deg, x' = a(ø) R cos ø = R, so we must have a(ø) = 1/cos ø. Similarly, for 45 < ø < 90 we must have the boundary of the circle map to y' = R, giving a(ø) = 1/sin ø in that region. Continuing round the circle, we see that a(ø) must always be positive, so the final mapping from the circle to the square is
x' = a(ø) x,
y' = a(ø) y
where
ø = |arctan y/x| = arctan |y/x|
and
a(ø) = 1/cos ø, when ø <= 45 deg (ie, when x < y), and
a(ø) = 1/sin ø, when ø > 45 deg.
That immediately gives you the mapping in the other direction. If you have coordinates (x', y') on the square (where x' <= R and y' <= R), then
x = x'/a(ø)
y = y'/a(ø)
with a(ø) as above.
A much simpler mapping, though, is to calculate the (r, ø) for the desired position on the circle, and map that to x' = r and y' = ø. That also maps every point in the circle into a rectangle, and vice versa, and might have better properties, depending on what you want to do.
So that's the real question: what is it you're actually aiming to do here?
I was implementing the solution above but the results are not satisfiying.
The square coordinates are not exact.
Here is a simple counter-example:
Consider the point (x,y)=(0.75, 1) on the square.
We map it to the circle with (u,v)=(0.53, 0.85) on the circle.
Applying the expression above we get the new square coordinates
(x',y')=(u/v,r)=(0.625543242, 1) with r=(u^2+v^2)^(1/2).
This point is close but not the expected precise solution.
I solved a root finding problem in order to get the inverse expression of the mapping from square to circle like above.
you need to solve the system equations like above:
I) u = x*(1-y^2/2)^(1/2)
II) v = y*(1-x^2/2)^(1/2)
One ends up with 8 root points as solution. One of the roots I implemented into Excel-VBA which I present here below and it works very fine.
' given the circle coordinates (u,v) caluclates the x coordinate on the square
Function circ2sqrX(u As Double, v As Double) As Double
Dim r As Double, signX As Double, u2 As Double, v2 As Double, uuvv As Double, temp1 As Double
u2 = u * u
v2 = v * v
r = Sqr(u2 + v2)
signX = 1
If v = 0 Or u = 0 Then
circ2sqrX = u
Exit Function
End If
If u < 0 Then
signX = -1
End If
If Abs(u) = Abs(v) And r = 1 Then
circ2sqrX = signX
Exit Function
End If
uuvv = (u2 - v2) * (u2 - v2) / 4
temp1 = 2 * Sqr(uuvv - u2 - v2 + 1)
circ2sqrX = -((temp1 - u2 + v2 - 2) * Sqr(temp1 + u2 - v2 + 2)) / (4 * u)
End Function
' given the circle coordinates (u,v) caluclates the y coordinate on the square
' make use of symetrie property
Function circ2sqrY(u As Double, v As Double) As Double
circ2sqrY=circ2sqrX(v,u)
End Function

Line of intersection between two planes

How can I find the line of intersection between two planes?
I know the mathematics idea, and I did the cross product between the the planes normal vectors
but how to get the line from the resulted vector programmatically
The equation of the plane is ax + by + cz + d = 0, where (a,b,c) is the plane's normal, and d is the distance to the origin. This means that every point (x,y,z) that satisfies that equation is a member of the plane.
Given two planes:
P1: a1x + b1y + c1z + d1 = 0
P2: a2x + b2y + c2z + d2 = 0
The intersection between the two is the set of points that verifies both equations. To find points along this line, you can simply pick a value for x, any value, and then solve the equations for y and z.
y = (-c1z -a1x -d1) / b1
z = ((b2/b1)*(a1x+d1) -a2x -d2)/(c2 - c1*b2/b1)
If you make x=0, this gets simpler:
y = (-c1z -d1) / b1
z = ((b2/b1)*d1 -d2)/(c2 - c1*b2/b1)
Finding the line between two planes can be calculated using a simplified version of the 3-plane intersection algorithm.
The 2'nd, "more robust method" from bobobobo's answer references the 3-plane intersection.
While this works well for 2 planes (where the 3rd plane can be calculated using the cross product of the first two), the problem can be further reduced for the 2-plane version.
No need to use a 3x3 matrix determinant,instead we can use the squared length of the cross product between the first and second plane (which is the direction of the 3'rd plane).
No need to include the 3rd planes distance,(calculating the final location).
No need to negate the distances.Save some cpu-cycles by swapping the cross product order instead.
Including this code-example, since it may not be immediately obvious.
// Intersection of 2-planes: a variation based on the 3-plane version.
// see: Graphics Gems 1 pg 305
//
// Note that the 'normal' components of the planes need not be unit length
bool isect_plane_plane_to_normal_ray(
const Plane& p1, const Plane& p2,
// output args
Vector3f& r_point, Vector3f& r_normal)
{
// logically the 3rd plane, but we only use the normal component.
const Vector3f p3_normal = p1.normal.cross(p2.normal);
const float det = p3_normal.length_squared();
// If the determinant is 0, that means parallel planes, no intersection.
// note: you may want to check against an epsilon value here.
if (det != 0.0) {
// calculate the final (point, normal)
r_point = ((p3_normal.cross(p2.normal) * p1.d) +
(p1.normal.cross(p3_normal) * p2.d)) / det;
r_normal = p3_normal;
return true;
}
else {
return false;
}
}
Adding this answer for completeness, since at time of writing, none of the answers here contain a working code-example which directly addresses the question.
Though other answers here already covered the principles.
Finding a point on the line
To get the intersection of 2 planes, you need a point on the line and the direction of that line.
Finding the direction of that line is really easy, just cross the 2 normals of the 2 planes that are intersecting.
lineDir = n1 × n2
But that line passes through the origin, and the line that runs along your plane intersections might not. So, Martinho's answer provides a great start to finding a point on the line of intersection (basically any point that is on both planes).
In case you wanted to see the derivation for how to solve this, here's the math behind it:
First let x=0. Now we have 2 unknowns in 2 equations instead of 3 unknowns in 2 equations (we arbitrarily chose one of the unknowns).
Then the plane equations are (A terms were eliminated since we chose x=0):
B1y + C1z + D1 = 0
B2y + C2z + D2 = 0
We want y and z such that those equations are both solved correctly (=0) for the B1, C1 given.
So, just multiply the top eq by (-B2/B1) to get
-B2y + (-B2/B1)*C1z + (-B2/B1)*D1 = 0
B2y + C2z + D2 = 0
Add the eqs to get
z = ( (-B2/B1)*D1 - D2 ) / (C2 * B2/B1)*C1)
Throw the z you find into the 1st equation now to find y as
y = (-D1 - C1z) / B1
Note the best variable to make 0 is the one with the lowest coefficients, because it carries no information anyway. So if C1 and C2 were both 0, choosing z=0 (instead of x=0) would be a better choice.
The above solution can still screw up if B1=0 (which isn't that unlikely). You could add in some if statements that check if B1=0, and if it is, be sure to solve for one of the other variables instead.
Solution using intersection of 3 planes
From user's answer, a closed form solution for the intersection of 3 planes was actually in Graphics Gems 1. The formula is:
P_intersection = (( point_on1 • n1 )( n2 × n3 ) + ( point_on2 • n2 )( n3 × n1 ) + ( point_on3 • n3 )( n1 × n2 )) / det(n1,n2,n3)
Actually point_on1 • n1 = -d1 (assuming you write your planes Ax + By + Cz + D=0, and not =-D). So, you could rewrite it as:
P_intersection = (( -d1 )( n2 × n3 ) + ( -d2 )( n3 × n1 ) + ( -d3 )( n1 × n2 )) / det(n1,n2,n3)
A function that intersects 3 planes:
// Intersection of 3 planes, Graphics Gems 1 pg 305
static Vector3f getIntersection( const Plane& plane1, const Plane& plane2, const Plane& plane3 )
{
float det = Matrix3f::det( plane1.normal, plane2.normal, plane3.normal ) ;
// If the determinant is 0, that means parallel planes, no intn.
if( det == 0.f ) return 0 ; //could return inf or whatever
return ( plane2.normal.cross( plane3.normal )*-plane1.d +
plane3.normal.cross( plane1.normal )*-plane2.d +
plane1.normal.cross( plane2.normal )*-plane3.d ) / det ;
}
Proof it works (yellow dot is intersection of rgb planes here)
Getting the line
Once you have a point of intersection common to the 2 planes, the line just goes
P + t*d
Where P is the point of intersection, t can go from (-inf, inf), and d is the direction vector that is the cross product of the normals of the two original planes.
The line of intersection between the red and blue planes looks like this
Efficiency and stability
The "robust" (2nd way) takes 48 elementary ops by my count, vs the 36 elementary ops that the 1st way (isolation of x,y) uses. There is a trade off between stability and # computations between these 2 ways.
It'd be pretty catastrophic to get (0,inf,inf) back from a call to the 1st way in the case that B1 was 0 and you didn't check. So adding in if statements and making sure not to divide by 0 to the 1st way may give you the stability at the cost of code bloat, and the added branching (which might be quite expensive). The 3 plane intersection method is almost branchless and won't give you infinities.
This method avoids division by zero as long as the two planes are not parallel.
If these are the planes:
A1*x + B1*y + C1*z + D1 = 0
A2*x + B2*y + C2*z + D2 = 0
1) Find a vector parallel to the line of intersection. This is also the normal of a 3rd plane which is perpendicular to the other two planes:
(A3,B3,C3) = (A1,B1,C1) cross (A2,B2,C2)
2) Form a system of 3 equations. These describe 3 planes which intersect at a point:
A1*x1 + B1*y1 + C1*z1 + D1 = 0
A2*x1 + B2*y1 + C2*z1 + D2 = 0
A3*x1 + B3*y1 + C3*z1 = 0
3) Solve them to find x1,y1,z1. This is a point on the line of intersection.
4) The parametric equations of the line of intersection are:
x = x1 + A3 * t
y = y1 + B3 * t
z = z1 + C3 * t
The determinant-based approach is neat, but it's hard to follow why it works.
Here's another way that's more intuitive.
The idea is to first go from the origin to the closest point on the first plane (p1), and then from there go to the closest point on the line of intersection of the two planes. (Along a vector that I'm calling v below.)
Given
=====
First plane: n1 • r = k1
Second plane: n2 • r = k2
Working
=======
dir = n1 × n2
p1 = (k1 / (n1 • n1)) * n1
v = n1 × dir
pt = LineIntersectPlane(line = (p1, v), plane = (n2, k2))
LineIntersectPlane
==================
#We have n2 • (p1 + lambda * v) = k2
lambda = (k2 - n2 • p1) / (n2 • v)
Return p1 + lambda * v
Output
======
Line where two planes intersect: (pt, dir)
This should give the same point as the determinant-based approach. There's almost certainly a link between the two. At least the denominator, n2 • v, is the same, if we apply the "scalar triple product" rule. So these methods are probably similar as far as condition numbers go.
Don't forget to check for (almost) parallel planes. For example: if (dir • dir < 1e-8) should work well if unit normals are used.
You can find the formula for the intersection line of two planes in this link.
P1: a1x + b1y + c1z = d1
P2: a2x + b2y + c2z = d2
n1=(a1,b1,c1); n2=(a2,b2,c2); n12=Norm[Cross[n1,n2]]^2
If n12 != 0
a1 = (d1*Norm[n2]^2 - d2*n1.n2)/n12;
a2 = (d2*Norm[n1]^2 - d1*n1.n2)/n12;
P = a1 n1 + a2 n2;
(*formula for the intersection line*)
Li[t_] := P + t*Cross[n1, n2];
The cross product of the line is the direction of the intersection line. Now you need a point in the intersection.
You can do this by taking a point on the cross product, then subtracting Normal of plane A * distance to plane A and Normal of plane B * distance to plane b. Cleaner:
p = Point on cross product
intersection point = ([p] - ([Normal of plane A] * [distance from p to plane A]) - ([Normal of plane B] * [distance from p to plane B]))
Edit:
You have two planes with two normals:
N1 and N2
The cross product is the direction of the Intersection Line:
C = N1 x N2
The class above has a function to calculate the distance between a point and a plane. Use it to get the distance of some point p on C to both planes:
p = C //p = 1 times C to get a point on C
d1 = plane1.getDistance(p)
d2 = plane2.getDistance(p)
Intersection line:
resultPoint1 = (p - (d1 * N1) - (d2 * N2))
resultPoint2 = resultPoint1 + C

Resources