diff --git a/src/axes.jl b/src/axes.jl index b330516ad..9c59d1818 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,10 @@ 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(dummy_attributes, 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 +539,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 +620,18 @@ 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::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) + 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 + 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..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, .._transform_ticks, ..process_limits +import ..locate_annotation, ..PlotText, ..Subplot, ..AVec, ..AMat, ..Axis const MissingOrQuantity = Union{Missing,<:Quantity} @@ -291,14 +290,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/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_dates.jl b/test/test_dates.jl index 68db3cfe4..7bfae01fd 100644 --- a/test/test_dates.jl +++ b/test/test_dates.jl @@ -21,6 +21,7 @@ end ref_xlims = 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 diff --git a/test/test_recipes.jl b/test/test_recipes.jl index 7ba088ce3..495e414fe 100644 --- a/test/test_recipes.jl +++ b/test/test_recipes.jl @@ -81,3 +81,18 @@ 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..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,26 +53,25 @@ end @test_throws DimensionError begin pl = plot(y) plot!(pl; ylims = (-1s, 5s)) - savefig(pl, testfile) + forcedraw(pl) end 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]) + forcedraw(pl) + @test compare_yticks(pl, [0.01, 0.03]) + pl = plot!(; ylims = encompassing_ylims, yticks = [-1cm, 4cm]) + 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 @@ -190,7 +190,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 @@ -338,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