Search code examples
python3dgeometry

How can one obtain a translation and rotation of 3D points, given points that have already been rotated?


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.


Solution

  • 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()
    

    Output: enter image description here