diff --git a/src/args.jl b/src/args.jl index 57dd3298..f2eeb4c5 100644 --- a/src/args.jl +++ b/src/args.jl @@ -1502,6 +1502,11 @@ function RecipesPipeline.preprocess_attributes!(plotattributes::AKW) plotattributes[:framestyle] = _framestyleAliases[plotattributes[:framestyle]] end + # contours + if haskey(plotattributes, :levels) + check_contour_levels(plotattributes[:levels]) + end + # warnings for moved recipes st = get(plotattributes, :seriestype, :path) if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatsPlots) @@ -1629,6 +1634,26 @@ convertLegendValue(v::AbstractArray) = map(convertLegendValue, v) # ----------------------------------------------------------------------------- +"""Throw an error if the `levels` keyword argument is not of the correct type +or `levels` is less than 1""" +function check_contour_levels(levels) + if !(levels isa Union{Integer,AVec}) + throw( + ArgumentError( + "the levels keyword argument must be an integer or AbstractVector", + ), + ) + elseif levels isa Integer && levels <= 0 + throw( + ArgumentError( + "must pass a positive number of contours to the levels keyword argument", + ), + ) + end +end + +# ----------------------------------------------------------------------------- + # 1-row matrices will give an element # multi-row matrices will give a column # InputWrapper just gives the contents diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 2ef25820..d0d237d3 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -390,8 +390,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series) elseif isvector(levels) extrakw[:levels] = levels () - else - error("Only numbers and vectors are supported with levels keyword") end # add custom frame shapes to markershape? diff --git a/test/test_contours.jl b/test/test_contours.jl index 134d4625..82e42bec 100644 --- a/test/test_contours.jl +++ b/test/test_contours.jl @@ -1,31 +1,68 @@ using Plots, Test +import RecipesPipeline @testset "Contours" begin - x = (-2π):0.1:(2π) - y = (-π):0.1:π - z = cos.(y) .* sin.(x') - - @testset "Default number" begin - @test contour(x, y, z)[1][1].plotattributes[:levels] == - Plots._series_defaults[:levels] + @testset "check_contour_levels" begin + @test Plots.check_contour_levels(2) === nothing + @test Plots.check_contour_levels(-1.0:0.2:10.0) === nothing + @test Plots.check_contour_levels([-100, -2, -1, 0, 1, 2, 100]) === nothing + @test_throws ArgumentError Plots.check_contour_levels(1.0) + @test_throws ArgumentError Plots.check_contour_levels((1, 2, 3)) + @test_throws ArgumentError Plots.check_contour_levels(-3) end - @testset "Number" begin - @testset "$n contours" for n in (2, 5, 100) - p = contour(x, y, z, levels = n) - attr = p[1][1].plotattributes - @test attr[:seriestype] == :contour - @test attr[:levels] == n + @testset "RecipesPipeline.preprocess_attributes!" begin + function equal_after_pipeline(kw) + kw′ = deepcopy(kw) + RecipesPipeline.preprocess_attributes!(kw′) + kw == kw′ + end + + @test equal_after_pipeline(KW(:levels => 1)) + @test equal_after_pipeline(KW(:levels => 1:10)) + @test equal_after_pipeline(KW(:levels => [1.0, 3.0, 5.0])) + @test_throws ArgumentError RecipesPipeline.preprocess_attributes!( + KW(:levels => 1.0), + ) + @test_throws ArgumentError RecipesPipeline.preprocess_attributes!( + KW(:levels => (1, 2, 3)), + ) + @test_throws ArgumentError RecipesPipeline.preprocess_attributes!(KW(:levels => -3)) + end + + @testset "contour[f]" begin + x = (-2π):0.1:(2π) + y = (-π):0.1:π + z = cos.(y) .* sin.(x') + + @testset "Incorrect input" begin + @test_throws ArgumentError contour(x, y, z, levels = 1.0) + @test_throws ArgumentError contour(x, y, z, levels = (1, 2, 3)) + @test_throws ArgumentError contour(x, y, z, levels = -3) + end + + @testset "Default number" begin + @test contour(x, y, z)[1][1].plotattributes[:levels] == + Plots._series_defaults[:levels] + end + + @testset "Number" begin + @testset "$n contours" for n in (2, 5, 100) + p = contour(x, y, z, levels = n) + attr = p[1][1].plotattributes + @test attr[:seriestype] == :contour + @test attr[:levels] == n + end + end + + @testset "Range" begin + levels = -1:0.5:1 + @test contour(x, y, z, levels = levels)[1][1].plotattributes[:levels] == levels + end + + @testset "Set of levels" begin + levels = [-1, 0.25, 0, 0.25, 1] + @test contour(x, y, z, levels = levels)[1][1].plotattributes[:levels] == levels end end - - @testset "Range" begin - levels = -1:0.5:1 - @test contour(x, y, z, levels = levels)[1][1].plotattributes[:levels] == levels - end - - @testset "Set of levels" begin - levels = [-1, 0.25, 0, 0.25, 1] - @test contour(x, y, z, levels = levels)[1][1].plotattributes[:levels] == levels - end end