diff --git a/src/Plots.jl b/src/Plots.jl index d320df7c..2e504051 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -215,8 +215,8 @@ xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...) xaxis!(args...; kw...) = plot!(; xaxis = args, kw...) yaxis!(args...; kw...) = plot!(; yaxis = args, kw...) -xgrid!(grid::Bool = true; kw...) = plot!(; xgrid = grid, kw...) -ygrid!(grid::Bool = true; kw...) = plot!(; ygrid = grid, kw...) +xgrid!(args...; kw...) = plot!(; xgrid = args, kw...) +ygrid!(args...; kw...) = plot!(; ygrid = args, kw...) let PlotOrSubplot = Union{Plot, Subplot} title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...) diff --git a/src/arg_desc.jl b/src/arg_desc.jl index 571400a7..ee1e99d8 100644 --- a/src/arg_desc.jl +++ b/src/arg_desc.jl @@ -76,7 +76,6 @@ const _arg_desc = KW( :background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).", :foreground_color_subplot => "Color Type or `:match` (matches `:foreground_color`). Base foreground color of the subplot.", :foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.", -:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.", :foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.", :color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.", :legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)", @@ -110,6 +109,9 @@ const _arg_desc = KW( :foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.", :foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).", :mirror => "Bool. Switch the side of the tick labels (right or top).", -:grid => "Bool. Show the grid lines?", - +:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`", +:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.", +:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.", +:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)", +:gridlinewidth => "Number. Width of the grid lines (in pixels)", ) diff --git a/src/args.jl b/src/args.jl index 0012bc7e..01cafb51 100644 --- a/src/args.jl +++ b/src/args.jl @@ -163,6 +163,24 @@ const _scaleAliases = Dict{Symbol,Symbol}( :log => :log10, ) +const _allGridSyms = [:x, :y, :z, + :xy, :xz, :yx, :yz, :zx, :zy, + :xyz, :xzy, :yxz, :yzx, :zxy, :zyx, + :all, :both, :on, + :none, :off,] +const _allGridArgs = [_allGridSyms; string.(_allGridSyms); nothing; false; true] +hasgrid(arg::Void, letter) = false +hasgrid(arg::Bool, letter) = arg +function hasgrid(arg::Symbol, letter) + if arg in _allGridSyms + arg in (:all, :both, :on) || contains(string(arg), string(letter)) + else + warn("Unknown grid argument $arg; $letter\grid was set to `true` instead") + true + end +end +hasgrid(arg::AbstractString, letter) = hasgrid(Symbol(arg), letter) + # ----------------------------------------------------------------------------- const _series_defaults = KW( @@ -285,6 +303,9 @@ const _axis_defaults = KW( :mirror => false, :grid => true, :foreground_color_grid => :match, # grid color + :gridalpha => 0.3, + :gridstyle => :dot, + :gridlinewidth => 1, ) const _suppress_warnings = Set{Symbol}([ @@ -414,6 +435,7 @@ add_aliases(:linealpha, :la, :lalpha, :lα, :lineopacity, :lopacity) add_aliases(:markeralpha, :ma, :malpha, :mα, :markeropacity, :mopacity) add_aliases(:markerstrokealpha, :msa, :msalpha, :msα, :markerstrokeopacity, :msopacity) add_aliases(:fillalpha, :fa, :falpha, :fα, :fillopacity, :fopacity) +add_aliases(:gridalpha, :ga, :galpha, :gα, :gridopacity, :gopacity) # series attributes add_aliases(:seriestype, :st, :t, :typ, :linetype, :lt) @@ -469,6 +491,8 @@ add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesa add_aliases(:html_output_format, :format, :fmt, :html_format) add_aliases(:orientation, :direction, :dir) add_aliases(:inset_subplots, :inset, :floating) +add_aliases(:gridlinewidth, :gridwidth, :grid_linewidth, :grid_width, :gridlw, :grid_lw) +add_aliases(:gridstyle, :grid_style, :gridlinestyle, :grid_linestyle, :grid_ls, :gridls) # add all pluralized forms to the _keyAliases dict @@ -645,6 +669,36 @@ function processFillArg(d::KW, arg) return end + +function processGridArg!(d::KW, arg, letter) + if arg in _allGridArgs + d[Symbol(letter, :grid)] = hasgrid(arg, letter) + + elseif allStyles(arg) + d[Symbol(letter, :gridstyle)] = arg + + elseif typeof(arg) <: Stroke + arg.width == nothing || (d[Symbol(letter, :gridlinewidth)] = arg.width) + arg.color == nothing || (d[Symbol(letter, :foreground_color_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color)) + arg.alpha == nothing || (d[Symbol(letter, :gridalpha)] = arg.alpha) + arg.style == nothing || (d[Symbol(letter, :gridstyle)] = arg.style) + + # linealpha + elseif allAlphas(arg) + d[Symbol(letter, :gridalpha)] = arg + + # linewidth + elseif allReals(arg) + d[Symbol(letter, :gridlinewidth)] = arg + + # color +elseif !handleColors!(d, arg, Symbol(letter, :foreground_color_grid)) + warn("Skipped grid arg $arg.") + + end +end + + _replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape) _replace_markershape(shapes::AVec) = map(_replace_markershape, shapes) _replace_markershape(shape) = shape @@ -688,6 +742,22 @@ function preprocessArgs!(d::KW) end end + # handle grid args common to all axes + args = pop!(d, :grid, ()) + for arg in wraptuple(args) + for letter in (:x, :y, :z) + processGridArg!(d, arg, letter) + end + end + # handle individual axes grid args + for letter in (:x, :y, :z) + gridsym = Symbol(letter, :grid) + args = pop!(d, gridsym, ()) + for arg in wraptuple(args) + processGridArg!(d, arg, letter) + end + end + # handle line args for arg in wraptuple(pop!(d, :line, ())) processLineArg(d, arg) diff --git a/src/axes.jl b/src/axes.jl index cafee577..734f2154 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -506,7 +506,7 @@ function axis_drawing_info(sp::Subplot) for xtick in xticks[1] push!(xspine_segs, (xtick, ymin), (xtick, t1)) # bottom tick # push!(xspine_segs, (xtick, ymax), (xtick, t2)) # top tick - !(xaxis[:grid] in (nothing, false)) && push!(xgrid_segs, (xtick, t1), (xtick, t2)) # vertical grid + xaxis[:grid] && push!(xgrid_segs, (xtick, t1), (xtick, t2)) # vertical grid end end @@ -521,7 +521,7 @@ function axis_drawing_info(sp::Subplot) for ytick in yticks[1] push!(yspine_segs, (xmin, ytick), (t1, ytick)) # left tick # push!(yspine_segs, (xmax, ytick), (t2, ytick)) # right tick - !(yaxis[:grid] in (nothing, false)) && push!(ygrid_segs, (t1, ytick), (t2, ytick)) # horizontal grid + yaxis[:grid] && push!(ygrid_segs, (t1, ytick), (t2, ytick)) # horizontal grid end end diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index 118db197..60c1730a 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -24,7 +24,8 @@ const _glvisualize_attr = merge_with_base_supported([ :window_title, :guide, :lims, :ticks, :scale, :flip, :rotation, :tickfont, :guidefont, :legendfont, - :grid, :legend, :colorbar, + :grid, :gridalpha, :gridstyle, :gridlinewidth, + :legend, :colorbar, :marker_z, :line_z, :levels, @@ -682,12 +683,12 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are xgc = Colors.color(Plots.gl_color(xaxis[:foreground_color_grid])) ygc = Colors.color(Plots.gl_color(yaxis[:foreground_color_grid])) axis_vis = [] - if !(xaxis[:grid] in (nothing, false)) - grid = draw_grid_lines(sp, xgrid_segs, 1f0, :dot, model, RGBA(xgc, 0.3f0)) + if xaxis[:grid] + grid = draw_grid_lines(sp, xgrid_segs, xaxis[:gridlinewidth], xaxis[:gridstyle], model, RGBA(xgc, xaxis[:gridalpha])) push!(axis_vis, grid) end - if !(yaxis[:grid] in (nothing, false)) - grid = draw_grid_lines(sp, ygrid_segs, 1f0, :dot, model, RGBA(ygc, 0.3f0)) + if yaxis[:grid] + grid = draw_grid_lines(sp, ygrid_segs, yaxis[:gridlinewidth], yaxis[:gridstyle], model, RGBA(ygc, yaxis[:gridalpha])) push!(axis_vis, grid) end diff --git a/src/backends/gr.jl b/src/backends/gr.jl index a0a9171d..061dee7f 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -20,7 +20,8 @@ const _gr_attr = merge_with_base_supported([ :title, :window_title, :guide, :lims, :ticks, :scale, :flip, :tickfont, :guidefont, :legendfont, - :grid, :legend, :legendtitle, :colorbar, + :grid, :gridalpha, :gridstyle, :gridlinewidth, + :legend, :legendtitle, :colorbar, :marker_z, :levels, :ribbon, :quiver, :orientation, @@ -691,15 +692,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) ticksize = 0.01 * (viewport_plotarea[2] - viewport_plotarea[1]) # GR.setlinetype(GR.LINETYPE_DOTTED) - if !(xaxis[:grid] in (nothing, false)) - GR.grid3d(xtick, 0, 0, xmin, ymax, zmin, 2, 0, 0) - end - if !(yaxis[:grid] in (nothing, false)) - GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0) - end - if !(zaxis[:grid] in (nothing, false)) - GR.grid3d(0, 0, ztick, xmin, ymax, zmin, 0, 0, 2) - end + xaxis[:grid] && GR.grid3d(xtick, 0, 0, xmin, ymax, zmin, 2, 0, 0) + yaxis[:grid] && GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0) + zaxis[:grid] && GR.grid3d(0, 0, ztick, xmin, ymax, zmin, 0, 0, 2) GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize) GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize) @@ -718,16 +713,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) # @show xticks yticks #spine_segs grid_segs # draw the grid lines - if !(xaxis[:grid] in (nothing, false)) + if xaxis[:grid] # gr_set_linecolor(sp[:foreground_color_grid]) # GR.grid(xtick, ytick, 0, 0, majorx, majory) - gr_set_line(1, :dot, xaxis[:foreground_color_grid]) - GR.settransparency(0.5) + gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid]) + GR.settransparency(xaxis[:gridalpha]) gr_polyline(coords(xgrid_segs)...) end - if !(yaxis[:grid] in (nothing, false)) - gr_set_line(1, :dot, yaxis[:foreground_color_grid]) - GR.settransparency(0.5) + if yaxis[:grid] + gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid]) + GR.settransparency(yaxis[:gridalpha]) gr_polyline(coords(ygrid_segs)...) end GR.settransparency(1.0) diff --git a/src/backends/inspectdr.jl b/src/backends/inspectdr.jl index 1feda4ba..a8224634 100644 --- a/src/backends/inspectdr.jl +++ b/src/backends/inspectdr.jl @@ -339,8 +339,8 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend}) const strip = plot.strips[1] #Only 1 strip supported with Plots.jl xaxis = sp[:xaxis]; yaxis = sp[:yaxis] - xgrid_show = !(xaxis[:grid] in (nothing, false)) - ygrid_show = !(yaxis[:grid] in (nothing, false)) + xgrid_show = xaxis[:grid] + ygrid_show = yaxis[:grid] strip.grid = InspectDR.GridRect( vmajor=xgrid_show, # vminor=xgrid_show, diff --git a/src/backends/pgfplots.jl b/src/backends/pgfplots.jl index cbbd645b..30169147 100644 --- a/src/backends/pgfplots.jl +++ b/src/backends/pgfplots.jl @@ -267,7 +267,7 @@ function pgf_axis(sp::Subplot, letter) end # grid on or off - if !(axis[:grid] in (nothing, false)) + if axis[:grid] push!(style, "$(letter)majorgrids = true") end diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index cc0aed9c..9e0537ce 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -5,7 +5,7 @@ const _plotly_attr = merge_with_base_supported([ :annotations, :background_color_legend, :background_color_inside, :background_color_outside, :foreground_color_legend, :foreground_color_guide, - # :foreground_color_grid, :foreground_color_axis, + :foreground_color_grid, :foreground_color_axis, :foreground_color_text, :foreground_color_border, :foreground_color_title, :label, @@ -19,7 +19,8 @@ const _plotly_attr = merge_with_base_supported([ :window_title, :guide, :lims, :ticks, :scale, :flip, :rotation, :tickfont, :guidefont, :legendfont, - :grid, :legend, :colorbar, :colorbar_title, + :grid, :gridalpha, :gridlinewidth, + :legend, :colorbar, :colorbar_title, :marker_z, :fill_z, :levels, :ribbon, :quiver, :orientation, @@ -213,7 +214,9 @@ function plotly_axis(axis::Axis, sp::Subplot) letter = axis[:letter] ax = KW( :title => axis[:guide], - :showgrid => !(axis[:grid] in (nothing, false)), + :showgrid => axis[:grid], + :gridcolor => rgba_string(plot_color(axis[:foreground_color_grid], axis[:gridalpha])), + :gridwidth => axis[:gridlinewidth], :zeroline => false, :ticks => "inside", ) @@ -229,8 +232,8 @@ function plotly_axis(axis::Axis, sp::Subplot) ax[:titlefont] = plotly_font(axis[:guidefont], axis[:foreground_color_guide]) ax[:type] = plotly_scale(axis[:scale]) ax[:tickfont] = plotly_font(axis[:tickfont], axis[:foreground_color_text]) - ax[:tickcolor] = rgba_string(axis[:foreground_color_border]) - ax[:linecolor] = rgba_string(axis[:foreground_color_border]) + ax[:tickcolor] = rgba_string(axis[:foreground_color_axis]) + ax[:linecolor] = rgba_string(axis[:foreground_color_axis]) # lims lims = axis[:lims] diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 4100ccbf..4b185e6e 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -17,7 +17,8 @@ const _pyplot_attr = merge_with_base_supported([ :window_title, :guide, :lims, :ticks, :scale, :flip, :rotation, :tickfont, :guidefont, :legendfont, - :grid, :legend, :legendtitle, :colorbar, + :grid, :gridalpha, :gridstyle, :gridlinewidth, + :legend, :legendtitle, :colorbar, :marker_z, :line_z, :fill_z, :levels, :ribbon, :quiver, :arrow, @@ -1064,9 +1065,13 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) lab[:set_family](axis[:tickfont].family) lab[:set_rotation](axis[:rotation]) end - if !(axis[:grid] in (nothing, false)) + if axis[:grid] fgcolor = py_color(axis[:foreground_color_grid]) - pyaxis[:grid](true, color = fgcolor, linestyle = ":") + pyaxis[:grid](true, + color = fgcolor, + linestyle = py_linestyle(:line, axis[:gridstyle]), + linewidth = axis[:gridlinewidth], + alpha = axis[:gridalpha]) ax[:set_axisbelow](true) end py_set_axis_colors(ax, axis) diff --git a/src/examples.jl b/src/examples.jl index a9b86960..640dde92 100644 --- a/src/examples.jl +++ b/src/examples.jl @@ -329,6 +329,18 @@ PlotExample("Spy", end)] ), +PlotExample("Magic grid argument", + "The grid lines can be modified individually for each axis with the magic grid argument", + [:(begin + x = rand(10) + p1 = plot(x, title = "Default looks") + p2 = plot(x, grid = (:y, :olivedrab, :solid, 0.5), title = "Modified y grid") + p3 = plot(deepcopy(p2), title = "Add x grid") + xgrid!(p3, :on, :cadetblue, 2, :dashdot) + plot(p1, p2, p3, layout = (1, 3), label = "", fillrange = 0, fillalpha = 0.3) + end)] +), + ] # ---------------------------------------------------------------------------------