import torch
import math


def perspective(aspect_ratio, fov_y, near_clip, far_clip):
    """Computes perspective transformation matrices.

    Functionality mimes gluPerspective (external/GL/glu/include/GLU/glu.h).
    See:
    https://unspecified.wordpress.com/2012/06/21/calculating-the-gluperspective-matrix-and-other-opengl-matrix-maths/

    Args:
        aspect_ratio: float value specifying the image aspect ratio
            (width/height).
        fov_y: 1D float32 Tensor with shape [batch_size] specifying output
            vertical field of views in degrees.
        near_clip: 1D float32 Tensor with shape [batch_size] specifying near
            clipping plane distance.
        far_clip: 1D float32 Tensor with shape [batch_size] specifying far
            clipping plane distance.

    Returns:
        A [batch_size, 4, 4] float tensor that maps from right-handed points in
        eye space to left-handed points in clip space.
    """
    # The multiplication of fov_y by pi/360.0 simultaneously converts to radians
    # and adds the half-angle factor of .5.
    focal_lengths_y = 1.0 / torch.tan(fov_y * (math.pi / 360.0))
    depth_range = far_clip - near_clip
    p_22 = -(far_clip + near_clip) / depth_range
    p_23 = -2.0 * (far_clip * near_clip / depth_range)

    zeros = torch.zeros_like(p_23, dtype=torch.float32)
    perspective_transform = torch.cat(
        [
            focal_lengths_y / aspect_ratio, zeros, zeros, zeros,
            zeros, focal_lengths_y, zeros, zeros,
            zeros, zeros, p_22, p_23,
            zeros, zeros, -torch.ones_like(p_23, dtype=torch.float32), zeros
        ], dim=0)

    perspective_transform = torch.reshape(perspective_transform, [4, 4, -1])
    return perspective_transform.permute(2, 0, 1)