Motivation I have a function f
on a 2d domain, which approximatively has a minimal negative value -1 and a maximal positive value of \approx 1/4 (not really important), which I want to visualise in a way, such that f \approx 0 is drawn as white (the function decays exponentially fast, so the background should essentially be white. As an example consider
xs = -3:0.03:3
ys = xs
f(r) = (sqrt(2)*r^2 - 1) * exp(-r^2)
data = [f(√(x^2 + y^2)) for x ∈ xs, y ∈ ys]
The Problem is, that Makie.jl seems to coarse-grain any colormap, probably doing a cubic interpolation or something. Now, since white is a corner point in RGB space, this makes it impossible to to get true white as an intermediate point.
Consider following example:
function make_test_fig(cmaps)
fig = Figure(resolution = (300 * length(cmaps), 300))
for i ∈ 1:length(cmaps)
heatmap(fig[1, i], xs, ys, data, colorrange = (-1.0, 0.25), colormap = cmaps[i])
Colorbar(fig[1, 2i],
limits = (-1.0, 0.25), ticks = -1.0:0.25:0.25, colormap = cmaps[i]
)
end
fig
end
With this I get these result
- Left most image is created with
ColorScheme(
[range(colorant"orange", colorant"white", length = 80)...,
range(colorant"white", colorant"blue", length = 20)...]
)
The idea simply was that the interval [-1,0] takes 80% of the total weight of the interval [-1,1/4]. But clearly f \approx 0 has a blue tint.
- The second idea was then to shift blue a little bit upward
ColorScheme(
[range(colorant"orange", colorant"white", length = 82)...,
range(colorant"white", colorant"blue", length = 18)...]
)
Still, this doesn’t seem to work perfectly, is hacky, doesn’t seem to generalise, and most of all: It requires some magic numbers, which are basically guesses
- Okay, if we cannot bring the prophet to the mountain, we bring the mountain to the prophet. I.e., let’s add manually a large white section in our color bar.
ColorScheme(
[range(colorant"orange", colorant"white", length = 78)...,
fill(colorant"white", 4)...,
range(colorant"white", colorant"blue", length = 18)...]
)
Instead of fill(colorant"white", 4)
I used fill(colorant"red", 4)
above to demonstrate whats happening next.
- So solution 3 seems to technically work, but the white area is quite large (speaking of relative weight in the [-1,1/4] domain) and this is a little bit of a dishonest representation. So let’s at least reduce the weight:
ColorScheme(
[range(colorant"orange", colorant"white", length = 79)...,
fill(colorant"white", 2)...,
range(colorant"white", colorant"blue", length = 19)...]
)
Again, I used red
above, instead of white
above for demonstration purposes. Aaaaand we end up with the last figure… So red lies a little bit below the wanted value f \approx 0, and it is clearly washed out. This then motivated my hypothesis: Makie.jl does some cubic interpolation for the colours.
So how can I fix the color value of my colormap at specific values and have nice interpolation between these values. Sadly, Makie’s documentation is no help at all
In the example I used the packages CairoMakie
, Colors
, and ColorSchemes
Addendum I tried fill(colorant"red", 1)
to fix to find out how fix the index for the f \approx 0 and I got a figure without any red in it . So apparantly, Makie probably does not even sample the entire provided colormap but instead just picks some values? Why is this no where documented?