From f232033cb8f67ff7cf5add533cb29572e8a400e7 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 8 Nov 2022 20:09:00 +0100 Subject: [PATCH 01/10] partial base fix --- src/recipes.jl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/recipes.jl b/src/recipes.jl index 1157796f8..7b4d8c510 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -413,7 +413,7 @@ end procx = if nx == ny cv elseif nx == ny + 1 - 0.5diff(cv) + cv[1:(end - 1)] + 0.5diff(cv) + @view(cv[1:(end - 1)]) else error( "bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))", @@ -440,7 +440,7 @@ end fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto) end - xseg, yseg = Segments(), Segments() + xseg, yseg = map(_ -> Segments(), 1:2) for i in 1:ny yi = procy[i] if !isnan(yi) @@ -539,7 +539,7 @@ RecipesPipeline.is_surface(::Type{Val{:hexbin}}) = true # --------------------------------------------------------------------------- # Histograms -_bin_centers(v::AVec) = (v[1:(end - 1)] + v[2:end]) / 2 +_bin_centers(v::AVec) = (@view(v[1:(end - 1)]) + @view(v[2:end])) / 2 _is_positive(x) = (x > 0) && !(x ≈ 0) @@ -549,16 +549,13 @@ _scale_adjusted_values( ::Type{T}, V::AbstractVector, scale::Symbol, -) where {T<:AbstractFloat} = - if scale in _logScales - [_positive_else_nan(T, x) for x in V] - else - [T(x) for x in V] - end +) where {T<:AbstractFloat} = scale in _logScales ? _positive_else_nan.(T, V) : T.(V) + +round_base(x, b) = b^floor(log(b, x)) _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real} = if scale in _logScales - isnan(min_value) ? T(1e-3) : min_value / T(_logScaleBases[scale]^log10(2)) + isnan(min_value) ? T(1e-3) : round_base(min_value, _logScaleBases[scale]) else zero(T) end From f00b02b0d06eb1422b75da90aecc81b1931c63d2 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 8 Nov 2022 20:43:27 +0100 Subject: [PATCH 02/10] alternative solution --- src/recipes.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/recipes.jl b/src/recipes.jl index 7b4d8c510..454524acc 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -437,7 +437,8 @@ end fillto = 0 end if yscale in _logScales && !all(_is_positive, fillto) - fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto) + # fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto) + fillto = 1 # github.com/JuliaPlots/Plots.jl/issues/4502 end xseg, yseg = map(_ -> Segments(), 1:2) From 139f6176f556d5f85114b9fb940793ca4b578a1a Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 8 Nov 2022 21:31:53 +0100 Subject: [PATCH 03/10] compute series extrema --- src/pipeline.jl | 26 ++++++++++++++++++++++++++ src/recipes.jl | 9 ++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/pipeline.jl b/src/pipeline.jl index fcf11d794..512ad9405 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -151,6 +151,28 @@ function RecipesPipeline.plot_setup!(plt::Plot, plotattributes, kw_list) end function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_list) + min_x = min_y = min_z = +Inf + max_x = max_y = max_z = -Inf + + # determine global extrema + for kw in kw_list + if (x = kw[:x]) ≢ nothing + mn, mx = extrema(x) + min_x = NaNMath.min(min_x, mn) + max_x = NaNMath.max(max_x, mx) + end + if (y = kw[:y]) ≢ nothing + mn, mx = extrema(y) + min_y = NaNMath.min(min_y, mn) + max_y = NaNMath.max(max_y, mx) + end + if (z = kw[:z]) ≢ nothing + mn, mx = extrema(z) + min_z = NaNMath.min(min_z, mn) + max_z = NaNMath.max(max_z, mx) + end + end + # swap errors err_inds = findall(kw -> get(kw, :seriestype, :path) in (:xerror, :yerror, :zerror), kw_list) @@ -163,6 +185,10 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_l end for kw in kw_list + kw[:ex] = min_x, max_x + kw[:ey] = min_y, max_y + kw[:ez] = min_z, max_z + rib = get(kw, :ribbon, default(:ribbon)) fr = get(kw, :fillrange, default(:fillrange)) # map ribbon if it's a Function diff --git a/src/recipes.jl b/src/recipes.jl index 454524acc..6987c4c31 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -406,7 +406,7 @@ end # create a bar plot as a filled step function @recipe function f(::Type{Val{:bar}}, x, y, z) # COV_EXCL_LINE - procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y) + procx, procy, xscale, yscale, _ = _preprocess_barlike(plotattributes, x, y) nx, ny = length(procx), length(procy) axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis] cv = [discrete_value!(plotattributes, :x, xi)[1] for xi in procx] @@ -437,8 +437,11 @@ end fillto = 0 end if yscale in _logScales && !all(_is_positive, fillto) - # fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto) - fillto = 1 # github.com/JuliaPlots/Plots.jl/issues/4502 + # github.com/JuliaPlots/Plots.jl/issues/4502 + T = float(eltype(y)) + min_y, _ = plotattributes[:ey] + baseline = round_base(min_y, _logScaleBases[yscale]) + fillto = map(x -> _is_positive(x) ? T(x) : T(baseline), fillto) end xseg, yseg = map(_ -> Segments(), 1:2) From 4f8547507f3084ba3c7426b856f7d101ff7cb028 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 8 Nov 2022 21:52:21 +0100 Subject: [PATCH 04/10] update --- src/pipeline.jl | 28 +++++++--------------------- src/recipes.jl | 4 +--- src/utils.jl | 9 +++++++++ 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/pipeline.jl b/src/pipeline.jl index 512ad9405..60a263478 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -151,26 +151,12 @@ function RecipesPipeline.plot_setup!(plt::Plot, plotattributes, kw_list) end function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_list) - min_x = min_y = min_z = +Inf - max_x = max_y = max_z = -Inf - # determine global extrema + ex = ey = ez = +Inf, -Inf for kw in kw_list - if (x = kw[:x]) ≢ nothing - mn, mx = extrema(x) - min_x = NaNMath.min(min_x, mn) - max_x = NaNMath.max(max_x, mx) - end - if (y = kw[:y]) ≢ nothing - mn, mx = extrema(y) - min_y = NaNMath.min(min_y, mn) - max_y = NaNMath.max(max_y, mx) - end - if (z = kw[:z]) ≢ nothing - mn, mx = extrema(z) - min_z = NaNMath.min(min_z, mn) - max_z = NaNMath.max(max_z, mx) - end + ex = nan_min_max(get(kw, :x, nothing), ex) + ey = nan_min_max(get(kw, :y, nothing), ey) + ez = nan_min_max(get(kw, :z, nothing), ez) end # swap errors @@ -185,9 +171,9 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_l end for kw in kw_list - kw[:ex] = min_x, max_x - kw[:ey] = min_y, max_y - kw[:ez] = min_z, max_z + kw[:ex] = ex + kw[:ey] = ey + kw[:ez] = ez rib = get(kw, :ribbon, default(:ribbon)) fr = get(kw, :fillrange, default(:fillrange)) diff --git a/src/recipes.jl b/src/recipes.jl index 6987c4c31..44811b7d3 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -555,11 +555,9 @@ _scale_adjusted_values( scale::Symbol, ) where {T<:AbstractFloat} = scale in _logScales ? _positive_else_nan.(T, V) : T.(V) -round_base(x, b) = b^floor(log(b, x)) - _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real} = if scale in _logScales - isnan(min_value) ? T(1e-3) : round_base(min_value, _logScaleBases[scale]) + isnan(min_value) ? T(1e-3) : floor_base(min_value, _logScaleBases[scale]) else zero(T) end diff --git a/src/utils.jl b/src/utils.jl index bc543715e..3ca14f904 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -69,6 +69,15 @@ function iter_segments(args...) NaNSegmentsIterator(tup, n1, n2) end +"floor number x in base b" +floor_base(x, b) = b^floor(log(b, x)) + +nan_min_max(::Any, ::Any) = nothing +function nan_min_max(x::AbstractArray{<:Number}, ex) + mn, mx = extrema(x) + NaNMath.min(ex[1], mn), NaNMath.max(ex[2], mx) +end + function series_segments(series::Series, seriestype::Symbol = :path; check = false) x, y, z = series[:x], series[:y], series[:z] (x === nothing || isempty(x)) && return UnitRange{Int}[] From 3da82251fefcf86c883c01978ce0331a966e2081 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 8 Nov 2022 22:26:35 +0100 Subject: [PATCH 05/10] add tests --- src/recipes.jl | 2 +- src/utils.jl | 4 +-- test/test_recipes.jl | 72 +++++++++++++++++++++----------------------- 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/recipes.jl b/src/recipes.jl index 44811b7d3..47ce5697f 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -440,7 +440,7 @@ end # github.com/JuliaPlots/Plots.jl/issues/4502 T = float(eltype(y)) min_y, _ = plotattributes[:ey] - baseline = round_base(min_y, _logScaleBases[yscale]) + baseline = floor_base(min_y, _logScaleBases[yscale]) fillto = map(x -> _is_positive(x) ? T(x) : T(baseline), fillto) end diff --git a/src/utils.jl b/src/utils.jl index 3ca14f904..5f16abe01 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -69,8 +69,8 @@ function iter_segments(args...) NaNSegmentsIterator(tup, n1, n2) end -"floor number x in base b" -floor_base(x, b) = b^floor(log(b, x)) +"floor number x in base b, since `floor(x; base=b)` only supports integers" +floor_base(x::T, b) = T(b^floor(log(b, x))) nan_min_max(::Any, ::Any) = nothing function nan_min_max(x::AbstractArray{<:Number}, ex) diff --git a/test/test_recipes.jl b/test/test_recipes.jl index f20276d1d..95d0011b4 100644 --- a/test/test_recipes.jl +++ b/test/test_recipes.jl @@ -77,54 +77,50 @@ end @test Plots.seriestype_supported(Plots.NoBackend(), :line) === :no end -@testset "error bars" begin - x = y = 1:10 - yerror = fill(1, length(y)) - xerror = fill(0.2, length(x)) - p = Plots.xerror(x, y; xerror, linestyle = :solid) - plot!(p, x, y; linestyle = :dash) - yerror!(p, x, y; yerror, linestyle = :dot) - @test length(p.series_list) == 3 - @test p[1][1][:linestyle] == :solid - @test p[1][2][:linestyle] == :dash - @test p[1][3][:linestyle] == :dot -end +with(:gr) do + @testset "error bars" begin + x = y = 1:10 + yerror = fill(1, length(y)) + xerror = fill(0.2, length(x)) + p = Plots.xerror(x, y; xerror, linestyle = :solid) + plot!(p, x, y; linestyle = :dash) + yerror!(p, x, y; yerror, linestyle = :dot) + @test length(p.series_list) == 3 + @test p[1][1][:linestyle] == :solid + @test p[1][2][:linestyle] == :dash + @test p[1][3][:linestyle] == :dot + end -@testset "parametric" begin - @test plot(sin, sin, cos, 0, 2π) isa Plot - @test plot(sin, sin, cos, collect((-2π):(π / 4):(2π))) isa Plot -end + @testset "parametric" begin + @test plot(sin, sin, cos, 0, 2π) isa Plot + @test plot(sin, sin, cos, collect((-2π):(π / 4):(2π))) isa Plot + end -@testset "dict" begin - @test plot(Dict(1 => 2, 3 => -1)) isa Plot -end + @testset "dict" begin + show(devnull, plot(Dict(1 => 2, 3 => -1))) + end -@testset "gray image" begin - with(:gr) do - @test plot(rand(Gray, 2, 2)) isa Plot + @testset "gray image" begin + show(devnull, plot(rand(Gray, 2, 2))) end -end -@testset "plots_heatmap" begin - with(:gr) do - @test plots_heatmap(rand(RGBA, 2, 2)) isa Plot + @testset "plots_heatmap" begin + show(devnull, plots_heatmap(rand(RGBA, 2, 2))) end -end -@testset "scatter3d" begin - with(:gr) do - @test scatter3d(1:2, 1:2, 1:2) isa Plot + @testset "scatter3d" begin + show(devnull, scatter3d(1:2, 1:2, 1:2)) end -end -@testset "sticks" begin - with(:gr) do - @test sticks(1:2, marker = :circle) isa Plot + @testset "sticks" begin + show(devnull, sticks(1:2, marker = :circle)) + end + + @testset "stephist" begin + show(devnull, stephist([1, 2], marker = :circle)) end -end -@testset "stephist" begin - with(:gr) do - @test stephist([1, 2], marker = :circle) isa Plot + @testset "bar with logscales" begin + show(devnull, bar([1 2 3], [0.02 125 10_000]; yscale = :log10)) end end From 05ecd98fa61329a1a683e6e8a1bc09f384643f68 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 8 Nov 2022 22:40:50 +0100 Subject: [PATCH 06/10] nanosoldier --- src/utils.jl | 4 ++-- test/test_output.jl | 2 +- test/test_pgfplotsx.jl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 5f16abe01..073f5e4f4 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -69,8 +69,8 @@ function iter_segments(args...) NaNSegmentsIterator(tup, n1, n2) end -"floor number x in base b, since `floor(x; base=b)` only supports integers" -floor_base(x::T, b) = T(b^floor(log(b, x))) +"floor number x in base b, since `Base.floor(x; base=b)` only supports integers" +floor_base(x::T, b) where {T} = T(b^floor(log(b, x))) nan_min_max(::Any, ::Any) = nothing function nan_min_max(x::AbstractArray{<:Number}, ex) diff --git a/test/test_output.jl b/test/test_output.jl index 686fc1a6d..5afdd0840 100644 --- a/test/test_output.jl +++ b/test/test_output.jl @@ -60,7 +60,7 @@ with(:plotly) do # @test_save :eps end -if Sys.islinux() +if Sys.islinux() && Sys.which("pdflatex") ≢ nothing with(:pgfplotsx) do @test_save :tex @test_save :png diff --git a/test/test_pgfplotsx.jl b/test/test_pgfplotsx.jl index 580acc547..b0874a708 100644 --- a/test/test_pgfplotsx.jl +++ b/test/test_pgfplotsx.jl @@ -451,7 +451,7 @@ with(:pgfplotsx) do @test plot(plt1, plt2, layout = (1, 2), plot_titles = ["(a)" "(b)"]) !== nothing end - if Sys.islinux() + if Sys.islinux() && Sys.which("pdflatex") ≢ nothing @testset "Issues - actually compile `.tex`" begin # Plots.jl/issues/4308 fn = tempname() * ".pdf" From c542dce929bfce00e1bd4018f030a3420562e4f1 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 9 Nov 2022 00:11:43 +0100 Subject: [PATCH 07/10] update --- src/args.jl | 3 +++ src/pipeline.jl | 12 ++++----- src/recipes.jl | 58 ++++++++++++++++++-------------------------- src/utils.jl | 6 ++--- test/test_unitful.jl | 8 +++--- 5 files changed, 40 insertions(+), 47 deletions(-) diff --git a/src/args.jl b/src/args.jl index 1eb97e09b..1336e1565 100644 --- a/src/args.jl +++ b/src/args.jl @@ -531,6 +531,9 @@ const _suppress_warnings = Set{Symbol}([ :relative_bbox, :layout_insets, :force_minpad, + :x_extrema, + :y_extrema, + :z_extrema, ]) is_subplot_attr(k) = k in _all_subplot_args diff --git a/src/pipeline.jl b/src/pipeline.jl index 60a263478..7354030ab 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -154,9 +154,9 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_l # determine global extrema ex = ey = ez = +Inf, -Inf for kw in kw_list - ex = nan_min_max(get(kw, :x, nothing), ex) - ey = nan_min_max(get(kw, :y, nothing), ey) - ez = nan_min_max(get(kw, :z, nothing), ez) + ex = ignorenan_min_max(get(kw, :x, nothing), ex) + ey = ignorenan_min_max(get(kw, :y, nothing), ey) + ez = ignorenan_min_max(get(kw, :z, nothing), ez) end # swap errors @@ -171,9 +171,9 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_l end for kw in kw_list - kw[:ex] = ex - kw[:ey] = ey - kw[:ez] = ez + kw[:x_extrema] = ex + kw[:y_extrema] = ey + kw[:z_extrema] = ez rib = get(kw, :ribbon, default(:ribbon)) fr = get(kw, :fillrange, default(:fillrange)) diff --git a/src/recipes.jl b/src/recipes.jl index 47ce5697f..953e2ed7f 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -409,7 +409,7 @@ end procx, procy, xscale, yscale, _ = _preprocess_barlike(plotattributes, x, y) nx, ny = length(procx), length(procy) axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis] - cv = [discrete_value!(plotattributes, :x, xi)[1] for xi in procx] + cv = map(xi -> discrete_value!(plotattributes, :x, xi)[1], procx) procx = if nx == ny cv elseif nx == ny + 1 @@ -439,28 +439,19 @@ end if yscale in _logScales && !all(_is_positive, fillto) # github.com/JuliaPlots/Plots.jl/issues/4502 T = float(eltype(y)) - min_y, _ = plotattributes[:ey] + min_y, _ = plotattributes[:y_extrema] baseline = floor_base(min_y, _logScaleBases[yscale]) fillto = map(x -> _is_positive(x) ? T(x) : T(baseline), fillto) end xseg, yseg = map(_ -> Segments(), 1:2) for i in 1:ny - yi = procy[i] - if !isnan(yi) - center = procx[i] - hwi = _cycle(hw, i) - fi = _cycle(fillto, i) - push!( - xseg, - center - hwi, - center - hwi, - center + hwi, - center + hwi, - center - hwi, - ) - push!(yseg, yi, fi, fi, yi, yi) - end + (yi = procy[i]) |> isnan && continue + center = procx[i] + hwi = _cycle(hw, i) + fi = _cycle(fillto, i) + push!(xseg, center - hwi, center - hwi, center + hwi, center + hwi, center - hwi) + push!(yseg, yi, fi, fi, yi, yi) end # widen limits out a bit @@ -513,11 +504,11 @@ end @recipe function f(::Type{Val{:plots_heatmap}}, x, y, z) # COV_EXCL_LINE xe, ye = heatmap_edges(x), heatmap_edges(y) m, n = size(z.surf) - x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n) + x_pts, y_pts = fill(NaN, 6m * n), fill(NaN, 6m * n) fz = zeros(m * n) for i in 1:m, j in 1:n # i ≡ y, j ≡ x k = (j - 1) * m + i - inds = (6 * (k - 1) + 1):(6 * k - 1) + inds = (6(k - 1) + 1):(6k - 1) x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]] y_pts[inds] .= [ye[i], ye[i], ye[i + 1], ye[i + 1], ye[i]] fz[k] = z.surf[i, j] @@ -733,9 +724,11 @@ function _auto_binning_nbins( # The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation nd = n_samples^(1 / (2 + N)) - nd = - N == 2 ? - min(n_samples^(1 / (2 + N)), nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8)) : nd # the >2-dimensional case does not have a nice solution to correlations + nd = if N == 2 + min(n_samples^(1 / (2 + N)), nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8)) + else # the >2-dimensional case does not have a nice solution to correlations + nd + end v = vs[dim] mode === :auto && (mode = :fd) @@ -1061,20 +1054,17 @@ function intersection_point(xA, yA, xB, yB, h, w) hh, hw = h / 2, w / 2 # left or right? if -hh <= s * hw <= hh - if xA > xB - # right - return xB + hw, yB + s * hw - else # left - return xB - hw, yB - s * hw + if xA > xB # right + xB + hw, yB + s * hw + else # left + xB - hw, yB - s * hw end # top or bot? elseif -hw <= hh / s <= hw - if yA > yB - # top - return xB + hh / s, yB + hh - else - # bottom - return xB - hh / s, yB - hh + if yA > yB # top + xB + hh / s, yB + hh + else # bottom + xB - hh / s, yB - hh end end end @@ -1125,7 +1115,7 @@ error_tuple(x::Tuple) = x function error_coords(errorbar, errordata, otherdata...) ed = Vector{float_extended_type(errordata)}(undef, 0) - od = [Vector{float_extended_type(odi)}(undef, 0) for odi in otherdata] + od = map(odi -> Vector{float_extended_type(odi)}(undef, 0), otherdata) for (i, edi) in enumerate(errordata) for (j, odj) in enumerate(otherdata) odi = _cycle(odj, i) diff --git a/src/utils.jl b/src/utils.jl index 073f5e4f4..6e93f2173 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -72,9 +72,9 @@ end "floor number x in base b, since `Base.floor(x; base=b)` only supports integers" floor_base(x::T, b) where {T} = T(b^floor(log(b, x))) -nan_min_max(::Any, ::Any) = nothing -function nan_min_max(x::AbstractArray{<:Number}, ex) - mn, mx = extrema(x) +ignorenan_min_max(::Any, ex) = ex +function ignorenan_min_max(x::AbstractArray{<:AbstractFloat}, ex::Tuple) + mn, mx = ignorenan_extrema(x) NaNMath.min(ex[1], mn), NaNMath.max(ex[2], mx) end diff --git a/test/test_unitful.jl b/test/test_unitful.jl index 24d1ec486..7640c66ef 100644 --- a/test/test_unitful.jl +++ b/test/test_unitful.jl @@ -149,8 +149,8 @@ end @test plot(f, x * m) isa Plot @test plot(x * m, f) isa Plot g(x) = x * m # If the unit comes from the function only then it throws - @test_throws DimensionError plot(x, g) isa Plot - @test_throws DimensionError plot(g, x) isa Plot + @test_throws DimensionError plot(x, g) + @test_throws DimensionError plot(g, x) end @testset "plot(x, y, f)" begin f(x, y) = x * y @@ -158,13 +158,13 @@ end @test plot(x * m, y, f) isa Plot @test plot(x, y * s, f) isa Plot g(x, y) = x * y * m # If the unit comes from the function only then it throws - @test_throws DimensionError plot(x, y, g) isa Plot + @test_throws DimensionError plot(x, y, g) end @testset "plot(f, u)" begin f(x) = x^2 pl = plot(x * m, f.(x * m)) @test plot!(pl, f, m) isa Plot - @test_throws DimensionError plot!(pl, f, s) isa Plot + @test_throws DimensionError plot!(pl, f, s) pl = plot(f, m) @test xguide(pl) == string(m) @test yguide(pl) == string(m^2) From 044180630be65f76be016e986be7f3826bf465ef Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 9 Nov 2022 08:37:19 +0100 Subject: [PATCH 08/10] use `NaN` instead --- src/pipeline.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pipeline.jl b/src/pipeline.jl index 7354030ab..68e342eaa 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -152,11 +152,11 @@ end function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_list) # determine global extrema - ex = ey = ez = +Inf, -Inf + xe = xe = ze = NaN, NaN for kw in kw_list - ex = ignorenan_min_max(get(kw, :x, nothing), ex) - ey = ignorenan_min_max(get(kw, :y, nothing), ey) - ez = ignorenan_min_max(get(kw, :z, nothing), ez) + xe = ignorenan_min_max(get(kw, :x, nothing), xe) + ye = ignorenan_min_max(get(kw, :y, nothing), ye) + ze = ignorenan_min_max(get(kw, :z, nothing), ze) end # swap errors @@ -171,9 +171,9 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_l end for kw in kw_list - kw[:x_extrema] = ex - kw[:y_extrema] = ey - kw[:z_extrema] = ez + kw[:x_extrema] = xe + kw[:y_extrema] = ye + kw[:z_extrema] = ze rib = get(kw, :ribbon, default(:ribbon)) fr = get(kw, :fillrange, default(:fillrange)) From 160929abb9f0d6b4b8f6a1f2808298f273471d3d Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 9 Nov 2022 09:00:39 +0100 Subject: [PATCH 09/10] update docs --- src/pipeline.jl | 2 +- src/recipes.jl | 11 +++------ src/shorthands.jl | 63 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/pipeline.jl b/src/pipeline.jl index 68e342eaa..41a156fcb 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -152,7 +152,7 @@ end function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_list) # determine global extrema - xe = xe = ze = NaN, NaN + xe = ye = ze = NaN, NaN for kw in kw_list xe = ignorenan_min_max(get(kw, :x, nothing), xe) ye = ignorenan_min_max(get(kw, :y, nothing), ye) diff --git a/src/recipes.jl b/src/recipes.jl index 953e2ed7f..4dbddd66d 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -292,8 +292,7 @@ end # create vertical line segments from fill @recipe function f(::Type{Val{:sticks}}, x, y, z) # COV_EXCL_LINE n = length(x) - fr = plotattributes[:fillrange] - if fr === nothing + if (fr = plotattributes[:fillrange]) === nothing sp = plotattributes[:subplot] fr = if sp[:yaxis][:scale] === :identity 0.0 @@ -301,8 +300,7 @@ end NaNMath.min(axis_limits(sp, :y)[1], ignorenan_minimum(y)) end end - newx, newy = zeros(3n), zeros(3n) - newz = z !== nothing ? zeros(3n) : nothing + newx, newy, newz = zeros(3n), zeros(3n), z !== nothing ? zeros(3n) : nothing for (i, (xi, yi, zi)) in enumerate(zip(x, y, z !== nothing ? z : 1:n)) rng = (3i - 2):(3i) newx[rng] = [xi, xi, NaN] @@ -368,8 +366,7 @@ end @recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30) # COV_EXCL_LINE args = z !== nothing ? (x, y, z) : (x, y) newx, newy = zeros(0), zeros(0) - fr = plotattributes[:fillrange] - newfr = fr !== nothing ? zeros(0) : nothing + newfr = (fr = plotattributes[:fillrange]) !== nothing ? zeros(0) : nothing newz = z !== nothing ? zeros(0) : nothing # for each line segment (point series with no NaNs), convert it into a bezier curve @@ -1276,7 +1273,7 @@ function quiver_using_hack(plotattributes::AKW) arrow_h = 0.1dist # height of arrowhead arrow_w = 0.5arrow_h # halfwidth of arrowhead U1 = v ./ dist # vector of arrowhead height - U2 = P2((-U1[2], U1[1])) # vector of arrowhead halfwidth + U2 = P2((-U1[2], U1[1])) # vector of arrowhead halfwidth U1 = U1 .* arrow_h U2 = U2 .* arrow_w diff --git a/src/shorthands.jl b/src/shorthands.jl index 2ef0b1143..d2dc48a35 100644 --- a/src/shorthands.jl +++ b/src/shorthands.jl @@ -4,7 +4,14 @@ scatter(x,y) scatter!(x,y) -Make a scatter plot of y vs x. +Make a scatter plot of `y` vs `x`. + +# Keyword arguments +- $(_document_argument("markersize")) +- $(_document_argument("markercolor")) +- $(_document_argument("markershape")) +- $(_document_argument("markercolor")) +- $(_document_argument("markeralpha")) # Examples ```julia-repl @@ -18,14 +25,14 @@ julia> scatter([(1,4),(2,5),(3,6)]) bar(x,y) bar!(x,y) -Make a bar plot of y vs x. - -# Arguments +Make a bar plot of `y` vs `x`. +# Keyword arguments - $(_document_argument("bar_position")) - $(_document_argument("bar_width")) - $(_document_argument("bar_edges")) -- $(_document_argument("orientation")) +- $(_document_argument("fillrange")) +- $(_document_argument("permute")) # Examples ```julia-repl @@ -44,7 +51,6 @@ julia> bar([(1,4),(2,5),(3,6)]) Plot a histogram. # Arguments - - `x`: AbstractVector of values to be binned - $(_document_argument("bins")) - `weights`: Vector of weights for the values in `x`, for weighted bin counts @@ -52,7 +58,7 @@ Plot a histogram. - $(_document_argument("bar_position")) - $(_document_argument("bar_width")) - $(_document_argument("bar_edges")) -- $(_document_argument("orientation")) +- $(_document_argument("permute")) # Example ```julia-repl @@ -95,7 +101,6 @@ instead of bars). See `histogram`. Plot a two-dimensional histogram. # Arguments - - `bins`: Number of bins (if an `Integer`) or bin edges (if an `AbtractVector`) - `weights`: Vector of weights for the values in `x`. Each entry of x contributes its weight to the height of its bin. @@ -114,11 +119,9 @@ julia> histogram2d(randn(10_000),randn(10_000)) Make a line plot of a kernel density estimate of x. The smoothness of the density plot is defined from `bandwidth` (real positive number). # Arguments - - `x`: AbstractVector of samples for probability density estimation # Keyword arguments - - `trim`::Bool: enables cutting off the distribution tails. - `bandwidth`::Number: a low bandwidth induces under-smoothing, whilst a high bandwidth induces over-smoothing. @@ -145,6 +148,9 @@ julia> density(randn(100_000)) Plot a heatmap of the rectangular array `z`. +# Keyword arguments +- $(_document_argument("aspect_ratio")) + # Example ```julia-repl julia> heatmap(randn(10,10)) @@ -158,7 +164,7 @@ julia> heatmap(randn(10,10)) hexbin!(x,y) Make a hexagonal binning plot (a histogram of the observations `(x[i],y[i])` -with hexagonal bins) +with hexagonal bins). # Example ```julia-repl @@ -171,7 +177,11 @@ julia> hexbin(randn(10_000), randn(10_000)) sticks(x,y) sticks!(x,y) -Draw a stick plot of y vs x. +Draw a stick plot of `y` vs `x`. + +# Arguments +- $(_document_argument("fillrange")) +- $(_document_argument("markershape")) # Example ```julia-repl @@ -185,7 +195,7 @@ julia> sticks(1:10) hline!(y) Draw horizontal lines at positions specified by the values in -the AbstractVector `y` +the AbstractVector `y`. # Example ```julia-repl @@ -199,7 +209,7 @@ julia> hline([-1,0,2]) vline!(x) Draw vertical lines at positions specified by the values in -the AbstractVector `x` +the AbstractVector `x`. # Example ```julia-repl @@ -216,6 +226,7 @@ and the horizontal line at position `y[2]`. If `length(y) ≥ 4`, then further rectangles are drawn between `y[3]` and `y[4]`, `y[5]` and `y[6]`, and so on. If `length(y)` is odd, then the last entry of `y` is ignored. + # Example ```julia-repl julia> hspan(1:6) @@ -231,6 +242,7 @@ and the vertical line at position `x[2]`. If `length(x) ≥ 4`, then further rectangles are drawn between `x[3]` and `x[4]`, `x[5]` and `x[6]`, and so on. If `length(x)` is odd, then the last entry of `x` is ignored. + # Example ```julia-repl julia> vspan(1:6) @@ -259,7 +271,7 @@ julia> ohlc(y) contour(x,y,z) contour!(x,y,z) -Draw contour lines of the `Surface` z. +Draw contour lines of the surface `z`. # Arguments - `levels`: Contour levels (if `AbstractVector`) or number of levels (if `Integer`) @@ -395,6 +407,9 @@ julia> violin(repeat([1,2,3],outer=100),randn(300)) Make a quiver (vector field) plot. The `i`th vector extends from `(x[i],y[i])` to `(x[i] + u[i], y[i] + v[i])`. +# Keyword arguments +- $(_document_argument("arrow")) + # Example ```julia-repl julia> quiver([1,2,3],[3,2,1],quiver=([1,1,1],[1,2,3])) @@ -407,7 +422,10 @@ julia> quiver([1,2,3],[3,2,1],quiver=([1,1,1],[1,2,3])) curves!(x,y) Draw a Bezier curve from `(x[1],y[1])` to `(x[end],y[end])` -with control points `(x[2],y[2]), ..., (x[end-1],y[end]-1)` +with control points `(x[2],y[2]), ..., (x[end-1],y[end]-1)`. + +# Keyword arguments +- $(_document_argument("fillrange")) # Example ```julia-repl @@ -416,7 +434,18 @@ julia> curves([1,2,3,4],[1,1,2,4]) """ @shorthands curves -"Plot a pie diagram" +""" + pie(x, y) + +Plot a pie diagram. + +# Example +```julia-repl +x = ["Nerds","Hackers","Scientists"] +y = [0.4,0.35,0.25] +pie(x, y, title="The Julia Community") +``` +""" @shorthands pie "Plot with seriestype :path3d" From 378d4c1784acb1f126aa58d391f76e8cfe6dfaaa Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 9 Nov 2022 10:02:28 +0100 Subject: [PATCH 10/10] enhance `floor_base` definition --- src/utils.jl | 10 ++++++++-- test/test_misc.jl | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 6e93f2173..095d512e2 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -69,8 +69,14 @@ function iter_segments(args...) NaNSegmentsIterator(tup, n1, n2) end -"floor number x in base b, since `Base.floor(x; base=b)` only supports integers" -floor_base(x::T, b) where {T} = T(b^floor(log(b, x))) +"floor number x in base b, note this is different from using Base.round(...; base=b) !" +floor_base(x, b) = round_base(x, b, RoundDown) + +"ceil number x in base b" +ceil_base(x, b) = round_base(x, b, RoundUp) + +round_base(x::T, b, ::RoundingMode{:Down}) where {T} = T(b^floor(log(b, x))) +round_base(x::T, b, ::RoundingMode{:Up}) where {T} = T(b^ceil(log(b, x))) ignorenan_min_max(::Any, ex) = ex function ignorenan_min_max(x::AbstractArray{<:AbstractFloat}, ex::Tuple) diff --git a/test/test_misc.jl b/test/test_misc.jl index e18e5414c..11c26a20b 100644 --- a/test/test_misc.jl +++ b/test/test_misc.jl @@ -69,6 +69,15 @@ end @test showtheme(:dark) isa Plot end +@testset "maths" begin + @test Plots.floor_base(15.0, 10.0) ≈ 10 + @test Plots.ceil_base(15.0, 10.0) ≈ 10^2 + @test Plots.floor_base(4.2, 2.0) ≈ 2^2 + @test Plots.ceil_base(4.2, 2.0) ≈ 2^3 + @test Plots.floor_base(1.5 * ℯ, ℯ) ≈ ℯ + @test Plots.ceil_base(1.5 * ℯ, ℯ) ≈ ℯ^2 +end + @testset "plotattr" begin tmp = tempname() open(tmp, "w") do io