/**
* @module resample
*/
/**
* Resample the input arrays using nearest neighbor value selection.
* @param {TypedArray[]} valueArrays The input arrays to resample
* @param {number} inWidth The width of the input rasters
* @param {number} inHeight The height of the input rasters
* @param {number} outWidth The desired width of the output rasters
* @param {number} outHeight The desired height of the output rasters
* @returns {TypedArray[]} The resampled rasters
*/
export function resampleNearest(valueArrays, inWidth, inHeight, outWidth, outHeight) {
const relX = inWidth / outWidth;
const relY = inHeight / outHeight;
return valueArrays.map((array) => {
const newArray = new (Object.getPrototypeOf(array).constructor)(outWidth * outHeight);
for (let y = 0; y < outHeight; ++y) {
const cy = Math.min(Math.round(relY * y), inHeight - 1);
for (let x = 0; x < outWidth; ++x) {
const cx = Math.min(Math.round(relX * x), inWidth - 1);
newArray[(y * outWidth) + x] = array[(cy * inWidth) + cx];
}
}
return newArray;
});
}
// simple linear interpolation, code from:
// https://en.wikipedia.org/wiki/Linear_interpolation#Programming_language_support
function lerp(v0, v1, t) {
return ((1 - t) * v0) + (t * v1);
}
/**
* Resample the input arrays using bilinear interpolation.
* @param {TypedArray[]} valueArrays The input arrays to resample
* @param {number} inWidth The width of the input rasters
* @param {number} inHeight The height of the input rasters
* @param {number} outWidth The desired width of the output rasters
* @param {number} outHeight The desired height of the output rasters
* @returns {TypedArray[]} The resampled rasters
*/
export function resampleBilinear(valueArrays, inWidth, inHeight, outWidth, outHeight) {
const relX = inWidth / outWidth;
const relY = inHeight / outHeight;
return valueArrays.map((array) => {
const newArray = new (Object.getPrototypeOf(array).constructor)(outWidth * outHeight);
for (let y = 0; y < outHeight; ++y) {
const rawY = relY * y;
const yl = Math.floor(rawY);
const yh = Math.min(Math.ceil(rawY), (inHeight - 1));
for (let x = 0; x < outWidth; ++x) {
const rawX = relX * x;
const tx = rawX % 1;
const xl = Math.floor(rawX);
const xh = Math.min(Math.ceil(rawX), (inWidth - 1));
const ll = array[(yl * inWidth) + xl];
const hl = array[(yl * inWidth) + xh];
const lh = array[(yh * inWidth) + xl];
const hh = array[(yh * inWidth) + xh];
const value = lerp(
lerp(ll, hl, tx),
lerp(lh, hh, tx),
rawY % 1,
);
newArray[(y * outWidth) + x] = value;
}
}
return newArray;
});
}
/**
* Resample the input arrays using nearest neighbor value selection.
* @param {TypedArray[]} valueArrays The input arrays to resample
* @param {number} inWidth The width of the input rasters
* @param {number} inHeight The height of the input rasters
* @param {number} outWidth The desired width of the output rasters
* @param {number} outHeight The desired height of the output rasters
* @param {string} [method = 'nearest'] The desired resampling method
* @returns {TypedArray[]} The resampled rasters
*/
export function resample(valueArrays, inWidth, inHeight, outWidth, outHeight, method = 'nearest') {
switch (method.toLowerCase()) {
case 'nearest':
return resampleNearest(valueArrays, inWidth, inHeight, outWidth, outHeight);
case 'bilinear':
case 'linear':
return resampleBilinear(valueArrays, inWidth, inHeight, outWidth, outHeight);
default:
throw new Error(`Unsupported resampling method: '${method}'`);
}
}