3d bars makie, colors and meshes

Probably, the following could be done without to much effort, but I don’t seem to find the right way to do it :smiley:

  1. A way to merge all Rect3 meshes? Plotting one by one becomes really slow after a while.
  2. Add a color gradient to each rectangle as a function of z. Namely, the colormap maps each rectangle.

This is the start:

using GeometryBasics, GLMakie, ColorSchemes
function test3dBars()
    x = y= 1:10
    z = rand(10,10)
    δx = (x[2] - x[1]) / 2
    δy = (y[2] - y[1]) / 2
    cbarPal = :Spectral_11
    ztmp = (z .- minimum(z)) ./ (maximum(z .- minimum(z)))
    cmap = get(colorschemes[cbarPal], ztmp)
    cmap2 = reshape(cmap, size(z))
    fig = Figure(resolution=(1200, 800), fontsize=26)
    ax = Axis3(fig[1, 1]; aspect=(1, 1, 1), elevation=π / 6, perspectiveness=0.5)
    for (idx, i) in enumerate(x), (idy, j) in enumerate(y)
        rectMesh = FRect3D(Vec3f0(i - δx, j - δy, 0), Vec3f0(2δx, 2δy, z[idx, idy]))
        recmesh = GeometryBasics.mesh(rectMesh)
        mesh!(ax, recmesh; color=cmap2[idx, idy], shading=false)
    end
    fig
end
test3dBars()

2 Likes

You should use meshscatter for this. See Convenience function for 3D barplots · Issue #624 · JuliaPlots/Makie.jl · GitHub.

this kinda fixes the speed issue with meshes. But, I was thinking about meshes because a want each Rect3 or marker in this case to have the colormap gradient (and not just one color per marker).

You should be able to do color = rand(100), colormap = :viridis or color = rand(RGBf, 100)

Again. That just maps one color per marker. And what i want is a colormap gradient for each marker.

Oh sorry, I read over that part. I don’t think you can get that with meshscatter right now.

With mesh you could use mesh!(ax, recmesh; color=last.(coordinates(recmesh)), colormap = :Spectral_11, colorrange = (0, 1), shading=false) but that just intepolates between two colors:

Alternatively you could work with a texture similar to Issues · JuliaPlots/Makie.jl · GitHub

function test3dBars()
    x = y= 1:10
    z = rand(10,10)
    δx = (x[2] - x[1]) / 2
    δy = (y[2] - y[1]) / 2
    cbarPal = :Spectral_11
    texture = reshape(get(colorschemes[cbarPal], 0:0.01:1), 1, 101)
    fig = Figure(resolution=(1200, 800), fontsize=26)
    ax = Axis3(fig[1, 1]; aspect=(1, 1, 1), elevation=π / 6, perspectiveness=0.5)
    for (idx, i) in enumerate(x), (idy, j) in enumerate(y)
        rectMesh = FRect3D(Vec3f0(i - δx, j - δy, 0), Vec3f0(2δx, 2δy, z[idx, idy]))
        recmesh = GeometryBasics.normal_mesh(rectMesh)
        uvs = [Point2f(p[3], 0) for p in coordinates(recmesh)] # normalize this so zmax = 1
        recmesh = GeometryBasics.Mesh(
            meta(coordinates(recmesh); normals=normals(recmesh), uv = uvs), 
            faces(recmesh)
        )
        mesh!(ax, recmesh; color=texture, shading=false)
    end
    fig
end
test3dBars()

A way to merge all Rect3 meshes?

GeometryBasics implements merge(meshes)

2 Likes

Nice! This is the one! Thanks!. Although, as it is, is still slow if we add more meshes in that way.

Edit: nice merge(meshes) works like a charm and simply passing the texture works nicely. Thanks again!

Is this UV stuff documented anywhere?

I don’t think so? I would say it should be documented in GeometryBasics but there isn’t much there…

A mesh with uv coordinates will have one per vertex. During rendering, the uv coordinates are interpolated from the 3 corners of a triangle face for each fragment (pixel) the fragment shader renders. That coordinate is then used index a texture (image) to get a color for that fragment.

I opened a pr that would allow solution 2 to work with meshscatter:

2 Likes