diff --git a/src/args.jl b/src/args.jl index bcf7dded..43e6542f 100644 --- a/src/args.jl +++ b/src/args.jl @@ -116,7 +116,7 @@ const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt] const _series_defaults = KW() # series-specific -_series_defaults[:axis] = :left +# _series_defaults[:axis] = :left _series_defaults[:label] = "AUTO" _series_defaults[:seriescolor] = :auto _series_defaults[:seriesalpha] = nothing @@ -162,8 +162,7 @@ _series_defaults[:subplot] = :auto # which subplot(s) does this se const _plot_defaults = KW() _plot_defaults[:title] = "" -_plot_defaults[:legend] = :best -_plot_defaults[:colorbar] = :legend +_plot_defaults[:titlefont] = font(14) _plot_defaults[:background_color] = colorant"white" # default for all backgrounds _plot_defaults[:background_color_outside] = :match # background outside grid _plot_defaults[:foreground_color] = :auto # default for all foregrounds, and title color @@ -175,7 +174,6 @@ _plot_defaults[:layout] = :auto _plot_defaults[:num_subplots] = -1 _plot_defaults[:num_rows] = -1 _plot_defaults[:num_cols] = -1 -_plot_defaults[:color_palette] = :auto _plot_defaults[:link] = false _plot_defaults[:linkx] = false _plot_defaults[:linky] = false @@ -192,6 +190,9 @@ _subplot_defaults[:background_color_inside] = :match # background in _subplot_defaults[:foreground_color_subplot] = :match # default for other fg colors... match takes plot default _subplot_defaults[:foreground_color_legend] = :match # foreground of legend _subplot_defaults[:foreground_color_grid] = :match # grid color +_subplot_defaults[:color_palette] = :auto +_subplot_defaults[:legend] = :best +_subplot_defaults[:colorbar] = :legend _subplot_defaults[:legendfont] = font(8) _subplot_defaults[:grid] = true _subplot_defaults[:annotation] = nothing # annotation tuple(s)... (x,y,annotation) @@ -426,41 +427,41 @@ function handleColors!(d::KW, arg, csym::Symbol) false end -# given one value (:log, or :flip, or (-1,1), etc), set the appropriate arg -# TODO: use trueOrAllTrue for subplots which can pass vectors for these -function processAxisArg(d::KW, letter::AbstractString, arg) - T = typeof(arg) - arg = get(_scaleAliases, arg, arg) - scale, flip, label, lim, tick = axis_symbols(letter, "scale", "flip", "label", "lims", "ticks") - - if typeof(arg) <: Font - d[:tickfont] = arg - - elseif arg in _allScales - d[scale] = arg - - elseif arg in (:flip, :invert, :inverted) - d[flip] = true - - elseif T <: @compat(AbstractString) - d[label] = arg - - # xlims/ylims - elseif (T <: Tuple || T <: AVec) && length(arg) == 2 - d[typeof(arg[1]) <: Number ? lim : tick] = arg - - # xticks/yticks - elseif T <: AVec - d[tick] = arg - - elseif arg == nothing - d[tick] = [] - - else - warn("Skipped $(letter)axis arg $arg") - - end -end +# # given one value (:log, or :flip, or (-1,1), etc), set the appropriate arg +# # TODO: use trueOrAllTrue for subplots which can pass vectors for these +# function processAxisArg(d::KW, letter::AbstractString, arg) +# T = typeof(arg) +# arg = get(_scaleAliases, arg, arg) +# scale, flip, label, lim, tick = axis_symbols(letter, "scale", "flip", "label", "lims", "ticks") +# +# if typeof(arg) <: Font +# d[:tickfont] = arg +# +# elseif arg in _allScales +# d[scale] = arg +# +# elseif arg in (:flip, :invert, :inverted) +# d[flip] = true +# +# elseif T <: @compat(AbstractString) +# d[label] = arg +# +# # xlims/ylims +# elseif (T <: Tuple || T <: AVec) && length(arg) == 2 +# d[typeof(arg[1]) <: Number ? lim : tick] = arg +# +# # xticks/yticks +# elseif T <: AVec +# d[tick] = arg +# +# elseif arg == nothing +# d[tick] = [] +# +# else +# warn("Skipped $(letter)axis arg $arg") +# +# end +# end function processLineArg(d::KW, arg) @@ -575,26 +576,26 @@ end function preprocessArgs!(d::KW) replaceAliases!(d, _keyAliases) - # handle axis args - for letter in ("x", "y", "z") - asym = symbol(letter * "axis") - for arg in wraptuple(pop!(d, asym, ())) - processAxisArg(d, letter, arg) - end - # delete!(d, asym) - - # # NOTE: this logic was moved to _add_plotargs... - # # turn :labels into :ticks_and_labels - # tsym = symbol(letter * "ticks") - # if haskey(d, tsym) && ticksType(d[tsym]) == :labels - # d[tsym] = (1:length(d[tsym]), d[tsym]) - # end - # - # ssym = symbol(letter * "scale") - # if haskey(d, ssym) && haskey(_scaleAliases, d[ssym]) - # d[ssym] = _scaleAliases[d[ssym]] - # end - end + # # handle axis args + # for letter in ("x", "y", "z") + # asym = symbol(letter * "axis") + # for arg in wraptuple(pop!(d, asym, ())) + # processAxisArg(d, letter, arg) + # end + # # delete!(d, asym) + # + # # # NOTE: this logic was moved to _add_plotargs... + # # # turn :labels into :ticks_and_labels + # # tsym = symbol(letter * "ticks") + # # if haskey(d, tsym) && ticksType(d[tsym]) == :labels + # # d[tsym] = (1:length(d[tsym]), d[tsym]) + # # end + # # + # # ssym = symbol(letter * "scale") + # # if haskey(d, ssym) && haskey(_scaleAliases, d[ssym]) + # # d[ssym] = _scaleAliases[d[ssym]] + # # end + # end # handle line args for arg in wraptuple(pop!(d, :line, ())) @@ -668,18 +669,18 @@ function preprocessArgs!(d::KW) delete!(d, :link) end - # pull out invalid keywords into their own KW dict... these are likely user-defined through recipes - kw = KW() - for k in keys(d) - try - # this should error for invalid keywords (assume they are user-defined) - k == :markershape_to_add || default(k) - catch - # not a valid key... pop and add to user list - kw[k] = pop!(d, k) - end - end - kw + # # pull out invalid keywords into their own KW dict... these are likely user-defined through recipes + # kw = KW() + # for k in keys(d) + # try + # # this should error for invalid keywords (assume they are user-defined) + # k == :markershape_to_add || default(k) + # catch + # # not a valid key... pop and add to user list + # kw[k] = pop!(d, k) + # end + # end + # kw end # ----------------------------------------------------------------------------- @@ -744,8 +745,8 @@ _markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supportedM _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes]) function warnOnUnsupported(pkg::AbstractBackend, d::KW) - (d[:axis] in supportedAxes(pkg) - || warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))")) + # (d[:axis] in supportedAxes(pkg) + # || warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))")) (d[:seriestype] == :none || d[:seriestype] in supportedTypes(pkg) || warn("seriestype $(d[:seriestype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))")) @@ -767,28 +768,16 @@ end # ----------------------------------------------------------------------------- -# 1-row matrices will give an element -# multi-row matrices will give a column -# InputWrapper just gives the contents -# anything else is returned as-is -# getArgValue(v::Tuple, idx::Int) = v[mod1(idx, length(v))] -function getArgValue(v::AMat, idx::Int) - c = mod1(idx, size(v,2)) - size(v,1) == 1 ? v[1,c] : v[:,c] -end -getArgValue(wrapper::InputWrapper, idx) = wrapper.obj -getArgValue(v, idx) = v - -# given an argument key (k), we want to extract the argument value for this index. -# if nothing is set (or container is empty), return the default. -function setDictValue(d_in::KW, d_out::KW, k::Symbol, idx::Int, defaults::KW) - if haskey(d_in, k) && !(typeof(d_in[k]) <: Union{AbstractMatrix, Tuple} && isempty(d_in[k])) - d_out[k] = getArgValue(d_in[k], idx) - else - d_out[k] = deepcopy(defaults[k]) - end -end +# # given an argument key (k), we want to extract the argument value for this index. +# # if nothing is set (rarg is empty), return the default. +# function setDictValue(d_in::KW, d_out::KW, k::Symbol, idx::Int, defaults::KW) +# if haskey(d_in, k) && !(typeof(d_in[k]) <: Union{AbstractMatrix, Tuple} && isempty(d_in[k])) +# d_out[k] = slice_arg(d_in[k], idx) +# else +# d_out[k] = deepcopy(defaults[k]) +# end +# end function convertLegendValue(val::Symbol) if val in (:both, :all, :yes) @@ -806,40 +795,140 @@ convertLegendValue(val::Bool) = val ? :best : :none # ----------------------------------------------------------------------------- # build the argument dictionary for the plot -function getPlotArgs(pkg::AbstractBackend, kw, idx::Int; set_defaults = true) - kwdict = KW(kw) - d = KW() +# function getPlotArgs(pkg::AbstractBackend, kw, idx::Int; set_defaults = true) +# d_in = KW(kw) +# d_out = KW() +# +# # add defaults? +# if set_defaults +# for k in keys(_plot_defaults) +# setDictValue(d_in, d_out, k, idx, _plot_defaults) +# end +# end +# +# # handle legend/colorbar +# d_out[:legend] = convertLegendValue(d_out[:legend]) +# d_out[:colorbar] = convertLegendValue(d_out[:colorbar]) +# if d_out[:colorbar] == :legend +# d_out[:colorbar] = d_out[:legend] +# end +# +# # convert color +# handlePlotColors(pkg, d_out) +# +# # no need for these +# delete!(d_out, :x) +# delete!(d_out, :y) +# +# d_out +# end - # add defaults? - if set_defaults - for k in keys(_plot_defaults) - setDictValue(kwdict, d, k, idx, _plot_defaults) - end +# 1-row matrices will give an element +# multi-row matrices will give a column +# InputWrapper just gives the contents +# anything else is returned as-is +function slice_arg(v::AMat, idx::Int) + c = mod1(idx, size(v,2)) + size(v,1) == 1 ? v[1,c] : v[:,c] +end +slice_arg(wrapper::InputWrapper, idx) = wrapper.obj +slice_arg(v, idx) = v + + +# given an argument key (k), we want to extract the argument value for this index. +# matrices are sliced by column, otherwise we +# if nothing is set (or container is empty), return the default or the existing value. +function slice_arg!(d_in::KW, d_out::KW, k::Symbol, default_value, idx::Int = 1) + v = pop!(d_in, k, get(d_out, k, default_value)) + d_out[k] = if haskey(d_in, k) && typeof(v) <: AMat && !isempty(v) + slice_arg(v, idx) + else + v + end +end + +# if the value is `:match` then we take whatever match_color is. +# this is mainly used for cascading defaults for foreground and background colors +function color_or_match!(d::KW, k::Symbol, match_color) + v = d[k] + d[k] = if v == :match + match_color + elseif v == nothing + colorscheme(RGBA(0,0,0,0)) + else + v + end +end + + +# update plotargs from an input dictionary +function _update_plot_args(plt::Plot, d_in::KW) + pargs = plt.plotargs + for (k,v) in _plot_defaults + slice_arg!(d_in, pargs, k, v) + end + + # handle colors + bg = convertColor(pargs[:background_color]) + fg = pargs[:foreground_color] + if fg == :auto + fg = isdark(bg) ? colorant"white" : colorant"black" + end + pargs[:background_color] = bg + pargs[:foreground_color] = convertColor(fg) + color_or_match!(pargs, :background_color_outside, bg) +end + + +# update a subplots args and axes +function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW) + pargs = plt.plotargs + spargs = sp.subplotargs + for (k,v) in _subplot_defaults + slice_arg!(d_in, spargs, k, v) end - # - # for k in (:xscale, :yscale) - # if haskey(_scaleAliases, d[k]) - # d[k] = _scaleAliases[d[k]] - # end - # end # handle legend/colorbar - d[:legend] = convertLegendValue(d[:legend]) - d[:colorbar] = convertLegendValue(d[:colorbar]) - if d[:colorbar] == :legend - d[:colorbar] = d[:legend] + spargs[:legend] = convertLegendValue(spargs[:legend]) + spargs[:colorbar] = convertLegendValue(spargs[:colorbar]) + if spargs[:colorbar] == :legend + spargs[:colorbar] = spargs[:legend] end - # convert color - handlePlotColors(pkg, d) + # background colors + bg = color_or_match!(spargs, :background_color_subplot, pargs[:background_color]) + spargs[:color_palette] = get_color_palette(spargs[:color_palette], bg, 30) + color_or_match!(spargs, :background_color_legend, bg) + color_or_match!(spargs, :background_color_inside, bg) - # no need for these - delete!(d, :x) - delete!(d, :y) + # foreground colors + fg = color_or_match!(spargs, :foreground_color_subplot, pargs[:foreground_color]) + color_or_match!(spargs, :foreground_color_legend, fg) + color_or_match!(spargs, :foreground_color_grid, fg) - d + for letter in (:x, :y, :z) + # get (maybe initialize) the axis + axissym = symbol(letter, :axis) + axis = get!(spargs, axissym, Axis(letter)) + + # grab magic args (for example `xaxis = (:flip, :log)`) + args = wraptuple(get(d_in, axissym, ())) + + # build the KW of arguments from the letter version (i.e. xticks --> ticks) + kw = KW() + for k in _axis_defaults + lk = symbol(letter, k) + if haskey(d_in, lk) + kw[k] = d_in[lk] + end + end + + # update the axis + update!(axis, args...; kw...) + end end + function has_black_border_for_default(st::Symbol) like_histogram(st) || st in (:hexbin, :bar) end diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 304bc7f7..f9c63bbf 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -22,7 +22,7 @@ supportedArgs(::GRBackend) = [ :title, :windowtitle, :show, :size, :x, :xlabel, :xlims, :xticks, :xscale, :xflip, :y, :ylabel, :ylims, :yticks, :yscale, :yflip, - :axis, :yrightlabel, + # :axis, :yrightlabel, :z, :zlabel, :zlims, :zticks, :zscale, :zflip, :z, :tickfont, :guidefont, :legendfont, @@ -80,7 +80,8 @@ function gr_getcolorind(v) end function gr_getaxisind(p) - axis = get(p, :axis, :none) + # axis = get(p, :axis, :none) + axis = :left if axis in [:none, :left] return 1 else @@ -423,14 +424,14 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true, GR.text(vp[1], 0.5 * (viewport[3] + viewport[4]), d[:ylabel]) GR.restorestate() end - if get(d, :yrightlabel, "") != "" - GR.savestate() - GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP) - GR.setcharup(1, 0) - GR.settextcolorind(fg) - GR.text(vp[2], 0.5 * (viewport[3] + viewport[4]), d[:yrightlabel]) - GR.restorestate() - end + # if get(d, :yrightlabel, "") != "" + # GR.savestate() + # GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP) + # GR.setcharup(1, 0) + # GR.settextcolorind(fg) + # GR.text(vp[2], 0.5 * (viewport[3] + viewport[4]), d[:yrightlabel]) + # GR.restorestate() + # end GR.setcolormap(1000 + GR.COLORMAP_COOLWARM) diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 3b040d95..6fd5e6d4 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -22,7 +22,7 @@ supportedArgs(::PyPlotBackend) = [ :title, :windowtitle, :show, :size, :x, :xlabel, :xlims, :xticks, :xscale, :xflip, :xrotation, :y, :ylabel, :ylims, :yticks, :yscale, :yflip, :yrotation, - :axis, :yrightlabel, + # :axis, :yrightlabel, :z, :zlabel, :zlims, :zticks, :zscale, :zflip, :zrotation, :z, :tickfont, :guidefont, :legendfont, @@ -881,7 +881,7 @@ function _add_series(plt::Plot{PyPlotBackend}, series::Series) end # this sets the bg color inside the grid - ax[:set_axis_bgcolor](getPyPlotColor(plt.plotargs[:background_color_inside])) + ax[:set_axis_bgcolor](getPyPlotColor(d[:subplot].subplotargs[:background_color_inside])) # handle area filling fillrange = d[:fillrange] @@ -1030,21 +1030,20 @@ function applyPyPlotScale(ax, scaleType::Symbol, letter) end -function updateAxisColors(ax, d::KW) - guidecolor = getPyPlotColor(d[:foreground_color_guide]) +function updateAxisColors(ax, a::Axis) + guidecolor = getPyPlotColor(a[:foreground_color_guide]) for (loc, spine) in ax[:spines] - spine[:set_color](getPyPlotColor(d[:foreground_color_border])) + spine[:set_color](getPyPlotColor(a[:foreground_color_border])) end - for letter in ("x", "y", "z") + # for letter in ("x", "y", "z") axis = axis_symbol(letter, "axis") if haskey(ax, axis) ax[:tick_params](axis=letter, which="both", - colors=getPyPlotColor(d[:foreground_color_axis]), - labelcolor=getPyPlotColor(d[:foreground_color_text])) + colors=getPyPlotColor(a[:foreground_color_axis]), + labelcolor=getPyPlotColor(a[:foreground_color_text])) ax[axis][:label][:set_color](guidecolor) end - end - ax[:title][:set_color](guidecolor) + # end end # function usingRightAxis(plt::Plot{PyPlotBackend}) @@ -1060,13 +1059,14 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW) # figorax = plt.o # ax = getLeftAxis(figorax) for sp in plt.subplots + spargs = sp.subplotargs ax = getAxis(sp) # ticksz = get(d, :tickfont, plt.plotargs[:tickfont]).pointsize - guidesz = get(d, :guidefont, plt.plotargs[:guidefont]).pointsize + # guidesz = get(d, :guidefont, spargs[:guidefont]).pointsize # title haskey(d, :title) && ax[:set_title](d[:title]) - ax[:title][:set_fontsize](guidesz) + ax[:title][:set_fontsize](plt.plotargs[:titlefont].pointsize) # axes = [ax] # # handle right y axis @@ -1079,9 +1079,9 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW) # end # end - for letter in ("x", "y", "z") - axissym = symbol(letter*"axis") - axis = plt.plotargs[axissym] + for letter in (:x, :y, :z) + axissym = symbol(letter, :axis) + axis = spargs[axissym] # @show axis haskey(ax, axissym) || continue applyPyPlotScale(ax, axis[:scale], letter) @@ -1099,7 +1099,7 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW) lab[:set_rotation](axis[:rotation]) end if get(d, :grid, false) - fgcolor = getPyPlotColor(plt.plotargs[:foreground_color_grid]) + fgcolor = getPyPlotColor(spargs[:foreground_color_grid]) tmpax[axissym][:grid](true, color = fgcolor) tmpax[:set_axisbelow](true) end @@ -1253,17 +1253,20 @@ const _pyplot_legend_pos = KW( ) # function addPyPlotLegend(plt::Plot) -function addPyPlotLegend(plt::Plot, ax) - leg = plt.plotargs[:legend] +# function addPyPlotLegend(plt::Plot, ax) +function addPyPlotLegend(plt::Plot, sp::Subplot, ax) + leg = sp.subplotargs[:legend] if leg != :none # gotta do this to ensure both axes are included labels = [] handles = [] for series in plt.series_list - if series.d[:label] != "" && !(series.d[:seriestype] in ( - :hist,:density,:hexbin,:hist2d,:hline,:vline, - :contour,:contour3d,:surface,:wireframe, - :heatmap,:path3d,:scatter3d, :pie, :image)) + if get_subplot(series) === sp && + series.d[:label] != "" && + !(series.d[:seriestype] in ( + :hist,:density,:hexbin,:hist2d,:hline,:vline, + :contour,:contour3d,:surface,:wireframe, + :heatmap,:path3d,:scatter3d, :pie, :image)) push!(handles, series.d[:serieshandle][1]) push!(labels, series.d[:label]) end @@ -1280,19 +1283,19 @@ function addPyPlotLegend(plt::Plot, ax) labels, #[d[:label] for d in args], loc = get(_pyplot_legend_pos, leg, "best"), scatterpoints = 1, - fontsize = plt.plotargs[:legendfont].pointsize + fontsize = sp.subplotargs[:legendfont].pointsize # framealpha = 0.6 ) leg[:set_zorder](1000) - fgcolor = getPyPlotColor(plt.plotargs[:foreground_color_legend]) + fgcolor = getPyPlotColor(sp.subplotargs[:foreground_color_legend]) for txt in leg[:get_texts]() PyPlot.plt[:setp](txt, color = fgcolor) end # set some legend properties frame = leg[:get_frame]() - frame[:set_facecolor](getPyPlotColor(plt.plotargs[:background_color_legend])) + frame[:set_facecolor](getPyPlotColor(sp.subplotargs[:background_color_legend])) frame[:set_edgecolor](fgcolor) end end @@ -1304,8 +1307,11 @@ function finalizePlot(plt::Plot{PyPlotBackend}) for sp in plt.subplots # ax = getLeftAxis(plt) ax = getAxis(sp) - addPyPlotLegend(plt, ax) - updateAxisColors(ax, plt.plotargs) + addPyPlotLegend(plt, sp, ax) + for asym in (:xaxis, :yaxis, :zaxis) + updateAxisColors(ax, sp.subplotargs[asym]) + end + ax[:title][:set_color](getPyPlotColor(plt.plotargs[:titlefont].color)) end drawfig(plt.o) update_bboxes!(plt.layout) diff --git a/src/backends/unicodeplots.jl b/src/backends/unicodeplots.jl index 07e11daa..0d40d64f 100644 --- a/src/backends/unicodeplots.jl +++ b/src/backends/unicodeplots.jl @@ -173,10 +173,10 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim) end -function handlePlotColors(::UnicodePlotsBackend, d::KW) - # TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette - d[:color_palette] = [RGB(0,0,0)] -end +# function handlePlotColors(::UnicodePlotsBackend, d::KW) +# # TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette +# d[:color_palette] = [RGB(0,0,0)] +# end # ------------------------------- @@ -189,6 +189,7 @@ function _create_backend_figure(plt::Plot{UnicodePlotsBackend}) if !haskey(plt.plotargs, :size) || plt.plotargs[:size] == default(:size) plt.plotargs[:size] = (60,20) end + plt.plotargs[:color_palette] = [RGB(0,0,0)] nothing # plt diff --git a/src/colors.jl b/src/colors.jl index 388537fd..f13ff41d 100644 --- a/src/colors.jl +++ b/src/colors.jl @@ -294,7 +294,7 @@ function generate_colorgradient(bgcolor = colorant"white"; gradient_from_list(colors) end -function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer) +function get_color_palette(palette, bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer) grad = if palette == :auto generate_colorgradient(bgcolor) else @@ -304,7 +304,8 @@ function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper RGBA[getColorZ(grad, z) for z in zrng] end -function get_color_palette(palette::Vector{RGBA}, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer) +function get_color_palette{C<:Colorant}(palette::Vector{C}, + bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer) palette end @@ -360,55 +361,55 @@ webcolor(c, α) = webcolor(convertColor(getColor(c), α)) # TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function? -function handlePlotColors(::AbstractBackend, d::KW) - if :background_color in supportedArgs() - bgcolor = convertColor(d[:background_color]) - else - bgcolor = default(:background_color) - if d[:background_color] != default(:background_color) - warn("Cannot set background_color with backend $(backend())") - end - end - - - d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100) - - - # set the foreground color (text, ticks, gridlines) to be white or black depending - # on how dark the background is. - fgcolor = get(d, :foreground_color, :auto) - fgcolor = if fgcolor == :auto - isdark(bgcolor) ? colorant"white" : colorant"black" - else - convertColor(fgcolor) - end - - # bg/fg color - d[:background_color] = colorscheme(bgcolor) - d[:foreground_color] = colorscheme(fgcolor) - - # update sub-background colors - for bgtype in ("legend", "inside", "outside") - bgsym = symbol("background_color_" * bgtype) - if d[bgsym] == :match - d[bgsym] = d[:background_color] - elseif d[bgsym] == nothing - d[bgsym] = colorscheme(RGBA(0,0,0,0)) - end - end - - # update sub-foreground colors - for fgtype in ("legend", "grid", "axis", "text", "border", "guide") - fgsym = symbol("foreground_color_" * fgtype) - if d[fgsym] == :match - d[fgsym] = d[:foreground_color] - elseif d[fgsym] == nothing - d[fgsym] = colorscheme(RGBA(0,0,0,0)) - end - end - - -end +# function handlePlotColors(::AbstractBackend, d::KW) +# if :background_color in supportedArgs() +# bgcolor = convertColor(d[:background_color]) +# else +# bgcolor = default(:background_color) +# if d[:background_color] != default(:background_color) +# warn("Cannot set background_color with backend $(backend())") +# end +# end +# +# +# d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100) +# +# +# # set the foreground color (text, ticks, gridlines) to be white or black depending +# # on how dark the background is. +# fgcolor = get(d, :foreground_color, :auto) +# fgcolor = if fgcolor == :auto +# isdark(bgcolor) ? colorant"white" : colorant"black" +# else +# convertColor(fgcolor) +# end +# +# # bg/fg color +# d[:background_color] = colorscheme(bgcolor) +# d[:foreground_color] = colorscheme(fgcolor) +# +# # update sub-background colors +# for bgtype in ("legend", "inside", "outside") +# bgsym = symbol("background_color_" * bgtype) +# if d[bgsym] == :match +# d[bgsym] = d[:background_color] +# elseif d[bgsym] == nothing +# d[bgsym] = colorscheme(RGBA(0,0,0,0)) +# end +# end +# +# # update sub-foreground colors +# for fgtype in ("legend", "grid", "axis", "text", "border", "guide") +# fgsym = symbol("foreground_color_" * fgtype) +# if d[fgsym] == :match +# d[fgsym] = d[:foreground_color] +# elseif d[fgsym] == nothing +# d[fgsym] = colorscheme(RGBA(0,0,0,0)) +# end +# end +# +# +# end # converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically function getSeriesRGBColor(c, plotargs::KW, n::Int) diff --git a/src/components.jl b/src/components.jl index 26d7affd..146e0eb2 100644 --- a/src/components.jl +++ b/src/components.jl @@ -258,21 +258,21 @@ end # ----------------------------------------------------------------------- -xaxis(args...) = Axis("x", args...) -yaxis(args...) = Axis("y", args...) -zaxis(args...) = Axis("z", args...) +xaxis(args...) = Axis(:x, args...) +yaxis(args...) = Axis(:y, args...) +zaxis(args...) = Axis(:z, args...) -const _axis_symbols = (:label, :lims, :ticks, :scale, :flip, :rotation) -const _axis_symbols_fonts_colors = ( - :guidefont, :tickfont, - :foreground_color_axis, - :foreground_color_border, - :foreground_color_text, - :foreground_color_guide - ) +# const _axis_symbols = (:label, :lims, :ticks, :scale, :flip, :rotation) +# const _axis_symbols_fonts_colors = ( +# :guidefont, :tickfont, +# :foreground_color_axis, +# :foreground_color_border, +# :foreground_color_text, +# :foreground_color_guide +# ) -function Axis(letter::AbstractString, args...; kw...) +function Axis(letter::Symbol, args...; kw...) # init with values from _plot_defaults d = KW( :letter => letter, @@ -283,13 +283,6 @@ function Axis(letter::AbstractString, args...; kw...) :show => true, # show or hide the axis? (useful for linked subplots) ) merge!(d, _axis_defaults) - # for sym in _axis_symbols - # k = symbol(letter * string(sym)) - # d[sym] = _plot_defaults[k] - # end - # for k in _axis_symbols_fonts_colors - # d[k] = _plot_defaults[k] - # end # update the defaults update!(Axis(d), args...; kw...) diff --git a/src/plot.jl b/src/plot.jl index 9fc125ba..63a5b28e 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -47,22 +47,18 @@ function plot(args...; kw...) d = KW(kw) preprocessArgs!(d) - # subplots = Subplot[layout] # TODO: build full list - # smap = SubplotMap(1 => layout) # TODO: actually build a map - - # TODO: this seems wrong... I only call getPlotArgs when creating a new plot?? - plotargs = merge(d, getPlotArgs(pkg, d, 1)) - # plt = _create_plot(pkg, plotargs) # create a new, blank plot - - # plt = Plot(nothing, pkg, 0, plotargs, Series[]) #, subplots, spmap, layout) - plt = Plot(plotargs) + # create an empty Plot, update the args using the inputs, then pass it + # to the backend to finish backend-specific initialization + plt = Plot() + _update_plot_args(plt, d) plt.o = _create_backend_figure(plt) - plt.layout, plt.subplots, plt.spmap = build_layout(pop!(d, :layout, :auto)) + # create the layout and subplots from the inputs + plt.layout, plt.subplots, plt.spmap = build_layout(plt.plotargs) for sp in plt.subplots - @show sp.o + # update the subplot/axis args from inputs, then pass to backend to init further + _update_subplot_args(plt, sp, d) _initialize_subplot(plt, sp) - @show sp.o end # now update the plot @@ -94,58 +90,58 @@ function strip_first_letter(s::Symbol) str[1:1], symbol(str[2:end]) end -# TODO: need to apply axis args to the axes, subplot args to the subplots, and plot args to the plot -# merge the KW d into the plot args -function _add_plotargs!(plt::Plot, d::KW) - # @show d - - # handle axis updates from a recipe - for letter in ("x","y","z") - # get the Axis object - asym = symbol(letter * "axis") - axis = plt.plotargs[asym] - if axis == nothing - # create a new one on first pass - axis = Axis(letter) - end - # @show 1,typeof(axis) - - # update xlabel, xscale, etc - for k in _axis_symbols - lk = symbol(letter * string(k)) - if haskey(d, lk) - axis[k] = d[lk] - end - end - # @show 2,axis - - # update guidefont, etc - for k in _axis_symbols_fonts_colors - if haskey(d, k) - axis[k] = d[k] - end - end - # @show 3,axis - - # update extrema and discrete values - datasym = symbol(letter) - if haskey(d, datasym) - v = d[datasym] - if eltype(v) <: Number - expand_extrema!(axis, v) - else - d[datasym] = discrete_value!(axis, v) - end - end - # @show 4,axis - end - - for k in keys(_plot_defaults) - if haskey(d, k) - plt.plotargs[k] = pop!(d, k) - end - end -end +# # TODO: need to apply axis args to the axes, subplot args to the subplots, and plot args to the plot +# # merge the KW d into the plot args +# function _add_plotargs!(plt::Plot, d::KW) +# # @show d +# +# # handle axis updates from a recipe +# for letter in ("x","y","z") +# # get the Axis object +# asym = symbol(letter * "axis") +# axis = plt.plotargs[asym] +# if axis == nothing +# # create a new one on first pass +# axis = Axis(letter) +# end +# # @show 1,typeof(axis) +# +# # update xlabel, xscale, etc +# for k in _axis_symbols +# lk = symbol(letter * string(k)) +# if haskey(d, lk) +# axis[k] = d[lk] +# end +# end +# # @show 2,axis +# +# # update guidefont, etc +# for k in _axis_symbols_fonts_colors +# if haskey(d, k) +# axis[k] = d[k] +# end +# end +# # @show 3,axis +# +# # update extrema and discrete values +# datasym = symbol(letter) +# if haskey(d, datasym) +# v = d[datasym] +# if eltype(v) <: Number +# expand_extrema!(axis, v) +# else +# d[datasym] = discrete_value!(axis, v) +# end +# end +# # @show 4,axis +# end +# +# for k in keys(_plot_defaults) +# if haskey(d, k) +# plt.plotargs[k] = pop!(d, k) +# end +# end +# end # this method recursively applies series recipes when the seriestype is not supported # natively by the backend @@ -154,11 +150,32 @@ function _apply_series_recipe(plt::Plot, d::KW) # dumpdict(d, "apply_series_recipe", true) if st in supportedTypes() # println("adding series!!") + + # getting ready to add the series... last update to subplot from anything + # that might have been added during series recipes + sp = d[:subplot] + _update_subplot_args(plt, sp, d) + + # adjust extrema and discrete info + for s in (:x, :y, :z) + data = d[s] + axis = sp.subplotargs[symbol(s, "axis")] + if eltype(data) <: Number + expand_extrema!(axis, data) + else + # TODO: need more here... gotta track the discrete reference value + # as well as any coord offset (think of boxplot shape coords... they all + # correspond to the same x-value) + d[s] = discrete_value!(axis, data) + end + end + + # add the series! warnOnUnsupported(plt.backend, d) series = Series(d) push!(plt.series_list, series) - # _add_series(plt.backend, plt, d) _add_series(plt, series) + else # get a sub list of series for this seriestype series_list = try @@ -275,15 +292,17 @@ function _plot!(plt::Plot, d::KW, args...) # @show typeof((kw[:x], kw[:y], kw[:z])) # end - # merge plot args... this is where we combine all the plot args from the user and - # from the recipes... axis info, colors, etc - # TODO: why do i need to check for the subplot key? - # if !haskey(d, :subplot) - for kw in vcat(kw_list, d) - _add_plotargs!(plt, kw) - end - handlePlotColors(plt.backend, plt.plotargs) + # # merge plot args... this is where we combine all the plot args from the user and + # # from the recipes... axis info, colors, etc + # # TODO: why do i need to check for the subplot key? + # # if !haskey(d, :subplot) + # # for kw in vcat(kw_list, d) + # for kw in kw_list + # _update_subplot_args(plt, kw[:subplot], kw) + # # _add_plotargs!(plt, kw) # end + # # handlePlotColors(plt.backend, plt.plotargs) + # # end # for kw in kw_list # @show typeof((kw[:x], kw[:y], kw[:z])) @@ -296,10 +315,20 @@ function _plot!(plt::Plot, d::KW, args...) plt.n += 1 end - # set default values, select from attribute cycles, and generally set the final attributes - _add_defaults!(kw, plt, i) + # get the Subplot object to which the series belongs + sp = slice_arg(kw[:subplot], i) + if sp == :auto + sp = 1 # TODO: something useful + end + sp = kw[:subplot] = get_subplot(plt, sp) - _replace_linewidth(kw) + # we update subplot args in case something like the color palatte is part of the recipe + _update_subplot_args(plt, sp, kw) + + # set default values, select from attribute cycles, and generally set the final attributes + _add_defaults!(kw, plt, sp, i) + + # # now we have a fully specified series, with colors chosen. we must recursively handle # series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype, diff --git a/src/series_new.jl b/src/series_new.jl index e1b78de2..ef3723a0 100644 --- a/src/series_new.jl +++ b/src/series_new.jl @@ -2,29 +2,30 @@ # we are going to build recipes to do the processing and splitting of the args -function _add_defaults!(d::KW, plt::Plot, commandIndex::Int) +function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int) pkg = plt.backend - n = plt.n - plotargs = getplotargs(plt, n) - plotIndex = convertSeriesIndex(plt, n) - globalIndex = n + # n = plt.n + # plotargs = getplotargs(plt, n) + # plotIndex = convertSeriesIndex(plt, n) + globalIndex = plt.n - # add defaults? - for k in keys(_series_defaults) - setDictValue(d, d, k, commandIndex, _series_defaults) + # # add defaults? + # for k in keys(_series_defaults) + # setDictValue(d, d, k, commandIndex, _series_defaults) + # end + for (k,v) in _series_defaults + slice_arg!(d, d, k, v, commandIndex) end - if d[:subplot] == :auto - # TODO: something useful - d[:subplot] = 1 - end + # this is how many series belong to this subplot + plotIndex = count(series -> series.d[:subplot] === sp, plt.series_list) - aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex) + # aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex) aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex) aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex) # update color - d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], plotargs, plotIndex) + d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], sp.subplotargs, plotIndex) # update colors for csym in (:linecolor, :markercolor, :fillcolor) @@ -35,13 +36,17 @@ function _add_defaults!(d::KW, plt::Plot, commandIndex::Int) d[:seriescolor] end else - getSeriesRGBColor(d[csym], plotargs, plotIndex) + getSeriesRGBColor(d[csym], sp.subplotargs, plotIndex) end end # update markerstrokecolor c = d[:markerstrokecolor] - c = (c == :match ? plotargs[:foreground_color] : getSeriesRGBColor(c, plotargs, plotIndex)) + c = if c == :match + sp.subplotargs[:foreground_color_subplot] + else + getSeriesRGBColor(c, sp.subplotargs, plotIndex) + end d[:markerstrokecolor] = c # update alphas @@ -62,11 +67,12 @@ function _add_defaults!(d::KW, plt::Plot, commandIndex::Int) # set label label = d[:label] label = (label == "AUTO" ? "y$globalIndex" : label) - if d[:axis] == :right && !(length(label) >= 4 && label[end-3:end] != " (R)") - label = string(label, " (R)") - end + # if d[:axis] == :right && !(length(label) >= 4 && label[end-3:end] != " (R)") + # label = string(label, " (R)") + # end d[:label] = label + _replace_linewidth(d) d end diff --git a/src/subplots.jl b/src/subplots.jl index 2ad77d7a..be7168ea 100644 --- a/src/subplots.jl +++ b/src/subplots.jl @@ -223,6 +223,7 @@ end get_subplot(plt::Plot, sp::Subplot) = sp get_subplot(plt::Plot, i::Integer) = plt.subplots[i] get_subplot(plt::Plot, k) = plt.spmap[k] +get_subplot(series::Series) = series.d[:subplot] # ---------------------------------------------------------------------- # ---------------------------------------------------------------------- diff --git a/src/types.jl b/src/types.jl index 16e5d749..a54d4a89 100644 --- a/src/types.jl +++ b/src/types.jl @@ -133,8 +133,9 @@ type Plot{T<:AbstractBackend} <: AbstractPlot{T} layout::AbstractLayout end -function Plot(plotargs::KW) - Plot(backend(), 0, plotargs, Series[], nothing, Subplot[], SubplotMap(), EmptyLayout()) +function Plot() + Plot(backend(), 0, KW(), Series[], nothing, + Subplot[], SubplotMap(), EmptyLayout()) end # ----------------------------------------------------------- diff --git a/src/utils.jl b/src/utils.jl index ae6ef516..17075ef9 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -268,8 +268,8 @@ limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits limsType(lims::Symbol) = lims == :auto ? :auto : :invalid limsType(lims) = :invalid -axis_symbol(letter, postfix) = symbol(letter * postfix) -axis_symbols(letter, postfix...) = map(s -> axis_symbol(letter, s), postfix) +# axis_symbol(letter, postfix) = symbol(letter * postfix) +# axis_symbols(letter, postfix...) = map(s -> axis_symbol(letter, s), postfix) Base.convert{T<:Real}(::Type{Vector{T}}, rng::Range{T}) = T[x for x in rng] Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]