z axis args; pyplot z axis; major pyplot cleanup; other cleanup/fixes

This commit is contained in:
Thomas Breloff 2016-04-11 16:16:47 -04:00
parent 0a636b5899
commit d70f462899
5 changed files with 604 additions and 785 deletions

View File

@ -159,6 +159,7 @@ const _plotDefaults = KW()
_plotDefaults[:title] = "" _plotDefaults[:title] = ""
_plotDefaults[:xlabel] = "" _plotDefaults[:xlabel] = ""
_plotDefaults[:ylabel] = "" _plotDefaults[:ylabel] = ""
_plotDefaults[:zlabel] = ""
_plotDefaults[:yrightlabel] = "" _plotDefaults[:yrightlabel] = ""
_plotDefaults[:legend] = :best _plotDefaults[:legend] = :best
_plotDefaults[:colorbar] = :legend _plotDefaults[:colorbar] = :legend
@ -169,10 +170,13 @@ _plotDefaults[:ylims] = :auto
_plotDefaults[:zlims] = :auto _plotDefaults[:zlims] = :auto
_plotDefaults[:xticks] = :auto _plotDefaults[:xticks] = :auto
_plotDefaults[:yticks] = :auto _plotDefaults[:yticks] = :auto
_plotDefaults[:zticks] = :auto
_plotDefaults[:xscale] = :identity _plotDefaults[:xscale] = :identity
_plotDefaults[:yscale] = :identity _plotDefaults[:yscale] = :identity
_plotDefaults[:zscale] = :identity
_plotDefaults[:xflip] = false _plotDefaults[:xflip] = false
_plotDefaults[:yflip] = false _plotDefaults[:yflip] = false
_plotDefaults[:zflip] = false
_plotDefaults[:size] = (600,400) _plotDefaults[:size] = (600,400)
_plotDefaults[:pos] = (0,0) _plotDefaults[:pos] = (0,0)
_plotDefaults[:windowtitle] = "Plots.jl" _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 autopick_ignore_none_auto(notarr, idx::Integer) = notarr
function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plotIndex::Int) function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plotIndex::Int)
if d[sym] == :auto if d[sym] == :auto
d[sym] = autopick_ignore_none_auto(options, plotIndex) d[sym] = autopick_ignore_none_auto(options, plotIndex)
elseif haskey(aliases, d[sym]) elseif haskey(aliases, d[sym])
d[sym] = aliases[d[sym]] d[sym] = aliases[d[sym]]
end end
end end
function aliases(aliasMap::KW, val) 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 end
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@ -278,6 +281,7 @@ end
:annotations => :annotation, :annotations => :annotation,
:xlab => :xlabel, :xlab => :xlabel,
:ylab => :ylabel, :ylab => :ylabel,
:zlab => :zlabel,
:yrlab => :yrightlabel, :yrlab => :yrightlabel,
:ylabr => :yrightlabel, :ylabr => :yrightlabel,
:y2lab => :yrightlabel, :y2lab => :yrightlabel,
@ -344,7 +348,7 @@ end
# add all pluralized forms to the _keyAliases dict # add all pluralized forms to the _keyAliases dict
for arg in keys(_seriesDefaults) for arg in keys(_seriesDefaults)
_keyAliases[makeplural(arg)] = arg _keyAliases[makeplural(arg)] = arg
end end
@ -360,189 +364,175 @@ end
""" """
function default(k::Symbol) function default(k::Symbol)
k = get(_keyAliases, k, k) k = get(_keyAliases, k, k)
if haskey(_seriesDefaults, k) if haskey(_seriesDefaults, k)
return _seriesDefaults[k] return _seriesDefaults[k]
elseif haskey(_plotDefaults, k) elseif haskey(_plotDefaults, k)
return _plotDefaults[k] return _plotDefaults[k]
else else
error("Unknown key: ", k) error("Unknown key: ", k)
end end
end end
function default(k::Symbol, v) function default(k::Symbol, v)
k = get(_keyAliases, k, k) k = get(_keyAliases, k, k)
if haskey(_seriesDefaults, k) if haskey(_seriesDefaults, k)
_seriesDefaults[k] = v _seriesDefaults[k] = v
elseif haskey(_plotDefaults, k) elseif haskey(_plotDefaults, k)
_plotDefaults[k] = v _plotDefaults[k] = v
else else
error("Unknown key: ", k) error("Unknown key: ", k)
end end
end end
function default(; kw...) function default(; kw...)
for (k,v) in kw for (k,v) in kw
default(k, v) default(k, v)
end end
end end
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
function handleColors!(d::KW, arg, csym::Symbol) function handleColors!(d::KW, arg, csym::Symbol)
try try
if arg == :auto if arg == :auto
d[csym] = :auto d[csym] = :auto
else else
c = colorscheme(arg) c = colorscheme(arg)
d[csym] = c d[csym] = c
end
return true
end end
return true false
end
false
end end
# given one value (:log, or :flip, or (-1,1), etc), set the appropriate arg # 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 # TODO: use trueOrAllTrue for subplots which can pass vectors for these
function processAxisArg(d::KW, axisletter::@compat(AbstractString), arg) function processAxisArg(d::KW, letter::AbstractString, arg)
T = typeof(arg) T = typeof(arg)
# if T <: Symbol 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 elseif arg in _allScales
d[symbol(axisletter * "scale")] = arg d[scale] = arg
elseif arg in (:flip, :invert, :inverted) elseif arg in (:flip, :invert, :inverted)
d[symbol(axisletter * "flip")] = true d[flip] = true
elseif T <: @compat(AbstractString) elseif T <: @compat(AbstractString)
d[symbol(axisletter * "label")] = arg d[label] = arg
# xlims/ylims # xlims/ylims
elseif (T <: Tuple || T <: AVec) && length(arg) == 2 elseif (T <: Tuple || T <: AVec) && length(arg) == 2
d[symbol(axisletter * "lims")] = arg d[typeof(arg[1]) <: Number ? lim : tick] = arg
# xticks/yticks # xticks/yticks
elseif T <: AVec elseif T <: AVec
d[symbol(axisletter * "ticks")] = arg d[tick] = arg
elseif arg == nothing elseif arg == nothing
d[symbol(axisletter * "ticks")] = [] d[tick] = []
else else
warn("Skipped $(axisletter)axis arg $arg") warn("Skipped $(letter)axis arg $arg")
end end
end end
function processLineArg(d::KW, arg) function processLineArg(d::KW, arg)
# linetype
if allLineTypes(arg)
d[:linetype] = arg
# linetype # linestyle
# if trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg) elseif allStyles(arg)
if allLineTypes(arg) d[:linestyle] = arg
d[:linetype] = arg
# linestyle elseif typeof(arg) <: Stroke
# elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg) arg.width == nothing || (d[:linewidth] = arg.width)
elseif allStyles(arg) arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
d[:linestyle] = arg arg.alpha == nothing || (d[:linealpha] = arg.alpha)
arg.style == nothing || (d[:linestyle] = arg.style)
elseif typeof(arg) <: Stroke elseif typeof(arg) <: Brush
arg.width == nothing || (d[:linewidth] = arg.width) arg.size == nothing || (d[:fillrange] = arg.size)
arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.alpha == nothing || (d[:linealpha] = arg.alpha) arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
arg.style == nothing || (d[:linestyle] = arg.style)
elseif typeof(arg) <: Brush # linealpha
arg.size == nothing || (d[:fillrange] = arg.size) elseif allAlphas(arg)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) d[:linealpha] = arg
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
# linealpha # linewidth
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg) elseif allReals(arg)
elseif allAlphas(arg) d[:linewidth] = arg
d[:linealpha] = arg
# linewidth # color
# elseif trueOrAllTrue(a -> typeof(a) <: Real, arg) elseif !handleColors!(d, arg, :linecolor)
elseif allReals(arg) warn("Skipped line arg $arg.")
d[:linewidth] = arg
# color end
elseif !handleColors!(d, arg, :linecolor)
warn("Skipped line arg $arg.")
end
end end
function processMarkerArg(d::KW, arg) function processMarkerArg(d::KW, arg)
# markershape
if allShapes(arg)
d[:markershape] = arg
# markershape # stroke style
# if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg) elseif allStyles(arg)
# d[:markershape] = arg d[:markerstrokestyle] = arg
# elseif trueOrAllTrue(a -> isa(a, Shape), arg) elseif typeof(arg) <: Stroke
if allShapes(arg) arg.width == nothing || (d[:markerstrokewidth] = arg.width)
d[:markershape] = arg 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 typeof(arg) <: Brush
# elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg) arg.size == nothing || (d[:markersize] = arg.size)
elseif allStyles(arg) arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
d[:markerstrokestyle] = arg arg.alpha == nothing || (d[:markeralpha] = arg.alpha)
elseif typeof(arg) <: Stroke # linealpha
arg.width == nothing || (d[:markerstrokewidth] = arg.width) elseif allAlphas(arg)
arg.color == nothing || (d[:markerstrokecolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) d[:markeralpha] = arg
arg.alpha == nothing || (d[:markerstrokealpha] = arg.alpha)
arg.style == nothing || (d[:markerstrokestyle] = arg.style)
elseif typeof(arg) <: Brush # markersize
arg.size == nothing || (d[:markersize] = arg.size) elseif allReals(arg)
arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) d[:markersize] = arg
arg.alpha == nothing || (d[:markeralpha] = arg.alpha)
# linealpha # markercolor
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg) elseif !handleColors!(d, arg, :markercolor)
elseif allAlphas(arg) warn("Skipped marker arg $arg.")
d[:markeralpha] = arg
# markersize end
# 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) 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 # fillrange function
arg.size == nothing || (d[:fillrange] = arg.size) elseif allFunctions(arg)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) d[:fillrange] = arg
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
# fillrange function # fillalpha
# elseif trueOrAllTrue(a -> isa(a, Function), arg) elseif allAlphas(arg)
elseif allFunctions(arg) d[:fillalpha] = arg
d[:fillrange] = arg
# fillalpha elseif !handleColors!(d, arg, :fillcolor)
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
d[:fillalpha] = arg
elseif !handleColors!(d, arg, :fillcolor) d[:fillrange] = arg
end
d[:fillrange] = arg
end
end end
_replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape) _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." "Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
function preprocessArgs!(d::KW) function preprocessArgs!(d::KW)
replaceAliases!(d, _keyAliases) replaceAliases!(d, _keyAliases)
# handle axis args # handle axis args
for axisletter in ("x", "y") for letter in ("x", "y", "z")
asym = symbol(axisletter * "axis") asym = symbol(letter * "axis")
for arg in wraptuple(get(d, asym, ())) for arg in wraptuple(get(d, asym, ()))
processAxisArg(d, axisletter, arg) 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 end
delete!(d, asym)
# turn :labels into :ticks_and_labels # handle line args
tsym = symbol(axisletter * "ticks") for arg in wraptuple(get(d, :line, ()))
if haskey(d, tsym) && ticksType(d[tsym]) == :labels processLineArg(d, arg)
d[tsym] = (1:length(d[tsym]), d[tsym])
end end
end delete!(d, :line)
# handle line args # handle marker args... default to ellipse if shape not set
for arg in wraptuple(get(d, :line, ())) anymarker = false
processLineArg(d, arg) for arg in wraptuple(get(d, :marker, ()))
end processMarkerArg(d, arg)
delete!(d, :line) 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 # handle fill
anymarker = false for arg in wraptuple(get(d, :fill, ()))
for arg in wraptuple(get(d, :marker, ())) processFillArg(d, arg)
processMarkerArg(d, arg) end
anymarker = true delete!(d, :fill)
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)
# convert into strokes and brushes # convert into strokes and brushes
# legends # legends
if haskey(d, :legend) if haskey(d, :legend)
d[:legend] = convertLegendValue(d[:legend]) d[:legend] = convertLegendValue(d[:legend])
end end
if haskey(d, :colorbar) if haskey(d, :colorbar)
d[:colorbar] = convertLegendValue(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)")
end 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 end
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
"A special type that will break up incoming data into groups, and allow for easier creation of grouped plots" "A special type that will break up incoming data into groups, and allow for easier creation of grouped plots"
type GroupBy type GroupBy
groupLabels::Vector{UTF8String} # length == numGroups groupLabels::Vector{UTF8String} # length == numGroups
groupIds::Vector{Vector{Int}} # list of indices for each group groupIds::Vector{Vector{Int}} # list of indices for each group
end end
# this is when given a vector-type of values to group by # this is when given a vector-type of values to group by
function extractGroupArgs(v::AVec, args...) function extractGroupArgs(v::AVec, args...)
groupLabels = sort(collect(unique(v))) groupLabels = sort(collect(unique(v)))
n = length(groupLabels) n = length(groupLabels)
if n > 20 if n > 20
error("Too many group labels. n=$n Is that intended?") error("Too many group labels. n=$n Is that intended?")
end end
groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels] groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels]
GroupBy(map(string, groupLabels), groupIds) GroupBy(map(string, groupLabels), groupIds)
end end
# expecting a mapping of "group label" to "group indices" # expecting a mapping of "group label" to "group indices"
function extractGroupArgs{T, V<:AVec{Int}}(idxmap::Dict{T,V}, args...) function extractGroupArgs{T, V<:AVec{Int}}(idxmap::Dict{T,V}, args...)
groupLabels = sortedkeys(idxmap) groupLabels = sortedkeys(idxmap)
groupIds = VecI[collect(idxmap[k]) for k in groupLabels] groupIds = VecI[collect(idxmap[k]) for k in groupLabels]
GroupBy(groupLabels, groupIds) GroupBy(groupLabels, groupIds)
end end
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
function warnOnUnsupportedArgs(pkg::AbstractBackend, d::KW) function warnOnUnsupportedArgs(pkg::AbstractBackend, d::KW)
for k in sortedkeys(d) for k in sortedkeys(d)
if (!(k in supportedArgs(pkg)) if (!(k in supportedArgs(pkg))
&& k != :subplot && k != :subplot
&& d[k] != default(k)) && d[k] != default(k))
warn("Keyword argument $k not supported with $pkg. Choose from: $(supportedArgs(pkg))") warn("Keyword argument $k not supported with $pkg. Choose from: $(supportedArgs(pkg))")
end
end end
end
end end
_markershape_supported(pkg::AbstractBackend, shape::Symbol) = shape in supportedMarkers(pkg) _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]) _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
function warnOnUnsupported(pkg::AbstractBackend, d::KW) function warnOnUnsupported(pkg::AbstractBackend, d::KW)
(d[:axis] in supportedAxes(pkg) (d[:axis] in supportedAxes(pkg)
|| warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))")) || warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))"))
(d[:linetype] == :none (d[:linetype] == :none
|| d[:linetype] in supportedTypes(pkg) || d[:linetype] in supportedTypes(pkg)
|| warn("linetype $(d[:linetype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))")) || warn("linetype $(d[:linetype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))"))
(d[:linestyle] in supportedStyles(pkg) (d[:linestyle] in supportedStyles(pkg)
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))")) || warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))"))
(d[:markershape] == :none (d[:markershape] == :none
|| _markershape_supported(pkg, d[:markershape]) || _markershape_supported(pkg, d[:markershape])
# || d[:markershape] in supportedMarkers(pkg) || warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))"))
# || (Shape in supportedMarkers(pkg) && typeof(d[:markershape]) <: Shape)
|| warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))"))
end end
function warnOnUnsupportedScales(pkg::AbstractBackend, d::KW) function warnOnUnsupportedScales(pkg::AbstractBackend, d::KW)
@ -702,8 +687,8 @@ end
# anything else is returned as-is # anything else is returned as-is
# getArgValue(v::Tuple, idx::Int) = v[mod1(idx, length(v))] # getArgValue(v::Tuple, idx::Int) = v[mod1(idx, length(v))]
function getArgValue(v::AMat, idx::Int) function getArgValue(v::AMat, idx::Int)
c = mod1(idx, size(v,2)) c = mod1(idx, size(v,2))
size(v,1) == 1 ? v[1,c] : v[:,c] size(v,1) == 1 ? v[1,c] : v[:,c]
end end
getArgValue(v, idx) = v 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. # 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. # 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) 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])) 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) d_out[k] = getArgValue(d_in[k], idx)
else else
d_out[k] = defaults[k] d_out[k] = defaults[k]
end end
end end
function convertLegendValue(val::Symbol) function convertLegendValue(val::Symbol)
if val in (:both, :all, :yes) if val in (:both, :all, :yes)
:best :best
elseif val in (:no, :none) elseif val in (:no, :none)
:none :none
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend) elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend)
val val
else else
error("Invalid symbol for legend: $val") error("Invalid symbol for legend: $val")
end end
end end
convertLegendValue(val::Bool) = val ? :best : :none convertLegendValue(val::Bool) = val ? :best : :none
@ -735,94 +720,93 @@ convertLegendValue(val::Bool) = val ? :best : :none
# build the argument dictionary for the plot # build the argument dictionary for the plot
function getPlotArgs(pkg::AbstractBackend, kw, idx::Int; set_defaults = true) function getPlotArgs(pkg::AbstractBackend, kw, idx::Int; set_defaults = true)
kwdict = KW(kw) kwdict = KW(kw)
d = KW() d = KW()
# add defaults? # add defaults?
if set_defaults if set_defaults
for k in keys(_plotDefaults) for k in keys(_plotDefaults)
setDictValue(kwdict, d, k, idx, _plotDefaults) setDictValue(kwdict, d, k, idx, _plotDefaults)
end
end end
end
for k in (:xscale, :yscale) for k in (:xscale, :yscale)
if haskey(_scaleAliases, d[k]) if haskey(_scaleAliases, d[k])
d[k] = _scaleAliases[d[k]] d[k] = _scaleAliases[d[k]]
end
end end
end
# handle legend/colorbar # handle legend/colorbar
d[:legend] = convertLegendValue(d[:legend]) d[:legend] = convertLegendValue(d[:legend])
d[:colorbar] = convertLegendValue(d[:colorbar]) d[:colorbar] = convertLegendValue(d[:colorbar])
if d[:colorbar] == :legend if d[:colorbar] == :legend
d[:colorbar] = d[:legend] d[:colorbar] = d[:legend]
end end
# convert color # convert color
handlePlotColors(pkg, d) handlePlotColors(pkg, d)
# no need for these # no need for these
delete!(d, :x) delete!(d, :x)
delete!(d, :y) delete!(d, :y)
d d
end end
# build the argument dictionary for a series # 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 function getSeriesArgs(pkg::AbstractBackend, plotargs::KW, kw, commandIndex::Int, plotIndex::Int, globalIndex::Int) # TODO, pass in plotargs, not plt
kwdict = KW(kw) kwdict = KW(kw)
d = KW() d = KW()
# add defaults? # add defaults?
for k in keys(_seriesDefaults) for k in keys(_seriesDefaults)
setDictValue(kwdict, d, k, commandIndex, _seriesDefaults) setDictValue(kwdict, d, k, commandIndex, _seriesDefaults)
end
# groupby args?
for k in (:idxfilter, :numUncounted, :dataframe)
if haskey(kwdict, k)
d[k] = kwdict[k]
end end
end
if haskey(_typeAliases, d[:linetype]) # groupby args?
d[:linetype] = _typeAliases[d[:linetype]] for k in (:idxfilter, :numUncounted, :dataframe)
end if haskey(kwdict, k)
d[k] = kwdict[k]
end
end
aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex) if haskey(_typeAliases, d[:linetype])
aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex) d[:linetype] = _typeAliases[d[:linetype]]
aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex) end
# update color aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex)
d[:linecolor] = getSeriesRGBColor(d[:linecolor], plotargs, plotIndex) aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex)
aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex)
# update markercolor # update color
c = d[:markercolor] d[:linecolor] = getSeriesRGBColor(d[:linecolor], plotargs, plotIndex)
c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex))
d[:markercolor] = c
# update markerstrokecolor # update markercolor
c = d[:markerstrokecolor] c = d[:markercolor]
c = (c == :match ? plotargs[:foreground_color] : getSeriesRGBColor(c, plotargs, plotIndex)) c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex))
d[:markerstrokecolor] = c d[:markercolor] = c
# update fillcolor # update markerstrokecolor
c = d[:fillcolor] c = d[:markerstrokecolor]
c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex)) c = (c == :match ? plotargs[:foreground_color] : getSeriesRGBColor(c, plotargs, plotIndex))
d[:fillcolor] = c d[:markerstrokecolor] = c
# set label # update fillcolor
label = d[:label] c = d[:fillcolor]
label = (label == "AUTO" ? "y$globalIndex" : label) c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex))
if d[:axis] == :right && !(length(label) >= 4 && label[end-3:end] != " (R)") d[:fillcolor] = c
label = string(label, " (R)")
end
d[:label] = label
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 end

View File

@ -2,30 +2,30 @@
# https://github.com/stevengj/PyPlot.jl # https://github.com/stevengj/PyPlot.jl
function _initialize_backend(::PyPlotBackend) function _initialize_backend(::PyPlotBackend)
@eval begin @eval begin
import PyPlot import PyPlot
export PyPlot export PyPlot
const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors")) const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path")) const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path"))
const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d")) const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d"))
const pypatches = PyPlot.pywrap(PyPlot.pyimport("matplotlib.patches")) const pypatches = PyPlot.pywrap(PyPlot.pyimport("matplotlib.patches"))
# const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar")) # const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar"))
end end
if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay) if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia
# # TODO: how the hell can I use PyQt4?? # # TODO: how the hell can I use PyQt4??
# "pyqt4"=>:qt_pyqt4 # "pyqt4"=>:qt_pyqt4
# PyPlot.backend[1] = "pyqt4" # PyPlot.backend[1] = "pyqt4"
# PyPlot.gui[1] = :qt_pyqt4 # PyPlot.gui[1] = :qt_pyqt4
# PyPlot.switch_backend("Qt4Agg") # PyPlot.switch_backend("Qt4Agg")
# only turn on the gui if we want it # only turn on the gui if we want it
if PyPlot.gui != :none if PyPlot.gui != :none
PyPlot.pygui(true) PyPlot.pygui(true)
end
end 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(cvec::ColorVector, α=nothing) = map(getPyPlotColor, convertColor(cvec, α).v)
getPyPlotColor(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α)) getPyPlotColor(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α))
getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α)) getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
# getPyPlotColor(c, alpha) = getPyPlotColor(colorscheme(c, alpha))
function getPyPlotColorMap(c::ColorGradient, α=nothing) function getPyPlotColorMap(c::ColorGradient, α=nothing)
# c = ColorGradient(c.colors, c.values, alpha=α) pyvals = [(v, getPyPlotColor(getColorZ(c, v), α)) for v in c.values]
# pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", map(getPyPlotColor, getColorVector(c))) pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
# 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)
end end
# anything else just gets a bluesred gradient # anything else just gets a bluesred gradient
@ -51,25 +46,24 @@ getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:bluesreds),
# get the style (solid, dashed, etc) # get the style (solid, dashed, etc)
function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol) function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol)
linetype == :none && return " " linetype == :none && return " "
linestyle == :solid && return "-" linestyle == :solid && return "-"
linestyle == :dash && return "--" linestyle == :dash && return "--"
linestyle == :dot && return ":" linestyle == :dot && return ":"
linestyle == :dashdot && return "-." linestyle == :dashdot && return "-."
warn("Unknown linestyle $linestyle") warn("Unknown linestyle $linestyle")
return "-" return "-"
end end
function getPyPlotMarker(marker::Shape) function getPyPlotMarker(marker::Shape)
n = length(marker.vertices) n = length(marker.vertices)
mat = zeros(n+1,2) mat = zeros(n+1,2)
for (i,vert) in enumerate(marker.vertices) for (i,vert) in enumerate(marker.vertices)
mat[i,1] = vert[1] mat[i,1] = vert[1]
mat[i,2] = vert[2] mat[i,2] = vert[2]
end end
mat[n+1,:] = mat[1,:] mat[n+1,:] = mat[1,:]
pypath.pymember("Path")(mat) pypath.pymember("Path")(mat)
# marker.vertices
end end
const _path_MOVETO = UInt8(1) const _path_MOVETO = UInt8(1)
@ -99,56 +93,51 @@ end
# get the marker shape # get the marker shape
function getPyPlotMarker(marker::Symbol) function getPyPlotMarker(marker::Symbol)
marker == :none && return " " marker == :none && return " "
marker == :ellipse && return "o" marker == :ellipse && return "o"
marker == :rect && return "s" marker == :rect && return "s"
marker == :diamond && return "D" marker == :diamond && return "D"
marker == :utriangle && return "^" marker == :utriangle && return "^"
marker == :dtriangle && return "v" marker == :dtriangle && return "v"
marker == :cross && return "+" marker == :cross && return "+"
marker == :xcross && return "x" marker == :xcross && return "x"
marker == :star5 && return "*" marker == :star5 && return "*"
marker == :pentagon && return "p" marker == :pentagon && return "p"
marker == :hexagon && return "h" marker == :hexagon && return "h"
marker == :octagon && return "8" marker == :octagon && return "8"
haskey(_shapes, marker) && return getPyPlotMarker(_shapes[marker]) haskey(_shapes, marker) && return getPyPlotMarker(_shapes[marker])
warn("Unknown marker $marker") warn("Unknown marker $marker")
return "o" return "o"
end end
# getPyPlotMarker(markers::AVec) = map(getPyPlotMarker, markers) # getPyPlotMarker(markers::AVec) = map(getPyPlotMarker, markers)
function getPyPlotMarker(markers::AVec) function getPyPlotMarker(markers::AVec)
warn("Vectors of markers are currently unsupported in PyPlot: $markers") warn("Vectors of markers are currently unsupported in PyPlot: $markers")
getPyPlotMarker(markers[1]) getPyPlotMarker(markers[1])
end end
# pass through # pass through
function getPyPlotMarker(marker::AbstractString) function getPyPlotMarker(marker::AbstractString)
@assert length(marker) == 1 @assert length(marker) == 1
marker marker
end end
function getPyPlotStepStyle(linetype::Symbol) function getPyPlotStepStyle(linetype::Symbol)
linetype == :steppost && return "steps-post" linetype == :steppost && return "steps-post"
linetype == :steppre && return "steps-pre" linetype == :steppre && return "steps-pre"
return "default" return "default"
end end
# ---------------------------------------------------------------------------
# immutable PyPlotFigWrapper
# fig
# kwargs # for add_subplot
# end
type PyPlotAxisWrapper type PyPlotAxisWrapper
ax ax
rightax rightax
fig fig
kwargs # for add_subplot kwargs # for add_subplot
end end
# getfig(wrap::@compat(Union{PyPlotAxisWrapper,PyPlotFigWrapper})) = wrap.fig
getfig(wrap::PyPlotAxisWrapper) = wrap.fig getfig(wrap::PyPlotAxisWrapper) = wrap.fig
@ -165,14 +154,12 @@ function getLeftAxis(wrap::PyPlotAxisWrapper)
wrap.ax wrap.ax
end end
end end
# getLeftAxis(wrap::PyPlotAxisWrapper) = wrap.ax
# getRightAxis(x) = getLeftAxis(x)[:twinx]()
function getRightAxis(wrap::PyPlotAxisWrapper) function getRightAxis(wrap::PyPlotAxisWrapper)
if wrap.rightax == nothing if wrap.rightax == nothing
wrap.rightax = getLeftAxis(wrap)[:twinx]() wrap.rightax = getLeftAxis(wrap)[:twinx]()
end end
wrap.rightax wrap.rightax
end end
getLeftAxis(plt::Plot{PyPlotBackend}) = getLeftAxis(plt.o) 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)] return ax[get(fmap, linetype, :plot)]
end 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) function handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Bool)
if smooth if smooth
xs, ys = regressionXY(d[:x], d[:y]) xs, ys = regressionXY(d[:x], d[:y])
ax[:plot](xs, ys, ax[:plot](xs, ys,
# linestyle = getPyPlotLineStyle(:path, :dashdot), # linestyle = getPyPlotLineStyle(:path, :dashdot),
color = getPyPlotColor(d[:linecolor]), color = getPyPlotColor(d[:linecolor]),
linewidth = 2 linewidth = 2
) )
end end
end end
handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Real) = handleSmooth(plt, ax, d, true) 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(wrap::PyPlotAxisWrapper) = wrap.ax == nothing ? PyPlot.figure(wrap.fig.o[:number]) : nothing
makePyPlotCurrent(plt::Plot{PyPlotBackend}) = plt.o == nothing ? nothing : makePyPlotCurrent(plt.o) makePyPlotCurrent(plt::Plot{PyPlotBackend}) = plt.o == nothing ? nothing : makePyPlotCurrent(plt.o)
function _before_add_series(plt::Plot{PyPlotBackend}) function _before_add_series(plt::Plot{PyPlotBackend})
makePyPlotCurrent(plt) makePyPlotCurrent(plt)
end end
# ------------------------------------------------------------------ # ------------------------------------------------------------------
function pyplot_figure(plotargs::KW) function pyplot_figure(plotargs::KW)
w,h = map(px2inch, plotargs[:size]) w,h = map(px2inch, plotargs[:size])
bgcolor = getPyPlotColor(plotargs[:background_color]) bgcolor = getPyPlotColor(plotargs[:background_color])
# reuse the current figure? # reuse the current figure?
fig = if plotargs[:overwrite_figure] fig = if plotargs[:overwrite_figure]
PyPlot.gcf() PyPlot.gcf()
else else
PyPlot.figure() PyPlot.figure()
end end
# update the specs # update the specs
# fig[:set_size_inches](w,h, (isijulia() ? [] : [true])...) # fig[:set_size_inches](w,h, (isijulia() ? [] : [true])...)
fig[:set_size_inches](w, h, forward = true) fig[:set_size_inches](w, h, forward = true)
fig[:set_facecolor](bgcolor) fig[:set_facecolor](bgcolor)
fig[:set_dpi](DPI) fig[:set_dpi](DPI)
fig[:set_tight_layout](true) fig[:set_tight_layout](true)
# clear the figure # clear the figure
PyPlot.clf() PyPlot.clf()
# resize the window # resize the window
PyPlot.plt[:get_current_fig_manager]()[:resize](plotargs[:size]...) PyPlot.plt[:get_current_fig_manager]()[:resize](plotargs[:size]...)
fig fig
end end
function pyplot_3d_setup!(wrap, d) function pyplot_3d_setup!(wrap, d)
# 3D? # 3D?
# if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper) # if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper)
if trueOrAllTrue(lt -> lt in _3dTypes, get(d, :linetype, :none)) if trueOrAllTrue(lt -> lt in _3dTypes, get(d, :linetype, :none))
push!(wrap.kwargs, (:projection, "3d")) push!(wrap.kwargs, (:projection, "3d"))
end end
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...) function _create_plot(pkg::PyPlotBackend; kw...)
# create the figure # create the figure
d = KW(kw) d = KW(kw)
# standalone plots will create a figure, but not if part of a subplot (do it later) # standalone plots will create a figure, but not if part of a subplot (do it later)
if haskey(d, :subplot) if haskey(d, :subplot)
wrap = nothing wrap = nothing
else else
wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(d), []) wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(d), [])
# wrap = PyPlotAxisWrapper(nothing, nothing, PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true), []) # 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) # if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper)
# push!(wrap.kwargs, (:projection, "3d")) # push!(wrap.kwargs, (:projection, "3d"))
# end # end
pyplot_3d_setup!(wrap, d) pyplot_3d_setup!(wrap, d)
if get(d, :polar, false) if get(d, :polar, false)
push!(wrap.kwargs, (:polar, true)) push!(wrap.kwargs, (:polar, true))
end
end end
end
plt = Plot(wrap, pkg, 0, d, KW[]) plt = Plot(wrap, pkg, 0, d, KW[])
plt plt
end end
# ---------------------------------------------------------------------------
function _add_series(pkg::PyPlotBackend, plt::Plot; kw...) function _add_series(pkg::PyPlotBackend, plt::Plot; kw...)
d = KW(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[:c] = convert(Vector{Float64}, d[:zcolor])
extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeralpha]) extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeralpha])
else else
# extra_kwargs[:c] = getPyPlotColor(c, d[:markeralpha])
ppc = getPyPlotColor(c, d[:markeralpha]) ppc = getPyPlotColor(c, d[:markeralpha])
# total hack due to PyPlot bug (see issue #145). # 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 extra_kwargs[:c] = ppc
end end
# if d[:markeralpha] != nothing
# extra_kwargs[:alpha] = d[:markeralpha]
# end
extra_kwargs[:edgecolors] = getPyPlotColor(d[:markerstrokecolor], d[:markerstrokealpha]) extra_kwargs[:edgecolors] = getPyPlotColor(d[:markerstrokecolor], d[:markerstrokealpha])
extra_kwargs[:linewidths] = d[:markerstrokewidth] extra_kwargs[:linewidths] = d[:markerstrokewidth]
else else
@ -456,12 +415,6 @@ function _add_series(pkg::PyPlotBackend, plt::Plot; kw...)
end end
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 # set these for all types
if !(lt in (:contour,:surface,:wireframe,:heatmap)) if !(lt in (:contour,:surface,:wireframe,:heatmap))
if !(lt in (:scatter, :scatter3d, :shape)) if !(lt in (:scatter, :scatter3d, :shape))
@ -555,190 +508,185 @@ end
function Base.getindex(plt::Plot{PyPlotBackend}, i::Integer) function Base.getindex(plt::Plot{PyPlotBackend}, i::Integer)
series = plt.seriesargs[i][:serieshandle] series = plt.seriesargs[i][:serieshandle]
try try
return series[:get_data]() return series[:get_data]()
catch catch
xy = series[:get_offsets]() xy = series[:get_offsets]()
return vec(xy[:,1]), vec(xy[:,2]) return vec(xy[:,1]), vec(xy[:,2])
end end
end end
function minmaxseries(ds, vec, axis) function minmaxseries(ds, vec, axis)
lo, hi = Inf, -Inf lo, hi = Inf, -Inf
for d in ds for d in ds
d[:axis] == axis || continue d[:axis] == axis || continue
v = d[vec] v = d[vec]
if length(v) > 0 if length(v) > 0
vlo, vhi = extrema(v) vlo, vhi = extrema(v)
lo = min(lo, vlo) lo = min(lo, vlo)
hi = max(hi, vhi) hi = max(hi, vhi)
end
end end
end if lo == hi
if lo == hi hi = if lo == 0
hi = if lo == 0 1e-6
1e-6 else
else hi + min(abs(1e-2hi), 1e-6)
hi + min(abs(1e-2hi), 1e-6) end
end end
end lo, hi
lo, hi
end end
# TODO: this needs to handle one-sided fixed limits # TODO: this needs to handle one-sided fixed limits
function set_lims!(plt::Plot{PyPlotBackend}, axis::Symbol) function set_lims!(plt::Plot{PyPlotBackend}, axis::Symbol)
ax = getAxis(plt, axis) ax = getAxis(plt, axis)
if plt.plotargs[:xlims] == :auto if plt.plotargs[:xlims] == :auto
ax[:set_xlim](minmaxseries(plt.seriesargs, :x, axis)...) ax[:set_xlim](minmaxseries(plt.seriesargs, :x, axis)...)
end end
if plt.plotargs[:ylims] == :auto if plt.plotargs[:ylims] == :auto
ax[:set_ylim](minmaxseries(plt.seriesargs, :y, axis)...) ax[:set_ylim](minmaxseries(plt.seriesargs, :y, axis)...)
end end
if plt.plotargs[:zlims] == :auto && haskey(ax, :set_zlim) if plt.plotargs[:zlims] == :auto && haskey(ax, :set_zlim)
ax[:set_zlim](minmaxseries(plt.seriesargs, :z, axis)...) ax[:set_zlim](minmaxseries(plt.seriesargs, :z, axis)...)
end end
end end
function Base.setindex!{X,Y}(plt::Plot{PyPlotBackend}, xy::Tuple{X,Y}, i::Integer) function Base.setindex!{X,Y}(plt::Plot{PyPlotBackend}, xy::Tuple{X,Y}, i::Integer)
d = plt.seriesargs[i] d = plt.seriesargs[i]
series = d[:serieshandle] series = d[:serieshandle]
x, y = xy x, y = xy
d[:x], d[:y] = x, y d[:x], d[:y] = x, y
try try
series[:set_data](x, y) series[:set_data](x, y)
catch catch
series[:set_offsets](hcat(x, y)) series[:set_offsets](hcat(x, y))
end end
set_lims!(plt, d[:axis]) set_lims!(plt, d[:axis])
plt plt
end end
function Base.setindex!{X,Y,Z}(plt::Plot{PyPlotBackend}, xyz::Tuple{X,Y,Z}, i::Integer) function Base.setindex!{X,Y,Z}(plt::Plot{PyPlotBackend}, xyz::Tuple{X,Y,Z}, i::Integer)
warn("setindex not implemented for xyz") warn("setindex not implemented for xyz")
plt plt
end end
# ----------------------------------------------------------------- # --------------------------------------------------------------------------
function addPyPlotLims(ax, lims, dimension) function addPyPlotLims(ax, lims, letter)
lims == :auto && return lims == :auto && return
ltype = limsType(lims) ltype = limsType(lims)
if ltype == :limits if ltype == :limits
if dimension == :xlim setf = ax[symbol("set_", letter, "lim")]
isfinite(lims[1]) && ax[:set_xlim](left = lims[1]) l1, l2 = lims
isfinite(lims[2]) && ax[:set_xlim](right = lims[2]) if isfinite(l1)
elseif dimension == :ylim letter == "x" ? setf(left = l1) : setf(bottom = l1)
isfinite(lims[1]) && ax[:set_ylim](bottom = lims[1]) end
isfinite(lims[2]) && ax[:set_ylim](top = lims[2]) if isfinite(l2)
elseif dimension == :zlim && haskey(ax, :set_zlim) letter == "x" ? setf(right = l2) : setf(top = l2)
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")
end end
else else
error("Invalid input for $dimension: ", lims) error("Invalid input for $letter: ", lims)
end end
end end
function addPyPlotTicks(ax, ticks, isx::Bool) function addPyPlotTicks(ax, ticks, letter)
ticks == :auto && return ticks == :auto && return
if ticks == :none || ticks == nothing if ticks == :none || ticks == nothing
ticks = zeros(0) ticks = zeros(0)
end end
ttype = ticksType(ticks) ttype = ticksType(ticks)
if ttype == :ticks tickfunc = symbol("set_", letter, "ticks")
ax[isx ? :set_xticks : :set_yticks](ticks) labfunc = symbol("set_", letter, "ticklabels")
elseif ttype == :ticks_and_labels if ttype == :ticks
ax[isx ? :set_xticks : :set_yticks](ticks[1]) ax[tickfunc](ticks)
ax[isx ? :set_xticklabels : :set_yticklabels](ticks[2]) elseif ttype == :ticks_and_labels
else ax[tickfunc](ticks[1])
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks) ax[labfunc](ticks[2])
end else
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
end
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) function _update_plot(plt::Plot{PyPlotBackend}, d::KW)
figorax = plt.o figorax = plt.o
ax = getLeftAxis(figorax) ax = getLeftAxis(figorax)
# PyPlot.sca(ax) 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 # handle right y axis
# haskey(d, :title) && PyPlot.title(d[:title]) axes = [getLeftAxis(figorax)]
haskey(d, :title) && ax[:set_title](d[:title]) if usingRightAxis(plt)
haskey(d, :xlabel) && ax[:set_xlabel](d[:xlabel]) push!(axes, getRightAxis(figorax))
if haskey(d, :ylabel) if get(d, :yrightlabel, "") != ""
ax[:set_ylabel](d[:ylabel]) rightax = getRightAxis(figorax)
end rightax[:set_ylabel](d[:yrightlabel])
if usingRightAxis(plt) && get(d, :yrightlabel, "") != "" end
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
end end
# grid # handle each axis in turn
if get(d, :grid, false) for letter in ("x", "y", "z")
ax[:xaxis][:grid](true) axis, scale, lims, ticks, flip, lab = axis_symbols(letter, "axis", "scale", "lims", "ticks", "flip", "label")
ax[:yaxis][:grid](true) haskey(ax, axis) || continue
ax[:set_axisbelow](true) 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
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
# ----------------------------------------------------------------- # -----------------------------------------------------------------

View File

@ -21,72 +21,36 @@ stringsSupported() = stringsSupported(backend())
supportedArgs(::GadflyBackend) = [ supportedArgs(::GadflyBackend) = [
:annotation, :annotation,
# :axis, :background_color, :foreground_color, :color_palette,
:background_color,
:linecolor,
:color_palette,
:fillrange,
:fillcolor,
:fillalpha,
:foreground_color,
:group, :group,
:label, :label,
:layout,
:legend,
:colorbar,
:linestyle,
:linetype, :linetype,
:linewidth, :linecolor, :linestyle, :linewidth, :linealpha,
:linealpha, :markershape, :markercolor, :markersize, :markeralpha,
:markershape, :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:markercolor, :fillrange, :fillcolor, :fillalpha,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokealpha,
# :markerstrokestyle,
:n,
:nbins, :nbins,
:nc, :n, :nc, :nr, :layout,
:nr,
# :pos,
:smooth, :smooth,
:show, :title, :windowtitle, :show, :size,
:size, :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
:title, :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
:windowtitle, # :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z, :z,
:zcolor, :tickfont, :guidefont, :legendfont,
:tickfont, :grid, :legend, :colorbar,
:guidefont, :zcolor, :levels,
:legendfont, :xerror, :yerror,
:grid, :ribbon, :quiver,
# :surface,
:levels,
:xerror,
:yerror,
:ribbon,
:quiver,
:orientation, :orientation,
] ]
supportedAxes(::GadflyBackend) = [:auto, :left] supportedAxes(::GadflyBackend) = [:auto, :left]
supportedTypes(::GadflyBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, supportedTypes(::GadflyBackend) = [
:scatter, :hist2d, :hexbin, :hist, :bar, :box, :violin, :quiver, :none, :line, :path, :steppre, :steppost, :sticks,
:hline, :vline, :contour, :shape] :scatter, :hist2d, :hexbin, :hist,
:bar, :box, :violin, :quiver,
:hline, :vline, :contour, :shape
]
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape) supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt] supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
@ -110,78 +74,41 @@ subplotSupported(::ImmerseBackend) = true
supportedArgs(::PyPlotBackend) = [ supportedArgs(::PyPlotBackend) = [
:annotation, :annotation,
:axis, :background_color, :foreground_color, :color_palette,
:background_color,
:linecolor,
:color_palette,
:fillrange,
:fillcolor,
:foreground_color,
:group, :group,
:label, :label,
:layout,
:legend,
:colorbar,
:linestyle,
:linetype, :linetype,
:linewidth, :linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markershape, :markercolor, :markersize, :markeralpha,
:markercolor, :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:markersize, :fillrange, :fillcolor, :fillalpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokealpha,
# :markerstrokestyle,
:n,
:nbins, :nbins,
:nc, :n, :nc, :nr, :layout,
:nr,
# :pos,
:smooth, :smooth,
# :ribbon, :title, :windowtitle, :show, :size,
:show, :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
:size, :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
:title, :axis, :yrightlabel,
:windowtitle, :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
:zlims,
:yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z, :z,
:zcolor, # only supported for scatter/scatter3d :tickfont, :guidefont, :legendfont,
:tickfont, :grid, :legend, :colorbar,
:guidefont, :zcolor, :levels,
:legendfont, :xerror, :yerror,
:grid, :ribbon, :quiver,
# :surface,
:levels,
:fillalpha,
:linealpha,
:markeralpha,
:overwrite_figure,
:xerror,
:yerror,
:ribbon,
:quiver,
:orientation, :orientation,
:overwrite_figure,
:polar, :polar,
] ]
supportedAxes(::PyPlotBackend) = _allAxes supportedAxes(::PyPlotBackend) = _allAxes
supportedTypes(::PyPlotBackend) = [:none, :line, :path, :steppre, :steppost, :shape, supportedTypes(::PyPlotBackend) = [
:scatter, :hist2d, :hexbin, :hist, :density, :bar, :box, :violin, :quiver, :none, :line, :path, :steppre, :steppost, :shape,
:hline, :vline, :contour, :path3d, :scatter3d, :surface, :wireframe, :heatmap] :scatter, :hist2d, :hexbin, :hist, :density,
:bar, :box, :violin, :quiver,
:hline, :vline, :heatmap,
:contour, :path3d, :scatter3d, :surface, :wireframe
]
supportedStyles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot] 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) supportedMarkers(::PyPlotBackend) = vcat(_allMarkers, Shape)
supportedScales(::PyPlotBackend) = [:identity, :ln, :log2, :log10] supportedScales(::PyPlotBackend) = [:identity, :ln, :log2, :log10]
subplotSupported(::PyPlotBackend) = true subplotSupported(::PyPlotBackend) = true

View File

@ -78,62 +78,19 @@ function plot!(plt::Plot, args...; kw...)
args = _apply_recipe(d, args...; kw...) args = _apply_recipe(d, args...; kw...)
dumpdict(d, "After plot! preprocessing") dumpdict(d, "After plot! preprocessing")
# @show groupargs map(typeof, args)
warnOnUnsupportedArgs(plt.backend, d) warnOnUnsupportedArgs(plt.backend, d)
# just in case the backend needs to set up the plot (make it current or something) # just in case the backend needs to set up the plot (make it current or something)
_before_add_series(plt) _before_add_series(plt)
# # grouping # # 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) groupby = if haskey(d, :group)
extractGroupArgs(d[:group], args...) extractGroupArgs(d[:group], args...)
else else
nothing nothing
end end
# dumpdict(d, "after", true)
# @show groupby map(typeof, args)
_add_series(plt, d, groupby, 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) _add_annotations(plt, d)
warnOnUnsupportedScales(plt.backend, d) warnOnUnsupportedScales(plt.backend, d)
@ -150,7 +107,7 @@ function plot!(plt::Plot, args...; kw...)
current(plt) 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? # # do we want to show it?
if haskey(d, :show) && d[:show] if haskey(d, :show) && d[:show]
gui() gui()
@ -212,6 +169,7 @@ function _add_series(plt::Plot, d::KW, ::Void, args...;
if !stringsSupported() if !stringsSupported()
setTicksFromStringVector(d, di, :x, :xticks) setTicksFromStringVector(d, di, :x, :xticks)
setTicksFromStringVector(d, di, :y, :yticks) setTicksFromStringVector(d, di, :y, :yticks)
setTicksFromStringVector(d, di, :z, :zticks)
end end
# remove plot args # remove plot args

View File

@ -231,6 +231,8 @@ limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :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}(::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] Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]