Files
Plots.jl/src/args.jl
T
2015-09-19 00:05:24 -04:00

425 lines
12 KiB
Julia

# const COLORS = [:black, :blue, :green, :red, :darkGray, :darkCyan, :darkYellow, :darkMagenta,
# :darkBlue, :darkGreen, :darkRed, :gray, :cyan, :yellow, :magenta]
# const COLORS = distinguishable_colors(20)
const AXES = [:left, :right]
const TYPES = [:line,
:step,
:stepinverted,
:sticks,
:scatter,
:heatmap,
:hexbin,
:hist,
:bar,
:hline,
:vline,
:ohlc,
]
const STYLES = [:solid, :dash, :dot, :dashdot, :dashdotdot]
const MARKERS = [:ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon, :octagon]
const ALL_AXES = vcat(:auto, AXES)
const ALL_TYPES = vcat(:none, TYPES)
const ALL_STYLES = vcat(:auto, STYLES)
const ALL_MARKERS = vcat(:none, :auto, MARKERS)
supportedAxes(::PlottingPackage) = ALL_AXES
supportedTypes(::PlottingPackage) = ALL_TYPES
supportedStyles(::PlottingPackage) = ALL_STYLES
supportedMarkers(::PlottingPackage) = ALL_MARKERS
subplotSupported(::PlottingPackage) = true
supportedAxes() = supportedAxes(plotter())
supportedTypes() = supportedTypes(plotter())
supportedStyles() = supportedStyles(plotter())
supportedMarkers() = supportedMarkers(plotter())
subplotSupported() = subplotSupported(plotter())
# -----------------------------------------------------------------------------
const _seriesDefaults = Dict{Symbol, Any}()
# series-specific
_seriesDefaults[:axis] = :left
_seriesDefaults[:color] = :auto
_seriesDefaults[:label] = "AUTO"
_seriesDefaults[:width] = 1
_seriesDefaults[:linetype] = :line
_seriesDefaults[:linestyle] = :solid
_seriesDefaults[:marker] = :none
_seriesDefaults[:markercolor] = :match
_seriesDefaults[:markersize] = 6
_seriesDefaults[:nbins] = 100 # number of bins for heatmaps and hists
_seriesDefaults[:heatmap_c] = (0.15, 0.5)
_seriesDefaults[:fillto] = nothing # fills in the area
_seriesDefaults[:reg] = false # regression line?
_seriesDefaults[:group] = nothing
_seriesDefaults[:ribbon] = nothing
_seriesDefaults[:args] = [] # additional args to pass to the backend
_seriesDefaults[:kwargs] = [] # additional keyword args to pass to the backend
# note: can be Vector{Dict} or Vector{Tuple}
const _plotDefaults = Dict{Symbol, Any}()
# plot globals
_plotDefaults[:title] = ""
_plotDefaults[:xlabel] = ""
_plotDefaults[:ylabel] = ""
_plotDefaults[:yrightlabel] = ""
_plotDefaults[:legend] = true
_plotDefaults[:background_color] = colorant"white"
_plotDefaults[:xticks] = true
_plotDefaults[:yticks] = true
_plotDefaults[:size] = (600,400)
_plotDefaults[:windowtitle] = "Plots.jl"
_plotDefaults[:show] = false
# TODO: x/y scales
const ARGS = sort(collect(intersect(keys(_seriesDefaults), keys(_plotDefaults))))
supportedArgs(::PlottingPackage) = ARGS
supportedArgs() = supportedArgs(plotter())
# -----------------------------------------------------------------------------
makeplural(s::Symbol) = Symbol(string(s,"s"))
autopick(arr::AVec, idx::Integer) = arr[mod1(idx,length(arr))]
autopick(notarr, idx::Integer) = notarr
autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
autopick_ignore_none_auto(notarr, idx::Integer) = notarr
# -----------------------------------------------------------------------------
# Alternate args
const keyAliases = Dict(
:c => :color,
:l => :label,
:w => :width,
:linewidth => :width,
:type => :linetype,
:t => :linetype,
:style => :linestyle,
:s => :linestyle,
:m => :marker,
:mc => :markercolor,
:mcolor => :markercolor,
:ms => :markersize,
:msize => :markersize,
:nb => :nbins,
:nbin => :nbins,
:fill => :fillto,
:g => :group,
:r => :ribbon,
:xlab => :xlabel,
:ylab => :ylabel,
:yrlab => :yrightlabel,
:ylabr => :yrightlabel,
:y2lab => :yrightlabel,
:ylab2 => :yrightlabel,
:ylabelright => :yrightlabel,
:ylabel2 => :yrightlabel,
:y2label => :yrightlabel,
:leg => :legend,
:bg => :background_color,
:bgcolor => :background_color,
:bg_color => :background_color,
:background => :background_color,
:windowsize => :size,
:wsize => :size,
:wtitle => :windowtitle,
:display => :show,
)
# add all pluralized forms to the keyAliases dict
for arg in keys(_seriesDefaults)
keyAliases[makeplural(arg)] = arg
end
function replaceAliases!(d::Dict)
for (k,v) in d
if haskey(keyAliases, k)
d[keyAliases[k]] = v
delete!(d, k)
end
end
end
# -----------------------------------------------------------------------------
# update the defaults globally
function plotDefault(k::Symbol)
if haskey(_seriesDefaults, k)
return _seriesDefaults[k]
elseif haskey(_plotDefaults, k)
return _plotDefaults[k]
else
error("Unknown key: ", k)
end
end
function plotDefault!(k::Symbol, v)
if haskey(_seriesDefaults, k)
_seriesDefaults[k] = v
elseif haskey(_plotDefaults, k)
_plotDefaults[k] = v
else
error("Unknown key: ", k)
end
end
# -----------------------------------------------------------------------------
convertColor(c::Union{AbstractString, Symbol}) = parse(Colorant, string(c))
convertColor(c::Colorant) = c
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
isbackgrounddark(bgcolor::Color) = Lab(bgcolor).l < 0.5
# move closer to lighter/darker depending on background value
function adjustAway(val, bgval, vmin=0., vmax=100.)
if bgval < 0.5 * (vmax+vmin)
tmp = max(val, bgval)
return 0.5 * (tmp + max(tmp, vmax))
else
tmp = min(val, bgval)
return 0.5 * (tmp + min(tmp, vmin))
end
end
function getBackgroundRGBColor(c, d::Dict)
bgcolor = convertColor(d[:background_color])
d[:background_color] = bgcolor
palette = distinguishable_colors(20, bgcolor)[2:end]
# try to adjust lightness away from background color
bg_lab = Lab(bgcolor)
palette = RGB{Float64}[begin
lab = Lab(rgb)
Lab(
adjustAway(lab.l, bg_lab.l, 25, 75),
lab.a,
lab.b
)
end for rgb in palette]
d[:color_palette] = palette
# set the foreground color (text, ticks, gridlines) to be white or black depending
# on how dark the background is. borrowed from http://stackoverflow.com/a/1855903
a = 0.299 * red(bgcolor) + 0.587 * green(bgcolor) + 0.114 * blue(bgcolor)
d[:foreground_color] = a < 0.5 ? colorant"white" : colorant"black"
bgcolor
end
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
function getSeriesRGBColor(c, d::Dict, n::Int)
if c == :auto
c = autopick(d[:color_palette], n)
else
c = convertColor(c)
end
# should be a RGB now... either it was passed in, generated automatically, or created from a string
@assert isa(c, RGB)
# return the RGB
c
end
function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
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[:marker] == :none || d[:marker] in supportedMarkers(pkg) || warn("marker $(d[:marker]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))")
end
function getPlotArgs(pkg::PlottingPackage, kw, idx::Int)
d = Dict(kw)
# add defaults?
for k in keys(_plotDefaults)
if haskey(d, k)
v = d[k]
if isa(v, AbstractVector) && !isempty(v)
# we got a vector, cycling through
d[k] = autopick(v, idx)
end
else
d[k] = _plotDefaults[k]
end
end
# convert color
d[:background_color] = getBackgroundRGBColor(d[:background_color], d)
# no need for these
delete!(d, :x)
delete!(d, :y)
d
end
# note: idx is the index of this series within this call, n is the index of the series from all calls to plot/subplot
function getSeriesArgs(pkg::PlottingPackage, initargs::Dict, kw, idx::Int, n::Int) # TODO, pass in initargs, not plt
d = Dict(kw)
# add defaults?
for k in keys(_seriesDefaults)
if haskey(d, k)
v = d[k]
if isa(v, AbstractVector) && !isempty(v)
# we got a vector, cycling through
d[k] = autopick(v, idx)
end
else
d[k] = _seriesDefaults[k]
end
end
# auto-pick
if d[:axis] == :auto
d[:axis] = autopick_ignore_none_auto(supportedAxes(pkg), n)
end
if d[:linestyle] == :auto
d[:linestyle] = autopick_ignore_none_auto(supportedStyles(pkg), n)
end
if d[:marker] == :auto
d[:marker] = autopick_ignore_none_auto(supportedMarkers(pkg), n)
end
# update color
d[:color] = getSeriesRGBColor(d[:color], initargs, n)
# update markercolor
mc = d[:markercolor]
mc = (mc == :match ? d[:color] : getSeriesRGBColor(mc, initargs, n))
d[:markercolor] = mc
# set label
label = d[:label]
label = (label == "AUTO" ? "y$n" : 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
end
# # note: idx is the index of this series within this call, n is the index of the series from all calls to plot/subplot
# function getPlotKeywordArgs(pkg::PlottingPackage, kw, idx::Int, n::Int)
# d = Dict(kw)
# # # replace alternate names
# # for tup in kw
# # if haskey(ALT_ARG_NAMES, tup)
# # d[tup[1]] = ALT_ARG_NAMES[tup]
# # end
# # end
# # default to a white background, but only on the initial call (so we don't change the background automatically)
# if haskey(d, :background_color)
# d[:background_color] = getRGBColor(d[:background_color])
# elseif n == 0
# d[:background_color] = colorant"white"
# end
# # fill in d with either 1) plural value, 2) value, 3) default
# for k in keys(PLOT_DEFAULTS)
# plural = makeplural(k)
# if !haskey(d, k)
# if n == 0 || k != :size
# d[k] = haskey(d, plural) ? autopick(d[plural], idx) : PLOT_DEFAULTS[k]
# end
# end
# delete!(d, plural)
# end
# # auto-pick
# if n > 0
# if d[:axis] == :auto
# d[:axis] = autopick_ignore_none_auto(supportedAxes(pkg), n)
# end
# # if d[:linetype] == :auto
# # d[:linetype] = autopick_ignore_none_auto(supportedTypes(pkg), n)
# # end
# if d[:linestyle] == :auto
# d[:linestyle] = autopick_ignore_none_auto(supportedStyles(pkg), n)
# end
# if d[:marker] == :auto
# d[:marker] = autopick_ignore_none_auto(supportedMarkers(pkg), n)
# end
# end
# # # swap out dots for no line and a marker
# # if haskey(d, :linetype) && d[:linetype] == :scatter
# # d[:linetype] = :none
# # if d[:marker] == :none
# # d[:marker] = :ellipse
# # end
# # end
# # handle plot initialization differently
# if n == 0
# delete!(d, :x)
# delete!(d, :y)
# else
# # once the plot is created, we can get line/marker colors
# # update color
# d[:color] = getRGBColor(d[:color], n)
# # update markercolor
# mc = d[:markercolor]
# mc = (mc == :match ? d[:color] : getRGBColor(mc, n))
# d[:markercolor] = mc
# # set label
# label = d[:label]
# label = (label == "AUTO" ? "y$n" : label)
# if d[:axis] == :right && length(label) >= 4 && label[end-3:end] != " (R)"
# label = string(label, " (R)")
# end
# d[:label] = label
# warnOnUnsupported(pkg, d)
# end
# d
# end
# -----------------------------------------------------------------------------
# TODO: arg aliases