def _get_perspective_transform(
    angle: float,
    width: int,
    height: int,
    focal: float,
    dist: float,
    crop_percent: float,
    crop_off_x: float,
    crop_off_y: float,
    pts: np.ndarray | None = None,
) -> tuple[np.ndarray, int, int]:
    """
    Computes parameters for perspective transform.

    :param angle: Angle to rotate.
    :param width: Width of image.
    :param height: Height of image.
    :param focal: Focal length.
    :param dist: Distance for transform.
    :param crop_percent: Percentage for cropping.
    :param crop_off_x: Cropping x offset.
    :param crop_off_y: Cropping y offset.
    :param pts: pts to include in the crop.
    :return: perspective transform matrix, crop width, crop_height
    """
    angle = math.radians(angle)
    x_cam_off = width / 2 - math.sin(angle) * dist
    z_cam_off = -math.cos(angle) * dist
    y_cam_off = height / 2

    rot_mat = np.array(
        [
            [math.cos(angle), 0, -math.sin(angle), 0],
            [0, 1, 0, 0],
            [math.sin(angle), 0, math.cos(angle), 0],
            [0, 0, 0, 1],
        ]
    )
    c_mat = np.array([[1, 0, 0, -x_cam_off], [0, 1, 0, -y_cam_off], [0, 0, 1, -z_cam_off], [0, 0, 0, 1]])

    rt_mat = np.matmul(rot_mat, c_mat)

    h_mat = np.array(
        [
            [focal * rt_mat[0, 0], focal * rt_mat[0, 1], focal * rt_mat[0, 3]],
            [focal * rt_mat[1, 0], focal * rt_mat[1, 1], focal * rt_mat[1, 3]],
            [rt_mat[2, 0], rt_mat[2, 1], rt_mat[2, 3]],
        ]
    )

    x_off, y_off, crop_size = get_offset_and_crop_size(
        width, height, h_mat, crop_percent, crop_off_x, crop_off_y, focal / dist, pts
    )

    affine_mat = np.array([[1, 0, x_off], [0, 1, y_off], [0, 0, 1]])
    perspective_mat = np.matmul(affine_mat, h_mat)

    if height > width:  # tall and narrow
        crop_x = int(crop_size)
        crop_y = int(round(crop_size / width * height))
    else:  # wide and short or square
        crop_y = int(crop_size)
        crop_x = int(round(crop_size / height * width))

    return perspective_mat, crop_x, crop_y

    Computes parameters for perspective transform.

    :param angle: Angle to rotate.
    :param width: Width of image.
    :param height: Height of image.
    :param focal: Focal length.
    :param dist: Distance for transform.
    :param crop_percent: Percentage for cropping.
    :param crop_off_x: Cropping x offset.
    :param crop_off_y: Cropping y offset.
    :param pts: pts to include in the crop.
    :return: perspective transform matrix, crop width, crop_height
    
art/attacks/evasion/graphite/utils.py