diff --git a/src/DimensionalData.jl b/src/DimensionalData.jl index 1600a41f6..ba9f1b3e6 100644 --- a/src/DimensionalData.jl +++ b/src/DimensionalData.jl @@ -26,6 +26,7 @@ import Adapt, Extents, InvertedIndices, IteratorInterfaceExtensions, + MakieCore, RecipesBase, PrecompileTools, TableTraits, diff --git a/src/plotrecipes.jl b/src/plotrecipes.jl index 0795a1eef..1d1576448 100644 --- a/src/plotrecipes.jl +++ b/src/plotrecipes.jl @@ -1,3 +1,5 @@ +## Plots.jl recipes + abstract type DimPlotMode end struct HeatMapLike <: DimPlotMode seriestype::Symbol end struct WireframeLike <: DimPlotMode seriestype::Symbol end @@ -128,6 +130,61 @@ end throw(ArgumentError("$(x.seriestype) not implemented in $N dimensions")) end +### Makie.jl recipes + +# First, define default plot types for DimArrays +MakieCore.plottype(A::AbstractDimArray{<:Union{Missing,Number},1}) = MakieCore.Scatter +MakieCore.plottype(A::AbstractDimArray{<:Union{Missing,Number},2}) = MakieCore.Heatmap +# 3d A are a little more complicated - if dim3 is a singleton, then heatmap, otherwise volume +function MakieCore.plottype(A::AbstractDimArray{<:Union{Missing,Number},3}) + if size(A, 3) == 1 + MakieCore.Heatmap + else + MakieCore.Volume + end +end + +# then, define how they are to be converted to plottable data +function MakieCore.convert_arguments(PB::MakieCore.PointBased, A::AbstractDimArray{<:Union{Number,Missing},1}) + A = _prepare_makie(A) + return MakieCore.convert_arguments(PB, map(index, dims(A))..., parent(A)) +end +# allow 3d A to be plotted as volumes +function MakieCore.convert_arguments( + ::MakieCore.VolumeLike, A::AbstractDimArray{<:Union{Real,Missing},3} +) + A = _prepare_makie(A) + xs, ys, zs = lookup(A) + return (xs, ys, zs, parent(A)) +end +# allow plotting 3d A with singleton third dimension (basically 2d A) +function MakieCore.convert_arguments( + ::MakieCore.SurfaceLike, A::AbstractDimArray{<:Union{Missing,Number},2} +) + A = _prepare_makie(A) + xs, ys = lookup(A) + return (xs, ys, parent(A)) +end +function MakieCore.convert_arguments( + ::MakieCore.PointBased, A::AbstractDimArray{<:Union{Missing,Number},1} +) + A = _prepare_makie(A) + xs = lookup(A, 1) + return parent(xs), parent(A) +end +function MakieCore.convert_arguments( + ::MakieCore.DiscreteSurface, A::AbstractDimArray{<:Union{Missing,Number},2} +) + A = _prepare_makie(A) + xs, ys = map(_lookup_edges, lookup(A)) + return (xs, ys, parent(A)) +end +# fallbacks with descriptive error messages +MakieCore.convert_arguments(t::MakieCore.ConversionTrait, r::AbstractDimArray) = + _makie_not_implemented_error(t, r) + +### Shared utils + _fwdorderdims(x) = dims(dims(x), (TimeDim, XDim, IndependentDim, YDim, ZDim, DependentDim, DependentDim, Dimension, Dimension, Dimension)) _revorderdims(x) = reverse(_fwdorderdims(reverse(dims(x)))) @@ -185,3 +242,33 @@ function refdims_title(lookup::LookupArray, refdim::Dimension; kw...) end end + +function _lookup_edges(l::LookupArray) + l = if l isa AbstractSampled + set(l, Intervals()) + else + set(l, Sampled(; sampling=Intervals())) + end + if l == 1 + return [bounds(l)...] + else + ib = intervalbounds(l) + if order(l) isa ForwardOrdered + edges = first.(ib) + push!(edges, last(last(ib))) + else + edges = last.(ib) + push!(edges, first(last(ib))) + end + return edges + end +end + +_prepare_makie(A) = _missing_or_float32.(A) |> _permute_fwd |> _reorder + +_missing_or_float32(num::Number) = Float32(num) +_missing_or_float32(::Missing) = missing + +_reorder(A) = reorder(A, DD.ForwardOrdered) +_permute_fwd(A) = permutedims(A, _fwdorderdims(A)) +_permute_rev(A) = permutedims(A, _revorderdims(A))