# Path: dataflow/illumination_integration/helper.py
# def getBilinearFromUv(env_map: tf.Tensor, uvs: tf.Tensor) -> tf.Tensor:
#     u = uvs[..., 0]
#     v = uvs[..., 1]
# 
#     # vFlipped = 1 - v
# 
#     # u corresponds to width and v to heights
#     u_reshaped = tf.reshape(u, (-1, tf.math.reduce_prod(u.shape[1:])))
#     v_reshaped = tf.reshape(v, (-1, tf.math.reduce_prod(v.shape[1:])))
# 
#     uvs = tf.stack(
#         [u_reshaped * (env_map.shape[2] - 1), v_reshaped * (env_map.shape[1] - 1)],
#         axis=-1,
#     )
# 
#     return tfa.image.interpolate_bilinear(env_map, uvs, indexing="xy")
# 
# Path: nn_utils/math_utils.py
# def shape_to_uv(height: int, width: int) -> tf.Tensor:
#     # UV
#     # 0,0              1,0
#     # 0,1              1,1
#     us, vs = tf.meshgrid(
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(width, tf.float32),
#             1.0 - 0.5 / tf.cast(width, tf.float32),
#             width,
#         ),
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(height, tf.float32),
#             1.0 - 0.5 / tf.cast(height, tf.float32),
#             height,
#         ),
#     )  # Use pixel centers
#     return tf.cast(tf.stack([us, vs], -1), tf.float32)
# 
# def uv_to_direction(uvs: tf.Tensor) -> tf.Tensor:
#     spherical = uv_to_spherical(uvs)
#     theta = spherical[..., 0:1]
#     phi = spherical[..., 1:2]
#     return spherical_to_cartesian(theta, phi)


from typing import List, Union
from dataflow.illumination_integration.helper import (
    getBilinearFromUv,
)
from nn_utils.math_utils import shape_to_uv, uv_to_direction
import numpy as np
import tensorflow as tf
import nn_utils.math_utils as math_utils
    for d in data_levels:
        samples_level = getBilinearFromUv(d[None, ...], uvs[None, ...])[0]
        smpl_list.append(samples_level)

    level_samples_batched = tf.stack(smpl_list, 0)  # M, S, 3

    return interpolate_roughness_levels(level_samples_batched, roughness)


@tf.function
def interpolate_roughness_levels(samples, roughness):
    tf.debugging.assert_shapes(
        [
            (samples, ("M", "S", 3)),
            (roughness, ("S", 1)),
        ]
    )

    # Setup the roughness interpolation
    roughness_mip_index = roughness[:, 0] * (samples.shape[0] - 1)
    # S
    lower_mip_index = tf.cast(tf.math.floor(roughness_mip_index), tf.int32)
    upper_mip_index = tf.cast(tf.math.ceil(roughness_mip_index), tf.int32)

    # Fetch the lower and upper roughness levels
    rgh_low = tf.gather(
        tf.transpose(samples, [1, 0, 2]), lower_mip_index[..., None], batch_dims=1
    )[:, 0]
    rgh_hgh = tf.gather(
        tf.transpose(samples, [1, 0, 2]), upper_mip_index[..., None], batch_dims=1

# Path: dataflow/illumination_integration/helper.py
# def getBilinearFromUv(env_map: tf.Tensor, uvs: tf.Tensor) -> tf.Tensor:
#     u = uvs[..., 0]
#     v = uvs[..., 1]
# 
#     # vFlipped = 1 - v
# 
#     # u corresponds to width and v to heights
#     u_reshaped = tf.reshape(u, (-1, tf.math.reduce_prod(u.shape[1:])))
#     v_reshaped = tf.reshape(v, (-1, tf.math.reduce_prod(v.shape[1:])))
# 
#     uvs = tf.stack(
#         [u_reshaped * (env_map.shape[2] - 1), v_reshaped * (env_map.shape[1] - 1)],
#         axis=-1,
#     )
# 
#     return tfa.image.interpolate_bilinear(env_map, uvs, indexing="xy")
# 
# Path: nn_utils/math_utils.py
# def shape_to_uv(height: int, width: int) -> tf.Tensor:
#     # UV
#     # 0,0              1,0
#     # 0,1              1,1
#     us, vs = tf.meshgrid(
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(width, tf.float32),
#             1.0 - 0.5 / tf.cast(width, tf.float32),
#             width,
#         ),
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(height, tf.float32),
#             1.0 - 0.5 / tf.cast(height, tf.float32),
#             height,
#         ),
#     )  # Use pixel centers
#     return tf.cast(tf.stack([us, vs], -1), tf.float32)
# 
# def uv_to_direction(uvs: tf.Tensor) -> tf.Tensor:
#     spherical = uv_to_spherical(uvs)
#     theta = spherical[..., 0:1]
#     phi = spherical[..., 1:2]
#     return spherical_to_cartesian(theta, phi)


# Path: dataflow/illumination_integration/dataflow.py
from typing import List, Union
from dataflow.illumination_integration.helper import (
    getBilinearFromUv,
)
from nn_utils.math_utils import shape_to_uv, uv_to_direction
import numpy as np
import tensorflow as tf
import nn_utils.math_utils as math_utils

    for d in data_levels:
        samples_level = getBilinearFromUv(d[None, ...], uvs[None, ...])[0]
        smpl_list.append(samples_level)

    level_samples_batched = tf.stack(smpl_list, 0)  # M, S, 3

    return interpolate_roughness_levels(level_samples_batched, roughness)


@tf.function
def interpolate_roughness_levels(samples, roughness):
    tf.debugging.assert_shapes(
        [
            (samples, ("M", "S", 3)),
            (roughness, ("S", 1)),
        ]
    )

    # Setup the roughness interpolation
    roughness_mip_index = roughness[:, 0] * (samples.shape[0] - 1)
    # S
    lower_mip_index = tf.cast(tf.math.floor(roughness_mip_index), tf.int32)
    upper_mip_index = tf.cast(tf.math.ceil(roughness_mip_index), tf.int32)

    # Fetch the lower and upper roughness levels
    rgh_low = tf.gather(
        tf.transpose(samples, [1, 0, 2]), lower_mip_index[..., None], batch_dims=1
    )[:, 0]
    rgh_hgh = tf.gather(
        tf.transpose(samples, [1, 0, 2]), upper_mip_index[..., None], batch_dims=1

    )[:, 0]