From dd434b89d2cc5105bbd2e74786e2cf47e72098a3 Mon Sep 17 00:00:00 2001 From: Will Grant Date: Wed, 11 Jul 2018 17:17:45 +1000 Subject: [PATCH 1/3] add minor grid and tick marks. Works, but doesn't extend beyond major ticks --- src/arg_desc.jl | 2 ++ src/args.jl | 2 ++ src/axes.jl | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/src/arg_desc.jl b/src/arg_desc.jl index 56e3719d..54c3ed48 100644 --- a/src/arg_desc.jl +++ b/src/arg_desc.jl @@ -139,6 +139,8 @@ const _arg_desc = KW( :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)", +:minorgrid => "Bool. Add minor grid lines in the same style as the major grid lines. Set minorticks to change number of gridlines", +:minorticks => "Integer. Intervals to divide the gap between major ticks into", :tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`", :showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`", :widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.", diff --git a/src/args.jl b/src/args.jl index 671485d0..69bdb8f4 100644 --- a/src/args.jl +++ b/src/args.jl @@ -381,6 +381,8 @@ const _axis_defaults = KW( :gridstyle => :solid, :gridlinewidth => 0.5, :tick_direction => :in, + :minorticks => false, + :minorgrid => false, :showaxis => true, :widen => true, ) diff --git a/src/axes.jl b/src/axes.jl index 1410b28b..12e989a1 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -286,6 +286,19 @@ _transform_ticks(ticks) = ticks _transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks) _transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2]) +function get_minor_ticks(axis,ticks) + axis[:minorticks] in (nothing, false) && !axis[:minorgrid] && return nothing + length(ticks[1]) < 2 && return nothing + amin, amax = axis_limits(axis) + #Default to 5 intervals between major ticks + n = typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] : 5 + minorticks = typeof(ticks[1][1])[] + for (i,hi) in enumerate(ticks[1][2:end]) + lo = ticks[1][i] + append!(minorticks,collect(lo:(hi-lo)/n:hi)) + end + minorticks +end # ------------------------------------------------------------------------- @@ -562,12 +575,16 @@ function axis_drawing_info(sp::Subplot) ymin, ymax = axis_limits(yaxis) xticks = get_ticks(xaxis) yticks = get_ticks(yaxis) + xminorticks = get_minor_ticks(xaxis,xticks) + yminorticks = get_minor_ticks(yaxis,yticks) xaxis_segs = Segments(2) yaxis_segs = Segments(2) xtick_segs = Segments(2) ytick_segs = Segments(2) xgrid_segs = Segments(2) ygrid_segs = Segments(2) + xminorgrid_segs = Segments(2) + yminorgrid_segs = Segments(2) xborder_segs = Segments(2) yborder_segs = Segments(2) @@ -610,6 +627,28 @@ function axis_drawing_info(sp::Subplot) xaxis[:grid] && push!(xgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid end end + if !(xaxis[:minorticks] in (nothing, false)) || xaxis[:minorgrid] + f = scalefunc(yaxis[:scale]) + invf = invscalefunc(yaxis[:scale]) + ticks_in = xaxis[:tick_direction] == :out ? -1 : 1 + t1 = invf(f(ymin) + 0.01 * (f(ymax) - f(ymin)) * ticks_in) + t2 = invf(f(ymax) - 0.01 * (f(ymax) - f(ymin)) * ticks_in) + t3 = invf(f(0) + 0.01 * (f(ymax) - f(ymin)) * ticks_in) + + for xminortick in xminorticks + if xaxis[:showaxis] + tick_start, tick_stop = if sp[:framestyle] == :origin + (0, t3) + else + xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, t2) : (ymin, t1) + end + push!(xtick_segs, (xminortick, tick_start), (xminortick, tick_stop)) # bottom tick + end + # sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick + xaxis[:minorgrid] && push!(xgrid_segs, (xminortick, ymin), (xminortick, ymax)) # vertical grid + end + end + # yaxis if yaxis[:showaxis] @@ -649,6 +688,27 @@ function axis_drawing_info(sp::Subplot) yaxis[:grid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid end end + if !(yaxis[:minorticks] in (nothing, false)) || yaxis[:minorgrid] + f = scalefunc(xaxis[:scale]) + invf = invscalefunc(xaxis[:scale]) + ticks_in = yaxis[:tick_direction] == :out ? -1 : 1 + t1 = invf(f(xmin) + 0.01 * (f(xmax) - f(xmin)) * ticks_in) + t2 = invf(f(xmax) - 0.01 * (f(xmax) - f(xmin)) * ticks_in) + t3 = invf(f(0) + 0.01 * (f(xmax) - f(xmin)) * ticks_in) + + for ytick in yminorticks + if yaxis[:showaxis] + tick_start, tick_stop = if sp[:framestyle] == :origin + (0, t3) + else + xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, t2) : (xmin, t1) + end + push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick + end + # sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick + yaxis[:minorgrid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid + end + end end xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs From 403b57c0765c69f6315f9f3693b7f18838eb9991 Mon Sep 17 00:00:00 2001 From: Will Grant Date: Wed, 11 Jul 2018 17:59:47 +1000 Subject: [PATCH 2/3] extend ticks beyond major grid, to axis limits --- src/axes.jl | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/axes.jl b/src/axes.jl index 12e989a1..d1804103 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -288,16 +288,32 @@ _transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2]) function get_minor_ticks(axis,ticks) axis[:minorticks] in (nothing, false) && !axis[:minorgrid] && return nothing - length(ticks[1]) < 2 && return nothing + ticks = ticks[1] + length(ticks) < 2 && return nothing + amin, amax = axis_limits(axis) + #Add one phantom tick either side of the ticks to ensure minor ticks extend to the axis limits + if length(ticks) > 2 + ratio = (ticks[3] - ticks[2])/(ticks[2] - ticks[1]) + elseif axis[:scale] == :none + ratio = 1 + else + return nothing + end + first_step = ticks[2] - ticks[1] + last_step = ticks[end] - ticks[end-1] + ticks = [ticks[1] - first_step/ratio; ticks; ticks[end] + last_step*ratio] + #Default to 5 intervals between major ticks n = typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] : 5 - minorticks = typeof(ticks[1][1])[] - for (i,hi) in enumerate(ticks[1][2:end]) - lo = ticks[1][i] + minorticks = typeof(ticks[1])[] + for (i,hi) in enumerate(ticks[2:end]) + lo = ticks[i] append!(minorticks,collect(lo:(hi-lo)/n:hi)) end - minorticks + minorticks[amin .<= minorticks .<= amax] + #Duplicate major ticks as a hack to darken the major grid lines + #minorticks[map(x->!in(x,ticks),minorticks)] end # ------------------------------------------------------------------------- From 7eaae3a6d551e20e9f35a0463867002367e1af8a Mon Sep 17 00:00:00 2001 From: Will Grant Date: Mon, 16 Jul 2018 17:37:19 +1000 Subject: [PATCH 3/3] Add full keyword control of minor grid --- src/arg_desc.jl | 6 +++- src/args.jl | 59 ++++++++++++++++++++++++++++++++++++- src/axes.jl | 11 ++++--- src/backends/glvisualize.jl | 10 ++++++- src/backends/gr.jl | 14 ++++++++- 5 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/arg_desc.jl b/src/arg_desc.jl index 54c3ed48..06407929 100644 --- a/src/arg_desc.jl +++ b/src/arg_desc.jl @@ -139,8 +139,12 @@ const _arg_desc = KW( :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)", -:minorgrid => "Bool. Add minor grid lines in the same style as the major grid lines. Set minorticks to change number of gridlines", +:foreground_color_minor_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of minor grid lines.", +:minorgrid => "Bool. Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines", :minorticks => "Integer. Intervals to divide the gap between major ticks into", +:minorgridalpha => "Number in [0,1]. The alpha/opacity override for the minorgrid lines.", +:minorgridstyle => "Symbol. Style of the minor grid lines. Choose from $(_allStyles)", +:minorgridlinewidth => "Number. Width of the minor grid lines (in pixels)", :tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`", :showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`", :widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.", diff --git a/src/args.jl b/src/args.jl index 69bdb8f4..71e3069c 100644 --- a/src/args.jl +++ b/src/args.jl @@ -380,6 +380,10 @@ const _axis_defaults = KW( :gridalpha => 0.1, :gridstyle => :solid, :gridlinewidth => 0.5, + :foreground_color_minor_grid => :match, # grid color + :minorgridalpha => 0.05, + :minorgridstyle => :solid, + :minorgridlinewidth => 0.5, :tick_direction => :in, :minorticks => false, :minorgrid => false, @@ -500,6 +504,8 @@ add_aliases(:foreground_color_subplot, :fg_subplot, :fgsubplot, :fgcolor_subplot :foreground_colour_subplot, :fgcolour_subplot, :fg_colour_subplot) add_aliases(:foreground_color_grid, :fg_grid, :fggrid, :fgcolor_grid, :fg_color_grid, :foreground_grid, :foreground_colour_grid, :fgcolour_grid, :fg_colour_grid, :gridcolor) +add_aliases(:foreground_color_minor_grid, :fg_minor_grid, :fgminorgrid, :fgcolor_minorgrid, :fg_color_minorgrid, :foreground_minorgrid, + :foreground_colour_minor_grid, :fgcolour_minorgrid, :fg_colour_minor_grid, :minorgridcolor) add_aliases(:foreground_color_title, :fg_title, :fgtitle, :fgcolor_title, :fg_color_title, :foreground_title, :foreground_colour_title, :fgcolour_title, :fg_colour_title, :titlecolor) add_aliases(:foreground_color_axis, :fg_axis, :fgaxis, :fgcolor_axis, :fg_color_axis, :foreground_axis, @@ -576,6 +582,8 @@ add_aliases(:inset_subplots, :inset, :floating) add_aliases(:stride, :wirefame_stride, :surface_stride, :surf_str, :str) add_aliases(:gridlinewidth, :gridwidth, :grid_linewidth, :grid_width, :gridlw, :grid_lw) add_aliases(:gridstyle, :grid_style, :gridlinestyle, :grid_linestyle, :grid_ls, :gridls) +add_aliases(:minorgridlinewidth, :minorgridwidth, :minorgrid_linewidth, :minorgrid_width, :minorgridlw, :minorgrid_lw) +add_aliases(:minorgridstyle, :minorgrid_style, :minorgridlinestyle, :minorgrid_linestyle, :minorgrid_ls, :minorgridls) add_aliases(:framestyle, :frame_style, :frame, :axesstyle, :axes_style, :boxstyle, :box_style, :box, :borderstyle, :border_style, :border) add_aliases(:tick_direction, :tickdirection, :tick_dir, :tickdir, :tick_orientation, :tickorientation, :tick_or, :tickor) add_aliases(:camera, :cam, :viewangle, :view_angle) @@ -796,6 +804,39 @@ function processGridArg!(d::KW, arg, letter) end end +function processMinorGridArg!(d::KW, arg, letter) + if arg in _allGridArgs || isa(arg, Bool) + d[Symbol(letter, :minorgrid)] = hasgrid(arg, letter) + + elseif allStyles(arg) + d[Symbol(letter, :minorgridstyle)] = arg + d[Symbol(letter, :minorgrid)] = true + + elseif typeof(arg) <: Stroke + arg.width == nothing || (d[Symbol(letter, :minorgridlinewidth)] = arg.width) + arg.color == nothing || (d[Symbol(letter, :foreground_color_minor_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color)) + arg.alpha == nothing || (d[Symbol(letter, :minorgridalpha)] = arg.alpha) + arg.style == nothing || (d[Symbol(letter, :minorgridstyle)] = arg.style) + d[Symbol(letter, :minorgrid)] = true + + # linealpha + elseif allAlphas(arg) + d[Symbol(letter, :minorgridalpha)] = arg + d[Symbol(letter, :minorgrid)] = true + + # linewidth + elseif allReals(arg) + d[Symbol(letter, :minorgridlinewidth)] = arg + d[Symbol(letter, :minorgrid)] = true + + # color + elseif handleColors!(d, arg, Symbol(letter, :foreground_color_minor_grid)) + d[Symbol(letter, :minorgrid)] = true + else + warn("Skipped grid arg $arg.") + end +end + function processFontArg!(d::KW, fontname::Symbol, arg) T = typeof(arg) if T <: Font @@ -895,7 +936,21 @@ function preprocessArgs!(d::KW) processGridArg!(d, arg, letter) end end - + # handle minor grid args common to all axes + args = pop!(d, :minorgrid, ()) + for arg in wraptuple(args) + for letter in (:x, :y, :z) + processMinorGridArg!(d, arg, letter) + end + end + # handle individual axes grid args + for letter in (:x, :y, :z) + gridsym = Symbol(letter, :minorgrid) + args = pop!(d, gridsym, ()) + for arg in wraptuple(args) + processMinorGridArg!(d, arg, letter) + end + end # fonts for fontname in (:titlefont, :legendfont) args = pop!(d, fontname, ()) @@ -1223,6 +1278,7 @@ const _match_map2 = KW( :foreground_color_axis => :foreground_color_subplot, :foreground_color_border => :foreground_color_subplot, :foreground_color_grid => :foreground_color_subplot, + :foreground_color_minor_grid=> :foreground_color_subplot, :foreground_color_guide => :foreground_color_subplot, :foreground_color_text => :foreground_color_subplot, :fontfamily_subplot => :fontfamily, @@ -1410,6 +1466,7 @@ function _update_axis_colors(axis::Axis) color_or_nothing!(axis.d, :foreground_color_guide) color_or_nothing!(axis.d, :foreground_color_text) color_or_nothing!(axis.d, :foreground_color_grid) + color_or_nothing!(axis.d, :foreground_color_minor_grid) return end diff --git a/src/axes.jl b/src/axes.jl index d1804103..7dd64d02 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -309,12 +309,11 @@ function get_minor_ticks(axis,ticks) minorticks = typeof(ticks[1])[] for (i,hi) in enumerate(ticks[2:end]) lo = ticks[i] - append!(minorticks,collect(lo:(hi-lo)/n:hi)) + append!(minorticks,collect(lo + (hi-lo)/n :(hi-lo)/n: hi - (hi-lo)/2n)) end minorticks[amin .<= minorticks .<= amax] - #Duplicate major ticks as a hack to darken the major grid lines - #minorticks[map(x->!in(x,ticks),minorticks)] end + # ------------------------------------------------------------------------- @@ -661,7 +660,7 @@ function axis_drawing_info(sp::Subplot) push!(xtick_segs, (xminortick, tick_start), (xminortick, tick_stop)) # bottom tick end # sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick - xaxis[:minorgrid] && push!(xgrid_segs, (xminortick, ymin), (xminortick, ymax)) # vertical grid + xaxis[:minorgrid] && push!(xminorgrid_segs, (xminortick, ymin), (xminortick, ymax)) # vertical grid end end @@ -722,10 +721,10 @@ function axis_drawing_info(sp::Subplot) push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick end # sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick - yaxis[:minorgrid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid + yaxis[:minorgrid] && push!(yminorgrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid end end end - xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs + xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs end diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index b8a4ed04..5b4c592a 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -696,7 +696,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, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs = Plots.axis_drawing_info(sp) + xticks, yticks, xspine_segs, yspine_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_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])) @@ -710,6 +710,14 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are grid = draw_grid_lines(sp, ygrid_segs, yaxis[:gridlinewidth], yaxis[:gridstyle], model, RGBA(ygc, yaxis[:gridalpha])) push!(axis_vis, grid) end + if xaxis[:minorgrid] + minorgrid = draw_minorgrid_lines(sp, xminorgrid_segs, xaxis[:minorgridlinewidth], xaxis[:minorgridstyle], model, RGBA(xgc, xaxis[:minorgridalpha])) + push!(axis_vis, minorgrid) + end + if yaxis[:minorgrid] + minorgrid = draw_minorgrid_lines(sp, yminorgrid_segs, yaxis[:minorgridlinewidth], yaxis[:minorgridstyle], model, RGBA(ygc, yaxis[:minorgridalpha])) + push!(axis_vis, minorgrid) + end xac = Colors.color(Plots.gl_color(xaxis[:foreground_color_axis])) yac = Colors.color(Plots.gl_color(yaxis[:foreground_color_axis])) diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 483e717f..ecb7e0d5 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -818,7 +818,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) GR.setwindow(xmin, xmax, ymin, ymax) end - xticks, yticks, xspine_segs, yspine_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs = axis_drawing_info(sp) + xticks, yticks, xspine_segs, yspine_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs = axis_drawing_info(sp) # @show xticks yticks #spine_segs grid_segs # draw the grid lines @@ -834,6 +834,18 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) gr_set_transparency(yaxis[:gridalpha]) gr_polyline(coords(ygrid_segs)...) end + if xaxis[:minorgrid] + # gr_set_linecolor(sp[:foreground_color_grid]) + # GR.grid(xtick, ytick, 0, 0, majorx, majory) + gr_set_line(xaxis[:minorgridlinewidth], xaxis[:minorgridstyle], xaxis[:foreground_color_minor_grid]) + gr_set_transparency(xaxis[:minorgridalpha]) + gr_polyline(coords(xminorgrid_segs)...) + end + if yaxis[:minorgrid] + gr_set_line(yaxis[:minorgridlinewidth], yaxis[:minorgridstyle], yaxis[:foreground_color_minor_grid]) + gr_set_transparency(yaxis[:minorgridalpha]) + gr_polyline(coords(yminorgrid_segs)...) + end gr_set_transparency(1.0) # axis lines