From f4b7604302f43690c6ced68ba17fd852ae034bf2 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Wed, 12 Oct 2022 10:56:07 +0200 Subject: [PATCH 1/3] Apply recipes to ticks and lims --- RecipesPipeline/src/type_recipe.jl | 1 + src/axes.jl | 38 ++++++++++++++++++++---------- src/unitful.jl | 12 +--------- test/test_dates.jl | 9 +++---- test/test_recipes.jl | 12 ++++++++++ test/test_unitful.jl | 19 +++++++-------- 6 files changed, 53 insertions(+), 38 deletions(-) diff --git a/RecipesPipeline/src/type_recipe.jl b/RecipesPipeline/src/type_recipe.jl index d60a6e9b4..6ea213205 100644 --- a/RecipesPipeline/src/type_recipe.jl +++ b/RecipesPipeline/src/type_recipe.jl @@ -21,6 +21,7 @@ function _apply_type_recipe(plotattributes, v, letter) rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v) warn_on_recipe_aliases!(plotattributes[:plot_object], plotattributes, :type, v) postprocess_axis_args!(plt, plotattributes, letter) + isnothing(rdvec) && return nothing return rdvec[1].args[1] end diff --git a/src/axes.jl b/src/axes.jl index b330516ad..956655520 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -81,6 +81,7 @@ end function attr!(axis::Axis, args...; kw...) # first process args plotattributes = axis.plotattributes + dummy_attributes = Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt) for arg in args process_axis_arg!(plotattributes, arg) end @@ -96,12 +97,6 @@ function attr!(axis::Axis, args...; kw...) for vi in v discrete_value!(axis, vi) end - #could perhaps use TimeType here, as Date and DateTime are both subtypes of TimeType - # or could perhaps check if dateformatter or datetimeformatter is in use - elseif k === :lims && isa(v, Tuple{Date,Date}) - plotattributes[k] = (v[1].instant.periods.value, v[2].instant.periods.value) - elseif k === :lims && isa(v, Tuple{DateTime,DateTime}) - plotattributes[k] = (v[1].instant.periods.value, v[2].instant.periods.value) else plotattributes[k] = v end @@ -335,8 +330,14 @@ get_ticks(ticks::Bool, args...) = get_ticks(::T, args...) where {T} = error("Unknown ticks type in get_ticks: $T") _transform_ticks(ticks, axis) = ticks -_transform_ticks(ticks::AbstractArray{T}, axis) where {T<:Dates.TimeType} = - Dates.value.(ticks) +function _transform_ticks(ticks::AbstractArray, axis) + dummy_attributes = Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt) + return RecipesPipeline._apply_type_recipe( + Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt), + ticks, + axis[:letter] + ) +end _transform_ticks(ticks::NTuple{2,Any}, axis) = (_transform_ticks(ticks[1], axis), ticks[2]) function get_minor_ticks(sp, axis, ticks) @@ -542,7 +543,7 @@ end scale_lims!([plt], [letter], factor) Scale the limits of the axis specified by `letter` (one of `:x`, `:y`, `:z`) by the -given `factor` around the limits' middle point. +given `factor` around the limits' middle point. If `letter` is omitted, all axes are affected. """ function scale_lims!(sp::Subplot, letter, factor) @@ -623,11 +624,22 @@ function round_limits(amin, amax, scale) amin, amax end -process_limits(lims::Tuple{<:Union{Symbol,Real},<:Union{Symbol,Real}}, axis) = lims +#process_limits(lims::Tuple{<:Union{Symbol,Real},<:Union{Symbol,Real}}, axis) = lims process_limits(lims::Symbol, axis) = lims -process_limits(lims::AVec, axis) = - length(lims) == 2 && all(map(x -> x isa Union{Symbol,Real}, lims)) ? Tuple(lims) : - nothing +process_limits(lims::Tuple, axis) = process_limits([lims...], axis) +function process_limits(lims::AVec, axis) + lims = RecipesPipeline._apply_type_recipe( + Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt), + lims, + axis[:letter] + ) + if lims isa Formatted + lims = lims.data + end + length(lims) == 2 || return nothing + all(x -> x isa Union{Symbol, Real}, lims) || return nothing + return Tuple(lims) +end process_limits(lims, axis) = nothing warn_invalid_limits(lims, letter) = @warn """ diff --git a/src/unitful.jl b/src/unitful.jl index 2433a72bc..f5dc76fcf 100644 --- a/src/unitful.jl +++ b/src/unitful.jl @@ -8,7 +8,7 @@ using ..RecipesBase export @P_str import ..locate_annotation, - ..PlotText, ..Subplot, ..AVec, ..AMat, ..Axis, .._transform_ticks, ..process_limits + ..PlotText, ..Subplot, ..AVec, ..AMat, ..Axis const MissingOrQuantity = Union{Missing,<:Quantity} @@ -291,14 +291,4 @@ locate_annotation( locate_annotation(sp::Subplot, rel::NTuple{N,<:MissingOrQuantity}, label) where {N} = locate_annotation(sp, ustrip.(rel), label) -#==================# -# ticks and limits # -#==================# -_transform_ticks(ticks::AbstractArray{T}, axis) where {T<:Quantity} = - ustrip.(getaxisunit(axis), ticks) -process_limits(lims::AbstractArray{T}, axis) where {T<:Quantity} = - ustrip.(getaxisunit(axis), lims) -process_limits(lims::Tuple{S,T}, axis) where {S<:Quantity,T<:Quantity} = - ustrip.(getaxisunit(axis), lims) - end diff --git a/test/test_dates.jl b/test/test_dates.jl index 68db3cfe4..f26bcba15 100644 --- a/test/test_dates.jl +++ b/test/test_dates.jl @@ -16,19 +16,20 @@ end @testset "Date xlims" begin y = [1.0 * i * i for i in 1:10] x = [Date(2019, 11, i) for i in 1:10] - span = (Date(2019, 10, 31), Date(2019, 11, 11)) + span = [Date(2019, 10, 31), Date(2019, 11, 11)] - ref_xlims = map(date -> date.instant.periods.value, span) + ref_xlims = Tuple(map(date -> date.instant.periods.value, span)) pl = plot(x, y, xlims = span, widen = false) + @test Plots.xlims(pl) == ref_xlims end @testset "DateTime xlims" begin y = [1.0 * i * i for i in 1:10] x = [DateTime(2019, 11, i, 11) for i in 1:10] - span = (DateTime(2019, 10, 31, 11, 59, 59), DateTime(2019, 11, 11, 12, 01, 15)) + span = [DateTime(2019, 10, 31, 11, 59, 59), DateTime(2019, 11, 11, 12, 01, 15)] - ref_xlims = map(date -> date.instant.periods.value, span) + ref_xlims = Tuple(map(date -> date.instant.periods.value, span)) pl = plot(x, y, xlims = span, widen = false) @test Plots.xlims(pl) == ref_xlims diff --git a/test/test_recipes.jl b/test/test_recipes.jl index 7ba088ce3..3b071b2cf 100644 --- a/test/test_recipes.jl +++ b/test/test_recipes.jl @@ -81,3 +81,15 @@ end @test p[1][2][:linestyle] == :dash @test p[1][3][:linestyle] == :dot end + +@testset "lims and ticks" begin + struct DoubleNumber + x + end + @recipe f(::Type{T}, v::T) where {T<:AbstractArray{DoubleNumber}} = [2*x.x for x in v] + + p = plot(1:3; ylims=DoubleNumber.([0.5, 2.0]), yticks=DoubleNumber.([0.4, 0.8, 1.2])) + @test ylims(p) == (1.0, 4.0) + @test first(first(yticks(p))) == [1.6, 2.4] +end + diff --git a/test/test_unitful.jl b/test/test_unitful.jl index 173794894..5753dcd24 100644 --- a/test/test_unitful.jl +++ b/test/test_unitful.jl @@ -59,15 +59,14 @@ end @testset "yticks" begin compare_yticks(pl, expected_ticks) = all(first(first(yticks(pl))) .≈ expected_ticks) encompassing_ylims = (-1m, 6m) - @test compare_yticks(plot(y; ylims = encompassing_ylims, yticks = (1:5)m), 1:5) - @test compare_yticks( - plot(y; ylims = encompassing_ylims, yticks = [1cm, 3cm]), - [0.01, 0.03], - ) - @test compare_yticks( - plot!(; ylims = encompassing_ylims, yticks = [-1cm, 4cm]), - [-0.01, 0.04], - ) + pl = plot(y; ylims = encompassing_ylims, yticks = (1:5)m) + @test compare_yticks(pl, 1:5) + pl = plot(y; ylims = encompassing_ylims, yticks = [1cm, 3cm]) + savefig(pl, testfile) + @test compare_yticks(pl, [0.01, 0.03]) + pl = plot!(; ylims = encompassing_ylims, yticks = [-1cm, 4cm]) + savefig(pl, testfile) + @test compare_yticks(pl, [-0.01, 0.04]) @test_throws DimensionError begin pl = plot(y) plot!(pl; yticks = (1:5)s) @@ -190,7 +189,7 @@ end @test plot(x * m, ylims = (-1, 1)) isa Plot @test plot(x * m, ylims = (-1, 1) .* m) isa Plot @test plot(x * m, yunit = u"km") isa Plot - @test plot(x * m, xticks = (1:3) * m) isa Plot + @test plot(x * m, yticks = (1:3) * m) isa Plot end @testset "Two arrays" begin From 5507cc582ab78a4a30511e3bed9379f9b994a156 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Wed, 12 Oct 2022 14:23:29 +0200 Subject: [PATCH 2/3] Clean up --- src/axes.jl | 20 ++++++-------------- src/unitful.jl | 3 +-- test/test_dates.jl | 8 ++++---- test/test_recipes.jl | 9 ++++++--- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/axes.jl b/src/axes.jl index 956655520..9c59d1818 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -81,7 +81,7 @@ end function attr!(axis::Axis, args...; kw...) # first process args plotattributes = axis.plotattributes - dummy_attributes = Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt) + dummy_attributes = Dict{Symbol,Any}(:plot_object => first(axis.sps).plt) for arg in args process_axis_arg!(plotattributes, arg) end @@ -331,12 +331,8 @@ get_ticks(::T, args...) where {T} = error("Unknown ticks type in get_ticks: $T") _transform_ticks(ticks, axis) = ticks function _transform_ticks(ticks::AbstractArray, axis) - dummy_attributes = Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt) - return RecipesPipeline._apply_type_recipe( - Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt), - ticks, - axis[:letter] - ) + dummy_attributes = Dict{Symbol,Any}(:plot_object => first(axis.sps).plt) + return RecipesPipeline._apply_type_recipe(dummy_attributes, ticks, axis[:letter]) end _transform_ticks(ticks::NTuple{2,Any}, axis) = (_transform_ticks(ticks[1], axis), ticks[2]) @@ -624,20 +620,16 @@ function round_limits(amin, amax, scale) amin, amax end -#process_limits(lims::Tuple{<:Union{Symbol,Real},<:Union{Symbol,Real}}, axis) = lims process_limits(lims::Symbol, axis) = lims process_limits(lims::Tuple, axis) = process_limits([lims...], axis) function process_limits(lims::AVec, axis) - lims = RecipesPipeline._apply_type_recipe( - Dict{Symbol, Any}(:plot_object=>first(axis.sps).plt), - lims, - axis[:letter] - ) + dummy_attributes = Dict{Symbol,Any}(:plot_object => first(axis.sps).plt) + lims = RecipesPipeline._apply_type_recipe(dummy_attributes, lims, axis[:letter]) if lims isa Formatted lims = lims.data end length(lims) == 2 || return nothing - all(x -> x isa Union{Symbol, Real}, lims) || return nothing + all(x -> x isa Union{Symbol,Real}, lims) || return nothing return Tuple(lims) end process_limits(lims, axis) = nothing diff --git a/src/unitful.jl b/src/unitful.jl index f5dc76fcf..b5603108d 100644 --- a/src/unitful.jl +++ b/src/unitful.jl @@ -7,8 +7,7 @@ using ..Unitful: Quantity, unit, ustrip, Unitful, dimension, Units, NoUnits using ..RecipesBase export @P_str -import ..locate_annotation, - ..PlotText, ..Subplot, ..AVec, ..AMat, ..Axis +import ..locate_annotation, ..PlotText, ..Subplot, ..AVec, ..AMat, ..Axis const MissingOrQuantity = Union{Missing,<:Quantity} diff --git a/test/test_dates.jl b/test/test_dates.jl index f26bcba15..7bfae01fd 100644 --- a/test/test_dates.jl +++ b/test/test_dates.jl @@ -16,9 +16,9 @@ end @testset "Date xlims" begin y = [1.0 * i * i for i in 1:10] x = [Date(2019, 11, i) for i in 1:10] - span = [Date(2019, 10, 31), Date(2019, 11, 11)] + span = (Date(2019, 10, 31), Date(2019, 11, 11)) - ref_xlims = Tuple(map(date -> date.instant.periods.value, span)) + ref_xlims = map(date -> date.instant.periods.value, span) pl = plot(x, y, xlims = span, widen = false) @test Plots.xlims(pl) == ref_xlims @@ -27,9 +27,9 @@ end @testset "DateTime xlims" begin y = [1.0 * i * i for i in 1:10] x = [DateTime(2019, 11, i, 11) for i in 1:10] - span = [DateTime(2019, 10, 31, 11, 59, 59), DateTime(2019, 11, 11, 12, 01, 15)] + span = (DateTime(2019, 10, 31, 11, 59, 59), DateTime(2019, 11, 11, 12, 01, 15)) - ref_xlims = Tuple(map(date -> date.instant.periods.value, span)) + ref_xlims = map(date -> date.instant.periods.value, span) pl = plot(x, y, xlims = span, widen = false) @test Plots.xlims(pl) == ref_xlims diff --git a/test/test_recipes.jl b/test/test_recipes.jl index 3b071b2cf..495e414fe 100644 --- a/test/test_recipes.jl +++ b/test/test_recipes.jl @@ -86,10 +86,13 @@ end struct DoubleNumber x end - @recipe f(::Type{T}, v::T) where {T<:AbstractArray{DoubleNumber}} = [2*x.x for x in v] + @recipe f(::Type{T}, v::T) where {T<:AbstractArray{DoubleNumber}} = [2 * x.x for x in v] - p = plot(1:3; ylims=DoubleNumber.([0.5, 2.0]), yticks=DoubleNumber.([0.4, 0.8, 1.2])) + p = plot( + 1:3; + ylims = DoubleNumber.([0.5, 2.0]), + yticks = DoubleNumber.([0.4, 0.8, 1.2]), + ) @test ylims(p) == (1.0, 4.0) @test first(first(yticks(p))) == [1.6, 2.4] end - From 2c9043940f108eded4a8532e05015f6778538e18 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Wed, 12 Oct 2022 14:48:14 +0200 Subject: [PATCH 3/3] Revert extraneous change, improve tests --- RecipesPipeline/src/type_recipe.jl | 1 - test/runtests.jl | 1 + test/test_unitful.jl | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/RecipesPipeline/src/type_recipe.jl b/RecipesPipeline/src/type_recipe.jl index 6ea213205..d60a6e9b4 100644 --- a/RecipesPipeline/src/type_recipe.jl +++ b/RecipesPipeline/src/type_recipe.jl @@ -21,7 +21,6 @@ function _apply_type_recipe(plotattributes, v, letter) rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v) warn_on_recipe_aliases!(plotattributes[:plot_object], plotattributes, :type, v) postprocess_axis_args!(plt, plotattributes, letter) - isnothing(rdvec) && return nothing return rdvec[1].args[1] end diff --git a/test/runtests.jl b/test/runtests.jl index 9f7992769..75d3a6f7b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,5 @@ import Unitful: m, s, cm, DimensionError +import Plots.UnitfulRecipes: getaxisunit import Plots: PLOTS_SEED, Plot, with import GeometryBasics import ImageMagick diff --git a/test/test_unitful.jl b/test/test_unitful.jl index 5753dcd24..5f8619601 100644 --- a/test/test_unitful.jl +++ b/test/test_unitful.jl @@ -7,7 +7,7 @@ xseries(pl, idx = length(pl.series_list)) = pl.series_list[idx].plotattributes[: yseries(pl, idx = length(pl.series_list)) = pl.series_list[idx].plotattributes[:y] zseries(pl, idx = length(pl.series_list)) = pl.series_list[idx].plotattributes[:z] -testfile = tempname() * ".png" +forcedraw(pl) = savefig(pl, tempname() * ".png") macro isplot(ex) # @isplot macro to streamline tests :(@test $(esc(ex)) isa Plot) @@ -41,6 +41,7 @@ end @testset "yunit" begin @test yguide(plot(y, yunit = cm)) == "cm" @test yseries(plot(y, yunit = cm)) ≈ ustrip.(cm, y) + @test getaxisunit(plot(y, yunit = cm).subplots[1][:yaxis]) == cm end @testset "ylims" begin # Using all(lims .≈ lims) because of uncontrolled type conversions? @@ -52,7 +53,7 @@ end @test_throws DimensionError begin pl = plot(y) plot!(pl; ylims = (-1s, 5s)) - savefig(pl, testfile) + forcedraw(pl) end end @@ -62,15 +63,15 @@ end pl = plot(y; ylims = encompassing_ylims, yticks = (1:5)m) @test compare_yticks(pl, 1:5) pl = plot(y; ylims = encompassing_ylims, yticks = [1cm, 3cm]) - savefig(pl, testfile) + forcedraw(pl) @test compare_yticks(pl, [0.01, 0.03]) pl = plot!(; ylims = encompassing_ylims, yticks = [-1cm, 4cm]) - savefig(pl, testfile) + forcedraw(pl) @test compare_yticks(pl, [-0.01, 0.04]) @test_throws DimensionError begin pl = plot(y) plot!(pl; yticks = (1:5)s) - savefig(pl, testfile) + forcedraw(pl) end end @@ -337,19 +338,18 @@ end @testset "Aspect ratio" begin pl = plot((1:10)u"m", (1:10)u"dm"; aspect_ratio = :equal) - savefig(pl, testfile) # Force a render, to make it evaluate aspect ratio + forcedraw(pl) @test abs(-(ylims(pl)...)) > 50 plot!(pl, (3:4)u"m", (4:5)u"m") @test first(pl.subplots)[:aspect_ratio] == 1 // 10 # This is what "equal" means when yunit==xunit/10 pl = plot((1:10)u"m", (1:10)u"dm"; aspect_ratio = 2) - savefig(pl, testfile) + forcedraw(pl) @test 25 < abs(-(ylims(pl)...)) < 50 pl = plot((1:10)u"m", (1:10)u"s"; aspect_ratio = 1u"m/s") - savefig(pl, testfile) + forcedraw(pl) @test 7.5 < abs(-(ylims(pl)...)) < 12.5 - @test_throws DimensionError savefig( + @test_throws DimensionError forcedraw( plot((1:10)u"m", (1:10)u"s"; aspect_ratio = :equal), - testfile, ) end