Repository: JuliaPlots/Makie.jl (julia)

Issue:
Plotting into an existing axis throws on v0.20.0
- [x] are you running newest version (version from docs) ?
- [x] can you reproduce the bug with a fresh environment ? (`]activate --temp; add Makie`)
- [x] What platform + GPU are you on? **Ubuntu 22.0.4, Nvidia**

Hello, thank you for all the hard work on Makie! After updating to latest, plotting into an existing axis throws. 

```
julia> using GLMakie

julia> let 
           fig = Figure()
           ax = Axis(fig[1, 1])
           plot!(ax, [10, 15, 20])
       end
ERROR: No recipe for plot
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] plot!
   @ ~/.julia/packages/Makie/AyuSG/src/interfaces.jl:223 [inlined]
 [3] connect_plot!(parent::Scene, plot::Plot{MakieCore.plot, Tuple{Vector{Int64}}})
   @ Makie ~/.julia/packages/Makie/AyuSG/src/interfaces.jl:251
 [4] plot!
   @ ~/.julia/packages/Makie/AyuSG/src/interfaces.jl:256 [inlined]
 [5] plot!(ax::Axis, plot::Plot{MakieCore.plot, Tuple{Vector{Int64}}})
   @ Makie ~/.julia/packages/Makie/AyuSG/src/figureplotting.jl:284
 [6] _create_plot!(::Function, ::Dict{Symbol, Any}, ::Axis, ::Vector{Int64})
   @ Makie ~/.julia/packages/Makie/AyuSG/src/figureplotting.jl:255
 [7] #plot!#11
   @ ~/.julia/packages/MakieCore/qQoPY/src/recipes.jl:39 [inlined]
 [8] plot!(::Axis, ::Vector{Int64})
   @ MakieCore ~/.julia/packages/MakieCore/qQoPY/src/recipes.jl:39
 [9] top-level scope
   @ REPL[4]:4
```

Pull request:
* fixes for GeoMakie (using  `@Block` outside of Makie)
* closes https://github.com/MakieOrg/Makie.jl/issues/3399
* allows `onany(f, scene, args...; update=true)`

Patch:
diff --git a/MakieCore/src/recipes.jl b/MakieCore/src/recipes.jl
index 5532d000c05..cf2b9f53f26 100644
--- a/MakieCore/src/recipes.jl
+++ b/MakieCore/src/recipes.jl
@@ -35,8 +35,9 @@ function _create_plot end
 function _create_plot! end
 
 
-plot(args...; kw...) = _create_plot(plotfunc(plottype(map(to_value, args)...)), Dict{Symbol, Any}(kw), args...)
-plot!(args...; kw...) = _create_plot!(plotfunc(plottype(map(to_value, args)...)), Dict{Symbol, Any}(kw), args...)
+
+plot(args...; kw...) = _create_plot(plot, Dict{Symbol, Any}(kw), args...)
+plot!(args...; kw...) = _create_plot!(plot, Dict{Symbol, Any}(kw), args...)
 
 """
 Each argument can be named for a certain plot type `P`. Falls back to `arg1`, `arg2`, etc.
@@ -231,4 +232,4 @@ e.g.:
     plottype(x::Array{<: AbstractFloat, 3}) = Volume
 ```
 """
-plottype(plot_args...) = Plot{plot, Tuple{map(typeof, plot_args)...}} # default to dispatch to type recipes!
+plottype(plot_args...) = Plot{plot} # default to dispatch to type recipes!
diff --git a/src/figureplotting.jl b/src/figureplotting.jl
index 49ca2cd8748..c1ba49e8453 100644
--- a/src/figureplotting.jl
+++ b/src/figureplotting.jl
@@ -237,11 +237,15 @@ function fig_keywords!(kws)
     return figkws
 end
 
+# Narrows down the default plotfunc early on, if `plot` is used
+default_plot_func(f::F, args) where {F} = f
+default_plot_func(::typeof(plot), args) = plotfunc(plottype(map(to_value, args)...))
+
 # Don't inline these, since they will get called from `scatter!(args...; kw...)` which gets specialized to all kw args
 @noinline function MakieCore._create_plot(F, attributes::Dict, args...)
     figarg, pargs = plot_args(args...)
     figkws = fig_keywords!(attributes)
-    plot = Plot{F}(pargs, attributes)
+    plot = Plot{default_plot_func(F, pargs)}(pargs, attributes)
     ax = create_axis_like(plot, figkws, figarg)
     plot!(ax, plot)
     return figurelike_return(ax, plot)
@@ -250,14 +254,14 @@ end
 @noinline function MakieCore._create_plot!(F, attributes::Dict, args...)
     figarg, pargs = plot_args(args...)
     figkws = fig_keywords!(attributes)
-    plot = Plot{F}(pargs, attributes)
+    plot = Plot{default_plot_func(F, pargs)}(pargs, attributes)
     ax = create_axis_like!(plot, figkws, figarg)
     plot!(ax, plot)
     return figurelike_return!(ax, plot)
 end
 
 @noinline function MakieCore._create_plot!(F, attributes::Dict, scene::SceneLike, args...)
-    plot = Plot{F}(args, attributes)
+    plot = Plot{default_plot_func(F, args)}(args, attributes)
     plot!(scene, plot)
     return plot
 end
diff --git a/src/interfaces.jl b/src/interfaces.jl
index 41d5e19bc56..19c35941eb0 100644
--- a/src/interfaces.jl
+++ b/src/interfaces.jl
@@ -147,8 +147,8 @@ function Plot{Func}(args::Tuple, plot_attributes::Dict) where {Func}
 
     ArgTyp = MakieCore.argtypes(converted)
     converted_obs = map(Observable, converted)
-    plot = Plot{plotfunc(PNew),ArgTyp}(plot_attributes, obs_args, converted_obs)
-    return plot
+    FinalPlotFunc = plotfunc(plottype(PNew, converted...))
+    return Plot{FinalPlotFunc,ArgTyp}(plot_attributes, obs_args, converted_obs)
 end
 
 """
@@ -178,11 +178,27 @@ used_attributes(args...) = ()
 
 
 ## generic definitions
-# If the Plot has no plot func, calculate them
-plottype(::Type{<: Plot{Any}}, argvalues...) = plottype(argvalues...)
-plottype(::Type{Any}, argvalues...) = plottype(argvalues...)
-# If it has something more concrete than Any, use it directly
-plottype(P::Type{<: Plot{T}}, argvalues...) where T = P
+# Chose the more specific plot type from arguments or input type
+# Note the plottype(Scatter, Plot{plot}) will return Scatter
+# And plottype(args...) falls back to Plot{plot}
+plottype(P::Type{<: Plot{T}}, argvalues...) where T = plottype(P, plottype(argvalues...))
+plottype(P::Type{<:Plot{T}}) where {T} = P
+plottype(P1::Type{<:Plot{T1}}, ::Type{<:Plot{T2}}) where {T1, T2} = P1
+plottype(::Type{Plot{plot}}, ::Type{Plot{plot}}) = Plot{plot}
+"""
+    plottype(P1::Type{<: Plot{T1}}, P2::Type{<: Plot{T2}})
+
+Chooses the more concrete plot type
+```julia
+function convert_arguments(P::PlotFunc, args...)
+    ptype = plottype(P, Lines)
+    ...
+end
+```
+"""
+plottype(::Type{Plot{plot}}, P::Type{<:Plot{T}}) where {T} = P
+plottype(P::Type{<:Plot{T}}, ::Type{Plot{plot}}) where {T} = P
+
 
 ## specialized definitions for types
 plottype(::AbstractVector, ::AbstractVector, ::AbstractVector) = Scatter
@@ -201,19 +217,7 @@ plottype(::GeometryBasics.AbstractPolygon) = Poly
 plottype(::AbstractVector{<:GeometryBasics.AbstractPolygon}) = Poly
 plottype(::MultiPolygon) = Lines
 
-"""
-    plottype(P1::Type{<: Plot{T1}}, P2::Type{<: Plot{T2}})
 
-Chooses the more concrete plot type
-```julia
-function convert_arguments(P::PlotFunc, args...)
-    ptype = plottype(P, Lines)
-    ...
-end
-```
-"""
-plottype(::Type{<: Plot{Any}}, P::Type{<: Plot{T}}) where T = P
-plottype(P::Type{<: Plot{T}}, ::Type{<: Plot}) where T = P
 
 # all the plotting functions that get a plot type
 const PlotFunc = Union{Type{Any},Type{<:AbstractPlot}}
diff --git a/src/makielayout/blocks.jl b/src/makielayout/blocks.jl
index 3b9d40f4d99..3d3d70efc79 100644
--- a/src/makielayout/blocks.jl
+++ b/src/makielayout/blocks.jl
@@ -65,7 +65,7 @@ macro Block(_name::Union{Expr, Symbol}, body::Expr = Expr(:block))
 
         function Makie.default_attribute_values(::Type{$(name)}, scene::Union{Scene, Nothing})
             sceneattrs = scene === nothing ? Attributes() : theme(scene)
-            curdeftheme = fast_deepcopy($(Makie).CURRENT_DEFAULT_THEME)
+            curdeftheme = Makie.fast_deepcopy($(Makie).CURRENT_DEFAULT_THEME)
             $(make_attr_dict_expr(attrs, :sceneattrs, :curdeftheme))
         end
 
@@ -277,7 +277,11 @@ function _block(T::Type{<:Block}, fig_or_scene::Union{Figure, Scene}, args...; b
 end
 
 function block_defaults(blockname::Symbol, attribute_kwargs::Dict, scene::Union{Nothing, Scene})
-    default_attrs = default_attribute_values(getfield(Makie, blockname), scene)
+    return block_defaults(getfield(Makie, blockname), attribute_kwargs, scene)
+end
+function block_defaults(::Type{B}, attribute_kwargs::Dict, scene::Union{Nothing, Scene}) where {B <: Block}
+    default_attrs = default_attribute_values(B, scene)
+    blockname = nameof(B)
     typekey_scene_attrs = get(theme(scene), blockname, Attributes())
     typekey_attrs = theme(blockname; default=Attributes())::Attributes
     attributes = Dict{Symbol,Any}()
@@ -321,7 +325,7 @@ function _block(T::Type{<:Block}, fig_or_scene::Union{Figure,Scene}, args, kwdic
     if kwdict_complete
         attributes = attribute_kwargs
     else
-        attributes = block_defaults(nameof(T), attribute_kwargs, topscene)
+        attributes = block_defaults(T, attribute_kwargs, topscene)
     end
     # create basic layout observables and connect attribute observables further down
     # after creating the block with its observable fields
diff --git a/src/scenes.jl b/src/scenes.jl
index 6802d2ea652..cf4a5b65b06 100644
--- a/src/scenes.jl
+++ b/src/scenes.jl
@@ -137,8 +137,8 @@ function Observables.on(@nospecialize(f), @nospecialize(scene::Union{Plot,Scene}
     return to_deregister
 end
 
-function Observables.onany(@nospecialize(f), @nospecialize(scene::Union{Plot,Scene}), @nospecialize(observables...); priority=0)
-    to_deregister = onany(f, observables...; priority=priority)
+function Observables.onany(@nospecialize(f), @nospecialize(scene::Union{Plot,Scene}), @nospecialize(observables...); update=false, priority=0)
+    to_deregister = onany(f, observables...; priority=priority, update=update)
     append!(scene.deregister_callbacks::Vector{Observables.ObserverFunction}, to_deregister)
     return to_deregister
 end
diff --git a/src/specapi.jl b/src/specapi.jl
index d9d5c13bebd..fe6eec19b61 100644
--- a/src/specapi.jl
+++ b/src/specapi.jl
@@ -735,7 +735,7 @@ function plot!(fig::Union{Figure, GridLayoutBase.GridPosition}, plot::Plot{Makie
     return fig
 end
 
-function apply_convert!(P, attributes::Attributes, x::GridLayoutSpec)
+function apply_convert!(P, ::Attributes, x::GridLayoutSpec)
     return (Plot{plot}, (x,))
 end
 


Test patch:
diff --git a/test/pipeline.jl b/test/pipeline.jl
index e54a1a52055..54b9e7c58f3 100644
--- a/test/pipeline.jl
+++ b/test/pipeline.jl
@@ -117,3 +117,31 @@ end
     pl2 = lines!(ax, 1:4; color=Cycled(1))
     @test pl2.calculated_colors[] == cpalette[1]
 end
+
+function test_default(arg)
+    _, _, pl1 = plot(arg)
+
+    fig = Figure()
+    _, pl2 = plot(fig[1, 1], arg)
+
+    fig = Figure()
+    ax = Axis(fig[1, 1])
+    pl3 = plot!(ax, arg)
+    return [pl1, pl2, pl3]
+end
+
+@testset "plot defaults" begin
+    plots = test_default([10, 15, 20])
+    @test all(x-> x isa Scatter, plots)
+
+    plots = test_default(rand(4, 4))
+    @test all(x -> x isa Heatmap, plots)
+
+    poly = Polygon(decompose(Point, Circle(Point2f(0), 1.0f0)))
+
+    plots = test_default(poly)
+    @test all(x -> x isa Poly, plots)
+
+    plots = test_default(rand(4, 4, 4))
+    @test all(x -> x isa Volume, plots)
+end