/*
 * Name
 *  cubic.c -- use cubic convolution interpolation for given row, col
 *
 * Description
 *  cubic returns the value in the buffer that is the result of cubic
 *  convolution interpolation for the given row, column indices.
 *  If the given row or column is outside the bounds of the input map,
 *  the corresponding point in the output map is set to NULL.
 *
 *  If single surrounding points in the interpolation matrix are
 *  NULL they where filled with their neighbor
 */

#include <math.h>
#include "global.h"

void p_cubic(struct cache *ibuffer,   /* input buffer                */
             void *obufptr,           /* ptr in output buffer        */
             int cell_type,           /* raster map type of obufptr  */
             double *row_idx,         /* row index (decimal)         */
             double *col_idx,         /* column index (decimal)      */
             struct Cell_head *cellhd /* information of output map   */
)
{
    int row; /* row indices for interp        */
    int col; /* column indices for interp     */
    int i, j;
    double t, u;  /* intermediate slope            */
    DCELL result; /* result of interpolation       */
    DCELL val[4]; /* buffer for temporary values   */
    DCELL cell[4][4];

    /* cut indices to integer */
    row = (int)floor(*row_idx - 0.5);
    col = (int)floor(*col_idx - 0.5);

    /* check for out of bounds of map - if out of bounds set NULL value     */
    if (row - 1 < 0 || row + 2 >= cellhd->rows || col - 1 < 0 ||
        col + 2 >= cellhd->cols) {
        Rast_set_null_value(obufptr, 1, cell_type);
        return;
    }

    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++) {
            const DCELL *cellp = CPTR(ibuffer, row - 1 + i, col - 1 + j);

            if (Rast_is_d_null_value(cellp)) {
                Rast_set_null_value(obufptr, 1, cell_type);
                return;
            }
            cell[i][j] = *cellp;
        }

    /* do the interpolation  */
    t = *col_idx - 0.5 - col;
    u = *row_idx - 0.5 - row;

    for (i = 0; i < 4; i++) {
        val[i] = Rast_interp_cubic(t, cell[i][0], cell[i][1], cell[i][2],
                                   cell[i][3]);
    }

    result = Rast_interp_cubic(u, val[0], val[1], val[2], val[3]);

    Rast_set_d_value(obufptr, result, cell_type);
}
