Search code examples
javascriptbilinear-interpolationcielab

How do you implement bilinear interpolation of colors using JavaScript?


I need help understanding if there is a better way to do a bilinear interpolation than that I've tried below. I used the Culori library's interpolate() function.

I want to bilinearly interpolate colors but I'm not sure if its really correct to first interpolate the individual sets of colors and then interpolating the two final results to get the final value.

I first created a function that takes two arrays and then does a linear interpolation of each and then finally returning the value of the two resultants interpolated.

import { formatHex8, interpolate, lerp } from '../libs/culori.mjs'
/**Interpolates  between two resultant interpolations. The function takes two sets of params and has 3 t control variables
 * @param c1 First array of colors to be initially interpolated
 * * @param t1 First control variable for the interpolation of colors in the first array
 * * @param c2 Second array of colors to be initially interpolated
 * * @param t2 Second control variable for the interpolation of colors in the second array
 * * @param mode Color space to perform the interpolation in. Default is LAB
 * * @param _t The third control variable the final color interpolation. Default is 1 
 */
export var bilerp = ([...c1], t1) =>
    ([...c2], t2) =>
        (mode = 'lab') =>
            (_t = 1) => {
                let lerp1 = interpolate(c1)(t1)
                let lerp2 = interpolate(c2)(t2)
                return interpolate([lerp1, lerp2], mode)(_t)
            }
// Must apply an overrides object that determines the fine tuning of the interpolations
// Testing the output
let c1 = ['blue', 'yellow',]
let c2 = ['green', 'purple', 'pink']
console.log(formatHex8(bilerp(c1, 0.3)(c2, 0.4)('lch')(0.9)))

Thank you in advance!


Solution

  • You can optimise and simplify this a bit by moving the function calls to the scope where all their arguments are available, to actually make use of the currying:

    export const bilerp = (c1, t1) => {
        const lerp1 = interpolate(c1)(t1);
        return (c2, t2) => {
            const lerp2 = interpolate(c2)(t2);
            return (mode = 'lab') => {
                return interpolate([lerp1, lerp2], mode);
            };
        };
    };
    

    (I've also omitted the innermost function, assuming interpolate already has the same default).

    However, if you do not actually partially apply this function, I guess it's not actually much of an improvement over 3 direct calls to interpolate instead of passing 6 arguments to bilerp.