diff --git a/src/arg_desc.jl b/src/arg_desc.jl index ee1e99d8..2aad42a5 100644 --- a/src/arg_desc.jl +++ b/src/arg_desc.jl @@ -93,6 +93,7 @@ const _arg_desc = KW( :bottom_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the bottom of the subplot.", :subplot_index => "Integer. Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list.", :colorbar_title => "String. Title of colorbar.", +:framestyle => "Symbol. Style of the axes frame. Choose from $(_allFramestyles)", # axis args :guide => "String. Axis guide (label).", diff --git a/src/args.jl b/src/args.jl index 37dd250e..5f9e320d 100644 --- a/src/args.jl +++ b/src/args.jl @@ -181,6 +181,14 @@ function hasgrid(arg::Symbol, letter) end hasgrid(arg::AbstractString, letter) = hasgrid(Symbol(arg), letter) +const _allFramestyles = [:box, :semi, :axes, :grid, :none] +const _framestyleAliases = Dict{Symbol, Symbol}( + :frame => :box, + :border => :box, + :on => :box, + :transparent => :semi, + :semitransparent => :semi, +) # ----------------------------------------------------------------------------- const _series_defaults = KW( @@ -282,6 +290,7 @@ const _subplot_defaults = KW( :bottom_margin => :match, :subplot_index => -1, :colorbar_title => "", + :framestyle => :axes, ) const _axis_defaults = KW( @@ -423,7 +432,7 @@ add_aliases(:foreground_color_title, :fg_title, :fgtitle, :fgcolor_title, :fg_co add_aliases(:foreground_color_axis, :fg_axis, :fgaxis, :fgcolor_axis, :fg_color_axis, :foreground_axis, :foreground_colour_axis, :fgcolour_axis, :fg_colour_axis, :axiscolor) add_aliases(:foreground_color_border, :fg_border, :fgborder, :fgcolor_border, :fg_color_border, :foreground_border, - :foreground_colour_border, :fgcolour_border, :fg_colour_border, :bordercolor, :border) + :foreground_colour_border, :fgcolour_border, :fg_colour_border, :bordercolor) add_aliases(:foreground_color_text, :fg_text, :fgtext, :fgcolor_text, :fg_color_text, :foreground_text, :foreground_colour_text, :fgcolour_text, :fg_colour_text, :textcolor) add_aliases(:foreground_color_guide, :fg_guide, :fgguide, :fgcolor_guide, :fg_color_guide, :foreground_guide, @@ -493,6 +502,7 @@ 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_aliases(:framestyle, :frame_style, :frame, :axesstyle, :axes_style, :boxstyle, :box_style, :box, :borderstyle, :border_style, :border) # add all pluralized forms to the _keyAliases dict @@ -719,6 +729,7 @@ function preprocessArgs!(d::KW) if haskey(d, :axis) && d[:axis] in (:none, nothing, false) d[:ticks] = nothing d[:foreground_color_border] = RGBA(0,0,0,0) + d[:foreground_color_axis] = RGBA(0,0,0,0) d[:grid] = false delete!(d, :axis) end @@ -821,6 +832,11 @@ function preprocessArgs!(d::KW) d[:colorbar] = convertLegendValue(d[:colorbar]) end + # framestyle + if haskey(d, :framestyle) && haskey(_framestyleAliases, d[:framestyle]) + d[:framestyle] = _framestyleAliases[d[:framestyle]] + end + # warnings for moved recipes st = get(d, :seriestype, :path) if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatPlots) diff --git a/src/axes.jl b/src/axes.jl index 66409529..6ce1532c 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -506,40 +506,46 @@ function axis_drawing_info(sp::Subplot) ymin, ymax = axis_limits(yaxis) xticks = get_ticks(xaxis) yticks = get_ticks(yaxis) - xspine_segs = Segments(2) - yspine_segs = Segments(2) + xaxis_segs = Segments(2) + yaxis_segs = Segments(2) xgrid_segs = Segments(2) ygrid_segs = Segments(2) + xborder_segs = Segments(2) + yborder_segs = Segments(2) - if !(xaxis[:ticks] in (nothing, false)) - f = scalefunc(yaxis[:scale]) - invf = invscalefunc(yaxis[:scale]) - t1 = invf(f(ymin) + 0.015*(f(ymax)-f(ymin))) - t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin))) + if !(sp[:framestyle] == :none) + # xaxis + sp[:framestyle] == :grid || push!(xaxis_segs, (xmin,ymin), (xmax,ymin)) # bottom spine / xaxis + sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin,ymax), (xmax,ymax)) # top spine + if !(xaxis[:ticks] in (nothing, false)) + f = scalefunc(yaxis[:scale]) + invf = invscalefunc(yaxis[:scale]) + t1 = invf(f(ymin) + 0.015*(f(ymax)-f(ymin))) + t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin))) - push!(xspine_segs, (xmin,ymin), (xmax,ymin)) # bottom spine - # push!(xspine_segs, (xmin,ymax), (xmax,ymax)) # top spine - 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] && push!(xgrid_segs, (xtick, t1), (xtick, t2)) # vertical grid + for xtick in xticks[1] + push!(xaxis_segs, (xtick, ymin), (xtick, t1)) # bottom tick + # sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick + xaxis[:grid] && push!(xgrid_segs, (xtick, t1), (xtick, t2)) # vertical grid + end + end + + # yaxis + sp[:framestyle] == :grid || push!(yaxis_segs, (xmin,ymin), (xmin,ymax)) # left spine / yaxis + sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (xmax,ymin), (xmax,ymax)) # right spine + if !(yaxis[:ticks] in (nothing, false)) + f = scalefunc(xaxis[:scale]) + invf = invscalefunc(xaxis[:scale]) + t1 = invf(f(xmin) + 0.015*(f(xmax)-f(xmin))) + t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin))) + + for ytick in yticks[1] + push!(yaxis_segs, (xmin, ytick), (t1, ytick)) # left tick + # sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick + yaxis[:grid] && push!(ygrid_segs, (t1, ytick), (t2, ytick)) # horizontal grid + end end end - if !(yaxis[:ticks] in (nothing, false)) - f = scalefunc(xaxis[:scale]) - invf = invscalefunc(xaxis[:scale]) - t1 = invf(f(xmin) + 0.015*(f(xmax)-f(xmin))) - t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin))) - - push!(yspine_segs, (xmin,ymin), (xmin,ymax)) # left spine - # push!(yspine_segs, (xmax,ymin), (xmax,ymax)) # right spine - 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] && push!(ygrid_segs, (t1, ytick), (t2, ytick)) # horizontal grid - end - end - - xticks, yticks, xspine_segs, yspine_segs, xgrid_segs, ygrid_segs + xticks, yticks, xaxis_segs, yaxis_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs end diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index 60c1730a..974b73b1 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -39,7 +39,8 @@ const _glvisualize_attr = merge_with_base_supported([ :clims, :inset_subplots, :dpi, - :hover + :hover, + :framestyle, ]) const _glvisualize_seriestype = [ :path, :shape, @@ -677,7 +678,7 @@ function text_model(font, pivot) end end function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, area) - xticks, yticks, xspine_segs, yspine_segs, xgrid_segs, ygrid_segs = Plots.axis_drawing_info(sp) + xticks, yticks, xspine_segs, yspine_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs = Plots.axis_drawing_info(sp) xaxis = sp[:xaxis]; yaxis = sp[:yaxis] xgc = Colors.color(Plots.gl_color(xaxis[:foreground_color_grid])) @@ -707,7 +708,7 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are xlim = Plots.axis_limits(xaxis) ylim = Plots.axis_limits(yaxis) - if !(xaxis[:ticks] in (nothing, false, :none)) + if !(xaxis[:ticks] in (nothing, false, :none)) && !(sp[:framestyle] == :none) ticklabels = map(model) do m mirror = xaxis[:mirror] t, positions, offsets = draw_ticks(xaxis, xticks, true, ylim, m) @@ -727,6 +728,15 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are push!(axis_vis, visualize(map(first, ticklabels), Style(:default), kw_args)) end + xbc = Colors.color(Plots.gl_color(xaxis[:foreground_color_border])) + ybc = Colors.color(Plots.gl_color(yaxis[:foreground_color_border])) + intensity = sp[:framestyle] == :semi ? 0.5f0 : 1.0f0 + if sp[:framestyle] in (:box, :semi) + xborder = draw_grid_lines(sp, xborder_segs, intensity, :solid, model, RGBA(xbc, intensity)) + yborder = draw_grid_lines(sp, yborder_segs, intensity, :solid, model, RGBA(ybc, intensity)) + push!(axis_vis, xborder, yborder) + end + area_w = GeometryTypes.widths(area) if sp[:title] != "" tf = sp[:titlefont]; color = gl_color(sp[:foreground_color_title]) diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 515aaa7e..092b00d3 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -32,6 +32,7 @@ const _gr_attr = merge_with_base_supported([ :inset_subplots, :bar_width, :arrow, + :framestyle, ]) const _gr_seriestype = [ :path, :scatter, @@ -653,7 +654,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) # TODO: can these be generic flags? outside_ticks = false cmap = false - draw_axes = true + draw_axes = sp[:framestyle] != :none # axes_2d = true for series in series_list(sp) st = series[:seriestype] @@ -755,7 +756,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) GR.setwindow(xmin, xmax, ymin, ymax) end - xticks, yticks, xspine_segs, yspine_segs, xgrid_segs, ygrid_segs = axis_drawing_info(sp) + xticks, yticks, xspine_segs, yspine_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs = axis_drawing_info(sp) # @show xticks yticks #spine_segs grid_segs # draw the grid lines @@ -773,7 +774,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) end GR.settransparency(1.0) - # spine (border) and tick marks + # axis lines gr_set_line(1, :solid, xaxis[:foreground_color_axis]) GR.setclip(0) gr_polyline(coords(xspine_segs)...) @@ -782,7 +783,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) gr_polyline(coords(yspine_segs)...) GR.setclip(1) - if !(xticks in (nothing, false)) + # tick marks + if !(xticks in (:none, nothing, false)) # x labels flip, mirror = gr_set_xticks_font(sp) for (cv, dv) in zip(xticks...) @@ -793,7 +795,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) end end - if !(yticks in (nothing, false)) + if !(yticks in (:none, nothing, false)) # y labels flip, mirror = gr_set_yticks_font(sp) for (cv, dv) in zip(yticks...) @@ -803,6 +805,17 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) gr_text(xi + (mirror ? 1 : -1) * 1e-2, yi, string(dv)) end end + + # border + intensity = sp[:framestyle] == :semi ? 0.5 : 1.0 + if sp[:framestyle] in (:box, :semi) + gr_set_line(intensity, :solid, xaxis[:foreground_color_border]) + GR.settransparency(intensity) + gr_polyline(coords(xborder_segs)...) + gr_set_line(intensity, :solid, yaxis[:foreground_color_border]) + GR.settransparency(intensity) + gr_polyline(coords(yborder_segs)...) + end end # end diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 4b185e6e..80a7f77c 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -32,6 +32,7 @@ const _pyplot_attr = merge_with_base_supported([ :inset_subplots, :dpi, :colorbar_title, + :framestyle, ]) const _pyplot_seriestype = [ :path, :steppre, :steppost, :shape, @@ -1053,7 +1054,8 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) end py_set_scale(ax, axis) py_set_lims(ax, axis) - py_set_ticks(ax, get_ticks(axis), letter) + ticks = sp[:framestyle] == :none ? nothing : get_ticks(axis) + py_set_ticks(ax, ticks, letter) ax[Symbol("set_", letter, "label")](axis[:guide]) if get(axis.d, :flip, false) ax[Symbol("invert_", letter, "axis")]() @@ -1065,7 +1067,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) lab[:set_family](axis[:tickfont].family) lab[:set_rotation](axis[:rotation]) end - if axis[:grid] + if axis[:grid] && !(ticks in (:none, nothing, false)) fgcolor = py_color(axis[:foreground_color_grid]) pyaxis[:grid](true, color = fgcolor, @@ -1088,6 +1090,24 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) # this sets the bg color inside the grid ax[set_facecolor_sym](py_color(sp[:background_color_inside])) + + # framestyle + if !ispolar(sp) && !is3d(sp) + if sp[:framestyle] == :semi + intensity = 0.5 + ax[:spines]["right"][:set_alpha](intensity) + ax[:spines]["top"][:set_alpha](intensity) + ax[:spines]["right"][:set_linewidth](intensity) + ax[:spines]["top"][:set_linewidth](intensity) + elseif sp[:framestyle] == :axes + ax[:spines]["right"][:set_visible](false) + ax[:spines]["top"][:set_visible](false) + elseif sp[:framestyle] in (:grid, :none) + for (loc, spine) in ax[:spines] + spine[:set_visible](false) + end + end + end end py_drawfig(fig) end diff --git a/src/examples.jl b/src/examples.jl index 8ae108ef..d1775f47 100644 --- a/src/examples.jl +++ b/src/examples.jl @@ -155,7 +155,7 @@ PlotExample("Subplots", """, [:(begin l = @layout([a{0.1h}; b [c;d e]]) - plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false, ticks=nothing, border=false) + plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false, ticks=nothing, border=:none) end)] ), @@ -330,7 +330,7 @@ PlotExample("Spy", ), PlotExample("Magic grid argument", - "The grid lines can be modified individually for each axis with the 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") @@ -341,6 +341,14 @@ PlotExample("Magic grid argument", end)] ), +PlotExample("Framestyle", + "The style of the frame/axes of a (sub)plot can be changed with the `framestyle` attribute. The default framestyle is `:axes`.", + [:(begin + histogram(fill(randn(1000), 5), framestyle = [:box :semi :axes :grid :none], + title = [":box" ":semi" ":axes" ":grid" ":none"], color = RowVector(1:5), layout = 5, label = "") + end)] +), + ] # --------------------------------------------------------------------------------- @@ -365,7 +373,7 @@ test_examples(pkgname[, idx]; debug = false, disp = true, sleep = nothing, skip = [], only = nothing Run the `idx` test example for a given backend, or all examples if `idx` -is not specified. +is not specified. """ function test_examples(pkgname::Symbol; debug = false, disp = true, sleep = nothing, skip = [], only = nothing)