essentials.clΒΆ

/// @file essentials.cl
///
/// Essential functions for streamline trajectory integration
///

///
/// @defgroup utilities Utility functions
/// Functions used frequently by kernels
///

/// Bilinearly interpolate a velocity vector (choice of row-major or column-major arrays).
///
/// Perform a fast, simple bilinear interpolation at arbitrary position vector
/// from regular grid of velocity vectors in global uv_array.
///
/// @param[in] vec      (float2 *, RO): real-valued vector position (x,y) onto which to
///                                     interpolate (u,v)
/// @param[in] uv_array (float2 *, RO): gridded velocity vector components (u,v)
///
/// @returns  normalized velocity vector (u,v) sampled at position vec (x,y)
///
/// @ingroup utilities
///
static float2 speed_interpolator(float2 vec, __global const float2 *uv_array)
{
    const uint x_lft = min( NX_PADDED-1, (uint)(max(0.0f, vec[0]+PAD_WIDTH_PP5)));
    const uint y_dwn = min( NY_PADDED-1, (uint)(max(0.0f, vec[1]+PAD_WIDTH_PP5)));
    const uint x_rgt = min( NX_PADDED-1, x_lft+1u );
    const uint y_upp = min( NY_PADDED-1, y_dwn+1u );

    // Get the fractional displacement of the sample point from the down-left vertex
    const float rx_weight = vec[0]-(float)x_lft+(float)PAD_WIDTH;
    const float lx_weight = 1.0f-rx_weight;
    const float uy_weight = vec[1]-(float)y_dwn+(float)PAD_WIDTH;
    const float dy_weight = 1.0f-uy_weight;

    // Use to weight the four corner values...
    const float2 uv_dwn = uv_array[NY_PADDED*x_lft+y_dwn]*lx_weight
                        + uv_array[NY_PADDED*x_rgt+y_dwn]*rx_weight;
    const float2 uv_upp = uv_array[NY_PADDED*x_lft+y_upp]*lx_weight
                        + uv_array[NY_PADDED*x_rgt+y_upp]*rx_weight;
    // Returns:
    //    interpolated 2d unit speed vector:
    return fast_normalize(uv_dwn*dy_weight+uv_upp*uy_weight);
}

/// Compute the array index of the padded grid pixel pointed to by
/// a float2 grid position vector (choice of row-major or column-major arrays).
///
/// @param[in]  vec (float2 *, RO): real-valued vector position (x,y)
///
/// @returns  padded grid array index at position vec (x,y)
///
/// @ingroup utilities
///
static inline uint get_array_idx(float2 vec) {
    return          ( min( NY_PADDED-1, (uint)(max(0.0f, vec[1]+PAD_WIDTH_PP5)) )
           +NY_PADDED*min( NX_PADDED-1, (uint)(max(0.0f, vec[0]+PAD_WIDTH_PP5)) ) );
}

/// Squish a float vector into a byte vector for O(<1 pixel) trajectory steps
/// Achieved through scaling by TRAJECTORY_RESOLUTION, e.g. x128,
/// and being content with 1/TRAJECTORY_RESOLUTION resolution.
///
/// @param[in]  raw_vector (float2): vector (trajectory step) to be compressed
///
/// @retval  char2 vector (trajectory step) in compressed (byte,byte) form
///
/// @ingroup utilities
///
static char2 compress(float2 raw_vector) {
    return (char2)(raw_vector[0]*TRAJECTORY_RESOLUTION,
                   raw_vector[1]*TRAJECTORY_RESOLUTION);
}

/// Unsquish a byte vector back to a float vector.
/// Used in connect_channels() to unpack a streamline trajectory.
///
/// @param[in]  compressed_vector (char2): vector (trajectory step) in
///                                        compressed (byte,byte) form
///
/// @retval  float2: uncompressed vector (trajectory step)
///
/// @ingroup utilities
///
static float2 uncompress(char2 compressed_vector) {
    return ((float2)(compressed_vector[0],
                     compressed_vector[1]))/TRAJECTORY_RESOLUTION;
}

/// Approximate a float vector at the resolution provided by a scaled byte vector.
/// Do this by compressing and then uncompressing at the TRAJECTORY_RESOLUTION,
///    which is usually 128.
/// This function is useful in trajectory step integration to make sure
///    that the progressively recorded, total trajectory length matches
///    the reduced resolution trajectory step sequence.
///
/// @param[in]  raw_position (float2): vector (trajectory step) to be approximated
///
/// @retval  float2: reduced-resolution vector
///
/// @ingroup utilities
///
static float2 approximate(float2 raw_position) {
    return ((float2)(
            (char)(raw_position[0]*TRAJECTORY_RESOLUTION),
            (char)(raw_position[1]*TRAJECTORY_RESOLUTION)
            ))/TRAJECTORY_RESOLUTION;
}

/// In Euler streamline integration (which is the last step), this function
///    provides the delta time required to reach the boundary precisely in one hop.
///
/// @param[in] x (float): position vector (the current point on the trajectory)
/// @param[in] u (float): flow speed vector (to be integrated to the grid boundary)
///
/// @retval  float: delta time that will integrate the flow vector onto the boundary
///
/// @ingroup utilities
///
static inline float dt_to_nearest_edge(float x,float u) {
    float dt = 0.0f;
    // going right?
    dt = select(dt,((int)(x+1.5f)-(x+0.5f))/u, isgreater(u,0.0f));
    // going left?
    dt = select(dt,-((x+0.5f)-(int)(x+0.5f))/u, isless(u,0.0f));
    // if dt=0 stuck
    return dt;
}

extern void use_unused_essentials(void);
extern void use_unused_essentials(void) {
char2 x=0;uncompress(x);
}