Пример #1
0
def quaternion_that_rotates_axes_frame(
    source_xyz_axes: Tuple[Vector3r, Vector3r, Vector3r],
    target_xyz_axes: Tuple[Vector3r, Vector3r, Vector3r],
    assume_normalized: bool = False,  # warn if it isn't
) -> Quaternionr:
    """ Returns the quaternion that rotates vectors from the `source` coordinate system to the `target` axes frame. """
    if not assume_normalized:
        assert all(np.isclose(_.get_length(), 1) for _ in source_xyz_axes + target_xyz_axes)

    # ref.: https://math.stackexchange.com/a/909245
    i, j, k = source_xyz_axes
    a, b, c = target_xyz_axes

    def quaternion_from_vector(v: Vector3r) -> Quaternionr:
        return Quaternionr(v.x_val, v.y_val, v.z_val, w_val=0)

    qx = quaternion_from_vector(a) * quaternion_from_vector(i)
    qy = quaternion_from_vector(b) * quaternion_from_vector(j)
    qz = quaternion_from_vector(c) * quaternion_from_vector(k)

    rx = qx.x_val + qy.x_val + qz.x_val
    ry = qx.y_val + qy.y_val + qz.y_val
    rz = qx.z_val + qy.z_val + qz.z_val
    rw = qx.w_val + qy.w_val + qz.w_val

    rotation = Quaternionr(-rx, -ry, -rz, w_val=(1 - rw))
    length = rotation.get_length()
    assert not np.isclose(length, 0)

    return rotation / length  # normalize
Пример #2
0
def vector_rotated_by_quaternion(v: Vector3r, q: Quaternionr) -> Vector3r:
    q /= q.get_length()  # normalize

    # Extract the vector and scalar parts of q:
    u, s = Vector3r(q.x_val, q.y_val, q.z_val), q.w_val

    # NOTE the results from these two methods are the same up to 7 decimal places.

    # ref.: https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion
    # return u * (2 * u.dot(v)) + v * (s * s - u.dot(u)) + u.cross(v) * (2 * s)

    # ref.: https://gitlab.com/libeigen/eigen/-/blob/master/Eigen/src/Geometry/Quaternion.h (_transformVector)
    uv = u.cross(v)
    uv += uv
    return v + uv * s + u.cross(uv)