0

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!

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • 1
    Why is this a function that returns a function that returns a function that returns a function? If you want people to get lost in your code, this is perfect. If you want folks to actually understand your code, though, this is not the way. – Mike 'Pomax' Kamermans Mar 09 '23 at 23:21
  • I had curried this function to make it easier for me to manage the functions arity since it takes a lot of parameters and splitting them like so seemed the best approach for me at that time. Any improvements would be greatly appreciated sir? How can I make it easier to understand for others – ディーン・タリサイ Mar 09 '23 at 23:41
  • JS lets you destructure arguments, so ```bilerp({c1, t: 0.3}, {c2, t: 0.4}, `lch`, 0.9)``` on the call side, and `function bilerp({c1, t:t1}, {c2, t:t2}, mode, _t) { ... }` on the definition side works just fine. Although that said, don't use `_` in var names, it's the convention for "I needed to capture this argument but I'm not going to do anything with it" which is not the case in your code. Just call it `t` or even `ratio` or the like. – Mike 'Pomax' Kamermans Mar 10 '23 at 00:08
  • Truly ignorance has no defense! I did my homework on the matter and found out that its bad practice to wrap a function inside of another function just to delay evaluation (for maintenance reasons). A refactoring is underway and I'll be sure to follow up with the revised function of that function. Thank you so much for the heads up – ディーン・タリサイ Mar 13 '23 at 00:38

1 Answers1

0

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This is undoubtedly more concise than my previous approach and I have taken a page from your technic. I'll follow up with the refactored version of this function. However, I am now concerned about the underlying mathematical implementation of a bilinear interpolation in the context of color. Am I doing it right? Are they any mathematical operations I should apply before computing my final results? Any references to the subject would be recommended. DISCLAIMER I'm very bad at math and I'm working on enthusiasm only and a feeble background in mathematical understanding – ディーン・タリサイ Mar 13 '23 at 00:46
  • That I have no idea about. You best ask a separate question about that, possible at [math.SE] instead of StackOverflow. – Bergi Mar 13 '23 at 00:51