diff --git a/src/args.jl b/src/args.jl index 91e5eada..caa2f7b7 100644 --- a/src/args.jl +++ b/src/args.jl @@ -185,7 +185,7 @@ function hasgrid(arg::Symbol, letter) end hasgrid(arg::AbstractString, letter) = hasgrid(Symbol(arg), letter) -const _allFramestyles = [:box, :semi, :axes, :grid, :none] +const _allFramestyles = [:box, :semi, :axes, :origin, :zerolines, :grid, :none] const _framestyleAliases = Dict{Symbol, Symbol}( :frame => :box, :border => :box, diff --git a/src/axes.jl b/src/axes.jl index 959274bc..61370a27 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -508,6 +508,8 @@ function axis_drawing_info(sp::Subplot) yticks = get_ticks(yaxis) xaxis_segs = Segments(2) yaxis_segs = Segments(2) + xtick_segs = Segments(2) + ytick_segs = Segments(2) xgrid_segs = Segments(2) ygrid_segs = Segments(2) xborder_segs = Segments(2) @@ -515,37 +517,65 @@ function axis_drawing_info(sp::Subplot) if !(sp[:framestyle] == :none) # xaxis - sp[:framestyle] == :grid || push!(xaxis_segs, (xmin,ymin), (xmax,ymin)) # bottom spine / xaxis + sp[:framestyle] in (:grid, :origin, :zerolines) || push!(xaxis_segs, (xmin,ymin), (xmax,ymin)) # bottom spine / xaxis + if sp[:framestyle] in (:origin, :zerolines) + push!(xaxis_segs, (xmin, 0.0), (xmax, 0.0)) + # don't show the 0 tick label for the origin framestyle + if sp[:framestyle] == :origin && length(xticks) > 1 + showticks = xticks[1] .!= 0 + xticks = (xticks[1][showticks], xticks[2][showticks]) + end + end 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))) + t3 = invf(f(0) - 0.015*(f(ymax)-f(ymin))) for xtick in xticks[1] - push!(xaxis_segs, (xtick, ymin), (xtick, t1)) # bottom tick + tick_start, tick_stop = if sp[:framestyle] == :origin + (0, xaxis[:mirror] ? -t3 : t3) + else + xaxis[:mirror] ? (ymax, t2) : (ymin, t1) + end + push!(xtick_segs, (xtick, tick_start), (xtick, tick_stop)) # 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 (:grid, :origin, :zerolines) || push!(yaxis_segs, (xmin,ymin), (xmin,ymax)) # left spine / yaxis + if sp[:framestyle] in (:origin, :zerolines) + push!(yaxis_segs, (0.0, ymin), (0.0, ymax)) + # don't show the 0 tick label for the origin framestyle + if sp[:framestyle] == :origin && length(yticks) > 1 + showticks = yticks[1] .!= 0 + yticks = (yticks[1][showticks], yticks[2][showticks]) + end + end 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))) + t3 = invf(f(0) - 0.015*(f(xmax)-f(xmin))) for ytick in yticks[1] - push!(yaxis_segs, (xmin, ytick), (t1, ytick)) # left tick + tick_start, tick_stop = if sp[:framestyle] == :origin + (0, yaxis[:mirror] ? -t3 : t3) + else + yaxis[:mirror] ? (xmax, t2) : (xmin, t1) + end + push!(ytick_segs, (tick_start, ytick), (tick_stop, 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 - xticks, yticks, xaxis_segs, yaxis_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs + xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs end diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index 974b73b1..f9c01f29 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -604,7 +604,7 @@ end pointsize(font) = font.pointsize * 2 function draw_ticks( - axis, ticks, isx, lims, m, text = "", + axis, ticks, isx, isorigin, lims, m, text = "", positions = Point2f0[], offsets=Vec2f0[] ) sz = pointsize(axis[:tickfont]) @@ -622,7 +622,11 @@ function draw_ticks( for (cv, dv) in zip(ticks...) x, y = cv, lims[1] - xy = isx ? (x, y) : (y, x) + xy = if isorigin + isx ? (x, 0) : (0, x) + else + isx ? (x, y) : (y, x) + end _pos = m * GeometryTypes.Vec4f0(xy[1], xy[2], 0, 1) startpos = Point2f0(_pos[1], _pos[2]) - axis_gap str = string(dv) @@ -678,7 +682,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, xborder_segs, yborder_segs = Plots.axis_drawing_info(sp) + xticks, yticks, xspine_segs, yspine_segs, xtick_segs, ytick_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])) @@ -703,6 +707,25 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are spine = draw_grid_lines(sp, yspine_segs, 1f0, :solid, model, RGBA(yac, 1.0f0)) push!(axis_vis, spine) end + if sp[:framestyle] in (:zerolines, :grid) + if alpha(xaxis[:foreground_color_grid]) > 0 + spine = draw_grid_lines(sp, xtick_segs, 1f0, :solid, model, RGBA(xgc, xaxis[:gridalpha])) + push!(axis_vis, spine) + end + if alpha(yaxis[:foreground_color_grid]) > 0 + spine = draw_grid_lines(sp, ytick_segs, 1f0, :solid, model, RGBA(ygc, yaxis[:gridalpha])) + push!(axis_vis, spine) + end + else + if alpha(xaxis[:foreground_color_axis]) > 0 + spine = draw_grid_lines(sp, xtick_segs, 1f0, :solid, model, RGBA(xac, 1.0f0)) + push!(axis_vis, spine) + end + if alpha(yaxis[:foreground_color_axis]) > 0 + spine = draw_grid_lines(sp, ytick_segs, 1f0, :solid, model, RGBA(yac, 1.0f0)) + push!(axis_vis, spine) + end + end fcolor = Plots.gl_color(xaxis[:foreground_color_axis]) xlim = Plots.axis_limits(xaxis) @@ -711,10 +734,10 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are 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) + t, positions, offsets = draw_ticks(xaxis, xticks, true, sp[:framestyle] == :origin, ylim, m) mirror = xaxis[:mirror] t, positions, offsets = draw_ticks( - yaxis, yticks, false, xlim, m, + yaxis, yticks, false, sp[:framestyle] == :origin, xlim, m, t, positions, offsets ) end diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 76faa6fd..6baac621 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -765,7 +765,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, xborder_segs, yborder_segs = axis_drawing_info(sp) + xticks, yticks, xspine_segs, yspine_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs = axis_drawing_info(sp) # @show xticks yticks #spine_segs grid_segs # draw the grid lines @@ -792,13 +792,32 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) gr_polyline(coords(yspine_segs)...) GR.setclip(1) + # axis ticks + if sp[:framestyle] in (:zerolines, :grid) + gr_set_line(1, :solid, xaxis[:foreground_color_grid]) + GR.settransparency(xaxis[:gridalpha]) + else + gr_set_line(1, :solid, xaxis[:foreground_color_axis]) + end + GR.setclip(0) + gr_polyline(coords(xtick_segs)...) + if sp[:framestyle] in (:zerolines, :grid) + gr_set_line(1, :solid, yaxis[:foreground_color_grid]) + GR.settransparency(yaxis[:gridalpha]) + else + gr_set_line(1, :solid, yaxis[:foreground_color_axis]) + end + GR.setclip(0) + gr_polyline(coords(ytick_segs)...) + GR.setclip(1) + # tick marks if !(xticks in (:none, nothing, false)) # x labels flip, mirror = gr_set_xticks_font(sp) for (cv, dv) in zip(xticks...) # use xor ($) to get the right y coords - xi, yi = GR.wctondc(cv, xor(flip, mirror) ? ymax : ymin) + xi, yi = GR.wctondc(cv, sp[:framestyle] == :origin ? 0 : xor(flip, mirror) ? ymax : ymin) # @show cv dv ymin xi yi flip mirror (flip $ mirror) gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3, string(dv)) end @@ -809,7 +828,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) flip, mirror = gr_set_yticks_font(sp) for (cv, dv) in zip(yticks...) # use xor ($) to get the right y coords - xi, yi = GR.wctondc(xor(flip, mirror) ? xmax : xmin, cv) + xi, yi = GR.wctondc(sp[:framestyle] == :origin ? 0 : xor(flip, mirror) ? xmax : xmin, cv) # @show cv dv xmin xi yi gr_text(xi + (mirror ? 1 : -1) * 1e-2, yi, string(dv)) end diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 2188191a..e2db4322 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -972,14 +972,15 @@ function py_set_scale(ax, axis::Axis) end -function py_set_axis_colors(ax, a::Axis) +function py_set_axis_colors(sp, ax, a::Axis) for (loc, spine) in ax[:spines] spine[:set_color](py_color(a[:foreground_color_border])) end axissym = Symbol(a[:letter], :axis) if haskey(ax, axissym) + tickcolor = sp[:framestyle] == :zerolines ? py_color(plot_color(a[:foreground_color_grid], a[:gridalpha])) : py_color(a[:foreground_color_axis]) ax[:tick_params](axis=string(a[:letter]), which="both", - colors=py_color(a[:foreground_color_axis]), + colors=tickcolor, labelcolor=py_color(a[:foreground_color_text])) ax[axissym][:label][:set_color](py_color(a[:foreground_color_guide])) end @@ -1041,6 +1042,32 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) # ax[:set_title](sp[:title], loc = loc) end + # 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] in (:axes, :origin) + ax[:spines]["right"][:set_visible](false) + ax[:spines]["top"][:set_visible](false) + if sp[:framestyle] == :origin + ax[:spines]["bottom"][:set_position]("zero") + ax[:spines]["left"][:set_position]("zero") + end + elseif sp[:framestyle] in (:grid, :none, :zerolines) + for (loc, spine) in ax[:spines] + spine[:set_visible](false) + end + if sp[:framestyle] == :zerolines + ax[:axhline](y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = 0.75) + ax[:axvline](x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = 0.75) + end + end + end + # axis attributes for letter in (:x, :y, :z) axissym = Symbol(letter, :axis) @@ -1056,6 +1083,10 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) py_set_scale(ax, axis) py_set_lims(ax, axis) ticks = sp[:framestyle] == :none ? nothing : get_ticks(axis) + # don't show the 0 tick label for the origin framestyle + if sp[:framestyle] == :origin && length(ticks) > 1 + ticks[2][ticks[1] .== 0] = "" + end py_set_ticks(ax, ticks, letter) ax[Symbol("set_", letter, "label")](axis[:guide]) if get(axis.d, :flip, false) @@ -1077,7 +1108,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) alpha = axis[:gridalpha]) ax[:set_axisbelow](true) end - py_set_axis_colors(ax, axis) + py_set_axis_colors(sp, ax, axis) end # aspect ratio @@ -1092,23 +1123,6 @@ 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/pipeline.jl b/src/pipeline.jl index e0041b77..a0f17ba4 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -364,6 +364,11 @@ function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol) elseif !(st in (:pie, :histogram, :bins2d, :histogram2d)) expand_extrema!(sp, d) end + # expand for zerolines (axes through origin) + if sp[:framestyle] in (:origin, :zerolines) + expand_extrema!(sp[:xaxis], 0.0) + expand_extrema!(sp[:yaxis], 0.0) + end end function _add_the_series(plt, sp, d)