Color Interpolation

The easiest form of interpolation is linear interpolation by using the function \(\text{linpol}(p,a,b) := a \cdot (1 - p) + b \cdot p\) where \(a\) is 'from' and \(b\) is to and \(0.0 \leq p \leq 1.0\), meaning if \(p = 0.0\) then \(\text{linpol}(p,a,b) = a\) and if \(p = 1.0\) then \(\text{linpol}(p,a,b) = b\). If we want to interpolate RGB colors we interpolate each channel seperately:

$$\text{linpol}(p,\{r_{0},g_{0},b_{0}\},\{r_{1},g_{1},b_{1}\} := \{\\r_{0} \cdot (1 - p) + r_{1} \cdot p, \\ g_{0} \cdot (1 - p) + g_{1} \cdot p, \\ b_{0} \cdot (1 - p) + b_{1} \cdot p\\\}$$

If we want to interpolate between more colors we do it in steps. Interpolating between color 1 and color 2, color 2 and color 3, ... and so forth. We divide \(0..1\) into intervals of size \(s\) and calculate in which interval \(p\) falls into and then interpolate between the corresponding colors. For example with three colors we interpolate between color 1 and color 2 for \(0.0 \leq p \leq 0.5\) and between color 2 and color 3 for \(0.5 \leq p \leq 1.0\). With this knowledge we can create color gradients.

red to white gradient heatmap gradient
1st image: Gradient from red to white. 2nd image: Gradient using heatmap colors.
Plot for linpol(x,5,10)
Plot for \(\text{linpol}(x, 5, 10)\) for \(0.0 \leq x \leq 1.0\).

The algorithm

proc linpol(f64 p, color a, color b) color
begin
	color c := (0.0, 0.0, 0.0)

	c[0] = a[0] * (1 - p) + b[0] * p
	c[1] = a[1] * (1 - p) + b[1] * p
	c[2] = a[2] * (1 - p) + b[2] * p

	return c
end

proc linpol_n(f64 p, color[] colors) color
begin
	f64 step_size := 1.0 / f64(size(colors) - 1)
	u32 color_index := u32(floor(p / step_size))

	; shift p to 0..step_size
	f64 pi = p - (step_size * f64(color_index))

	; scale 0..step_size to 0..1
	pi /= step_size

	; color for pi ~= 0.0
	color a := colors[color_index]

	; color for pi ~= 1.0
	; in the edge case of p = 1.0 color_index points to the last color
	; so color_index+1 would be invalid. 
	color b := colors[min(size(colors)-1, color_index+1)]

	return linpol(pi, a, b)
end