MakieOrg / Makie.jl

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

Using many polygon shapes for scatter runs into texture atlas limit #3204

Open bjarthur opened 10 months ago

bjarthur commented 10 months ago

per the instructions in https://github.com/MakieOrg/Makie.jl/pull/1494 i'm filing an issue.

i tried following the suggestions there, but seemingly the API has changed:

julia> Makie.get_cache_path()
ERROR: MethodError: no method matching get_cache_path()

Closest candidates are:
  get_cache_path(::Int64, ::Int64)
   @ Makie ~/.julia/packages/Makie/Ylh0P/src/utilities/texture_atlas.jl:76

Stacktrace:
 [1] top-level scope
   @ REPL[32]:1
julia> empty!(Makie.global_texture_atlas)
ERROR: UndefVarError: `global_texture_atlas` not defined
Stacktrace:
 [1] getproperty(x::Module, f::Symbol)
   @ Base ./Base.jl:31
 [2] top-level scope
   @ REPL[33]:1

i supplied two Int64's to Makie.get_cache_path() and found some old files in the directory returned, but deleting them manually didn't help:

julia> Makie.get_cache_path(1,1)
"/Users/arthurb/.julia/makie/v3_texture_atlas_1_1.bin"

shell> ls -lh ~/.julia/makie
total 41184
-rw-r--r--  1 arthurb  staff   2.0M May  4 11:29 v1_texture_atlas_1024_32.bin
-rw-r--r--  1 arthurb  staff   8.0M May  4 11:24 v1_texture_atlas_2048_64.bin
-rw-r--r--  1 arthurb  staff   2.0M Jul 24 15:45 v3_texture_atlas_1024_32.bin
-rw-r--r--  1 arthurb  staff   8.0M Jul 20 15:35 v3_texture_atlas_2048_64.bin

shell> date
Fri Sep  1 18:07:22 EDT 2023

i found Makie.get_texture_atlas() but empty! is not defined on it:

julia> empty!(Makie.get_texture_atlas())
ERROR: MethodError: no method matching empty!(::Makie.TextureAtlas)

Closest candidates are:
  empty!(::Union{DataStructures.SortedDict, DataStructures.SortedMultiDict, DataStructures.SortedSet})
   @ DataStructures ~/.julia/packages/DataStructures/MKv4P/src/container_loops.jl:321
  empty!(::OnlineStatsBase.OnlineStat)
   @ OnlineStatsBase ~/.julia/packages/OnlineStatsBase/q7tWg/src/OnlineStatsBase.jl:64
  empty!(::OffsetArrays.OffsetVector{T} where T)
   @ OffsetArrays ~/.julia/packages/OffsetArrays/0MOrf/src/OffsetArrays.jl:605
  ...

Stacktrace:
 [1] top-level scope
   @ REPL[28]:1

julia> methodswith(typeof(Makie.get_texture_atlas()))
[1] show(io::IO, atlas::Makie.TextureAtlas) @ Makie ~/.julia/packages/Makie/Ylh0P/src/utilities/texture_atlas.jl:63
[2] size(atlas::Makie.TextureAtlas) @ Makie ~/.julia/packages/Makie/Ylh0P/src/utilities/texture_atlas.jl:28
[3] size(atlas::Makie.TextureAtlas, dim) @ Makie ~/.julia/packages/Makie/Ylh0P/src/utilities/texture_atlas.jl:29

what should i do?

for context, i'm using GeometryTypes.jl/Polygon to create custom marker shapes for scatter. 100s of them for each frame in a movie. it gets through the first few frames and then errors out. i'm hoping that if i can just clear the cache after each frame it will work.

thanks!

jkrumbiegel commented 10 months ago

Oh yeah that's kind of the worst case scenario for GLMakie if you change markers all the time :) I should put a note in the scatter docs about this behavior. CairoMakie will not care if you can use that for your animation. Or you could use poly which doesn't use the atlas

SimonDanisch commented 10 months ago

any chance that you can just scale them via markersize=Vec(xscale, yscale), rotation=rad? When back at my PC I can also look at clearing the texture atlas in the most efficient way.. I guess the best would be to overwrite the same texture atlas slot for every frame

SimonDanisch commented 10 months ago

In theory, you could also pass your own signed distance field as a marker, which may be the preferred solution...one would just need to figure out the low level api to render the poly to a signed distance field

bjarthur commented 10 months ago

CairoMakie works! thanks. poly would probably also work, but i didn't try it. might have used that from the start if i had known about it. my bad as it is documented.

SimonDanisch commented 10 months ago

I gave it a shot today, and the code path for passing an SDF directly is completely bitrotten :D Maybe we need to introduce a DistanceField type, and implement it accordingly for (W)GLMakie.

Marc-3d commented 2 days ago

Hi,

I run into the same error while DIYing a way to individually scale the arrowheads in a quiver (arrows) plot.

Since I couldn't find a way to do it, I ended up passing a matrix of individually scaled BezierCurves for each vector to arrows!:


function scaled_bezier( scale, min_scale=0.1 )
    s = max( scale, min_scale )
    return BezierPath( [ Makie.MoveTo([0.0, 0.485 * s] ), 
                         Makie.LineTo([-0.36375 * s, -0.24250000000000002 * s] ), 
                         Makie.LineTo([0.36375 * s, -0.24250000000000002 * s] ), 
                         Makie.ClosePath() ] )
end 
arrowheads = Observable( scaled_bezier.( arrow_magnitudes, 0.1 ) )

[....]

Makie.arrows!( ax1, x, y, U, V, arrowhead=arrowheads )

I am using Observables because I have vector fields at multiple timepoints and I want to use a slider to scroll through the vector fields... which leads to creating custom BezierCurves for the arrowheads every time I move the slider...

You mentioned poly doesn't run into this error. Maybe arrows should use poly instead of scatter for 2D plots if the performance isn't compromised. Or maybe there is a much simpler way to individually scale arrowheads....

SimonDanisch commented 22 hours ago

@Marc-3d, you should be able to pass arrowsize=max.(arrow_magnitudes, 0.1), if I'm not mistaken.