//  [python]
def plane_pts(rays, planes, id_planes, near, method="planes"):
    """Ray-Plane intersection for given planes in the scene

    Args:
        rays: ray origin and directions
        planes: first plane position, plane normal and distance between planes
        id_planes: ids of used planes
        near: distance between camera pose and first intersecting plane
        method: Method used

    Returns:
        pts: [N_rays, N_samples+N_importance] - intersection points of rays and selected planes
        z_vals: position of the point along each ray respectively
    """
    # Extract ray and plane definitions
    rays_o, rays_d = rays
    N_rays = rays_o.shape[0]
    # N_rays = rays_o.get_shape().as_list()[0]
    plane_bds, plane_normal, delta = planes
    plane_bds = torch.from_numpy(plane_bds).to(torch.float32)
    plane_normal = torch.from_numpy(plane_normal).to(torch.float32)

    # Get amount of all planes
    n_planes = torch.ceil(torch.linalg.norm(plane_bds[:, -1] - plane_bds[:, 0]) / delta) + 1
    # n_planes = tf.math.ceil(tf.norm(plane_bds[:, -1] - plane_bds[:, 0]) / delta) + 1

    # Calculate how far the ray_origins lies apart from each plane
    d_ray_first_plane = torch.matmul(plane_bds[:, 0] - rays_o, plane_normal[:, None])
    d_ray_first_plane = torch.maximum(-d_ray_first_plane, -near)
    # d_ray_first_plane = tf.matmul(plane_bds[:, 0]-rays_o, plane_normal[:, None])
    # d_ray_first_plane = tf.maximum(-d_ray_first_plane, -near)

    # Get the ids of the planes in front of each ray starting from near distance upto the far plane
    start_id = torch.ceil((d_ray_first_plane + near) / delta)
    plane_id = start_id + torch.from_numpy(id_planes).to(torch.float32)
    if method == "planes":
        plane_id = torch.cat([plane_id[:, :-1], n_planes.repeat_interleave(N_rays)[:, None]], dim=1)
        # plane_id = tf.concat([plane_id[:, :-1], tf.repeat(n_planes, N_rays)[:, None]], axis=1)
    elif method == "planes_plus":
        # Experimental setup, that got discarded due to lower or the same quality
        plane_id = torch.cat(
            [
                plane_id[:, :1],
                id_planes[None, 1:-1].repeat_interleave(N_rays, dim=0),
                n_planes.repeat_interleave(N_rays)[:, None],
            ],
            dim=1,
        )
        # plane_id = tf.concat([plane_id[:, :1],
        #                       tf.repeat(id_planes[None, 1:-1], N_rays, axis=0),
        #                       tf.repeat(n_planes, N_rays)[:, None]], axis=1)

    # [N_samples, N_rays, xyz]
    z_planes = plane_normal[None, None, :] * torch.transpose(plane_id * delta, 0, 1)[..., None]
    # z_planes = plane_normal[None, None, :] * tf.transpose(plane_id*delta)[..., None]
    relevant_plane_origins = plane_bds[:, 0][None, None, :] + z_planes

    # Distance between each ray's origin and associated planes
    d_plane_pose = relevant_plane_origins - rays_o[None, :, :]

    n = torch.matmul(d_plane_pose, plane_normal[..., None])
    z = torch.matmul(rays_d, plane_normal[..., None])

    z_vals = torch.transpose(torch.squeeze(n / z), 0, 1)

    pts = rays_o[..., None, :] + rays_d[..., None, :] * z_vals[..., None]

    return pts, z_vals