From ee706ad8c7247d5e2d5311c7bb43d283ea94f004 Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Wed, 13 Jul 2016 16:31:21 -0400 Subject: [PATCH] axis_drawing_info and get_ticks; parameterized Segments; GR manual drawing of 2D axes --- src/Plots.jl | 11 +------- src/axes.jl | 66 ++++++++++++++++++++++++++++++++++++++-------- src/backends/gr.jl | 63 +++++++++++++++++++++++++++++++------------ src/utils.jl | 35 +++++++++++++++++------- 4 files changed, 128 insertions(+), 47 deletions(-) diff --git a/src/Plots.jl b/src/Plots.jl index 71d7b8d1..6affa79d 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -81,16 +81,6 @@ export arrow, Segments, - # colorscheme, - # ColorScheme, - # ColorGradient, - # ColorVector, - # ColorWrapper, - # ColorFunction, - # ColorZFunction, - # getColor, - # getColorZ, - debugplots, supported_args, @@ -109,6 +99,7 @@ export test_examples, iter_segments, + coords, translate, translate!, diff --git a/src/axes.jl b/src/axes.jl index 39d01420..3c4efffe 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -117,21 +117,29 @@ Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...) Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k) Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax)) -# get discrete ticks, or not +# return (continuous_values, discrete_values) for the ticks on this axis function get_ticks(axis::Axis) ticks = axis[:ticks] dvals = axis[:discrete_values] - if !isempty(dvals) && ticks == :auto - cv, dv = axis[:continuous_values], dvals - # TODO: better/smarter cutoff values for sampling ticks - if length(cv) > 30 - rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)] - cv[rng], dv[rng] - else - cv, dv - end - else + cv, dv = if !isempty(dvals) && ticks == :auto + # discrete ticks... + axis[:continuous_values], dvals + elseif ticks == :auto + cv = optimize_ticks(map(Float64, axis_limits(axis))..., k_max=5)[1] + cv, cv + elseif typeof(ticks) <: NTuple{2} ticks + else + error("Unknown ticks type in get_ticks: $(typeof(ticks))") + end + # @show ticks dvals cv dv + + # TODO: better/smarter cutoff values for sampling ticks + if length(cv) > 30 + rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)] + cv[rng], dv[rng] + else + cv, dv end end @@ -339,3 +347,39 @@ function pie_labels(sp::Subplot, series::Series) d[:x] end end + +# ------------------------------------------------------------------------- + +# compute the line segments which should be drawn for this axis +function axis_drawing_info(sp::Subplot) + xaxis, yaxis = sp[:xaxis], sp[:yaxis] + xmin, xmax = axis_limits(xaxis) + ymin, ymax = axis_limits(yaxis) + xticks = get_ticks(xaxis) + yticks = get_ticks(yaxis) + spine_segs = Segments(2) + grid_segs = Segments(2) + + # x axis + ticksz = 0.015 * (ymax - ymin) + push!(spine_segs, (xmin,ymin), (xmax,ymin)) # bottom spine + push!(spine_segs, (xmin,ymax), (xmax,ymax)) # top spine + for xtick in xticks[1] + push!(spine_segs, (xtick, ymin), (xtick, ymin+ticksz)) # bottom tick + push!(grid_segs, (xtick, ymin+ticksz), (xtick, ymax-ticksz)) # vertical grid + push!(spine_segs, (xtick, ymax), (xtick, ymax-ticksz)) # top tick + end + + # y axis + ticksz = 0.015 * (xmax - xmin) + push!(spine_segs, (xmin,ymin), (xmin,ymax)) # left spine + push!(spine_segs, (xmax,ymin), (xmax,ymax)) # right spine + for ytick in yticks[1] + push!(spine_segs, (xmin, ytick), (xmin+ticksz, ytick)) # left tick + push!(grid_segs, (xmin+ticksz, ytick), (xmax-ticksz, ytick)) # horizontal grid + push!(spine_segs, (xmax, ytick), (xmax-ticksz, ytick)) # right tick + end + + xticks, yticks, spine_segs, grid_segs +end + diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 33e28925..30465121 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -328,7 +328,7 @@ end const _gr_point_mult = zeros(1) # set the font attributes... assumes _gr_point_mult has been populated already -function gr_set_font(f::Font) +function gr_set_font(f::Font; halign = f.halign, valign = f.valign) family = lowercase(f.family) GR.setcharheight(_gr_point_mult[1] * f.pointsize) GR.setcharup(sin(f.rotation), cos(f.rotation)) @@ -336,7 +336,7 @@ function gr_set_font(f::Font) GR.settextfontprec(100 + gr_font_family[family], GR.TEXT_PRECISION_STRING) end gr_set_textcolor(f.color) - GR.settextalign(gr_halign[f.halign], gr_valign[f.valign]) + GR.settextalign(gr_halign[halign], gr_valign[valign]) end # -------------------------------------------------------------------------------------- @@ -448,7 +448,7 @@ function gr_display(plt::Plot) # update point mult px_per_pt = px / pt - _gr_point_mult[1] = px_per_pt / h + _gr_point_mult[1] = px_per_pt / max(h,w) # subplots: for sp in plt.subplots @@ -561,26 +561,55 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) gr_polaraxes(rmin, rmax) elseif draw_axes + if xmax > xmin && ymax > ymin + GR.setwindow(xmin, xmax, ymin, ymax) + end + + xticks, yticks, spine_segs, grid_segs = axis_drawing_info(sp) + # @show xticks yticks spine_segs grid_segs + # draw the grid lines - # TODO: control line style/width - # GR.setlinetype(GR.LINETYPE_DOTTED) if sp[:grid] - gr_set_linecolor(sp[:foreground_color_grid]) - GR.grid(xtick, ytick, 0, 0, majorx, majory) + # gr_set_linecolor(sp[:foreground_color_grid]) + # GR.grid(xtick, ytick, 0, 0, majorx, majory) + gr_set_line(1, :dot, sp[:foreground_color_grid]) + GR.settransparency(0.5) + gr_polyline(coords(grid_segs)...) + end + GR.settransparency(1.0) + + # spine (border) and tick marks + gr_set_line(1, :solid, sp[:xaxis][:foreground_color_axis]) + gr_polyline(coords(spine_segs)...) + + # x labels + gr_set_font(sp[:xaxis][:tickfont], valign = :top) + for (cv, dv) in zip(xticks...) + xi, yi = GR.wctondc(cv, ymin) + # @show cv dv ymin xi yi + gr_text(xi, yi-0.01, string(dv)) end - window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2) - ticksize = 0.0075 * window_diag - if outside_ticks - ticksize = -ticksize + # y labels + gr_set_font(sp[:yaxis][:tickfont], halign = :right) + for (cv, dv) in zip(yticks...) + xi, yi = GR.wctondc(xmin, cv) + # @show cv dv xmin xi yi + gr_text(xi-0.01, yi, string(dv)) end - # TODO: this should be done for each axis separately - gr_set_linecolor(xaxis[:foreground_color_axis]) - x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax) - y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax) - GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize) - GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize) + # window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2) + # ticksize = 0.0075 * window_diag + # if outside_ticks + # ticksize = -ticksize + # end + # # TODO: this should be done for each axis separately + # gr_set_linecolor(xaxis[:foreground_color_axis]) + + # x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax) + # y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax) + # GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize) + # GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize) end # end diff --git a/src/utils.jl b/src/utils.jl index 434ee17f..08a17b9f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -138,24 +138,41 @@ end # --------------------------------------------------------------- -type Segments - pts::Vector{Float64} +type Segments{T} + pts::Vector{T} end -Segments() = Segments(zeros(0)) +# Segments() = Segments{Float64}(zeros(0)) -function Base.push!(segments::Segments, vs...) - push!(segments.pts, NaN) +Segments() = Segments(Float64) +Segments{T}(::Type{T}) = Segments(T[]) +Segments(p::Int) = Segments(NTuple{2,Float64}[]) + + +# Segments() = Segments(zeros(0)) + +to_nan(::Type{Float64}) = NaN +to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN) + +coords(segs::Segments{Float64}) = segs.pts +coords(segs::Segments{NTuple{2,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts] + +function Base.push!{T}(segments::Segments{T}, vs...) + if !isempty(segments.pts) + push!(segments.pts, to_nan(T)) + end for v in vs - push!(segments.pts, v) + push!(segments.pts, convert(T,v)) end segments end -function Base.push!(segments::Segments, vs::AVec) - push!(segments.pts, NaN) +function Base.push!{T}(segments::Segments{T}, vs::AVec) + if !isempty(segments.pts) + push!(segments.pts, to_nan(T)) + end for v in vs - push!(segments.pts, v) + push!(segments.pts, convert(T,v)) end segments end