MakieOrg / Makie.jl

Interactive data visualizations and plotting in Julia
https://docs.makie.org/stable
MIT License
2.43k stars 312 forks source link

Periodic cmaps on meshes #2312

Open JonasIsensee opened 2 years ago

JonasIsensee commented 2 years ago

Hi, I stumbled over the following:

When plotting meshes, the color interpolation appears to be done in the number space and not in the color space. (apparently on all backends) This has weird side effects when using periodic color maps: (code below)

When the numeric value goes from 1 to 0, the interpolation goes through all colors instead directly interpolating between blue and blue. grafik

For reference: This is what I would like to get. Which is what I do get when manually computing the colors. grafik

Here's my most minimal example (inspired by [https://docs.makie.org/v0.17.13/examples/plotting_functions/mesh/#example_4580049485859152347](docs example))

x = 0.:0.1:2 |> collect
y= 0:1. |> collect
X = x .* ones(size(y))'
Y = ones(size(x)) .* y'
vertices=vec(Point2f.(X,Y))
color= mod.(X,1)
faces = decompose(QuadFace{GLIndex}, Tesselation(Rect(0, 0, 1, 1), size(X)))
uv = vec(map(CartesianIndices(size(X))) do ci
    tup =  ((ci[1],ci[2]) .-1) ./ (size(X).-1)
    return Vec2f(reverse(tup))
end)
gb_mesh = GeometryBasics.Mesh(meta(vertices, uv=uv), faces)
mesh(gb_mesh; 
    color=color,  
    colormap=Makie.ColorSchemes.cyclic_mygbm_30_95_c78_n256_s25,
    shading=false)

mesh(gb_mesh; 
    color=get(Makie.ColorSchemes.cyclic_mygbm_30_95_c78_n256_s25,color),
    shading=false  
)
jkrumbiegel commented 2 years ago

You should not interpolate in color space in general as that can give you colors that are not even in the colormap you're using. Not in the example you gave because the distance is small there, but in principle. I'm not sure what common solutions for periodic data are, it's an interesting problem.

JonasIsensee commented 2 years ago

Hm, I guess that makes sense. This problem comes up whenever you care about angles which are naturally periodic.

It's usually helpful to avoid the branch-cut by instead describing the variable by a complex number. Example plot of complex function: https://upload.wikimedia.org/wikipedia/commons/e/e7/Sin1z-cplot.svg There, we additionally have the combination of abs(z) and angle(z).

Would it be possible to use a complex number, or more likely, a 2-vector to compute colors?

SimonDanisch commented 2 years ago

Just pass the colors directly instead of numbers?

JonasIsensee commented 2 years ago

Yes, that is what I'm currently doing and what I probably will have to keep doing.

I was just wondering, if there would be a more convenient way. It seemed not easy to incorporate this into a recipe, since color (to float) computation usually happens before the colormap is known (or normalized). To compute the colors, I have to have the ColorScheme object, but if a Symbol such as :viridis is passed, the recipe has to re-implement the logic to retrieve the colormaps and so on.

So, to solve this case, i need to mix the layers of abstraction within Makie, which seems unsatisfactory. But since this appears to be a niche use-case, it may still be best

sunbubble commented 1 year ago

I've also encountered this issue. Would interpolating over the shortest distance between the numbers not work, or would that not be correct in general?

asinghvi17 commented 1 year ago

I guess that with calculated_colormap, we could pass a hint to e.g. set the colormap texture wrapping to GL_REPEAT, as well as maybe changing the UV coordinate calculation somehow? That could probably help with this.

sunbubble commented 1 year ago

I guess that with calculated_colormap, we could pass a hint to e.g. set the colormap texture wrap to GL_REPEAT? That could probably help with this.

These colormaps tend to start with :cyclic_...

jkrumbiegel commented 1 year ago

Would interpolating over the shortest distance between the numbers not work, or would that not be correct in general?

Sounds to me like that could work. Pass in a bool to the shader that governs whether interpolation goes between two values or wrapped around the color range Extrema if that distance is shorter.

sunbubble commented 1 year ago

@jkrumbiegel I believe @asinghvi17's suggestion would automatically achieve that assuming the colormaps are loaded as textures.

I guess that with calculated_colormap, we could pass a hint to e.g. set the colormap texture wrapping to GL_REPEAT, as well as maybe changing the UV coordinate calculation somehow? That could probably help with this.

jkrumbiegel commented 1 year ago

I don't think so, GL_REPEAT is about sampling outside 0..1. But if you have a point between 0.9 and 0.1 that's not the case, even though with a cyclical colormap you'd want the value at 0 or 1, not 0.5.