I have 3D points A,B,C,D and E. These points are not coplanar or collinear. I can obtain points A' and B' in another coordinate system that preserve the original AB distance. With just this information, can I obtain points C', D' and E' that have A'C', A'D', etc, distances, angles and relationships just as the original AC, AD, etc?
I have tried various methods attempting rotation matrices that didn't work. I was trying to find a rotation matrix that would map A to A' and B to B', and use that to obtain C', D' and E', but it didn't work. The closest I got was with:
C_prime = A_prime + (C - A)
D_prime = A_prime + (D - A)
E_prime = A_prime + (E - A)
(A_prime, A, C, D and E are numpy arrays of the 3D coordinates). This gives me C', D' and E' that are the correct distances from A', but not B'. Using B and B_prime instead of A and A_prime gives the opposite. How do I combine the two constraints? I am using python and numpy.
ONE possible image comes from C_prime = A_prime + R(C - A)
etc, where R
is the rotation matrix that would take AB
to A_prime B_prime
and C is any of the other points. The rotation matrix can (usually) be found from the angle between those two vectors and an axis formed from their cross product ... except in the case where they are colinear and hence the cross product is 0.
Note that this transformation is NOT UNIQUE, since you could subsequently apply any rotation that you liked about A_prime B_prime
and still preserve distances and relative angles.
You must also enforce that AB
has the same length as A_prime B_prime
.
In the code below the points are the rows of array pts[,]
. My apologies for the lousy 3-d plot: not my strong point.
import math
import numpy as np
import matplotlib.pyplot as plt
def rotationMatrix( a, theta ): # rotation matrix for active rotation, angle theta about axis a
R = np.zeros( ( 3, 3 ) )
n = a / np.linalg.norm( a )
C = math.cos( theta )
S = math.sin( theta )
R[0,0] = C + n[0] * n[0] * ( 1.0 - C )
R[0,1] = n[0] * n[1] * ( 1.0 - C ) - n[2] * S
R[0,2] = n[0] * n[2] * ( 1.0 - C ) + n[1] * S
R[1,1] = C + n[1] * n[1] * ( 1.0 - C )
R[1,2] = n[1] * n[2] * ( 1.0 - C ) - n[0] * S
R[1,0] = n[1] * n[0] * ( 1.0 - C ) + n[2] * S
R[2,2] = C + n[2] * n[2] * ( 1.0 - C )
R[2,0] = n[2] * n[0] * ( 1.0 - C ) - n[1] * S
R[2,1] = n[2] * n[1] * ( 1.0 - C ) + n[0] * S
return R
pts = np.array( [ [0,0,0], [1,0,0], [1,1,0], [0.5,1.5,0.5], [0,1,0] ], dtype=float )
A = pts[0,:]
B = pts[1,:]
Aprime = np.array( [2,2,0] )
Bprime = np.array( [2,2,-1] )
# Ensure that A'B' has the same length as AB
length = np.linalg.norm( B - A )
Bprime = Aprime + length * ( Bprime - Aprime ) / np.linalg.norm( Bprime - Aprime )
# Rotation matrix
rotate = rotationMatrix( np.cross( B - A, Bprime - Aprime ), math.acos( np.dot( Bprime - Aprime, B - A ) / length ** 2 ) )
# Transform points
ptsprime = np.zeros_like( pts )
for i in range( len( pts[:,0] ) ): ptsprime[i,:] = Aprime + rotate @ ( pts[i,:] - A ).T
# Draw it
ax = plt.figure().add_subplot( projection='3d' )
ax.plot( pts[:,0], pts[:,1], pts[:,2], color='r' )
ax.plot( ptsprime[:,0], ptsprime[:,1], ptsprime[:,2], color='b' )
ax.set_aspect( 'equal' )
plt.show()