From d70f462899a80635c2099bb80058eeaa9dd3ef78 Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Mon, 11 Apr 2016 16:16:47 -0400 Subject: [PATCH] z axis args; pyplot z axis; major pyplot cleanup; other cleanup/fixes --- src/args.jl | 598 +++++++++++++++++++------------------- src/backends/pyplot.jl | 580 +++++++++++++++++------------------- src/backends/supported.jl | 163 +++-------- src/plot.jl | 46 +-- src/utils.jl | 2 + 5 files changed, 604 insertions(+), 785 deletions(-) diff --git a/src/args.jl b/src/args.jl index 4627d400..540daf6e 100644 --- a/src/args.jl +++ b/src/args.jl @@ -159,6 +159,7 @@ const _plotDefaults = KW() _plotDefaults[:title] = "" _plotDefaults[:xlabel] = "" _plotDefaults[:ylabel] = "" +_plotDefaults[:zlabel] = "" _plotDefaults[:yrightlabel] = "" _plotDefaults[:legend] = :best _plotDefaults[:colorbar] = :legend @@ -169,10 +170,13 @@ _plotDefaults[:ylims] = :auto _plotDefaults[:zlims] = :auto _plotDefaults[:xticks] = :auto _plotDefaults[:yticks] = :auto +_plotDefaults[:zticks] = :auto _plotDefaults[:xscale] = :identity _plotDefaults[:yscale] = :identity +_plotDefaults[:zscale] = :identity _plotDefaults[:xflip] = false _plotDefaults[:yflip] = false +_plotDefaults[:zflip] = false _plotDefaults[:size] = (600,400) _plotDefaults[:pos] = (0,0) _plotDefaults[:windowtitle] = "Plots.jl" @@ -214,16 +218,15 @@ autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:non autopick_ignore_none_auto(notarr, idx::Integer) = notarr function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plotIndex::Int) - if d[sym] == :auto - d[sym] = autopick_ignore_none_auto(options, plotIndex) - elseif haskey(aliases, d[sym]) - d[sym] = aliases[d[sym]] - end + if d[sym] == :auto + d[sym] = autopick_ignore_none_auto(options, plotIndex) + elseif haskey(aliases, d[sym]) + d[sym] = aliases[d[sym]] + end end function aliases(aliasMap::KW, val) - # sort(vcat(val, collect(keys(filter((k,v)-> v==val, aliasMap))))) - sortedkeys(filter((k,v)-> v==val, aliasMap)) + sortedkeys(filter((k,v)-> v==val, aliasMap)) end # ----------------------------------------------------------------------------- @@ -278,6 +281,7 @@ end :annotations => :annotation, :xlab => :xlabel, :ylab => :ylabel, + :zlab => :zlabel, :yrlab => :yrightlabel, :ylabr => :yrightlabel, :y2lab => :yrightlabel, @@ -344,7 +348,7 @@ end # add all pluralized forms to the _keyAliases dict for arg in keys(_seriesDefaults) - _keyAliases[makeplural(arg)] = arg + _keyAliases[makeplural(arg)] = arg end @@ -360,189 +364,175 @@ end """ function default(k::Symbol) - k = get(_keyAliases, k, k) - if haskey(_seriesDefaults, k) - return _seriesDefaults[k] - elseif haskey(_plotDefaults, k) - return _plotDefaults[k] - else - error("Unknown key: ", k) - end + k = get(_keyAliases, k, k) + if haskey(_seriesDefaults, k) + return _seriesDefaults[k] + elseif haskey(_plotDefaults, k) + return _plotDefaults[k] + else + error("Unknown key: ", k) + end end function default(k::Symbol, v) - k = get(_keyAliases, k, k) - if haskey(_seriesDefaults, k) - _seriesDefaults[k] = v - elseif haskey(_plotDefaults, k) - _plotDefaults[k] = v - else - error("Unknown key: ", k) - end + k = get(_keyAliases, k, k) + if haskey(_seriesDefaults, k) + _seriesDefaults[k] = v + elseif haskey(_plotDefaults, k) + _plotDefaults[k] = v + else + error("Unknown key: ", k) + end end function default(; kw...) - for (k,v) in kw - default(k, v) - end + for (k,v) in kw + default(k, v) + end end # ----------------------------------------------------------------------------- function handleColors!(d::KW, arg, csym::Symbol) - try - if arg == :auto - d[csym] = :auto - else - c = colorscheme(arg) - d[csym] = c + try + if arg == :auto + d[csym] = :auto + else + c = colorscheme(arg) + d[csym] = c + end + return true end - return true - end - false + 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, axisletter::@compat(AbstractString), arg) - T = typeof(arg) - # if T <: Symbol +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") - arg = get(_scaleAliases, arg, arg) + if typeof(arg) <: Font + d[:tickfont] = arg - if arg in _allScales - d[symbol(axisletter * "scale")] = arg + elseif arg in _allScales + d[scale] = arg - elseif arg in (:flip, :invert, :inverted) - d[symbol(axisletter * "flip")] = true + elseif arg in (:flip, :invert, :inverted) + d[flip] = true - elseif T <: @compat(AbstractString) - d[symbol(axisletter * "label")] = arg + elseif T <: @compat(AbstractString) + d[label] = arg - # xlims/ylims - elseif (T <: Tuple || T <: AVec) && length(arg) == 2 - d[symbol(axisletter * "lims")] = 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[symbol(axisletter * "ticks")] = arg + # xticks/yticks + elseif T <: AVec + d[tick] = arg - elseif arg == nothing - d[symbol(axisletter * "ticks")] = [] + elseif arg == nothing + d[tick] = [] - else - warn("Skipped $(axisletter)axis arg $arg") + else + warn("Skipped $(letter)axis arg $arg") - end + end end function processLineArg(d::KW, arg) + # linetype + if allLineTypes(arg) + d[:linetype] = arg - # linetype - # if trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg) - if allLineTypes(arg) - d[:linetype] = arg + # linestyle + elseif allStyles(arg) + d[:linestyle] = arg - # linestyle - # elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg) - elseif allStyles(arg) - d[:linestyle] = arg + elseif typeof(arg) <: Stroke + arg.width == nothing || (d[:linewidth] = arg.width) + arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) + arg.alpha == nothing || (d[:linealpha] = arg.alpha) + arg.style == nothing || (d[:linestyle] = arg.style) - elseif typeof(arg) <: Stroke - arg.width == nothing || (d[:linewidth] = arg.width) - arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) - arg.alpha == nothing || (d[:linealpha] = arg.alpha) - arg.style == nothing || (d[:linestyle] = arg.style) + elseif typeof(arg) <: Brush + arg.size == nothing || (d[:fillrange] = arg.size) + arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) + arg.alpha == nothing || (d[:fillalpha] = arg.alpha) - elseif typeof(arg) <: Brush - arg.size == nothing || (d[:fillrange] = arg.size) - arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) - arg.alpha == nothing || (d[:fillalpha] = arg.alpha) + # linealpha + elseif allAlphas(arg) + d[:linealpha] = arg - # linealpha - # elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg) - elseif allAlphas(arg) - d[:linealpha] = arg + # linewidth + elseif allReals(arg) + d[:linewidth] = arg - # linewidth - # elseif trueOrAllTrue(a -> typeof(a) <: Real, arg) - elseif allReals(arg) - d[:linewidth] = arg + # color + elseif !handleColors!(d, arg, :linecolor) + warn("Skipped line arg $arg.") - # color - elseif !handleColors!(d, arg, :linecolor) - warn("Skipped line arg $arg.") - - end + end end function processMarkerArg(d::KW, arg) + # markershape + if allShapes(arg) + d[:markershape] = arg - # markershape - # if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg) - # d[:markershape] = arg + # stroke style + elseif allStyles(arg) + d[:markerstrokestyle] = arg - # elseif trueOrAllTrue(a -> isa(a, Shape), arg) - if allShapes(arg) - d[:markershape] = arg + elseif typeof(arg) <: Stroke + arg.width == nothing || (d[:markerstrokewidth] = arg.width) + arg.color == nothing || (d[:markerstrokecolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) + arg.alpha == nothing || (d[:markerstrokealpha] = arg.alpha) + arg.style == nothing || (d[:markerstrokestyle] = arg.style) - # stroke style - # elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg) - elseif allStyles(arg) - d[:markerstrokestyle] = arg + elseif typeof(arg) <: Brush + arg.size == nothing || (d[:markersize] = arg.size) + arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) + arg.alpha == nothing || (d[:markeralpha] = arg.alpha) - elseif typeof(arg) <: Stroke - arg.width == nothing || (d[:markerstrokewidth] = arg.width) - arg.color == nothing || (d[:markerstrokecolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) - arg.alpha == nothing || (d[:markerstrokealpha] = arg.alpha) - arg.style == nothing || (d[:markerstrokestyle] = arg.style) + # linealpha + elseif allAlphas(arg) + d[:markeralpha] = arg - elseif typeof(arg) <: Brush - arg.size == nothing || (d[:markersize] = arg.size) - arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) - arg.alpha == nothing || (d[:markeralpha] = arg.alpha) + # markersize + elseif allReals(arg) + d[:markersize] = arg - # linealpha - # elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg) - elseif allAlphas(arg) - d[:markeralpha] = arg + # markercolor + elseif !handleColors!(d, arg, :markercolor) + warn("Skipped marker arg $arg.") - # markersize - # elseif trueOrAllTrue(a -> typeof(a) <: Real, arg) - elseif allReals(arg) - d[:markersize] = arg - - # markercolor - elseif !handleColors!(d, arg, :markercolor) - warn("Skipped marker arg $arg.") - - end + end end function processFillArg(d::KW, arg) + if typeof(arg) <: Brush + arg.size == nothing || (d[:fillrange] = arg.size) + arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) + arg.alpha == nothing || (d[:fillalpha] = arg.alpha) - if typeof(arg) <: Brush - arg.size == nothing || (d[:fillrange] = arg.size) - arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) - arg.alpha == nothing || (d[:fillalpha] = arg.alpha) + # fillrange function + elseif allFunctions(arg) + d[:fillrange] = arg - # fillrange function - # elseif trueOrAllTrue(a -> isa(a, Function), arg) - elseif allFunctions(arg) - d[:fillrange] = arg + # fillalpha + elseif allAlphas(arg) + d[:fillalpha] = arg - # fillalpha - # elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg) - elseif allAlphas(arg) - d[:fillalpha] = arg + elseif !handleColors!(d, arg, :fillcolor) - elseif !handleColors!(d, arg, :fillcolor) - - d[:fillrange] = arg - end + d[:fillrange] = arg + end end _replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape) @@ -552,119 +542,116 @@ _replace_markershape(shape) = shape "Handle all preprocessing of args... break out colors/sizes/etc and replace aliases." function preprocessArgs!(d::KW) - replaceAliases!(d, _keyAliases) + replaceAliases!(d, _keyAliases) - # handle axis args - for axisletter in ("x", "y") - asym = symbol(axisletter * "axis") - for arg in wraptuple(get(d, asym, ())) - processAxisArg(d, axisletter, arg) + # handle axis args + for letter in ("x", "y", "z") + asym = symbol(letter * "axis") + for arg in wraptuple(get(d, asym, ())) + processAxisArg(d, letter, arg) + end + delete!(d, asym) + + # 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 end - delete!(d, asym) - # turn :labels into :ticks_and_labels - tsym = symbol(axisletter * "ticks") - if haskey(d, tsym) && ticksType(d[tsym]) == :labels - d[tsym] = (1:length(d[tsym]), d[tsym]) + # handle line args + for arg in wraptuple(get(d, :line, ())) + processLineArg(d, arg) end - end + delete!(d, :line) - # handle line args - for arg in wraptuple(get(d, :line, ())) - processLineArg(d, arg) - end - delete!(d, :line) + # handle marker args... default to ellipse if shape not set + anymarker = false + for arg in wraptuple(get(d, :marker, ())) + processMarkerArg(d, arg) + anymarker = true + end + delete!(d, :marker) + if haskey(d, :markershape) + d[:markershape] = _replace_markershape(d[:markershape]) + elseif anymarker + d[:markershape] = :ellipse + end - # handle marker args... default to ellipse if shape not set - anymarker = false - for arg in wraptuple(get(d, :marker, ())) - processMarkerArg(d, arg) - anymarker = true - end - delete!(d, :marker) - # if anymarker && !haskey(d, :markershape) - # d[:markershape] = :ellipse - # end - if haskey(d, :markershape) - d[:markershape] = _replace_markershape(d[:markershape]) - elseif anymarker - d[:markershape] = :ellipse - end - - # handle fill - for arg in wraptuple(get(d, :fill, ())) - processFillArg(d, arg) - end - delete!(d, :fill) + # handle fill + for arg in wraptuple(get(d, :fill, ())) + processFillArg(d, arg) + end + delete!(d, :fill) # convert into strokes and brushes - # legends - if haskey(d, :legend) - d[:legend] = convertLegendValue(d[:legend]) - end - if haskey(d, :colorbar) - d[:colorbar] = convertLegendValue(d[:colorbar]) - end - - # handle subplot links - if haskey(d, :link) - l = d[:link] - if isa(l, Bool) - d[:linkx] = l - d[:linky] = l - elseif isa(l, Function) - d[:linkx] = true - d[:linky] = true - d[:linkfunc] = l - else - warn("Unhandled/invalid link $l. Should be a Bool or a function mapping (row,column) -> (linkx, linky), where linkx/y can be Bool or Void (nothing)") + # legends + if haskey(d, :legend) + d[:legend] = convertLegendValue(d[:legend]) + end + if haskey(d, :colorbar) + d[:colorbar] = convertLegendValue(d[:colorbar]) end - delete!(d, :link) - end - return + # handle subplot links + if haskey(d, :link) + l = d[:link] + if isa(l, Bool) + d[:linkx] = l + d[:linky] = l + elseif isa(l, Function) + d[:linkx] = true + d[:linky] = true + d[:linkfunc] = l + else + warn("Unhandled/invalid link $l. Should be a Bool or a function mapping (row,column) -> (linkx, linky), where linkx/y can be Bool or Void (nothing)") + end + delete!(d, :link) + end + + return end # ----------------------------------------------------------------------------- "A special type that will break up incoming data into groups, and allow for easier creation of grouped plots" type GroupBy - groupLabels::Vector{UTF8String} # length == numGroups - groupIds::Vector{Vector{Int}} # list of indices for each group + groupLabels::Vector{UTF8String} # length == numGroups + groupIds::Vector{Vector{Int}} # list of indices for each group end # this is when given a vector-type of values to group by function extractGroupArgs(v::AVec, args...) - groupLabels = sort(collect(unique(v))) - n = length(groupLabels) - if n > 20 - error("Too many group labels. n=$n Is that intended?") - end - groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels] - GroupBy(map(string, groupLabels), groupIds) + groupLabels = sort(collect(unique(v))) + n = length(groupLabels) + if n > 20 + error("Too many group labels. n=$n Is that intended?") + end + groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels] + GroupBy(map(string, groupLabels), groupIds) end # expecting a mapping of "group label" to "group indices" function extractGroupArgs{T, V<:AVec{Int}}(idxmap::Dict{T,V}, args...) - groupLabels = sortedkeys(idxmap) - groupIds = VecI[collect(idxmap[k]) for k in groupLabels] - GroupBy(groupLabels, groupIds) + groupLabels = sortedkeys(idxmap) + groupIds = VecI[collect(idxmap[k]) for k in groupLabels] + GroupBy(groupLabels, groupIds) end # ----------------------------------------------------------------------------- function warnOnUnsupportedArgs(pkg::AbstractBackend, d::KW) - for k in sortedkeys(d) - if (!(k in supportedArgs(pkg)) - && k != :subplot - && d[k] != default(k)) - warn("Keyword argument $k not supported with $pkg. Choose from: $(supportedArgs(pkg))") + for k in sortedkeys(d) + if (!(k in supportedArgs(pkg)) + && k != :subplot + && d[k] != default(k)) + warn("Keyword argument $k not supported with $pkg. Choose from: $(supportedArgs(pkg))") + end end - end end _markershape_supported(pkg::AbstractBackend, shape::Symbol) = shape in supportedMarkers(pkg) @@ -672,18 +659,16 @@ _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[:linetype] == :none - || d[:linetype] in supportedTypes(pkg) - || warn("linetype $(d[:linetype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))")) - (d[:linestyle] in supportedStyles(pkg) - || warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))")) - (d[:markershape] == :none - || _markershape_supported(pkg, d[:markershape]) - # || d[:markershape] in supportedMarkers(pkg) - # || (Shape in supportedMarkers(pkg) && typeof(d[:markershape]) <: Shape) - || warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))")) + (d[:axis] in supportedAxes(pkg) + || warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))")) + (d[:linetype] == :none + || d[:linetype] in supportedTypes(pkg) + || warn("linetype $(d[:linetype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))")) + (d[:linestyle] in supportedStyles(pkg) + || warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))")) + (d[:markershape] == :none + || _markershape_supported(pkg, d[:markershape]) + || warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))")) end function warnOnUnsupportedScales(pkg::AbstractBackend, d::KW) @@ -702,8 +687,8 @@ end # 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] + c = mod1(idx, size(v,2)) + size(v,1) == 1 ? v[1,c] : v[:,c] end getArgValue(v, idx) = v @@ -711,23 +696,23 @@ 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]) <: @compat(Union{AbstractArray, Tuple}) && isempty(d_in[k])) - d_out[k] = getArgValue(d_in[k], idx) - else - d_out[k] = defaults[k] - end + if haskey(d_in, k) && !(typeof(d_in[k]) <: @compat(Union{AbstractArray, Tuple}) && isempty(d_in[k])) + d_out[k] = getArgValue(d_in[k], idx) + else + d_out[k] = defaults[k] + end end function convertLegendValue(val::Symbol) - if val in (:both, :all, :yes) - :best - elseif val in (:no, :none) - :none - elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend) - val - else - error("Invalid symbol for legend: $val") - end + if val in (:both, :all, :yes) + :best + elseif val in (:no, :none) + :none + elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend) + val + else + error("Invalid symbol for legend: $val") + end end convertLegendValue(val::Bool) = val ? :best : :none @@ -735,94 +720,93 @@ 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() + kwdict = KW(kw) + d = KW() - # add defaults? - if set_defaults - for k in keys(_plotDefaults) - setDictValue(kwdict, d, k, idx, _plotDefaults) + # add defaults? + if set_defaults + for k in keys(_plotDefaults) + setDictValue(kwdict, d, k, idx, _plotDefaults) + end end - end - for k in (:xscale, :yscale) - if haskey(_scaleAliases, d[k]) - d[k] = _scaleAliases[d[k]] + for k in (:xscale, :yscale) + if haskey(_scaleAliases, d[k]) + d[k] = _scaleAliases[d[k]] + end end - end - # handle legend/colorbar - d[:legend] = convertLegendValue(d[:legend]) - d[:colorbar] = convertLegendValue(d[:colorbar]) - if d[:colorbar] == :legend - d[:colorbar] = d[:legend] - end + # handle legend/colorbar + d[:legend] = convertLegendValue(d[:legend]) + d[:colorbar] = convertLegendValue(d[:colorbar]) + if d[:colorbar] == :legend + d[:colorbar] = d[:legend] + end - # convert color - handlePlotColors(pkg, d) + # convert color + handlePlotColors(pkg, d) - # no need for these - delete!(d, :x) - delete!(d, :y) + # no need for these + delete!(d, :x) + delete!(d, :y) - d + d end # build the argument dictionary for a series function getSeriesArgs(pkg::AbstractBackend, plotargs::KW, kw, commandIndex::Int, plotIndex::Int, globalIndex::Int) # TODO, pass in plotargs, not plt - kwdict = KW(kw) - d = KW() + kwdict = KW(kw) + d = KW() - # add defaults? - for k in keys(_seriesDefaults) - setDictValue(kwdict, d, k, commandIndex, _seriesDefaults) - end - - # groupby args? - for k in (:idxfilter, :numUncounted, :dataframe) - if haskey(kwdict, k) - d[k] = kwdict[k] + # add defaults? + for k in keys(_seriesDefaults) + setDictValue(kwdict, d, k, commandIndex, _seriesDefaults) end - end - if haskey(_typeAliases, d[:linetype]) - d[:linetype] = _typeAliases[d[:linetype]] - end + # groupby args? + for k in (:idxfilter, :numUncounted, :dataframe) + if haskey(kwdict, k) + d[k] = kwdict[k] + end + end - aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex) - aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex) - aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex) + if haskey(_typeAliases, d[:linetype]) + d[:linetype] = _typeAliases[d[:linetype]] + end - # update color - d[:linecolor] = getSeriesRGBColor(d[:linecolor], plotargs, plotIndex) + aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex) + aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex) + aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex) - # update markercolor - c = d[:markercolor] - c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex)) - d[:markercolor] = c + # update color + d[:linecolor] = getSeriesRGBColor(d[:linecolor], plotargs, plotIndex) - # update markerstrokecolor - c = d[:markerstrokecolor] - c = (c == :match ? plotargs[:foreground_color] : getSeriesRGBColor(c, plotargs, plotIndex)) - d[:markerstrokecolor] = c + # update markercolor + c = d[:markercolor] + c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex)) + d[:markercolor] = c - # update fillcolor - c = d[:fillcolor] - c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex)) - d[:fillcolor] = c + # update markerstrokecolor + c = d[:markerstrokecolor] + c = (c == :match ? plotargs[:foreground_color] : getSeriesRGBColor(c, plotargs, plotIndex)) + d[:markerstrokecolor] = c - # 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 - d[:label] = label + # update fillcolor + c = d[:fillcolor] + c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex)) + d[:fillcolor] = c - warnOnUnsupported(pkg, d) + # 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 + d[:label] = label + warnOnUnsupported(pkg, d) - d + d end diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 5c389462..2c1793b8 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -2,30 +2,30 @@ # https://github.com/stevengj/PyPlot.jl function _initialize_backend(::PyPlotBackend) - @eval begin - import PyPlot - export PyPlot - const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors")) - const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path")) - const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d")) - const pypatches = PyPlot.pywrap(PyPlot.pyimport("matplotlib.patches")) - # const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar")) - end - - if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay) - PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia - - # # TODO: how the hell can I use PyQt4?? - # "pyqt4"=>:qt_pyqt4 - # PyPlot.backend[1] = "pyqt4" - # PyPlot.gui[1] = :qt_pyqt4 - # PyPlot.switch_backend("Qt4Agg") - - # only turn on the gui if we want it - if PyPlot.gui != :none - PyPlot.pygui(true) + @eval begin + import PyPlot + export PyPlot + const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors")) + const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path")) + const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d")) + const pypatches = PyPlot.pywrap(PyPlot.pyimport("matplotlib.patches")) + # const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar")) + end + + if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay) + PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia + + # # TODO: how the hell can I use PyQt4?? + # "pyqt4"=>:qt_pyqt4 + # PyPlot.backend[1] = "pyqt4" + # PyPlot.gui[1] = :qt_pyqt4 + # PyPlot.switch_backend("Qt4Agg") + + # only turn on the gui if we want it + if PyPlot.gui != :none + PyPlot.pygui(true) + end end - end end # ------------------------------- @@ -35,15 +35,10 @@ getPyPlotColor(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), ( getPyPlotColor(cvec::ColorVector, α=nothing) = map(getPyPlotColor, convertColor(cvec, α).v) getPyPlotColor(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α)) getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α)) -# getPyPlotColor(c, alpha) = getPyPlotColor(colorscheme(c, alpha)) function getPyPlotColorMap(c::ColorGradient, α=nothing) - # c = ColorGradient(c.colors, c.values, alpha=α) - # pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", map(getPyPlotColor, getColorVector(c))) - # pyvals = [(c.values[i], getPyPlotColor(c.colors[i], α)) for i in 1:length(c.colors)] - pyvals = [(v, getPyPlotColor(getColorZ(c, v), α)) for v in c.values] - # @show c α pyvals - pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals) + pyvals = [(v, getPyPlotColor(getColorZ(c, v), α)) for v in c.values] + pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals) end # anything else just gets a bluesred gradient @@ -51,25 +46,24 @@ getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:bluesreds), # get the style (solid, dashed, etc) function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol) - linetype == :none && return " " - linestyle == :solid && return "-" - linestyle == :dash && return "--" - linestyle == :dot && return ":" - linestyle == :dashdot && return "-." - warn("Unknown linestyle $linestyle") - return "-" + linetype == :none && return " " + linestyle == :solid && return "-" + linestyle == :dash && return "--" + linestyle == :dot && return ":" + linestyle == :dashdot && return "-." + warn("Unknown linestyle $linestyle") + return "-" end function getPyPlotMarker(marker::Shape) - n = length(marker.vertices) - mat = zeros(n+1,2) - for (i,vert) in enumerate(marker.vertices) - mat[i,1] = vert[1] - mat[i,2] = vert[2] - end - mat[n+1,:] = mat[1,:] - pypath.pymember("Path")(mat) - # marker.vertices + n = length(marker.vertices) + mat = zeros(n+1,2) + for (i,vert) in enumerate(marker.vertices) + mat[i,1] = vert[1] + mat[i,2] = vert[2] + end + mat[n+1,:] = mat[1,:] + pypath.pymember("Path")(mat) end const _path_MOVETO = UInt8(1) @@ -99,56 +93,51 @@ end # get the marker shape function getPyPlotMarker(marker::Symbol) - marker == :none && return " " - marker == :ellipse && return "o" - marker == :rect && return "s" - marker == :diamond && return "D" - marker == :utriangle && return "^" - marker == :dtriangle && return "v" - marker == :cross && return "+" - marker == :xcross && return "x" - marker == :star5 && return "*" - marker == :pentagon && return "p" - marker == :hexagon && return "h" - marker == :octagon && return "8" - haskey(_shapes, marker) && return getPyPlotMarker(_shapes[marker]) + marker == :none && return " " + marker == :ellipse && return "o" + marker == :rect && return "s" + marker == :diamond && return "D" + marker == :utriangle && return "^" + marker == :dtriangle && return "v" + marker == :cross && return "+" + marker == :xcross && return "x" + marker == :star5 && return "*" + marker == :pentagon && return "p" + marker == :hexagon && return "h" + marker == :octagon && return "8" + haskey(_shapes, marker) && return getPyPlotMarker(_shapes[marker]) - warn("Unknown marker $marker") - return "o" + warn("Unknown marker $marker") + return "o" end # getPyPlotMarker(markers::AVec) = map(getPyPlotMarker, markers) function getPyPlotMarker(markers::AVec) - warn("Vectors of markers are currently unsupported in PyPlot: $markers") - getPyPlotMarker(markers[1]) + warn("Vectors of markers are currently unsupported in PyPlot: $markers") + getPyPlotMarker(markers[1]) end # pass through function getPyPlotMarker(marker::AbstractString) - @assert length(marker) == 1 - marker + @assert length(marker) == 1 + marker end function getPyPlotStepStyle(linetype::Symbol) - linetype == :steppost && return "steps-post" - linetype == :steppre && return "steps-pre" - return "default" + linetype == :steppost && return "steps-post" + linetype == :steppre && return "steps-pre" + return "default" end - -# immutable PyPlotFigWrapper -# fig -# kwargs # for add_subplot -# end +# --------------------------------------------------------------------------- type PyPlotAxisWrapper - ax - rightax - fig - kwargs # for add_subplot + ax + rightax + fig + kwargs # for add_subplot end -# getfig(wrap::@compat(Union{PyPlotAxisWrapper,PyPlotFigWrapper})) = wrap.fig getfig(wrap::PyPlotAxisWrapper) = wrap.fig @@ -165,14 +154,12 @@ function getLeftAxis(wrap::PyPlotAxisWrapper) wrap.ax end end -# getLeftAxis(wrap::PyPlotAxisWrapper) = wrap.ax -# getRightAxis(x) = getLeftAxis(x)[:twinx]() function getRightAxis(wrap::PyPlotAxisWrapper) - if wrap.rightax == nothing - wrap.rightax = getLeftAxis(wrap)[:twinx]() - end - wrap.rightax + if wrap.rightax == nothing + wrap.rightax = getLeftAxis(wrap)[:twinx]() + end + wrap.rightax end getLeftAxis(plt::Plot{PyPlotBackend}) = getLeftAxis(plt.o) @@ -209,118 +196,94 @@ function getPyPlotFunction(plt::Plot, axis::Symbol, linetype::Symbol) return ax[get(fmap, linetype, :plot)] end -function updateAxisColors(ax, fgcolor) - # for loc in ("bottom", "top", "left", "right") - for (loc, spine) in ax[:spines] - # ax[:spines][loc][:set_color](fgcolor) - spine[:set_color](fgcolor) - end - for axis in ("x", "y") - ax[:tick_params](axis=axis, colors=fgcolor, which="both") - end - for axis in (:yaxis, :xaxis) - ax[axis][:label][:set_color](fgcolor) - end - ax[:title][:set_color](fgcolor) -end - function handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Bool) - if smooth - xs, ys = regressionXY(d[:x], d[:y]) - ax[:plot](xs, ys, - # linestyle = getPyPlotLineStyle(:path, :dashdot), - color = getPyPlotColor(d[:linecolor]), - linewidth = 2 - ) - end + if smooth + xs, ys = regressionXY(d[:x], d[:y]) + ax[:plot](xs, ys, + # linestyle = getPyPlotLineStyle(:path, :dashdot), + color = getPyPlotColor(d[:linecolor]), + linewidth = 2 + ) + end end handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Real) = handleSmooth(plt, ax, d, true) +# --------------------------------------------------------------------------- - - -# makePyPlotCurrent(wrap::PyPlotFigWrapper) = PyPlot.figure(wrap.fig.o[:number]) -# makePyPlotCurrent(wrap::PyPlotAxisWrapper) = nothing #PyPlot.sca(wrap.ax.o) makePyPlotCurrent(wrap::PyPlotAxisWrapper) = wrap.ax == nothing ? PyPlot.figure(wrap.fig.o[:number]) : nothing makePyPlotCurrent(plt::Plot{PyPlotBackend}) = plt.o == nothing ? nothing : makePyPlotCurrent(plt.o) function _before_add_series(plt::Plot{PyPlotBackend}) - makePyPlotCurrent(plt) + makePyPlotCurrent(plt) end # ------------------------------------------------------------------ function pyplot_figure(plotargs::KW) - w,h = map(px2inch, plotargs[:size]) - bgcolor = getPyPlotColor(plotargs[:background_color]) + w,h = map(px2inch, plotargs[:size]) + bgcolor = getPyPlotColor(plotargs[:background_color]) - # reuse the current figure? - fig = if plotargs[:overwrite_figure] - PyPlot.gcf() - else - PyPlot.figure() - end + # reuse the current figure? + fig = if plotargs[:overwrite_figure] + PyPlot.gcf() + else + PyPlot.figure() + end - # update the specs - # fig[:set_size_inches](w,h, (isijulia() ? [] : [true])...) - fig[:set_size_inches](w, h, forward = true) - fig[:set_facecolor](bgcolor) - fig[:set_dpi](DPI) - fig[:set_tight_layout](true) + # update the specs + # fig[:set_size_inches](w,h, (isijulia() ? [] : [true])...) + fig[:set_size_inches](w, h, forward = true) + fig[:set_facecolor](bgcolor) + fig[:set_dpi](DPI) + fig[:set_tight_layout](true) - # clear the figure - PyPlot.clf() + # clear the figure + PyPlot.clf() - # resize the window - PyPlot.plt[:get_current_fig_manager]()[:resize](plotargs[:size]...) - fig + # resize the window + PyPlot.plt[:get_current_fig_manager]()[:resize](plotargs[:size]...) + fig end function pyplot_3d_setup!(wrap, d) - # 3D? - # if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper) - if trueOrAllTrue(lt -> lt in _3dTypes, get(d, :linetype, :none)) - push!(wrap.kwargs, (:projection, "3d")) - end + # 3D? + # if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper) + if trueOrAllTrue(lt -> lt in _3dTypes, get(d, :linetype, :none)) + push!(wrap.kwargs, (:projection, "3d")) + end end - -# TODO: -# fillto # might have to use barHack/histogramHack?? -# reg # true or false, add a regression line for each line -# pos # (Int,Int), move the enclosing window to this position -# windowtitle # string or symbol, set the title of the enclosing windowtitle -# screen # Integer, move enclosing window to this screen number (for multiscreen desktops) -# show # true or false, show the plot (in case you don't want the window to pop up right away) +# --------------------------------------------------------------------------- function _create_plot(pkg::PyPlotBackend; kw...) - # create the figure - d = KW(kw) + # create the figure + d = KW(kw) - # standalone plots will create a figure, but not if part of a subplot (do it later) - if haskey(d, :subplot) - wrap = nothing - else - wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(d), []) - # wrap = PyPlotAxisWrapper(nothing, nothing, PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true), []) + # standalone plots will create a figure, but not if part of a subplot (do it later) + if haskey(d, :subplot) + wrap = nothing + else + wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(d), []) + # wrap = PyPlotAxisWrapper(nothing, nothing, PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true), []) - # if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper) - # push!(wrap.kwargs, (:projection, "3d")) - # end - pyplot_3d_setup!(wrap, d) + # if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper) + # push!(wrap.kwargs, (:projection, "3d")) + # end + pyplot_3d_setup!(wrap, d) - if get(d, :polar, false) - push!(wrap.kwargs, (:polar, true)) + if get(d, :polar, false) + push!(wrap.kwargs, (:polar, true)) + end end - end - plt = Plot(wrap, pkg, 0, d, KW[]) - plt + plt = Plot(wrap, pkg, 0, d, KW[]) + plt end +# --------------------------------------------------------------------------- function _add_series(pkg::PyPlotBackend, plt::Plot; kw...) d = KW(kw) @@ -430,7 +393,6 @@ function _add_series(pkg::PyPlotBackend, plt::Plot; kw...) extra_kwargs[:c] = convert(Vector{Float64}, d[:zcolor]) extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeralpha]) else - # extra_kwargs[:c] = getPyPlotColor(c, d[:markeralpha]) ppc = getPyPlotColor(c, d[:markeralpha]) # total hack due to PyPlot bug (see issue #145). @@ -442,9 +404,6 @@ function _add_series(pkg::PyPlotBackend, plt::Plot; kw...) extra_kwargs[:c] = ppc end - # if d[:markeralpha] != nothing - # extra_kwargs[:alpha] = d[:markeralpha] - # end extra_kwargs[:edgecolors] = getPyPlotColor(d[:markerstrokecolor], d[:markerstrokealpha]) extra_kwargs[:linewidths] = d[:markerstrokewidth] else @@ -456,12 +415,6 @@ function _add_series(pkg::PyPlotBackend, plt::Plot; kw...) end end - # if d[:markeralpha] != nothing - # extra_kwargs[:alpha] = d[:markeralpha] - # elseif d[:linealpha] != nothing - # extra_kwargs[:alpha] = d[:linealpha] - # end - # set these for all types if !(lt in (:contour,:surface,:wireframe,:heatmap)) if !(lt in (:scatter, :scatter3d, :shape)) @@ -555,190 +508,185 @@ end function Base.getindex(plt::Plot{PyPlotBackend}, i::Integer) - series = plt.seriesargs[i][:serieshandle] - try - return series[:get_data]() - catch - xy = series[:get_offsets]() - return vec(xy[:,1]), vec(xy[:,2]) - end + series = plt.seriesargs[i][:serieshandle] + try + return series[:get_data]() + catch + xy = series[:get_offsets]() + return vec(xy[:,1]), vec(xy[:,2]) + end end function minmaxseries(ds, vec, axis) - lo, hi = Inf, -Inf - for d in ds - d[:axis] == axis || continue - v = d[vec] - if length(v) > 0 - vlo, vhi = extrema(v) - lo = min(lo, vlo) - hi = max(hi, vhi) + lo, hi = Inf, -Inf + for d in ds + d[:axis] == axis || continue + v = d[vec] + if length(v) > 0 + vlo, vhi = extrema(v) + lo = min(lo, vlo) + hi = max(hi, vhi) + end end - end - if lo == hi - hi = if lo == 0 - 1e-6 - else - hi + min(abs(1e-2hi), 1e-6) + if lo == hi + hi = if lo == 0 + 1e-6 + else + hi + min(abs(1e-2hi), 1e-6) + end end - end - lo, hi + lo, hi end # TODO: this needs to handle one-sided fixed limits function set_lims!(plt::Plot{PyPlotBackend}, axis::Symbol) - ax = getAxis(plt, axis) - if plt.plotargs[:xlims] == :auto - ax[:set_xlim](minmaxseries(plt.seriesargs, :x, axis)...) - end - if plt.plotargs[:ylims] == :auto - ax[:set_ylim](minmaxseries(plt.seriesargs, :y, axis)...) - end - if plt.plotargs[:zlims] == :auto && haskey(ax, :set_zlim) - ax[:set_zlim](minmaxseries(plt.seriesargs, :z, axis)...) - end + ax = getAxis(plt, axis) + if plt.plotargs[:xlims] == :auto + ax[:set_xlim](minmaxseries(plt.seriesargs, :x, axis)...) + end + if plt.plotargs[:ylims] == :auto + ax[:set_ylim](minmaxseries(plt.seriesargs, :y, axis)...) + end + if plt.plotargs[:zlims] == :auto && haskey(ax, :set_zlim) + ax[:set_zlim](minmaxseries(plt.seriesargs, :z, axis)...) + end end function Base.setindex!{X,Y}(plt::Plot{PyPlotBackend}, xy::Tuple{X,Y}, i::Integer) - d = plt.seriesargs[i] - series = d[:serieshandle] - x, y = xy - d[:x], d[:y] = x, y - try - series[:set_data](x, y) - catch - series[:set_offsets](hcat(x, y)) - end + d = plt.seriesargs[i] + series = d[:serieshandle] + x, y = xy + d[:x], d[:y] = x, y + try + series[:set_data](x, y) + catch + series[:set_offsets](hcat(x, y)) + end - set_lims!(plt, d[:axis]) - plt + set_lims!(plt, d[:axis]) + plt end function Base.setindex!{X,Y,Z}(plt::Plot{PyPlotBackend}, xyz::Tuple{X,Y,Z}, i::Integer) - warn("setindex not implemented for xyz") - plt + warn("setindex not implemented for xyz") + plt end -# ----------------------------------------------------------------- +# -------------------------------------------------------------------------- -function addPyPlotLims(ax, lims, dimension) +function addPyPlotLims(ax, lims, letter) lims == :auto && return ltype = limsType(lims) if ltype == :limits - if dimension == :xlim - isfinite(lims[1]) && ax[:set_xlim](left = lims[1]) - isfinite(lims[2]) && ax[:set_xlim](right = lims[2]) - elseif dimension == :ylim - isfinite(lims[1]) && ax[:set_ylim](bottom = lims[1]) - isfinite(lims[2]) && ax[:set_ylim](top = lims[2]) - elseif dimension == :zlim && haskey(ax, :set_zlim) - isfinite(lims[1]) && ax[:set_zlim](bottom = lims[1]) - isfinite(lims[2]) && ax[:set_zlim](top = lims[2]) - else - error("Invalid argument at position 3: $dimension") + setf = ax[symbol("set_", letter, "lim")] + l1, l2 = lims + if isfinite(l1) + letter == "x" ? setf(left = l1) : setf(bottom = l1) + end + if isfinite(l2) + letter == "x" ? setf(right = l2) : setf(top = l2) end else - error("Invalid input for $dimension: ", lims) + error("Invalid input for $letter: ", lims) end end -function addPyPlotTicks(ax, ticks, isx::Bool) - ticks == :auto && return - if ticks == :none || ticks == nothing - ticks = zeros(0) - end +function addPyPlotTicks(ax, ticks, letter) + ticks == :auto && return + if ticks == :none || ticks == nothing + ticks = zeros(0) + end - ttype = ticksType(ticks) - if ttype == :ticks - ax[isx ? :set_xticks : :set_yticks](ticks) - elseif ttype == :ticks_and_labels - ax[isx ? :set_xticks : :set_yticks](ticks[1]) - ax[isx ? :set_xticklabels : :set_yticklabels](ticks[2]) - else - error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks) - end + ttype = ticksType(ticks) + tickfunc = symbol("set_", letter, "ticks") + labfunc = symbol("set_", letter, "ticklabels") + if ttype == :ticks + ax[tickfunc](ticks) + elseif ttype == :ticks_and_labels + ax[tickfunc](ticks[1]) + ax[labfunc](ticks[2]) + else + error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks) + end end -usingRightAxis(plt::Plot{PyPlotBackend}) = any(args -> args[:axis] in (:right,:auto), plt.seriesargs) +function applyPyPlotScale(ax, scaleType::Symbol, letter) + func = ax[symbol("set_", letter, "scale")] + scaleType == :identity && return func("linear") + scaleType == :ln && return func("log", basex = e, basey = e) + scaleType == :log2 && return func("log", basex = 2, basey = 2) + scaleType == :log10 && return func("log", basex = 10, basey = 10) + warn("Unhandled scaleType: ", scaleType) +end + + +function updateAxisColors(ax, fgcolor) + for (loc, spine) in ax[:spines] + spine[:set_color](fgcolor) + end + for letter in ("x", "y", "z") + axis = axis_symbol(letter, "axis") + if haskey(ax, axis) + ax[:tick_params](axis=letter, colors=fgcolor, which="both") + ax[axis][:label][:set_color](fgcolor) + end + end + ax[:title][:set_color](fgcolor) +end + +function usingRightAxis(plt::Plot{PyPlotBackend}) + any(args -> args[:axis] in (:right,:auto), plt.seriesargs) +end + + +# -------------------------------------------------------------------------- + function _update_plot(plt::Plot{PyPlotBackend}, d::KW) - figorax = plt.o - ax = getLeftAxis(figorax) - # PyPlot.sca(ax) + figorax = plt.o + ax = getLeftAxis(figorax) + ticksz = get(d, :tickfont, plt.plotargs[:tickfont]).pointsize + guidesz = get(d, :guidefont, plt.plotargs[:guidefont]).pointsize + # title + haskey(d, :title) && ax[:set_title](d[:title]) + ax[:title][:set_fontsize](guidesz) - # title and axis labels - # haskey(d, :title) && PyPlot.title(d[:title]) - haskey(d, :title) && ax[:set_title](d[:title]) - haskey(d, :xlabel) && ax[:set_xlabel](d[:xlabel]) - if haskey(d, :ylabel) - ax[:set_ylabel](d[:ylabel]) - end - if usingRightAxis(plt) && get(d, :yrightlabel, "") != "" - rightax = getRightAxis(figorax) - rightax[:set_ylabel](d[:yrightlabel]) - end - - # scales - haskey(d, :xscale) && applyPyPlotScale(ax, d[:xscale], true) - haskey(d, :yscale) && applyPyPlotScale(ax, d[:yscale], false) - - # limits and ticks - haskey(d, :xlims) && addPyPlotLims(ax, d[:xlims], :xlim) - haskey(d, :ylims) && addPyPlotLims(ax, d[:ylims], :ylim) - haskey(d, :zlims) && addPyPlotLims(ax, d[:zlims], :zlim) - haskey(d, :xticks) && addPyPlotTicks(ax, d[:xticks], true) - haskey(d, :yticks) && addPyPlotTicks(ax, d[:yticks], false) - - if get(d, :xflip, false) - ax[:invert_xaxis]() - end - if get(d, :yflip, false) - ax[:invert_yaxis]() - end - - axes = [getLeftAxis(figorax)] - if usingRightAxis(plt) - push!(axes, getRightAxis(figorax)) - end - - # font sizes - for ax in axes - # haskey(d, :yrightlabel) || continue - - - # guides - sz = get(d, :guidefont, plt.plotargs[:guidefont]).pointsize - ax[:title][:set_fontsize](sz) - ax[:xaxis][:label][:set_fontsize](sz) - ax[:yaxis][:label][:set_fontsize](sz) - - # ticks - sz = get(d, :tickfont, plt.plotargs[:tickfont]).pointsize - for sym in (:get_xticklabels, :get_yticklabels) - for lab in ax[sym]() - lab[:set_fontsize](sz) - end + # handle right y axis + axes = [getLeftAxis(figorax)] + if usingRightAxis(plt) + push!(axes, getRightAxis(figorax)) + if get(d, :yrightlabel, "") != "" + rightax = getRightAxis(figorax) + rightax[:set_ylabel](d[:yrightlabel]) + end end - # grid - if get(d, :grid, false) - ax[:xaxis][:grid](true) - ax[:yaxis][:grid](true) - ax[:set_axisbelow](true) + # handle each axis in turn + for letter in ("x", "y", "z") + axis, scale, lims, ticks, flip, lab = axis_symbols(letter, "axis", "scale", "lims", "ticks", "flip", "label") + haskey(ax, axis) || continue + haskey(d, scale) && applyPyPlotScale(ax, d[scale], letter) + haskey(d, lims) && addPyPlotLims(ax, d[lims], letter) + haskey(d, ticks) && addPyPlotTicks(ax, d[ticks], letter) + haskey(d, lab) && ax[symbol("set_", letter, "label")](d[lab]) + if get(d, flip, false) + ax[symbol("invert_", letter, "axis")]() + end + for tmpax in axes + tmpax[axis][:label][:set_fontsize](guidesz) + for lab in tmpax[symbol("get_", letter, "ticklabels")]() + lab[:set_fontsize](ticksz) + end + if get(d, :grid, false) + fgcolor = getPyPlotColor(plt.plotargs[:foreground_color]) + tmpax[axis][:grid](true, color = fgcolor) + tmpax[:set_axisbelow](true) + end + end end - end - end -function applyPyPlotScale(ax, scaleType::Symbol, isx::Bool) - func = ax[isx ? :set_xscale : :set_yscale] - scaleType == :identity && return func("linear") - scaleType == :ln && return func("log", basex = e, basey = e) - scaleType == :log2 && return func("log", basex = 2, basey = 2) - scaleType == :log10 && return func("log", basex = 10, basey = 10) - warn("Unhandled scaleType: ", scaleType) -end # ----------------------------------------------------------------- diff --git a/src/backends/supported.jl b/src/backends/supported.jl index 51aeb5ca..a5eccb20 100644 --- a/src/backends/supported.jl +++ b/src/backends/supported.jl @@ -21,72 +21,36 @@ stringsSupported() = stringsSupported(backend()) supportedArgs(::GadflyBackend) = [ :annotation, - # :axis, - :background_color, - :linecolor, - :color_palette, - :fillrange, - :fillcolor, - :fillalpha, - :foreground_color, + :background_color, :foreground_color, :color_palette, :group, :label, - :layout, - :legend, - :colorbar, - :linestyle, :linetype, - :linewidth, - :linealpha, - :markershape, - :markercolor, - :markersize, - :markeralpha, - :markerstrokewidth, - :markerstrokecolor, - :markerstrokealpha, - # :markerstrokestyle, - :n, + :linecolor, :linestyle, :linewidth, :linealpha, + :markershape, :markercolor, :markersize, :markeralpha, + :markerstrokewidth, :markerstrokecolor, :markerstrokealpha, + :fillrange, :fillcolor, :fillalpha, :nbins, - :nc, - :nr, - # :pos, + :n, :nc, :nr, :layout, :smooth, - :show, - :size, - :title, - :windowtitle, - :x, - :xlabel, - :xlims, - :xticks, - :y, - :ylabel, - :ylims, - # :yrightlabel, - :yticks, - :xscale, - :yscale, - :xflip, - :yflip, + :title, :windowtitle, :show, :size, + :x, :xlabel, :xlims, :xticks, :xscale, :xflip, + :y, :ylabel, :ylims, :yticks, :yscale, :yflip, + # :z, :zlabel, :zlims, :zticks, :zscale, :zflip, :z, - :zcolor, - :tickfont, - :guidefont, - :legendfont, - :grid, - # :surface, - :levels, - :xerror, - :yerror, - :ribbon, - :quiver, + :tickfont, :guidefont, :legendfont, + :grid, :legend, :colorbar, + :zcolor, :levels, + :xerror, :yerror, + :ribbon, :quiver, :orientation, ] supportedAxes(::GadflyBackend) = [:auto, :left] -supportedTypes(::GadflyBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, - :scatter, :hist2d, :hexbin, :hist, :bar, :box, :violin, :quiver, - :hline, :vline, :contour, :shape] +supportedTypes(::GadflyBackend) = [ + :none, :line, :path, :steppre, :steppost, :sticks, + :scatter, :hist2d, :hexbin, :hist, + :bar, :box, :violin, :quiver, + :hline, :vline, :contour, :shape + ] supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape) supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt] @@ -110,78 +74,41 @@ subplotSupported(::ImmerseBackend) = true supportedArgs(::PyPlotBackend) = [ :annotation, - :axis, - :background_color, - :linecolor, - :color_palette, - :fillrange, - :fillcolor, - :foreground_color, + :background_color, :foreground_color, :color_palette, :group, :label, - :layout, - :legend, - :colorbar, - :linestyle, :linetype, - :linewidth, - :markershape, - :markercolor, - :markersize, - :markerstrokewidth, - :markerstrokecolor, - :markerstrokealpha, - # :markerstrokestyle, - :n, + :linecolor, :linestyle, :linewidth, :linealpha, + :markershape, :markercolor, :markersize, :markeralpha, + :markerstrokewidth, :markerstrokecolor, :markerstrokealpha, + :fillrange, :fillcolor, :fillalpha, :nbins, - :nc, - :nr, - # :pos, + :n, :nc, :nr, :layout, :smooth, - # :ribbon, - :show, - :size, - :title, - :windowtitle, - :x, - :xlabel, - :xlims, - :xticks, - :y, - :ylabel, - :ylims, - :zlims, - :yrightlabel, - :yticks, - :xscale, - :yscale, - :xflip, - :yflip, + :title, :windowtitle, :show, :size, + :x, :xlabel, :xlims, :xticks, :xscale, :xflip, + :y, :ylabel, :ylims, :yticks, :yscale, :yflip, + :axis, :yrightlabel, + :z, :zlabel, :zlims, :zticks, :zscale, :zflip, :z, - :zcolor, # only supported for scatter/scatter3d - :tickfont, - :guidefont, - :legendfont, - :grid, - # :surface, - :levels, - :fillalpha, - :linealpha, - :markeralpha, - :overwrite_figure, - :xerror, - :yerror, - :ribbon, - :quiver, + :tickfont, :guidefont, :legendfont, + :grid, :legend, :colorbar, + :zcolor, :levels, + :xerror, :yerror, + :ribbon, :quiver, :orientation, + :overwrite_figure, :polar, ] supportedAxes(::PyPlotBackend) = _allAxes -supportedTypes(::PyPlotBackend) = [:none, :line, :path, :steppre, :steppost, :shape, - :scatter, :hist2d, :hexbin, :hist, :density, :bar, :box, :violin, :quiver, - :hline, :vline, :contour, :path3d, :scatter3d, :surface, :wireframe, :heatmap] +supportedTypes(::PyPlotBackend) = [ + :none, :line, :path, :steppre, :steppost, :shape, + :scatter, :hist2d, :hexbin, :hist, :density, + :bar, :box, :violin, :quiver, + :hline, :vline, :heatmap, + :contour, :path3d, :scatter3d, :surface, :wireframe + ] supportedStyles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot] -# supportedMarkers(::PyPlotBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon] supportedMarkers(::PyPlotBackend) = vcat(_allMarkers, Shape) supportedScales(::PyPlotBackend) = [:identity, :ln, :log2, :log10] subplotSupported(::PyPlotBackend) = true diff --git a/src/plot.jl b/src/plot.jl index a688ee4f..3a773f70 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -78,62 +78,19 @@ function plot!(plt::Plot, args...; kw...) args = _apply_recipe(d, args...; kw...) dumpdict(d, "After plot! preprocessing") - # @show groupargs map(typeof, args) - warnOnUnsupportedArgs(plt.backend, d) # just in case the backend needs to set up the plot (make it current or something) _before_add_series(plt) # # grouping - # groupargs = get(d, :group, nothing) == nothing ? [nothing] : [extractGroupArgs(d[:group], args...)] - # # @show groupargs - - # TODO: get the GroupBy object (or nothing), and loop through the groups, doing the following section many times - # dumpdict(d, "before", true) groupby = if haskey(d, :group) extractGroupArgs(d[:group], args...) else nothing end - # dumpdict(d, "after", true) - # @show groupby map(typeof, args) _add_series(plt, d, groupby, args...) - - # - # # get the list of dictionaries, one per series - # @show groupargs map(typeof, args) - # dumpdict(d, "before process_inputs") - # process_inputs(plt, d, groupargs..., args...) - # dumpdict(d, "after process_inputs", true) - # seriesArgList, xmeta, ymeta = build_series_args(plt, d) - # # seriesArgList, xmeta, ymeta = build_series_args(plt, groupargs..., args...; d...) - # - # # if we were able to extract guide information from the series inputs, then update the plot - # # @show xmeta, ymeta - # updateDictWithMeta(d, plt.plotargs, xmeta, true) - # updateDictWithMeta(d, plt.plotargs, ymeta, false) - # - # # now we can plot the series - # for (i,di) in enumerate(seriesArgList) - # plt.n += 1 - # - # if !stringsSupported() - # setTicksFromStringVector(d, di, :x, :xticks) - # setTicksFromStringVector(d, di, :y, :yticks) - # end - # - # # remove plot args - # for k in keys(_plotDefaults) - # delete!(di, k) - # end - # - # dumpdict(di, "Series $i") - # - # _add_series(plt.backend, plt; di...) - # end - _add_annotations(plt, d) warnOnUnsupportedScales(plt.backend, d) @@ -150,7 +107,7 @@ function plot!(plt::Plot, args...; kw...) current(plt) - # NOTE: lets ignore the show param and effectively use the semicolon at the end of the REPL statement + # note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement # # do we want to show it? if haskey(d, :show) && d[:show] gui() @@ -212,6 +169,7 @@ function _add_series(plt::Plot, d::KW, ::Void, args...; if !stringsSupported() setTicksFromStringVector(d, di, :x, :xticks) setTicksFromStringVector(d, di, :y, :yticks) + setTicksFromStringVector(d, di, :z, :zticks) end # remove plot args diff --git a/src/utils.jl b/src/utils.jl index 2682c6f8..c3aa2288 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -231,6 +231,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) 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]