MakieOrg / Makie.jl

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

Adding meshes to an empty plot only works in 2D #2475

Open lhupe opened 1 year ago

lhupe commented 1 year ago

I need to create a mesh plot from an observable which contains a vector of meshes. The vector is guaranteed to have a concrete element type with either 2D or 3D meshes, but it may be empty on initialisation. In that case, the plot command works fine for both 2D and 3D, but adding meshes to the vector later only works for 2D meshes. For 3D meshes, notifying the observable results in an error, which (as far as I can tell) implies that somewhere down the line Makie has at some point made a wrong assumption on the type of the meshes.

error message, click to expand ``` ERROR: MethodError: Cannot `convert` an object of type GeometryBasics.Mesh{3,Float32{},GeometryBasics.Ngon{3,Float32{},3,PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}},FaceView{GeometryBasics.Ngon{3,Float32{},3,PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}},PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}},NgonFace{3,OffsetInteger{-1,UInt32{}}},StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64},Array{NgonFace{3,OffsetInteger{-1,UInt32{}}},1}}} to an object of type GeometryBasics.Mesh{2,Float32{},GeometryBasics.Ngon{2,Float32{},3,Point{2, Float32}},FaceView{GeometryBasics.Ngon{2,Float32{},3,Point{2, Float32}},Point{2, Float32},NgonFace{3,OffsetInteger{-1,UInt32{}}},Vector{Point{2, Float32}},Array{NgonFace{3,OffsetInteger{-1,UInt32{}}},1}}} Closest candidates are: convert(::Type{T}, ::LinearAlgebra.Factorization) where T<:AbstractArray at .julia/juliaup/julia-1.8.3+0.x64/share/julia/stdlib/v1.8/LinearAlgebra/src/factorization.jl:58 convert(::Type, ::GeoInterface.AbstractGeometryTrait, ::Any) at .julia/packages/GeoInterface/J298z/src/fallbacks.jl:112 convert(::Type{T}, ::T) where T<:AbstractArray at abstractarray.jl:16 ... Stacktrace: [1] setproperty!(x::Observable{GeometryBasics.Mesh{2, Float32, GeometryBasics.Ngon{2, Float32, 3, Point{2, Float32}}, SimpleFaceView{2, Float32, 3, OffsetInteger{-1, UInt32}, Point{2, Float32}, NgonFace{3, OffsetInteger{-1, UInt32}}}}}, f::Symbol, v::GeometryBasics.Mesh{3, Float32, TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, FaceView{TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}) @ Base ./Base.jl:39 [2] setindex!(observable::Observable, val::Any) @ Observables .julia/packages/Observables/PHGQ8/src/Observables.jl:85 [3] (::Observables.MapCallback)(value::Any) @ Observables .julia/packages/Observables/PHGQ8/src/Observables.jl:431 [4] #invokelatest#2 @ ./essentials.jl:729 [inlined] [5] invokelatest @ ./essentials.jl:726 [inlined] [6] notify @ .julia/packages/Observables/PHGQ8/src/Observables.jl:169 [inlined] [7] setindex!(observable::Observable, val::Any) @ Observables .julia/packages/Observables/PHGQ8/src/Observables.jl:86 [8] (::Observables.MapCallback)(value::Any) @ Observables .julia/packages/Observables/PHGQ8/src/Observables.jl:431 [9] #invokelatest#2 @ ./essentials.jl:729 [inlined] [10] invokelatest @ ./essentials.jl:726 [inlined] [11] notify @ .julia/packages/Observables/PHGQ8/src/Observables.jl:169 [inlined] [12] setindex!(observable::Observable, val::Any) @ Observables .julia/packages/Observables/PHGQ8/src/Observables.jl:86 [13] (::Makie.var"#169#171"{Attributes, Observable{Tuple{Vector{GeometryBasics.Mesh{3, Float32, TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, FaceView{TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}}}, DataType})(kwargs::Tuple{}, args::Vector{GeometryBasics.Mesh{3, Float32, TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, FaceView{TriangleP{3, Float32, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NgonFace{3, OffsetInteger{-1, UInt32}}, StructArrays.StructVector{PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{NgonFace{3, OffsetInteger{-1, UInt32}}}}}}) @ Makie .julia/packages/Makie/Ggejq/src/interfaces.jl:333 [14] invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}) @ Base ./essentials.jl:729 [15] invokelatest(::Any, ::Any, ::Vararg{Any}) @ Base ./essentials.jl:726 [16] (::Observables.OnAny)(value::Any) @ Observables .julia/packages/Observables/PHGQ8/src/Observables.jl:415 [17] #invokelatest#2 @ ./essentials.jl:729 [inlined] [18] invokelatest @ ./essentials.jl:726 [inlined] [19] notify(observable::Observables.AbstractObservable) @ Observables .julia/packages/Observables/PHGQ8/src/Observables.jl:169 [20] top-level scope ```

MWE

using GLMakie
using GeometryBasics

meshes_2D = normal_mesh.([Circle(Point2f(0.0), 1.0), Circle(Point2f(1,0),0.5)])
meshes_3D = normal_mesh.([Sphere(Point3f(0.0), 1.0), Sphere(Point3f(1,0,1),0.5)])

# create empty mesh vectors with concrete types
mo2d = Observable(eltype(meshes_2D)[])
mo3d = Observable(eltype(meshes_3D)[])

fig = Figure()
display(fig)

# 1st case: 2D
ax = Axis(fig[1,1])
# plot empty mesh vector
mesh!(ax, mo2d)

# add meshes
append!(mo2d[], meshes_2D)
notify(mo2d) # this works

# 2nd case: 3D
sc = LScene(fig[1,2])
mesh!(sc, mo3d)

append!(mo3d[], meshes_3D)
notify(mo3d) # this doesn't

Versions: GLMakie v0.8.0 and GeometryBasics v0.4.5 on Julia 1.8.3

ffreyer commented 1 month ago

There might be some difficulty with e.g. the existence of normals being embedded in the type here? Ref https://github.com/JuliaGeometry/GeometryBasics.jl/pull/173