Search code examples
pythonnumpy3drotationangle

How can I place a line exactly on the Y-axis?


Suppose we have several points located in three-dimensional space. Now, we specify two points, named point1 and point2. To simplify the problem, we consider these two points as a line. Now we move point1 to the origin. This translation is such that all the points are displaced accordingly. Now, all points must be rotated so that point2 is exactly on the Y-axis, meaning its x and z coordinates should be zero. But what specific calculations need to be performed to determine the directions in which all these points should rotate so that point2 is precisely positioned on the Y-axis?

I would really appreciate your help.

If I want to ask my question more precisely, how should the values of Ax, Ay, and Az be calculated?

import numpy as np

points = np.array([[0.36266313,0.70320135,0.88975275],
 [0.26227164,0.32188661,0.39514979],
 [0.26100571,0.63643259,0.20245194],
 [0.25701545,0.59125069,0.80146842],
 [0.23185588,0.19422526,0.68689653]])

point1, point2 = points[0], points[2]

points = points - point1
point2 = point2 - point1
point1 = point1 - point1

def rotate_points_around_point(points, angle_degrees, axis, center_point):
    translated_points = points - center_point
    angle_radians = np.radians(angle_degrees)
    rotation_matrix = {
        'x': np.array([[1, 0, 0],
                       [0, np.cos(angle_radians), -np.sin(angle_radians)],
                       [0, np.sin(angle_radians), np.cos(angle_radians)]]),
        'y': np.array([[np.cos(angle_radians), 0, np.sin(angle_radians)],
                       [0, 1, 0],
                       [-np.sin(angle_radians), 0, np.cos(angle_radians)]]),
        'z': np.array([[np.cos(angle_radians), -np.sin(angle_radians), 0],
                       [np.sin(angle_radians), np.cos(angle_radians), 0],
                       [0, 0, 1]])
    }
    rotated_points = np.dot(translated_points, rotation_matrix[axis].T)
    rotated_points = rotated_points + center_point
    return rotated_points

# ======== Question ========
Ax = 96
Ay = -3.0626
Az = -8.38675
# ======== Question ========

points = rotate_points_around_point(points, Ax, "x", point1)
points = rotate_points_around_point(points, Ay, "y", point1)
points = rotate_points_around_point(points, Az, "z", point1)

print(points)

Result:

[[ 0.        0.        0.      ]
 [-0.004306  0.538135 -0.332422]
 [ 0.        0.697979  0.      ]
 [-0.084459  0.11303  -0.107608]
 [-0.066404  0.267493 -0.49128 ]]

I value the insights and guidance you provide.


Solution

  • Translate the points such that your first reference point is at the origin.

    Let B be the position vector of the second reference point after translation.

    KEY STEP: take the vector cross product of B and a unit vector in the y direction (in that order). The resulting vector will have direction the ultimate rotation axis and magnitude norm(B)x1xsin(theta), where theta is the angle of rotation.

    You can create a rotation matrix from this axis and the rotation angle. This is the single rotation matrix that you require.

    The following code does this. Note that floating-point operations leave you with x and z coordinates in point[2] that are of order 1e-16: effectively 0.

    import math
    import numpy as np
    import matplotlib.pyplot as plt
    
    
    def rotationMatrix( a, theta ):        # rotation matrix for a rotation of 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
    
    
    points = np.array([[0.36266313,0.70320135,0.88975275],
                       [0.26227164,0.32188661,0.39514979],
                       [0.26100571,0.63643259,0.20245194],
                       [0.25701545,0.59125069,0.80146842],
                       [0.23185588,0.19422526,0.68689653]])
    
    A = points[0]                                            # First reference point
    points -= A                                              # Translate such that A goes to origin
    B = points[2]                                            # B is the point you want to rotate onto the y axis (did you mean [2]?)
    
    Y = np.array( [0.0, 1.0, 0.0 ] )                         # Unit vector along y axis
    axis = np.cross( B, Y )                                  # Axis of rotation (not normalised)
    mag = np.linalg.norm( axis )
    if ( abs(mag) < 1.0e-20 ): axis = np.array([1.0,0,0])    # Already on y axis, so rotation axis is irrelevant
    sintheta = mag / np.linalg.norm( B )                     # Sine of angle of rotation
    theta = math.asin( sintheta )
    if np.dot( B, Y ) < 0: theta = math.pi - theta           # Appropriate angle to rotate to the POSITIVE y axis
    
    R = rotationMatrix( axis, theta )                        # Find rotation matrix
    
    points = ( R @ (points.T) ).T                            # Rotate points
    print( points )
    

    Output:

    [[ 0.00000000e+00  0.00000000e+00  0.00000000e+00]
     [-7.51354446e-02  5.38134329e-01 -3.23848081e-01]
     [-3.85256819e-17  6.97979012e-01 -2.79186184e-16]
     [-1.05473850e-01  1.13030002e-01 -8.71090761e-02]
     [-1.69698494e-01  2.67492963e-01 -4.65798002e-01]]