Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a1fe30567 | |||
| db685b6ee0 | |||
| fcb9078838 | |||
| 3310025602 | |||
| 122a470078 | |||
| bb0b6e5d33 | |||
| 7185e36795 | |||
| d111c2ba91 | |||
| 5dff00e2a3 | |||
| 648f26f075 | |||
| 808e20588e | |||
| 82dd5bf3eb | |||
| 0950c738e3 | |||
| f12f6db310 | |||
| ae4031b993 | |||
| 2495d2a1e9 | |||
| 0a97b08c72 | |||
| e0a89c55ac | |||
| 75ac34b719 | |||
| 2072c54c53 | |||
| 6e1ed63005 | |||
| 17e59daeb4 | |||
| 69e83ef89f | |||
| b2707f5c8a | |||
| 0fd24e5210 | |||
| 3dcc7e5556 | |||
| 57ebeae1ad | |||
| 9e35cc3fe1 | |||
| 3e1258aa0c | |||
| 96e1b1d105 | |||
| fe80e486aa | |||
| ad051f21c1 | |||
| ac24a4ae19 | |||
| 7ae43ee9f6 | |||
| 0e79070ad8 |
@@ -11,6 +11,14 @@
|
||||
---
|
||||
## (current master)
|
||||
|
||||
## 0.26.2
|
||||
- improve empty animation build error
|
||||
- fix GR axis flip for heatmaps and images
|
||||
- fix ribbons specified as tuples
|
||||
- add Char recipe
|
||||
- fix Plotly plots with single-element series
|
||||
- rewrite PlotlyJS backend
|
||||
|
||||
## 0.26.1
|
||||
- handle `Char`s as input data
|
||||
- fix html saving for Plotly
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
name = "Plots"
|
||||
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
author = ["Tom Breloff (@tbreloff)"]
|
||||
version = "0.26.1"
|
||||
version = "0.26.2"
|
||||
|
||||
[deps]
|
||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
|
||||
+2
-1
@@ -220,6 +220,7 @@ end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
const CURRENT_BACKEND = Plots.CurrentBackend(:gr)
|
||||
gr()
|
||||
|
||||
end # module
|
||||
|
||||
+9
-4
@@ -60,17 +60,22 @@ end
|
||||
|
||||
file_extension(fn) = Base.Filesystem.splitext(fn)[2][2:end]
|
||||
|
||||
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim.dir, fn, false; kw...)
|
||||
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim.dir, fn, false; kw...)
|
||||
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim, fn; kw...)
|
||||
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim, fn, false; kw...)
|
||||
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim, fn, false; kw...)
|
||||
|
||||
|
||||
function buildanimation(animdir::AbstractString, fn::AbstractString,
|
||||
function buildanimation(anim::Animation, fn::AbstractString,
|
||||
is_animated_gif::Bool=true;
|
||||
fps::Integer = 20, loop::Integer = 0,
|
||||
variable_palette::Bool=false,
|
||||
show_msg::Bool=true)
|
||||
if length(anim.frames) == 0
|
||||
throw(ArgumentError("Cannot build empty animations"))
|
||||
end
|
||||
|
||||
fn = abspath(expanduser(fn))
|
||||
animdir = anim.dir
|
||||
|
||||
if is_animated_gif
|
||||
if variable_palette
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
|
||||
const _arg_desc = KW(
|
||||
const _arg_desc = Dict{Symbol,String}(
|
||||
|
||||
# series args
|
||||
:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
|
||||
@@ -50,7 +50,7 @@ const _arg_desc = KW(
|
||||
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
|
||||
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
|
||||
:colorbar_entry => "Bool. Include this series in the color bar? Set to `false` to exclude.",
|
||||
|
||||
|
||||
# plot args
|
||||
:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
|
||||
:background_color => "Color Type. Base color for all backgrounds.",
|
||||
|
||||
+83
-83
@@ -2,7 +2,7 @@
|
||||
|
||||
const _keyAliases = Dict{Symbol,Symbol}()
|
||||
|
||||
function add_aliases(sym::Symbol, aliases::Symbol...)
|
||||
@noinline function add_aliases(sym::Symbol, aliases::Symbol...)
|
||||
for alias in aliases
|
||||
if haskey(_keyAliases, alias)
|
||||
error("Already an alias $alias => $(_keyAliases[alias])... can't also alias $sym")
|
||||
@@ -11,7 +11,7 @@ function add_aliases(sym::Symbol, aliases::Symbol...)
|
||||
end
|
||||
end
|
||||
|
||||
function add_non_underscore_aliases!(aliases::Dict{Symbol,Symbol})
|
||||
@noinline function add_non_underscore_aliases!(aliases::Dict{Symbol,Symbol})
|
||||
for (k,v) in aliases
|
||||
s = string(k)
|
||||
if '_' in s
|
||||
@@ -84,17 +84,17 @@ const _histogram_like = [:histogram, :barhist, :barbins]
|
||||
const _line_like = [:line, :path, :steppre, :steppost]
|
||||
const _surface_like = [:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image]
|
||||
|
||||
like_histogram(seriestype::Symbol) = seriestype in _histogram_like
|
||||
like_line(seriestype::Symbol) = seriestype in _line_like
|
||||
like_surface(seriestype::Symbol) = seriestype in _surface_like
|
||||
@noinline like_histogram(seriestype::Symbol) = seriestype in _histogram_like
|
||||
@noinline like_line(seriestype::Symbol) = seriestype in _line_like
|
||||
@noinline like_surface(seriestype::Symbol) = seriestype in _surface_like
|
||||
|
||||
is3d(seriestype::Symbol) = seriestype in _3dTypes
|
||||
is3d(series::Series) = is3d(series.plotattributes)
|
||||
is3d(plotattributes::KW) = trueOrAllTrue(is3d, Symbol(plotattributes[:seriestype]))
|
||||
@noinline is3d(seriestype::Symbol) = seriestype in _3dTypes
|
||||
@noinline is3d(series::Series) = is3d(series.plotattributes)
|
||||
@noinline is3d(plotattributes::KW) = trueOrAllTrue(is3d, Symbol(plotattributes[:seriestype]))
|
||||
|
||||
is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d"
|
||||
ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar"
|
||||
ispolar(series::Series) = ispolar(series.plotattributes[:subplot])
|
||||
@noinline is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d"
|
||||
@noinline ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar"
|
||||
@noinline ispolar(series::Series) = ispolar(series.plotattributes[:subplot])
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@@ -188,9 +188,9 @@ const _allGridSyms = [:x, :y, :z,
|
||||
:all, :both, :on, :yes, :show,
|
||||
:none, :off, :no, :hide]
|
||||
const _allGridArgs = [_allGridSyms; string.(_allGridSyms); nothing]
|
||||
hasgrid(arg::Nothing, letter) = false
|
||||
hasgrid(arg::Bool, letter) = arg
|
||||
function hasgrid(arg::Symbol, letter)
|
||||
@noinline hasgrid(arg::Nothing, letter) = false
|
||||
@noinline hasgrid(arg::Bool, letter) = arg
|
||||
@noinline function hasgrid(arg::Symbol, letter)
|
||||
if arg in _allGridSyms
|
||||
arg in (:all, :both, :on) || occursin(string(letter), string(arg))
|
||||
else
|
||||
@@ -198,7 +198,7 @@ function hasgrid(arg::Symbol, letter)
|
||||
true
|
||||
end
|
||||
end
|
||||
hasgrid(arg::AbstractString, letter) = hasgrid(Symbol(arg), letter)
|
||||
@noinline hasgrid(arg::AbstractString, letter) = hasgrid(Symbol(arg), letter)
|
||||
|
||||
const _allShowaxisSyms = [:x, :y, :z,
|
||||
:xy, :xz, :yx, :yz, :zx, :zy,
|
||||
@@ -206,9 +206,9 @@ const _allShowaxisSyms = [:x, :y, :z,
|
||||
:all, :both, :on, :yes, :show,
|
||||
:off, :no, :hide]
|
||||
const _allShowaxisArgs = [_allGridSyms; string.(_allGridSyms)]
|
||||
showaxis(arg::Nothing, letter) = false
|
||||
showaxis(arg::Bool, letter) = arg
|
||||
function showaxis(arg::Symbol, letter)
|
||||
@noinline showaxis(arg::Nothing, letter) = false
|
||||
@noinline showaxis(arg::Bool, letter) = arg
|
||||
@noinline function showaxis(arg::Symbol, letter)
|
||||
if arg in _allGridSyms
|
||||
arg in (:all, :both, :on, :yes) || occursin(string(letter), string(arg))
|
||||
else
|
||||
@@ -216,7 +216,7 @@ function showaxis(arg::Symbol, letter)
|
||||
true
|
||||
end
|
||||
end
|
||||
showaxis(arg::AbstractString, letter) = hasgrid(Symbol(arg), letter)
|
||||
@noinline showaxis(arg::AbstractString, letter) = hasgrid(Symbol(arg), letter)
|
||||
|
||||
const _allFramestyles = [:box, :semi, :axes, :origin, :zerolines, :grid, :none]
|
||||
const _framestyleAliases = Dict{Symbol, Symbol}(
|
||||
@@ -448,7 +448,7 @@ const _initial_defaults = deepcopy(_all_defaults)
|
||||
const _initial_axis_defaults = deepcopy(_axis_defaults)
|
||||
|
||||
# to be able to reset font sizes to initial values
|
||||
const _initial_fontsizes = Dict(:titlefontsize => _subplot_defaults[:titlefontsize],
|
||||
const _initial_fontsizes = Dict{Any,Any}(:titlefontsize => _subplot_defaults[:titlefontsize],
|
||||
:legendfontsize => _subplot_defaults[:legendfontsize],
|
||||
:tickfontsize => _axis_defaults[:tickfontsize],
|
||||
:guidefontsize => _axis_defaults[:guidefontsize])
|
||||
@@ -459,15 +459,15 @@ RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
makeplural(s::Symbol) = Symbol(string(s,"s"))
|
||||
@noinline makeplural(s::Symbol) = Symbol(string(s,"s"))
|
||||
|
||||
autopick(arr::AVec, idx::Integer) = arr[mod1(idx,length(arr))]
|
||||
autopick(notarr, idx::Integer) = notarr
|
||||
@noinline autopick(arr::AVec, idx::Integer) = arr[mod1(idx,length(arr))]
|
||||
@noinline 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
|
||||
@noinline autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
|
||||
@noinline autopick_ignore_none_auto(notarr, idx::Integer) = notarr
|
||||
|
||||
function aliasesAndAutopick(plotattributes::KW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
|
||||
@noinline function aliasesAndAutopick(plotattributes::KW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
|
||||
if plotattributes[sym] == :auto
|
||||
plotattributes[sym] = autopick_ignore_none_auto(options, plotIndex)
|
||||
elseif haskey(aliases, plotattributes[sym])
|
||||
@@ -475,7 +475,7 @@ function aliasesAndAutopick(plotattributes::KW, sym::Symbol, aliases::Dict{Symbo
|
||||
end
|
||||
end
|
||||
|
||||
function aliases(aliasMap::Dict{Symbol,Symbol}, val)
|
||||
@noinline function aliases(aliasMap::Dict{Symbol,Symbol}, val)
|
||||
sortedkeys(filter((k,v)-> v==val, aliasMap))
|
||||
end
|
||||
|
||||
@@ -685,15 +685,15 @@ function processLineArg(plotattributes::KW, arg)
|
||||
plotattributes[:linestyle] = arg
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[:linewidth] = arg.width)
|
||||
arg.color == nothing || (plotattributes[:linecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:linealpha] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[:linestyle] = arg.style)
|
||||
arg.width === nothing || (plotattributes[:linewidth] = arg.width)
|
||||
arg.color === nothing || (plotattributes[:linecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:linealpha] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[:linestyle] = arg.style)
|
||||
|
||||
elseif typeof(arg) <: Brush
|
||||
arg.size == nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color == nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
arg.size === nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color === nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
|
||||
elseif typeof(arg) <: Arrow || arg in (:arrow, :arrows)
|
||||
plotattributes[:arrow] = arg
|
||||
@@ -724,15 +724,15 @@ function processMarkerArg(plotattributes::KW, arg)
|
||||
plotattributes[:markerstrokestyle] = arg
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[:markerstrokewidth] = arg.width)
|
||||
arg.color == nothing || (plotattributes[:markerstrokecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:markerstrokealpha] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[:markerstrokestyle] = arg.style)
|
||||
arg.width === nothing || (plotattributes[:markerstrokewidth] = arg.width)
|
||||
arg.color === nothing || (plotattributes[:markerstrokecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:markerstrokealpha] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[:markerstrokestyle] = arg.style)
|
||||
|
||||
elseif typeof(arg) <: Brush
|
||||
arg.size == nothing || (plotattributes[:markersize] = arg.size)
|
||||
arg.color == nothing || (plotattributes[:markercolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:markeralpha] = arg.alpha)
|
||||
arg.size === nothing || (plotattributes[:markersize] = arg.size)
|
||||
arg.color === nothing || (plotattributes[:markercolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:markeralpha] = arg.alpha)
|
||||
|
||||
# linealpha
|
||||
elseif allAlphas(arg)
|
||||
@@ -757,9 +757,9 @@ end
|
||||
function processFillArg(plotattributes::KW, arg)
|
||||
# fr = get(plotattributes, :fillrange, 0)
|
||||
if typeof(arg) <: Brush
|
||||
arg.size == nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color == nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
arg.size === nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color === nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
|
||||
elseif typeof(arg) <: Bool
|
||||
plotattributes[:fillrange] = arg ? 0 : nothing
|
||||
@@ -793,10 +793,10 @@ function processGridArg!(plotattributes::KW, arg, letter)
|
||||
plotattributes[Symbol(letter, :gridstyle)] = arg
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[Symbol(letter, :gridlinewidth)] = arg.width)
|
||||
arg.color == nothing || (plotattributes[Symbol(letter, :foreground_color_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[Symbol(letter, :gridalpha)] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[Symbol(letter, :gridstyle)] = arg.style)
|
||||
arg.width === nothing || (plotattributes[Symbol(letter, :gridlinewidth)] = arg.width)
|
||||
arg.color === nothing || (plotattributes[Symbol(letter, :foreground_color_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[Symbol(letter, :gridalpha)] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[Symbol(letter, :gridstyle)] = arg.style)
|
||||
|
||||
# linealpha
|
||||
elseif allAlphas(arg)
|
||||
@@ -822,10 +822,10 @@ function processMinorGridArg!(plotattributes::KW, arg, letter)
|
||||
plotattributes[Symbol(letter, :minorgrid)] = true
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[Symbol(letter, :minorgridlinewidth)] = arg.width)
|
||||
arg.color == nothing || (plotattributes[Symbol(letter, :foreground_color_minor_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[Symbol(letter, :minorgridalpha)] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[Symbol(letter, :minorgridstyle)] = arg.style)
|
||||
arg.width === nothing || (plotattributes[Symbol(letter, :minorgridlinewidth)] = arg.width)
|
||||
arg.color === nothing || (plotattributes[Symbol(letter, :foreground_color_minor_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[Symbol(letter, :minorgridalpha)] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[Symbol(letter, :minorgridstyle)] = arg.style)
|
||||
plotattributes[Symbol(letter, :minorgrid)] = true
|
||||
|
||||
# linealpha
|
||||
@@ -879,9 +879,9 @@ function processFontArg!(plotattributes::KW, fontname::Symbol, arg)
|
||||
end
|
||||
end
|
||||
|
||||
_replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
|
||||
_replace_markershape(shapes::AVec) = map(_replace_markershape, shapes)
|
||||
_replace_markershape(shape) = shape
|
||||
@noinline _replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
|
||||
@noinline _replace_markershape(shapes::AVec) = map(_replace_markershape, shapes)
|
||||
@noinline _replace_markershape(shape) = shape
|
||||
|
||||
function _add_markershape(plotattributes::KW)
|
||||
# add the markershape if it needs to be added... hack to allow "m=10" to add a shape,
|
||||
@@ -1074,7 +1074,7 @@ end
|
||||
|
||||
|
||||
# this is when given a vector-type of values to group by
|
||||
function extractGroupArgs(v::AVec, args...; legendEntry = string)
|
||||
@noinline function extractGroupArgs(v::AVec, args...; legendEntry = string)
|
||||
groupLabels = sort(collect(unique(v)))
|
||||
n = length(groupLabels)
|
||||
if n > 100
|
||||
@@ -1084,17 +1084,17 @@ function extractGroupArgs(v::AVec, args...; legendEntry = string)
|
||||
GroupBy(map(legendEntry, groupLabels), groupIds)
|
||||
end
|
||||
|
||||
legendEntryFromTuple(ns::Tuple) = join(ns, ' ')
|
||||
@noinline legendEntryFromTuple(ns::Tuple) = join(ns, ' ')
|
||||
|
||||
# this is when given a tuple of vectors of values to group by
|
||||
function extractGroupArgs(vs::Tuple, args...)
|
||||
@noinline function extractGroupArgs(vs::Tuple, args...)
|
||||
isempty(vs) && return GroupBy([""], [1:size(args[1],1)])
|
||||
v = map(tuple, vs...)
|
||||
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple)
|
||||
end
|
||||
|
||||
# allow passing NamedTuples for a named legend entry
|
||||
legendEntryFromTuple(ns::NamedTuple) =
|
||||
@noinline legendEntryFromTuple(ns::NamedTuple) =
|
||||
join(["$k = $v" for (k, v) in pairs(ns)], ", ")
|
||||
|
||||
function extractGroupArgs(vs::NamedTuple, args...)
|
||||
@@ -1104,24 +1104,24 @@ function extractGroupArgs(vs::NamedTuple, args...)
|
||||
end
|
||||
|
||||
# expecting a mapping of "group label" to "group indices"
|
||||
function extractGroupArgs(idxmap::Dict{T,V}, args...) where {T, V<:AVec{Int}}
|
||||
@noinline function extractGroupArgs(idxmap::Dict{T,V}, args...) where {T, V<:AVec{Int}}
|
||||
groupLabels = sortedkeys(idxmap)
|
||||
groupIds = Vector{Int}[collect(idxmap[k]) for k in groupLabels]
|
||||
GroupBy(groupLabels, groupIds)
|
||||
end
|
||||
|
||||
filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
|
||||
filter_data(v, idxfilter) = v
|
||||
@noinline filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
|
||||
@noinline filter_data(v, idxfilter) = v
|
||||
|
||||
function filter_data!(plotattributes::KW, idxfilter)
|
||||
@noinline function filter_data!(plotattributes::KW, idxfilter)
|
||||
for s in (:x, :y, :z)
|
||||
plotattributes[s] = filter_data(get(plotattributes, s, nothing), idxfilter)
|
||||
end
|
||||
end
|
||||
|
||||
function _filter_input_data!(plotattributes::KW)
|
||||
@noinline function _filter_input_data!(plotattributes::KW)
|
||||
idxfilter = pop!(plotattributes, :idxfilter, nothing)
|
||||
if idxfilter != nothing
|
||||
if idxfilter !== nothing
|
||||
filter_data!(plotattributes, idxfilter)
|
||||
end
|
||||
end
|
||||
@@ -1182,7 +1182,7 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function convertLegendValue(val::Symbol)
|
||||
@noinline function convertLegendValue(val::Symbol)
|
||||
if val in (:both, :all, :yes)
|
||||
:best
|
||||
elseif val in (:no, :none)
|
||||
@@ -1193,10 +1193,10 @@ function convertLegendValue(val::Symbol)
|
||||
error("Invalid symbol for legend: $val")
|
||||
end
|
||||
end
|
||||
convertLegendValue(val::Bool) = val ? :best : :none
|
||||
convertLegendValue(val::Nothing) = :none
|
||||
convertLegendValue(v::Tuple{S,T}) where {S<:Real, T<:Real} = v
|
||||
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||
@noinline convertLegendValue(val::Bool) = val ? :best : :none
|
||||
@noinline convertLegendValue(val::Nothing) = :none
|
||||
@noinline convertLegendValue(v::Tuple{S,T}) where {S<:Real, T<:Real} = v
|
||||
@noinline convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -1205,12 +1205,12 @@ convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||
# multi-row matrices will give a column
|
||||
# InputWrapper just gives the contents
|
||||
# anything else is returned as-is
|
||||
function slice_arg(v::AMat, idx::Int)
|
||||
@noinline function slice_arg(v::AMat, idx::Int)
|
||||
c = mod1(idx, size(v,2))
|
||||
size(v,1) == 1 ? v[1,c] : v[:,c]
|
||||
end
|
||||
slice_arg(wrapper::InputWrapper, idx) = wrapper.obj
|
||||
slice_arg(v, idx) = v
|
||||
@noinline slice_arg(wrapper::InputWrapper, idx) = wrapper.obj
|
||||
@noinline slice_arg(v, idx) = v
|
||||
|
||||
|
||||
# given an argument key (k), we want to extract the argument value for this index.
|
||||
@@ -1237,7 +1237,7 @@ end
|
||||
# v = plotattributes[k]
|
||||
# plotattributes[k] = if v == :match
|
||||
# match_color
|
||||
# elseif v == nothing
|
||||
# elseif v === nothing
|
||||
# plot_color(RGBA(0,0,0,0))
|
||||
# else
|
||||
# v
|
||||
@@ -1246,7 +1246,7 @@ end
|
||||
|
||||
function color_or_nothing!(plotattributes::KW, k::Symbol)
|
||||
v = plotattributes[k]
|
||||
plotattributes[k] = if v == nothing || v == false
|
||||
plotattributes[k] = if v === nothing || v == false
|
||||
RGBA{Float64}(0,0,0,0)
|
||||
elseif v != :match
|
||||
plot_color(v)
|
||||
@@ -1509,9 +1509,9 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
has_black_border_for_default(st) = error("The seriestype attribute only accepts Symbols, you passed the $(typeof(st)) $st.")
|
||||
has_black_border_for_default(st::Function) = error("The seriestype attribute only accepts Symbols, you passed the function $st.")
|
||||
function has_black_border_for_default(st::Symbol)
|
||||
@noinline has_black_border_for_default(st) = error("The seriestype attribute only accepts Symbols, you passed the $(typeof(st)) $st.")
|
||||
@noinline has_black_border_for_default(st::Function) = error("The seriestype attribute only accepts Symbols, you passed the function $st.")
|
||||
@noinline function has_black_border_for_default(st::Symbol)
|
||||
like_histogram(st) || st in (:hexbin, :bar, :shape)
|
||||
end
|
||||
|
||||
@@ -1563,11 +1563,11 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
|
||||
# update alphas
|
||||
for asym in (:linealpha, :markeralpha, :fillalpha)
|
||||
if plotattributes[asym] == nothing
|
||||
if plotattributes[asym] === nothing
|
||||
plotattributes[asym] = plotattributes[:seriesalpha]
|
||||
end
|
||||
end
|
||||
if plotattributes[:markerstrokealpha] == nothing
|
||||
if plotattributes[:markerstrokealpha] === nothing
|
||||
plotattributes[:markerstrokealpha] = plotattributes[:markeralpha]
|
||||
end
|
||||
|
||||
@@ -1602,13 +1602,13 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
end
|
||||
|
||||
# if marker_z, fill_z or line_z are set, ensure we have a gradient
|
||||
if plotattributes[:marker_z] != nothing
|
||||
if plotattributes[:marker_z] !== nothing
|
||||
ensure_gradient!(plotattributes, :markercolor, :markeralpha)
|
||||
end
|
||||
if plotattributes[:line_z] != nothing
|
||||
if plotattributes[:line_z] !== nothing
|
||||
ensure_gradient!(plotattributes, :linecolor, :linealpha)
|
||||
end
|
||||
if plotattributes[:fill_z] != nothing
|
||||
if plotattributes[:fill_z] !== nothing
|
||||
ensure_gradient!(plotattributes, :fillcolor, :fillalpha)
|
||||
end
|
||||
|
||||
|
||||
+8
-8
@@ -67,7 +67,7 @@ function process_axis_arg!(plotattributes::KW, arg, letter = "")
|
||||
elseif T <: AVec
|
||||
plotattributes[Symbol(letter,:ticks)] = arg
|
||||
|
||||
elseif arg == nothing
|
||||
elseif arg === nothing
|
||||
plotattributes[Symbol(letter,:ticks)] = []
|
||||
|
||||
elseif T <: Bool || arg in _allShowaxisArgs
|
||||
@@ -166,7 +166,7 @@ function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing)
|
||||
# or DateTime) is chosen based on the time span between amin and amax
|
||||
# rather than on the input format
|
||||
# TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime
|
||||
if ticks == nothing && scale == :identity
|
||||
if ticks === nothing && scale == :identity
|
||||
if axis[:formatter] == dateformatter
|
||||
# optimize_datetime_ticks returns ticks and labels(!) based on
|
||||
# integers/floats corresponding to the DateTime type. Thus, the axes
|
||||
@@ -184,7 +184,7 @@ function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing)
|
||||
end
|
||||
|
||||
# get a list of well-laid-out ticks
|
||||
if ticks == nothing
|
||||
if ticks === nothing
|
||||
scaled_ticks = optimize_ticks(
|
||||
sf(amin),
|
||||
sf(amax);
|
||||
@@ -382,7 +382,7 @@ function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
data = plotattributes[letter] = Surface(Matrix{Float64}(data.surf))
|
||||
end
|
||||
expand_extrema!(axis, data)
|
||||
elseif data != nothing
|
||||
elseif data !== nothing
|
||||
# TODO: need more here... gotta track the discrete reference value
|
||||
# as well as any coord offset (think of boxplot shape coords... they all
|
||||
# correspond to the same x-value)
|
||||
@@ -399,10 +399,10 @@ function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
|
||||
# expand for fillrange
|
||||
fr = plotattributes[:fillrange]
|
||||
if fr == nothing && plotattributes[:seriestype] == :bar
|
||||
if fr === nothing && plotattributes[:seriestype] == :bar
|
||||
fr = 0.0
|
||||
end
|
||||
if fr != nothing && !all3D(plotattributes)
|
||||
if fr !== nothing && !all3D(plotattributes)
|
||||
axis = sp.attr[vert ? :yaxis : :xaxis]
|
||||
if typeof(fr) <: Tuple
|
||||
for fri in fr
|
||||
@@ -419,7 +419,7 @@ function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
data = plotattributes[dsym]
|
||||
|
||||
bw = plotattributes[:bar_width]
|
||||
if bw == nothing
|
||||
if bw === nothing
|
||||
bw = plotattributes[:bar_width] = _bar_width * ignorenan_minimum(filter(x->x>0,diff(sort(data))))
|
||||
end
|
||||
axis = sp.attr[Symbol(dsym, :axis)]
|
||||
@@ -754,5 +754,5 @@ function axis_drawing_info(sp::Subplot)
|
||||
end
|
||||
end
|
||||
|
||||
xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs
|
||||
Any[xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs]
|
||||
end
|
||||
|
||||
+30
-20
@@ -47,17 +47,17 @@ end
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# don't do anything as a default
|
||||
_create_backend_figure(plt::Plot) = nothing
|
||||
_prepare_plot_object(plt::Plot) = nothing
|
||||
_initialize_subplot(plt::Plot, sp::Subplot) = nothing
|
||||
@noinline _create_backend_figure(plt::Plot) = nothing
|
||||
@noinline _prepare_plot_object(plt::Plot) = nothing
|
||||
@noinline _initialize_subplot(plt::Plot, sp::Subplot) = nothing
|
||||
|
||||
_series_added(plt::Plot, series::Series) = nothing
|
||||
_series_updated(plt::Plot, series::Series) = nothing
|
||||
@noinline _series_added(plt::Plot, series::Series) = nothing
|
||||
@noinline _series_updated(plt::Plot, series::Series) = nothing
|
||||
|
||||
_before_layout_calcs(plt::Plot) = nothing
|
||||
@noinline _before_layout_calcs(plt::Plot) = nothing
|
||||
|
||||
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefontsize] * pt
|
||||
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefontsize] * pt
|
||||
@noinline title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefontsize] * pt
|
||||
@noinline guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefontsize] * pt
|
||||
|
||||
"Returns the (width,height) of a text label."
|
||||
function text_size(lablen::Int, sz::Number, rot::Number = 0)
|
||||
@@ -77,7 +77,7 @@ text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(l
|
||||
# account for the size/length/rotation of tick labels
|
||||
function tick_padding(sp::Subplot, axis::Axis)
|
||||
ticks = get_ticks(sp, axis)
|
||||
if ticks == nothing
|
||||
if ticks === nothing
|
||||
0mm
|
||||
else
|
||||
vals, labs = ticks
|
||||
@@ -123,20 +123,32 @@ function _update_min_padding!(sp::Subplot)
|
||||
sp.minpad = (leftpad, toppad, rightpad, bottompad)
|
||||
end
|
||||
|
||||
_update_plot_object(plt::Plot) = nothing
|
||||
@noinline _update_plot_object(plt::Plot) = nothing
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
mutable struct CurrentBackend
|
||||
sym::Symbol
|
||||
pkg::AbstractBackend
|
||||
mutable struct CurrentBackend{sym,T}
|
||||
pkg::T
|
||||
end
|
||||
function CurrentBackend(sym::Symbol)
|
||||
bkend = _backend_instance(sym)
|
||||
CurrentBackend{sym,typeof(bkend)}(bkend)
|
||||
end
|
||||
|
||||
function Base.getproperty(bkend::CurrentBackend{sym,T},x::Symbol) where {sym,T}
|
||||
if x === :sym
|
||||
return sym
|
||||
elseif x === :pkg
|
||||
return getfield(bkend,:pkg)
|
||||
else
|
||||
error("Must be sym or pkg")
|
||||
end
|
||||
end
|
||||
CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
_fallback_default_backend() = backend(GRBackend())
|
||||
@noinline _fallback_default_backend() = backend(GRBackend())
|
||||
|
||||
function _pick_default_backend()
|
||||
env_default = get(ENV, "PLOTS_DEFAULT_BACKEND", "")
|
||||
@@ -177,8 +189,7 @@ function backend(pkg::AbstractBackend)
|
||||
_initialize_backend(pkg)
|
||||
push!(_initialized_backends, sym)
|
||||
end
|
||||
CURRENT_BACKEND.sym = sym
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
CURRENT_BACKEND = Plots.CurrentBackend(sym)
|
||||
pkg
|
||||
end
|
||||
|
||||
@@ -294,9 +305,8 @@ function _initialize_backend(pkg::AbstractBackend)
|
||||
end
|
||||
end
|
||||
|
||||
_initialize_backend(pkg::GRBackend) = nothing
|
||||
|
||||
_initialize_backend(pkg::PlotlyBackend) = nothing
|
||||
@noinline _initialize_backend(pkg::GRBackend) = nothing
|
||||
@noinline _initialize_backend(pkg::PlotlyBackend) = nothing
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
+608
-557
File diff suppressed because it is too large
Load Diff
@@ -121,9 +121,9 @@ function _create_backend_figure(plt::Plot{InspectDRBackend})
|
||||
gplot = _inspectdr_getgui(plt.o)
|
||||
|
||||
#:overwrite_figure: want to reuse current figure
|
||||
if plt[:overwrite_figure] && mplot != nothing
|
||||
if plt[:overwrite_figure] && mplot !== nothing
|
||||
mplot.subplots = [] #Reset
|
||||
if gplot != nothing #Ensure still references current plot
|
||||
if gplot !== nothing #Ensure still references current plot
|
||||
gplot.src = mplot
|
||||
end
|
||||
else #want new one:
|
||||
|
||||
@@ -96,7 +96,7 @@ pgf_thickness_scaling(series) = pgf_thickness_scaling(series[:subplot])
|
||||
function pgf_fillstyle(plotattributes, i = 1)
|
||||
cstr,a = pgf_color(get_fillcolor(plotattributes, i))
|
||||
fa = get_fillalpha(plotattributes, i)
|
||||
if fa != nothing
|
||||
if fa !== nothing
|
||||
a = fa
|
||||
end
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
@@ -216,12 +216,12 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
|
||||
# add to legend?
|
||||
if i == 1 && sp[:legend] != :none && should_add_to_legend(series)
|
||||
if plotattributes[:fillrange] != nothing
|
||||
if plotattributes[:fillrange] !== nothing
|
||||
push!(style, "forget plot")
|
||||
push!(series_collection, pgf_fill_legend_hack(plotattributes, args))
|
||||
else
|
||||
kw[:legendentry] = plotattributes[:label]
|
||||
if st == :shape # || plotattributes[:fillrange] != nothing
|
||||
if st == :shape # || plotattributes[:fillrange] !== nothing
|
||||
push!(style, "area legend")
|
||||
end
|
||||
end
|
||||
@@ -238,7 +238,7 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
kw[:style] = join(style, ',')
|
||||
|
||||
# add fillrange
|
||||
if series[:fillrange] != nothing && st != :shape
|
||||
if series[:fillrange] !== nothing && st != :shape
|
||||
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
|
||||
end
|
||||
|
||||
|
||||
+25
-21
@@ -403,7 +403,7 @@ end
|
||||
function plotly_data(series::Series, letter::Symbol, data)
|
||||
axis = series[:subplot][Symbol(letter, :axis)]
|
||||
|
||||
data = if axis[:ticks] == :native && data != nothing
|
||||
data = if axis[:ticks] == :native && data !== nothing
|
||||
plotly_native_data(axis, data)
|
||||
else
|
||||
data
|
||||
@@ -415,7 +415,7 @@ function plotly_data(series::Series, letter::Symbol, data)
|
||||
plotly_data(data)
|
||||
end
|
||||
end
|
||||
plotly_data(v) = v != nothing ? collect(v) : v
|
||||
plotly_data(v) = v !== nothing ? collect(v) : v
|
||||
plotly_data(surf::Surface) = surf.surf
|
||||
plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v)
|
||||
|
||||
@@ -529,7 +529,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
else
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:opacity] = series[:fillalpha]
|
||||
if series[:fill_z] != nothing
|
||||
if series[:fill_z] !== nothing
|
||||
plotattributes_out[:surfacecolor] = plotly_surface_data(series, series[:fill_z])
|
||||
end
|
||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||
@@ -611,11 +611,11 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
plotly_hover!(plotattributes_out, _cycle(series[:hover], i))
|
||||
plotattributes_outs[i] = plotattributes_out
|
||||
end
|
||||
if series[:fill_z] != nothing
|
||||
if series[:fill_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill))
|
||||
elseif series[:line_z] != nothing
|
||||
elseif series[:line_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line))
|
||||
elseif series[:marker_z] != nothing
|
||||
elseif series[:marker_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
|
||||
end
|
||||
plotattributes_outs
|
||||
@@ -631,11 +631,9 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
|
||||
|
||||
segments = iter_segments(series)
|
||||
plotattributes_outs = Vector{KW}(undef, (hasfillrange ? 2 : 1 ) * length(segments))
|
||||
plotattributes_outs = fill(KW(), (hasfillrange ? 2 : 1 ) * length(segments))
|
||||
|
||||
for (i,rng) in enumerate(segments)
|
||||
!isscatter && length(rng) < 2 && continue
|
||||
|
||||
plotattributes_out = deepcopy(plotattributes_base)
|
||||
plotattributes_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
|
||||
plotattributes_out[:legendgroup] = series[:label]
|
||||
@@ -735,11 +733,11 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
end
|
||||
end
|
||||
|
||||
if series[:line_z] != nothing
|
||||
if series[:line_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line))
|
||||
elseif series[:fill_z] != nothing
|
||||
elseif series[:fill_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill))
|
||||
elseif series[:marker_z] != nothing
|
||||
elseif series[:marker_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
|
||||
end
|
||||
|
||||
@@ -784,7 +782,7 @@ function plotly_hover!(plotattributes_out::KW, hover)
|
||||
# hover text
|
||||
if hover in (:none, false)
|
||||
plotattributes_out[:hoverinfo] = "none"
|
||||
elseif hover != nothing
|
||||
elseif hover !== nothing
|
||||
plotattributes_out[:hoverinfo] = "text"
|
||||
plotattributes_out[:text] = hover
|
||||
end
|
||||
@@ -804,9 +802,12 @@ plotly_series_json(plt::Plot) = JSON.json(plotly_series(plt), 4)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
html_head(plt::Plot{PlotlyBackend}) = plotly_html_head(plt)
|
||||
html_body(plt::Plot{PlotlyBackend}) = plotly_html_body(plt)
|
||||
|
||||
const ijulia_initialized = Ref(false)
|
||||
|
||||
function html_head(plt::Plot{PlotlyBackend})
|
||||
function plotly_html_head(plt::Plot)
|
||||
local_file = ("file://" * plotly_local_file_path)
|
||||
plotly = use_local_dependencies[] ? local_file : plotly_remote_file_path
|
||||
if isijulia() && !ijulia_initialized[]
|
||||
@@ -825,8 +826,8 @@ function html_head(plt::Plot{PlotlyBackend})
|
||||
return "<script src=$(repr(plotly))></script>"
|
||||
end
|
||||
|
||||
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||
if style == nothing
|
||||
function plotly_html_body(plt, style = nothing)
|
||||
if style === nothing
|
||||
w, h = plt[:size]
|
||||
style = "width:$(w)px;height:$(h)px;"
|
||||
end
|
||||
@@ -841,17 +842,14 @@ function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||
html
|
||||
end
|
||||
|
||||
function js_body(plt::Plot{PlotlyBackend}, uuid)
|
||||
function js_body(plt::Plot, uuid)
|
||||
js = """
|
||||
PLOT = document.getElementById('$(uuid)');
|
||||
Plotly.plot(PLOT, $(plotly_series_json(plt)), $(plotly_layout_json(plt)));
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{PlotlyBackend})
|
||||
function plotly_show_js(io::IO, plot::Plot)
|
||||
data = []
|
||||
for series in plot.series_list
|
||||
append!(data, plotly_series(plot, series))
|
||||
@@ -860,6 +858,12 @@ function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{Plotly
|
||||
JSON.print(io, Dict(:data => data, :layout => layout))
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{PlotlyBackend})
|
||||
plotly_show_js(io, plot)
|
||||
end
|
||||
|
||||
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
|
||||
write(io, standalone_html(plt))
|
||||
|
||||
+29
-63
@@ -1,78 +1,44 @@
|
||||
# https://github.com/sglyon/PlotlyJS.jl
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||
PlotlyJS.SyncPlot(PlotlyJS.Plot(), options = current().o.options)
|
||||
else
|
||||
PlotlyJS.plot()
|
||||
function plotlyjs_syncplot(plt::Plot{PlotlyJSBackend})
|
||||
plt[:overwrite_figure] && closeall()
|
||||
plt.o = PlotlyJS.plot()
|
||||
traces = PlotlyJS.GenericTrace[]
|
||||
for series_dict in plotly_series(plt)
|
||||
plotly_type = pop!(series_dict, :type)
|
||||
push!(traces, PlotlyJS.GenericTrace(plotly_type; series_dict...))
|
||||
end
|
||||
PlotlyJS.addtraces!(plt.o, traces...)
|
||||
layout = plotly_layout(plt)
|
||||
w, h = plt[:size]
|
||||
PlotlyJS.relayout!(plt.o, layout, width = w, height = h)
|
||||
return plt.o
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
syncplot = plt.o
|
||||
pdicts = plotly_series(plt, series)
|
||||
for pdict in pdicts
|
||||
typ = pop!(pdict, :type)
|
||||
gt = PlotlyJS.GenericTrace(typ; pdict...)
|
||||
PlotlyJS.addtraces!(syncplot, gt)
|
||||
end
|
||||
const _plotlyjs_mimeformats = Dict(
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"image/svg+xml" => "svg",
|
||||
"image/eps" => "eps",
|
||||
)
|
||||
|
||||
for (mime, fmt) in _plotlyjs_mimeformats
|
||||
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plotlyjs_syncplot(plt), format = $fmt)
|
||||
end
|
||||
|
||||
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
|
||||
kw = KW(xsym => (series.plotattributes[:x],), ysym => (series.plotattributes[:y],))
|
||||
z = series[:z]
|
||||
if z != nothing
|
||||
kw[:z] = (isa(z,Surface) ? transpose_z(series, series[:z].surf, false) : z,)
|
||||
end
|
||||
PlotlyJS.restyle!(
|
||||
plt.o,
|
||||
findfirst(isequal(series), plt.series_list),
|
||||
kw
|
||||
)
|
||||
end
|
||||
# Use the Plotly implementation for json and html:
|
||||
_show(io::IO, mime::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend}) = plotly_show_js(io, plt)
|
||||
|
||||
html_head(plt::Plot{PlotlyJSBackend}) = plotly_html_head(plt)
|
||||
html_body(plt::Plot{PlotlyJSBackend}) = plotly_html_body(plt)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = write(io, standalone_html(plt))
|
||||
|
||||
function _update_plot_object(plt::Plot{PlotlyJSBackend})
|
||||
pdict = plotly_layout(plt)
|
||||
syncplot = plt.o
|
||||
w,h = plt[:size]
|
||||
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = show(io, MIME("text/html"), plt.o)
|
||||
_show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="svg")
|
||||
_show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="png")
|
||||
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="pdf")
|
||||
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="eps")
|
||||
|
||||
function _show(io::IO, m::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend})
|
||||
show(io, m, plt.o)
|
||||
end
|
||||
|
||||
|
||||
function write_temp_html(plt::Plot{PlotlyJSBackend})
|
||||
filename = string(tempname(), ".html")
|
||||
savefig(plt, filename)
|
||||
filename
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PlotlyJSBackend})
|
||||
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
|
||||
display(plt.o)
|
||||
else
|
||||
standalone_html_window(plt)
|
||||
end
|
||||
end
|
||||
_display(plt::Plot{PlotlyJSBackend}) = display(plotlyjs_syncplot(plt))
|
||||
|
||||
@require WebIO = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" begin
|
||||
function WebIO.render(plt::Plot{PlotlyJSBackend})
|
||||
|
||||
+16
-16
@@ -216,7 +216,7 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
|
||||
if series[:x] != nothing
|
||||
if series[:x] !== nothing
|
||||
x, y = series[:x], series[:y]
|
||||
nx, ny = length(x), length(y)
|
||||
if !isa(get(series.plotattributes, :z, nothing), Surface) && nx != ny
|
||||
@@ -434,7 +434,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
if maximum(series[:linewidth]) > 0
|
||||
segments = iter_segments(series)
|
||||
# TODO: check LineCollection alternative for speed
|
||||
# if length(segments) > 1 && (any(typeof(series[attr]) <: AbstractVector for attr in (:fillcolor, :fillalpha)) || series[:fill_z] != nothing) && !(typeof(series[:linestyle]) <: AbstractVector)
|
||||
# if length(segments) > 1 && (any(typeof(series[attr]) <: AbstractVector for attr in (:fillcolor, :fillalpha)) || series[:fill_z] !== nothing) && !(typeof(series[:linestyle]) <: AbstractVector)
|
||||
# # multicolored line segments
|
||||
# n = length(segments)
|
||||
# # segments = Array(Any,n)
|
||||
@@ -478,7 +478,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# end
|
||||
|
||||
a = series[:arrow]
|
||||
if a != nothing && !is3d(st) # TODO: handle 3d later
|
||||
if a !== nothing && !is3d(st) # TODO: handle 3d later
|
||||
if typeof(a) != Arrow
|
||||
@warn("Unexpected type for arrow: $(typeof(a))")
|
||||
else
|
||||
@@ -508,7 +508,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
if series[:markershape] != :none && st in (:path, :scatter, :path3d,
|
||||
:scatter3d, :steppre, :steppost,
|
||||
:bar)
|
||||
markercolor = if any(typeof(series[arg]) <: AVec for arg in (:markercolor, :markeralpha)) || series[:marker_z] != nothing
|
||||
markercolor = if any(typeof(series[arg]) <: AVec for arg in (:markercolor, :markeralpha)) || series[:marker_z] !== nothing
|
||||
# py_color(plot_color.(get_markercolor.(series, clims, eachindex(x)), get_markeralpha.(series, eachindex(x))))
|
||||
[py_color(plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i))) for i in eachindex(x)]
|
||||
else
|
||||
@@ -672,7 +672,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
push!(handles, handle)
|
||||
|
||||
# contour fills
|
||||
if series[:fillrange] != nothing
|
||||
if series[:fillrange] !== nothing
|
||||
handle = ax."contourf"(x, y, z, levelargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
@@ -691,7 +691,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
z = transpose_z(series, z)
|
||||
if st == :surface
|
||||
if series[:fill_z] != nothing
|
||||
if series[:fill_z] !== nothing
|
||||
# the surface colors are different than z-value
|
||||
extrakw[:facecolors] = py_shading(series[:fillcolor], transpose_z(series, series[:fill_z].surf))
|
||||
extrakw[:shade] = false
|
||||
@@ -830,7 +830,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
# handle area filling
|
||||
fillrange = series[:fillrange]
|
||||
if fillrange != nothing && st != :contour
|
||||
if fillrange !== nothing && st != :contour
|
||||
for (i, rng) in enumerate(iter_segments(series))
|
||||
f, dim1, dim2 = if isvertical(series)
|
||||
:fill_between, x[rng], y[rng]
|
||||
@@ -871,7 +871,7 @@ end
|
||||
function py_set_ticks(ax, ticks, letter)
|
||||
ticks == :auto && return
|
||||
axis = getproperty(ax, Symbol(letter,"axis"))
|
||||
if ticks == :none || ticks == nothing || ticks == false
|
||||
if ticks == :none || ticks === nothing || ticks == false
|
||||
kw = KW()
|
||||
for dir in (:top,:bottom,:left,:right)
|
||||
kw[dir] = kw[Symbol(:label,dir)] = false
|
||||
@@ -978,7 +978,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
# update subplots
|
||||
for sp in plt.subplots
|
||||
ax = sp.o
|
||||
if ax == nothing
|
||||
if ax === nothing
|
||||
continue
|
||||
end
|
||||
|
||||
@@ -1018,12 +1018,12 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
kw[:ticks] = locator
|
||||
kw[:format] = formatter
|
||||
kw[:boundaries] = vcat(0, kw[:values] + 0.5)
|
||||
elseif any(colorbar_series[attr] != nothing for attr in (:line_z, :fill_z, :marker_z))
|
||||
elseif any(colorbar_series[attr] !== nothing for attr in (:line_z, :fill_z, :marker_z))
|
||||
cmin, cmax = get_clims(sp)
|
||||
norm = pycolors."Normalize"(vmin = cmin, vmax = cmax)
|
||||
f = if colorbar_series[:line_z] != nothing
|
||||
f = if colorbar_series[:line_z] !== nothing
|
||||
py_linecolormap
|
||||
elseif colorbar_series[:fill_z] != nothing
|
||||
elseif colorbar_series[:fill_z] !== nothing
|
||||
py_fillcolormap
|
||||
else
|
||||
py_markercolormap
|
||||
@@ -1186,7 +1186,7 @@ end
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
ax = sp.o
|
||||
ax == nothing && return sp.minpad
|
||||
ax === nothing && return sp.minpad
|
||||
plotbb = py_bbox(ax)
|
||||
|
||||
# TODO: this should initialize to the margin from sp.attr
|
||||
@@ -1297,7 +1297,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
for series in series_list(sp)
|
||||
if should_add_to_legend(series)
|
||||
# add a line/marker and a label
|
||||
push!(handles, if series[:seriestype] == :shape || series[:fillrange] != nothing
|
||||
push!(handles, if series[:seriestype] == :shape || series[:fillrange] !== nothing
|
||||
pypatches."Patch"(
|
||||
edgecolor = py_color(single_color(get_linecolor(series, clims)), get_linealpha(series)),
|
||||
facecolor = py_color(single_color(get_fillcolor(series, clims)), get_fillalpha(series)),
|
||||
@@ -1335,7 +1335,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
frame = leg."get_frame"()
|
||||
frame."set_linewidth"(py_thickness_scale(plt, 1))
|
||||
leg."set_zorder"(1000)
|
||||
sp[:legendtitle] != nothing && leg."set_title"(sp[:legendtitle])
|
||||
sp[:legendtitle] !== nothing && leg."set_title"(sp[:legendtitle])
|
||||
|
||||
for txt in leg."get_texts"()
|
||||
PyPlot.plt."setp"(txt, color = py_color(sp[:legendfontcolor]), family = sp[:legendfontfamily])
|
||||
@@ -1352,7 +1352,7 @@ end
|
||||
function _update_plot_object(plt::Plot{PyPlotBackend})
|
||||
for sp in plt.subplots
|
||||
ax = sp.o
|
||||
ax == nothing && return
|
||||
ax === nothing && return
|
||||
figw, figh = sp.plt[:size]
|
||||
figw, figh = figw*px, figh*px
|
||||
pcts = bbox_to_pcts(sp.plotarea, figw, figh)
|
||||
|
||||
+3
-3
@@ -137,7 +137,7 @@ const _shape_keys = Symbol[
|
||||
:x,
|
||||
]
|
||||
|
||||
const _shapes = KW(
|
||||
const _shapes = Dict{Symbol,Shape}(
|
||||
:circle => makeshape(20),
|
||||
:rect => makeshape(4, offset=-0.25),
|
||||
:diamond => makeshape(4),
|
||||
@@ -529,7 +529,7 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
# end
|
||||
|
||||
# @show msw msh
|
||||
if anns != nothing && anns.baseshape != nothing
|
||||
if anns !== nothing && anns.baseshape !== nothing
|
||||
# we use baseshape to overwrite the markershape attribute
|
||||
# with a list of custom shapes for each
|
||||
msw,msh = anns.scalefactor
|
||||
@@ -568,7 +568,7 @@ mutable struct EachAnn
|
||||
end
|
||||
|
||||
function Base.iterate(ea::EachAnn, i = 1)
|
||||
if ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
if ea.anns === nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
+2
-2
@@ -496,7 +496,7 @@ function test_examples(pkgname::Symbol; debug = false, disp = true, sleep = noth
|
||||
Plots._debugMode.on = debug
|
||||
plts = Dict()
|
||||
for i in 1:length(_examples)
|
||||
only != nothing && !(i in only) && continue
|
||||
only !== nothing && !(i in only) && continue
|
||||
i in skip && continue
|
||||
try
|
||||
plt = test_examples(pkgname, i, debug=debug, disp=disp)
|
||||
@@ -505,7 +505,7 @@ function test_examples(pkgname::Symbol; debug = false, disp = true, sleep = noth
|
||||
# TODO: put error info into markdown?
|
||||
@warn("Example $pkgname:$i:$(_examples[i].header) failed with: $ex")
|
||||
end
|
||||
if sleep != nothing
|
||||
if sleep !== nothing
|
||||
Base.sleep(sleep)
|
||||
end
|
||||
end
|
||||
|
||||
+4
-1
@@ -20,7 +20,10 @@ frontends like jupyterlab and nteract.
|
||||
_ijulia__extra_mime_info!(plt::Plot, out::Dict) = out
|
||||
|
||||
function _ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = JSON.lower(plt.o)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out
|
||||
end
|
||||
|
||||
|
||||
+48
-48
@@ -188,29 +188,29 @@ parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
|
||||
# padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout)
|
||||
# padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
|
||||
|
||||
update_position!(layout::AbstractLayout) = nothing
|
||||
update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm,0mm,0mm,0mm]) = nothing
|
||||
@noinline update_position!(layout::AbstractLayout) = nothing
|
||||
@noinline update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm,0mm,0mm,0mm]) = nothing
|
||||
|
||||
left(layout::AbstractLayout) = left(bbox(layout))
|
||||
top(layout::AbstractLayout) = top(bbox(layout))
|
||||
right(layout::AbstractLayout) = right(bbox(layout))
|
||||
bottom(layout::AbstractLayout) = bottom(bbox(layout))
|
||||
width(layout::AbstractLayout) = width(bbox(layout))
|
||||
height(layout::AbstractLayout) = height(bbox(layout))
|
||||
@noinline left(layout::AbstractLayout) = left(bbox(layout))
|
||||
@noinline top(layout::AbstractLayout) = top(bbox(layout))
|
||||
@noinline right(layout::AbstractLayout) = right(bbox(layout))
|
||||
@noinline bottom(layout::AbstractLayout) = bottom(bbox(layout))
|
||||
@noinline width(layout::AbstractLayout) = width(bbox(layout))
|
||||
@noinline height(layout::AbstractLayout) = height(bbox(layout))
|
||||
|
||||
# pass these through to the bbox methods if there's no plotarea
|
||||
plotarea(layout::AbstractLayout) = bbox(layout)
|
||||
plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb)
|
||||
@noinline plotarea(layout::AbstractLayout) = bbox(layout)
|
||||
@noinline plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb)
|
||||
|
||||
attr(layout::AbstractLayout, k::Symbol) = layout.attr[k]
|
||||
attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v)
|
||||
attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v)
|
||||
hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k)
|
||||
@noinline attr(layout::AbstractLayout, k::Symbol) = layout.attr[k]
|
||||
@noinline attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v)
|
||||
@noinline attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v)
|
||||
@noinline hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k)
|
||||
|
||||
leftpad(layout::AbstractLayout) = 0mm
|
||||
toppad(layout::AbstractLayout) = 0mm
|
||||
rightpad(layout::AbstractLayout) = 0mm
|
||||
bottompad(layout::AbstractLayout) = 0mm
|
||||
@noinline leftpad(layout::AbstractLayout) = 0mm
|
||||
@noinline toppad(layout::AbstractLayout) = 0mm
|
||||
@noinline rightpad(layout::AbstractLayout) = 0mm
|
||||
@noinline bottompad(layout::AbstractLayout) = 0mm
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# RootLayout
|
||||
@@ -227,8 +227,8 @@ bbox(::RootLayout) = defaultbox
|
||||
|
||||
# contains blank space
|
||||
mutable struct EmptyLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
bbox::BoundingBox
|
||||
parent
|
||||
bbox#::BoundingBox
|
||||
attr::KW # store label, width, and height for initialization
|
||||
# label # this is the label that the subplot will take (since we create a layout before initialization)
|
||||
end
|
||||
@@ -245,12 +245,12 @@ _update_min_padding!(layout::EmptyLayout) = nothing
|
||||
|
||||
# nested, gridded layout with optional size percentages
|
||||
mutable struct GridLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
bbox::BoundingBox
|
||||
grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion
|
||||
widths::Vector{Measure}
|
||||
heights::Vector{Measure}
|
||||
parent
|
||||
minpad::Tuple{AbsoluteLength,AbsoluteLength,AbsoluteLength,AbsoluteLength} # leftpad, toppad, rightpad, bottompad
|
||||
bbox#::BoundingBox
|
||||
grid::Matrix{Any} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion
|
||||
widths::Vector{Any}
|
||||
heights::Vector{Any}
|
||||
attr::KW
|
||||
end
|
||||
|
||||
@@ -268,14 +268,14 @@ function GridLayout(dims...;
|
||||
widths = zeros(dims[2]),
|
||||
heights = zeros(dims[1]),
|
||||
kw...)
|
||||
grid = Matrix{AbstractLayout}(undef, dims...)
|
||||
grid = Matrix{Any}(undef, dims...)
|
||||
layout = GridLayout(
|
||||
parent,
|
||||
(20mm, 5mm, 2mm, 10mm),
|
||||
defaultbox,
|
||||
grid,
|
||||
Measure[w*pct for w in widths],
|
||||
Measure[h*pct for h in heights],
|
||||
Any[w*pct for w in widths],
|
||||
Any[h*pct for h in heights],
|
||||
# convert(Vector{Float64}, widths),
|
||||
# convert(Vector{Float64}, heights),
|
||||
KW(kw))
|
||||
@@ -349,18 +349,18 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
|
||||
# # create a matrix for each minimum padding direction
|
||||
# _update_min_padding!(layout)
|
||||
|
||||
minpad_left = map(leftpad, layout.grid)
|
||||
minpad_top = map(toppad, layout.grid)
|
||||
minpad_right = map(rightpad, layout.grid)
|
||||
minpad_bottom = map(bottompad, layout.grid)
|
||||
minpad_left::Matrix{AbsoluteLength} = map(leftpad, layout.grid)
|
||||
minpad_top::Matrix{AbsoluteLength} = map(toppad, layout.grid)
|
||||
minpad_right::Matrix{AbsoluteLength} = map(rightpad, layout.grid)
|
||||
minpad_bottom::Matrix{AbsoluteLength} = map(bottompad, layout.grid)
|
||||
|
||||
# get the max horizontal (left and right) padding over columns,
|
||||
# and max vertical (bottom and top) padding over rows
|
||||
# TODO: add extra padding here
|
||||
pad_left = maximum(minpad_left, dims = 1)
|
||||
pad_top = maximum(minpad_top, dims = 2)
|
||||
pad_right = maximum(minpad_right, dims = 1)
|
||||
pad_bottom = maximum(minpad_bottom, dims = 2)
|
||||
pad_left::Matrix{AbsoluteLength} = maximum(minpad_left, dims = 1)
|
||||
pad_top::Matrix{AbsoluteLength} = maximum(minpad_top, dims = 2)
|
||||
pad_right::Matrix{AbsoluteLength} = maximum(minpad_right, dims = 1)
|
||||
pad_bottom::Matrix{AbsoluteLength} = maximum(minpad_bottom, dims = 2)
|
||||
|
||||
# make sure the perimeter match the parent
|
||||
pad_left[1] = max(pad_left[1], minimum_perimeter[1])
|
||||
@@ -389,14 +389,14 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
|
||||
child = layout[r,c]
|
||||
|
||||
# get the top-left corner of this child... the first one is top-left of the parent (i.e. layout)
|
||||
child_left = (c == 1 ? left(layout.bbox) : right(layout[r, c-1].bbox))
|
||||
child_top = (r == 1 ? top(layout.bbox) : bottom(layout[r-1, c].bbox))
|
||||
child_left::AbsoluteLength = (c == 1 ? left(layout.bbox) : right(layout[r, c-1].bbox))
|
||||
child_top::AbsoluteLength = (r == 1 ? top(layout.bbox) : bottom(layout[r-1, c].bbox))
|
||||
|
||||
# compute plot area
|
||||
plotarea_left = child_left + pad_left[c]
|
||||
plotarea_top = child_top + pad_top[r]
|
||||
plotarea_width = total_plotarea_horizontal * layout.widths[c]
|
||||
plotarea_height = total_plotarea_vertical * layout.heights[r]
|
||||
plotarea_left::AbsoluteLength = child_left + pad_left[c]
|
||||
plotarea_top::AbsoluteLength = child_top + pad_top[r]
|
||||
plotarea_width::AbsoluteLength = total_plotarea_horizontal * layout.widths[c]
|
||||
plotarea_height::AbsoluteLength = total_plotarea_vertical * layout.heights[r]
|
||||
plotarea!(child, BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height))
|
||||
|
||||
# compute child bbox
|
||||
@@ -512,14 +512,14 @@ end
|
||||
|
||||
# # just a single subplot
|
||||
# function build_layout(sp::Subplot, n::Integer)
|
||||
# sp, Subplot[sp], SubplotMap(gensym() => sp)
|
||||
# sp, Subplot[sp], KW(gensym() => sp)
|
||||
# end
|
||||
|
||||
# n is the number of subplots... build a grid and initialize the inner subplots recursively
|
||||
function build_layout(layout::GridLayout, n::Integer)
|
||||
nr, nc = size(layout)
|
||||
subplots = Subplot[]
|
||||
spmap = SubplotMap()
|
||||
subplots = Any[]
|
||||
spmap = KW()
|
||||
i = 0
|
||||
for r=1:nr, c=1:nc
|
||||
l = layout[r,c]
|
||||
@@ -560,8 +560,8 @@ end
|
||||
# TODO... much of the logic overlaps with the method above... can we merge?
|
||||
function build_layout(layout::GridLayout, numsp::Integer, plts::AVec{Plot})
|
||||
nr, nc = size(layout)
|
||||
subplots = Subplot[]
|
||||
spmap = SubplotMap()
|
||||
subplots = Any[]
|
||||
spmap = KW()
|
||||
i = 0
|
||||
for r=1:nr, c=1:nc
|
||||
l = layout[r,c]
|
||||
|
||||
+13
-13
@@ -129,29 +129,29 @@ savefig(fn::AbstractString) = savefig(current(), fn)
|
||||
|
||||
Display a plot using the backends' gui window
|
||||
"""
|
||||
gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
|
||||
@noinline gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
|
||||
|
||||
# IJulia only... inline display
|
||||
function inline(plt::Plot = current())
|
||||
@noinline function inline(plt::Plot = current())
|
||||
isijulia() || error("inline() is IJulia-only")
|
||||
Main.IJulia.clear_output(true)
|
||||
display(Main.IJulia.InlineDisplay(), plt)
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot)
|
||||
@noinline function Base.display(::PlotsDisplay, plt::Plot)
|
||||
prepare_output(plt)
|
||||
_display(plt)
|
||||
end
|
||||
|
||||
_do_plot_show(plt, showval::Bool) = showval && gui(plt)
|
||||
function _do_plot_show(plt, showval::Symbol)
|
||||
@noinline _do_plot_show(plt, showval::Bool) = showval && gui(plt)
|
||||
@noinline function _do_plot_show(plt, showval::Symbol)
|
||||
showval == :gui && gui(plt)
|
||||
showval in (:inline,:ijulia) && inline(plt)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
const _best_html_output_type = Dict{Symbol,Symbol}(
|
||||
:pyplot => :png,
|
||||
:unicodeplots => :txt,
|
||||
:plotlyjs => :html,
|
||||
@@ -159,7 +159,7 @@ const _best_html_output_type = KW(
|
||||
)
|
||||
|
||||
# a backup for html... passes to svg or png depending on the html_output_format arg
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
@noinline function _show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
@@ -178,11 +178,11 @@ function _show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
end
|
||||
|
||||
# delegate showable to _show instead
|
||||
function Base.showable(m::M, plt::P) where {M<:MIME, P<:Plot}
|
||||
@noinline function Base.showable(m::M, plt::P) where {M<:MIME, P<:Plot}
|
||||
return hasmethod(_show, Tuple{IO, M, P})
|
||||
end
|
||||
|
||||
function _display(plt::Plot)
|
||||
@noinline function _display(plt::Plot)
|
||||
@warn("_display is not defined for this backend.")
|
||||
end
|
||||
|
||||
@@ -202,7 +202,7 @@ for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml
|
||||
end
|
||||
|
||||
# default text/plain for all backends
|
||||
_show(io::IO, ::MIME{Symbol("text/plain")}, plt::Plot) = show(io, plt)
|
||||
@noinline _show(io::IO, ::MIME{Symbol("text/plain")}, plt::Plot) = show(io, plt)
|
||||
|
||||
"Close all open gui windows of the current backend"
|
||||
closeall() = closeall(backend())
|
||||
@@ -228,7 +228,7 @@ closeall() = closeall(backend())
|
||||
# ---------------------------------------------------------
|
||||
# Atom PlotPane
|
||||
# ---------------------------------------------------------
|
||||
function showjuno(io::IO, m, plt)
|
||||
@noinline function showjuno(io::IO, m, plt)
|
||||
sz = plt[:size]
|
||||
dpi = plt[:dpi]
|
||||
thickness_scaling = plt[:thickness_scaling]
|
||||
@@ -250,7 +250,7 @@ function showjuno(io::IO, m, plt)
|
||||
end
|
||||
end
|
||||
|
||||
function _showjuno(io::IO, m::MIME"image/svg+xml", plt)
|
||||
@noinline function _showjuno(io::IO, m::MIME"image/svg+xml", plt)
|
||||
if Symbol(plt.attr[:html_output_format]) ≠ :svg
|
||||
throw(MethodError(show, (typeof(m), typeof(plt))))
|
||||
else
|
||||
@@ -258,4 +258,4 @@ function _showjuno(io::IO, m::MIME"image/svg+xml", plt)
|
||||
end
|
||||
end
|
||||
|
||||
_showjuno(io::IO, m, plt) = _show(io, m, plt)
|
||||
@noinline _showjuno(io::IO, m, plt) = _show(io, m, plt)
|
||||
|
||||
+18
-19
@@ -3,11 +3,11 @@
|
||||
# ------------------------------------------------------------------
|
||||
# preprocessing
|
||||
|
||||
function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
@noinline function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
Int(kw[:series_plotindex]) - Int(kw_list[1][:series_plotindex]) + 1
|
||||
end
|
||||
|
||||
function _expand_seriestype_array(plotattributes::KW, args)
|
||||
@noinline function _expand_seriestype_array(plotattributes::KW, args)
|
||||
sts = get(plotattributes, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(plotattributes, :seriestype)
|
||||
@@ -23,7 +23,7 @@ function _expand_seriestype_array(plotattributes::KW, args)
|
||||
end
|
||||
end
|
||||
|
||||
function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{RecipeData})
|
||||
@noinline function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{RecipeData})
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
@@ -57,7 +57,7 @@ end
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
@noinline function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(plotattributes, args, still_to_process)
|
||||
|
||||
@@ -90,7 +90,7 @@ function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
kw_list
|
||||
end
|
||||
|
||||
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
|
||||
@noinline function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
kw = recipedata.plotattributes
|
||||
@@ -108,7 +108,7 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD
|
||||
return
|
||||
end
|
||||
|
||||
function _preprocess_userrecipe(kw::KW)
|
||||
@noinline function _preprocess_userrecipe(kw::KW)
|
||||
_add_markershape(kw)
|
||||
|
||||
# if there was a grouping, filter the data here
|
||||
@@ -126,17 +126,17 @@ function _preprocess_userrecipe(kw::KW)
|
||||
end
|
||||
|
||||
# convert a ribbon into a fillrange
|
||||
if get(kw, :ribbon, nothing) != nothing
|
||||
if get(kw, :ribbon, nothing) !== nothing
|
||||
make_fillrange_from_ribbon(kw)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
@noinline function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
# handle error bars by creating new recipedata data... these will have
|
||||
# the same recipedata index as the recipedata they are copied from
|
||||
for esym in (:xerror, :yerror)
|
||||
if get(kw, esym, nothing) != nothing
|
||||
if get(kw, esym, nothing) !== nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
errkw = copy(kw)
|
||||
errkw[:seriestype] = esym
|
||||
@@ -147,7 +147,7 @@ function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
@noinline function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
# handle smoothing by adding a new series
|
||||
if get(kw, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
@@ -203,7 +203,7 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
|
||||
function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
function _plot_setup(plt::Plot{T}, plotattributes::KW, kw_list::Vector{KW}) where {T}
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (plotattributes[k] = pop!(kw, k))
|
||||
@@ -227,7 +227,7 @@ function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
|
||||
# handle inset subplots
|
||||
insets = plt[:inset_subplots]
|
||||
if insets != nothing
|
||||
if insets !== nothing
|
||||
if !(typeof(insets) <: AVec)
|
||||
insets = [insets]
|
||||
end
|
||||
@@ -252,18 +252,17 @@ function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
plt[:inset_subplots] = nothing
|
||||
end
|
||||
|
||||
function _subplot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
function _subplot_setup(plt::Plot{T}, plotattributes::KW, kw_list::Vector{KW}) where T
|
||||
# we'll keep a map of subplot to an attribute override dict.
|
||||
# Subplot/Axis attributes set by a user/series recipe apply only to the
|
||||
# Subplot object which they belong to.
|
||||
# TODO: allow matrices to still apply to all subplots
|
||||
sp_attrs = Dict{Subplot,Any}()
|
||||
sp_attrs = Dict{Any,Any}()
|
||||
for kw in kw_list
|
||||
# get the Subplot object to which the series belongs.
|
||||
sps = get(kw, :subplot, :auto)
|
||||
sp = get_subplot(plt, _cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
|
||||
kw[:subplot] = sp
|
||||
|
||||
# extract subplot/axis attributes from kw and add to sp_attr
|
||||
attr = KW()
|
||||
for (k,v) in collect(kw)
|
||||
@@ -303,7 +302,7 @@ end
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
# that might have been added during series recipes
|
||||
function _prepare_subplot(plt::Plot{T}, plotattributes::KW) where T
|
||||
@noinline function _prepare_subplot(plt::Plot{T}, plotattributes::KW) where T
|
||||
st::Symbol = plotattributes[:seriestype]
|
||||
sp::Subplot{T} = plotattributes[:subplot]
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
@@ -327,7 +326,7 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# series types
|
||||
|
||||
function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
@noinline function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !is3d(st) && !(st in (:contour,:contour3d))
|
||||
z = plotattributes[:z]
|
||||
@@ -339,7 +338,7 @@ function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
st
|
||||
end
|
||||
|
||||
function _prepare_annotations(sp::Subplot, plotattributes::KW)
|
||||
@noinline function _prepare_annotations(sp::Subplot, plotattributes::KW)
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
@@ -372,7 +371,7 @@ function _expand_subplot_extrema(sp::Subplot, plotattributes::KW, st::Symbol)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_the_series(plt, sp, plotattributes)
|
||||
@noinline function _add_the_series(plt, sp, plotattributes)
|
||||
warnOnUnsupported_args(plt.backend, plotattributes)
|
||||
warnOnUnsupported(plt.backend, plotattributes)
|
||||
series = Series(plotattributes)
|
||||
|
||||
+22
-15
@@ -4,19 +4,19 @@ mutable struct CurrentPlot
|
||||
end
|
||||
const CURRENT_PLOT = CurrentPlot(nothing)
|
||||
|
||||
isplotnull() = CURRENT_PLOT.nullableplot == nothing
|
||||
@noinline isplotnull() = CURRENT_PLOT.nullableplot === nothing
|
||||
|
||||
"""
|
||||
current()
|
||||
Returns the Plot object for the current plot
|
||||
"""
|
||||
function current()
|
||||
@noinline function current()
|
||||
if isplotnull()
|
||||
error("No current plot/subplot")
|
||||
end
|
||||
CURRENT_PLOT.nullableplot
|
||||
end
|
||||
current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = plot)
|
||||
@noinline current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = plot)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -25,9 +25,9 @@ Base.string(plt::Plot) = "Plot{$(plt.backend) n=$(plt.n)}"
|
||||
Base.print(io::IO, plt::Plot) = print(io, string(plt))
|
||||
Base.show(io::IO, plt::Plot) = print(io, string(plt))
|
||||
|
||||
getplot(plt::Plot) = plt
|
||||
getattr(plt::Plot, idx::Int = 1) = plt.attr
|
||||
convertSeriesIndex(plt::Plot, n::Int) = n
|
||||
@noinline getplot(plt::Plot) = plt
|
||||
@noinline getattr(plt::Plot, idx::Int = 1) = plt.attr
|
||||
@noinline convertSeriesIndex(plt::Plot, n::Int) = n
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -50,11 +50,10 @@ function plot(args...; kw...)
|
||||
# this creates a new plot with args/kw and sets it to be the current plot
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
|
||||
# create an empty Plot then process
|
||||
plt = Plot()
|
||||
# plt.user_attr = plotattributes
|
||||
_plot!(plt, plotattributes, args)
|
||||
_plot!(plt, plotattributes, Any[args...])
|
||||
end
|
||||
|
||||
# build a new plot from existing plots
|
||||
@@ -155,7 +154,7 @@ function plot!(plt::Plot, args...; kw...)
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
# merge!(plt.user_attr, plotattributes)
|
||||
_plot!(plt, plotattributes, args)
|
||||
_plot!(plt, plotattributes, Any[args...])
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
@@ -163,9 +162,10 @@ end
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
function _plot!(plt::Plot{T}, plotattributes::KW, args::Vector{Any}) where {T}
|
||||
plotattributes[:plot_object] = plt
|
||||
|
||||
|
||||
if !isempty(args) && !isdefined(Main, :StatsPlots) &&
|
||||
first(split(string(typeof(args[1])), ".")) == "DataFrames"
|
||||
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots")
|
||||
@@ -175,6 +175,7 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
# "USER RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# 1 second
|
||||
kw_list = _process_userrecipes(plt, plotattributes, args)
|
||||
|
||||
# @info(1)
|
||||
@@ -189,6 +190,7 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
# the plot layout is created, which allows for setting layouts and other plot-wide attributes.
|
||||
# we get inputs which have been fully processed by "user recipes" and "type recipes",
|
||||
# so we can expect standard vectors, surfaces, etc. No defaults have been set yet.
|
||||
|
||||
still_to_process = kw_list
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
@@ -202,7 +204,11 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
# --------------------------------
|
||||
# Plot/Subplot/Layout setup
|
||||
# --------------------------------
|
||||
|
||||
# 2.5 seconds
|
||||
_plot_setup(plt, plotattributes, kw_list)
|
||||
|
||||
# 6 seconds
|
||||
_subplot_setup(plt, plotattributes, kw_list)
|
||||
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
@@ -216,7 +222,7 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
# map(DD, kw_list)
|
||||
|
||||
for kw in kw_list
|
||||
sp::Subplot = kw[:subplot]
|
||||
sp::Subplot{T} = kw[:subplot]
|
||||
# idx = get_subplot_index(plt, sp)
|
||||
|
||||
# # we update subplot args in case something like the color palatte is part of the recipe
|
||||
@@ -233,9 +239,9 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
# be able to support step, bar, and histogram plots (and any recipes that use those components).
|
||||
_process_seriesrecipe(plt, kw)
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
|
||||
# 7 seconds
|
||||
current(plt)
|
||||
|
||||
# do we want to force display?
|
||||
@@ -243,14 +249,14 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
# gui(plt)
|
||||
# end
|
||||
_do_plot_show(plt, plt[:show])
|
||||
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# we're getting ready to display/output. prep for layout calcs, then update
|
||||
# the plot object after
|
||||
function prepare_output(plt::Plot)
|
||||
@noinline function prepare_output(plt::Plot)
|
||||
|
||||
_before_layout_calcs(plt)
|
||||
|
||||
w, h = plt.attr[:size]
|
||||
@@ -271,9 +277,10 @@ function prepare_output(plt::Plot)
|
||||
|
||||
# the backend callback, to reposition subplots, etc
|
||||
_update_plot_object(plt)
|
||||
|
||||
end
|
||||
|
||||
function backend_object(plt::Plot)
|
||||
@noinline function backend_object(plt::Plot)
|
||||
prepare_output(plt)
|
||||
plt.o
|
||||
end
|
||||
|
||||
+21
-16
@@ -239,7 +239,7 @@ end
|
||||
@recipe function f(::Type{Val{:sticks}}, x, y, z)
|
||||
n = length(x)
|
||||
fr = plotattributes[:fillrange]
|
||||
if fr == nothing
|
||||
if fr === nothing
|
||||
sp = plotattributes[:subplot]
|
||||
yaxis = sp[:yaxis]
|
||||
fr = if yaxis[:scale] == :identity
|
||||
@@ -291,13 +291,13 @@ end
|
||||
|
||||
# create segmented bezier curves in place of line segments
|
||||
@recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30)
|
||||
args = z != nothing ? (x,y,z) : (x,y)
|
||||
args = z !== nothing ? (x,y,z) : (x,y)
|
||||
newx, newy = zeros(0), zeros(0)
|
||||
fr = plotattributes[:fillrange]
|
||||
newfr = fr != nothing ? zeros(0) : nothing
|
||||
newz = z != nothing ? zeros(0) : nothing
|
||||
newfr = fr !== nothing ? zeros(0) : nothing
|
||||
newz = z !== nothing ? zeros(0) : nothing
|
||||
# lz = plotattributes[:line_z]
|
||||
# newlz = lz != nothing ? zeros(0) : nothing
|
||||
# newlz = lz !== nothing ? zeros(0) : nothing
|
||||
|
||||
# for each line segment (point series with no NaNs), convert it into a bezier curve
|
||||
# where the points are the control points of the curve
|
||||
@@ -306,13 +306,13 @@ end
|
||||
ts = range(0, stop = 1, length = npoints)
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x,rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y,rng), t), ts))
|
||||
if z != nothing
|
||||
if z !== nothing
|
||||
nanappend!(newz, map(t -> bezier_value(_cycle(z,rng), t), ts))
|
||||
end
|
||||
if fr != nothing
|
||||
if fr !== nothing
|
||||
nanappend!(newfr, map(t -> bezier_value(_cycle(fr,rng), t), ts))
|
||||
end
|
||||
# if lz != nothing
|
||||
# if lz !== nothing
|
||||
# lzrng = _cycle(lz, rng) # the line_z's for this segment
|
||||
# push!(newlz, 0.0)
|
||||
# append!(newlz, map(t -> lzrng[1+floor(Int, t * (length(rng)-1))], ts))
|
||||
@@ -321,16 +321,16 @@ end
|
||||
|
||||
x := newx
|
||||
y := newy
|
||||
if z == nothing
|
||||
if z === nothing
|
||||
seriestype := :path
|
||||
else
|
||||
seriestype := :path3d
|
||||
z := newz
|
||||
end
|
||||
if fr != nothing
|
||||
if fr !== nothing
|
||||
fillrange := newfr
|
||||
end
|
||||
# if lz != nothing
|
||||
# if lz !== nothing
|
||||
# # line_z := newlz
|
||||
# linecolor := (isa(plotattributes[:linecolor], ColorGradient) ? plotattributes[:linecolor] : cgrad())
|
||||
# end
|
||||
@@ -357,7 +357,7 @@ end
|
||||
|
||||
# compute half-width of bars
|
||||
bw = plotattributes[:bar_width]
|
||||
hw = if bw == nothing
|
||||
hw = if bw === nothing
|
||||
if nx > 1
|
||||
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
|
||||
else
|
||||
@@ -369,7 +369,7 @@ end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
fillto = plotattributes[:fillrange]
|
||||
if fillto == nothing
|
||||
if fillto === nothing
|
||||
fillto = 0
|
||||
end
|
||||
if (yscale in _logScales) && !all(_is_positive, fillto)
|
||||
@@ -491,7 +491,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
if (plotattributes[:bar_width] == nothing)
|
||||
if (plotattributes[:bar_width] === nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
x := _bin_centers(edge)
|
||||
@@ -533,7 +533,7 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
||||
|
||||
last_w = eltype(weights)(NaN)
|
||||
|
||||
while it_tuple_e != nothing && it_tuple_w != nothing
|
||||
while it_tuple_e !== nothing && it_tuple_w !== nothing
|
||||
b, it_state_e = it_tuple_e
|
||||
w, it_state_w = it_tuple_w
|
||||
|
||||
@@ -667,7 +667,7 @@ end
|
||||
function _make_hist(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing) where N
|
||||
localvs = _filternans(vs)
|
||||
edges = _hist_edges(localvs, binning)
|
||||
h = float( weights == nothing ?
|
||||
h = float( weights === nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, StatsBase.Weights(weights), edges, closed = :left)
|
||||
)
|
||||
@@ -1101,6 +1101,11 @@ timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
@recipe f(::Type{Dates.Time}, t::Dates.Time) = (t -> Dates.value(t), timeformatter)
|
||||
@recipe f(::Type{P}, t::P) where P <: Dates.Period = (t -> Dates.value(t), t -> string(P(t)))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Characters
|
||||
|
||||
@recipe f(::Type{<:AbstractChar}, ::AbstractChar) = (string, string)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
|
||||
+21
-16
@@ -7,12 +7,12 @@
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
|
||||
const DataPoint = Union{Number, AbstractString, AbstractChar, Missing}
|
||||
const DataPoint = Union{Number, AbstractString, Missing}
|
||||
const SeriesData = Union{AVec{<:DataPoint}, Function, Surface, Volume}
|
||||
|
||||
prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plotting")
|
||||
prepareSeriesData(::Nothing) = nothing
|
||||
prepareSeriesData(s::SeriesData) = handlemissings(handlechars(s))
|
||||
prepareSeriesData(s::SeriesData) = handlemissings(s)
|
||||
|
||||
handlemissings(v) = v
|
||||
handlemissings(v::AbstractArray{Union{T,Missing}}) where T <: Number = replace(v, missing => NaN)
|
||||
@@ -20,9 +20,6 @@ handlemissings(v::AbstractArray{Union{T,Missing}}) where T <: AbstractString = r
|
||||
handlemissings(s::Surface) = Surface(handlemissings(s.surf))
|
||||
handlemissings(v::Volume) = Volume(handlemissings(v.v), v.x_extents, v.y_extents, v.z_extents)
|
||||
|
||||
handlechars(x) = x
|
||||
handlechars(c::AVec{<:AbstractChar}) = string.(c)
|
||||
|
||||
# default: assume x represents a single series
|
||||
convertToAnyVector(x) = Any[prepareSeriesData(x)]
|
||||
|
||||
@@ -38,6 +35,20 @@ convertToAnyVector(v::AVec) = vcat((convertToAnyVector(vi) for vi in v)...)
|
||||
# Matrix is split into columns
|
||||
convertToAnyVector(v::AMat{<:DataPoint}) = Any[prepareSeriesData(v[:,i]) for i in 1:size(v,2)]
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Fillranges & ribbons
|
||||
|
||||
|
||||
process_fillrange(range::Number) = [range]
|
||||
process_fillrange(range) = convertToAnyVector(range)
|
||||
|
||||
process_ribbon(ribbon::Number) = [ribbon]
|
||||
process_ribbon(ribbon) = convertToAnyVector(ribbon)
|
||||
# ribbon as a tuple: (lower_ribbons, upper_ribbons)
|
||||
process_ribbon(ribbon::Tuple{Any,Any}) = collect(zip(convertToAnyVector(ribbon[1]),
|
||||
convertToAnyVector(ribbon[2])))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
||||
@@ -105,19 +116,11 @@ struct SliceIt end
|
||||
|
||||
|
||||
fr = pop!(plotattributes, :fillrange, nothing)
|
||||
fillranges = if typeof(fr) <: Number
|
||||
[fr]
|
||||
else
|
||||
convertToAnyVector(fr)
|
||||
end
|
||||
fillranges = process_fillrange(fr)
|
||||
mf = length(fillranges)
|
||||
|
||||
rib = pop!(plotattributes, :ribbon, nothing)
|
||||
ribbons = if typeof(rib) <: Number
|
||||
[rib]
|
||||
else
|
||||
convertToAnyVector(rib)
|
||||
end
|
||||
ribbons = process_ribbon(rib)
|
||||
mr = length(ribbons)
|
||||
|
||||
# @show zs
|
||||
@@ -140,7 +143,7 @@ struct SliceIt end
|
||||
rib = ribbons[mod1(i,mr)]
|
||||
di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib
|
||||
|
||||
push!(series_list, RecipeData(di, ()))
|
||||
push!(series_list, RecipeData(di, Any[]))
|
||||
end
|
||||
end
|
||||
nothing # don't add a series for the main block
|
||||
@@ -352,7 +355,9 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
# Dicts: each entry is a data point (x,y)=(key,value)
|
||||
|
||||
@recipe f(d::AbstractDict) = collect(keys(d)), collect(values(d))
|
||||
|
||||
# function without range... use the current range of the x-axis
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ function _get_defaults(s::Symbol)
|
||||
:fglegend => thm.text,
|
||||
:palette => thm.palette,
|
||||
)
|
||||
if thm.gradient != nothing
|
||||
if thm.gradient !== nothing
|
||||
push!(defaults, :gradient => thm.gradient)
|
||||
end
|
||||
return defaults
|
||||
|
||||
+12
-16
@@ -31,11 +31,11 @@ attr!(series::Series, v, k::Symbol) = (series.plotattributes[k] = v)
|
||||
|
||||
# a single subplot
|
||||
mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
parent
|
||||
series_list::Vector{Series} # arguments for each series
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
bbox::BoundingBox # the canvas area which is available to this subplot
|
||||
plotarea::BoundingBox # the part where the data goes
|
||||
minpad::Tuple{AbsoluteLength,AbsoluteLength,AbsoluteLength,AbsoluteLength} # leftpad, toppad, rightpad, bottompad
|
||||
bbox#::BoundingBox # the canvas area which is available to this subplot
|
||||
plotarea#::BoundingBox # the part where the data goes
|
||||
attr::KW # args specific to this subplot
|
||||
o # can store backend-specific data... like a pyplot ax
|
||||
plt # the enclosing Plot object (can't give it a type because of no forward declarations)
|
||||
@@ -59,10 +59,6 @@ Extrema() = Extrema(Inf, -Inf)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
const SubplotMap = Dict{Any, Subplot}
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
|
||||
mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
backend::T # the backend type
|
||||
@@ -71,17 +67,17 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
user_attr::KW # raw arg inputs (after aliases). these are used as the input dict in `_plot!`
|
||||
series_list::Vector{Series} # arguments for each series
|
||||
o # the backend's plot object
|
||||
subplots::Vector{Subplot}
|
||||
spmap::SubplotMap # provide any label as a map to a subplot
|
||||
layout::AbstractLayout
|
||||
inset_subplots::Vector{Subplot} # list of inset subplots
|
||||
subplots::Vector{Subplot{T}}
|
||||
spmap::KW # provide any label as a map to a subplot
|
||||
layout
|
||||
inset_subplots::Vector{Subplot{T}} # list of inset subplots
|
||||
init::Bool
|
||||
end
|
||||
|
||||
function Plot()
|
||||
Plot(backend(), 0, KW(), KW(), Series[], nothing,
|
||||
Subplot[], SubplotMap(), EmptyLayout(),
|
||||
Subplot[], false)
|
||||
function Plot(_backend = CURRENT_BACKEND)
|
||||
Plot(_backend.pkg, 0, KW(), KW(), Series[], nothing,
|
||||
Subplot{typeof(_backend.pkg)}[], KW(), EmptyLayout(),
|
||||
Subplot{typeof(_backend.pkg)}[], false)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
+9
-9
@@ -26,7 +26,7 @@ function histogramHack(; kw...)
|
||||
plotattributes[:x] = midpoints
|
||||
plotattributes[:y] = float(counts)
|
||||
plotattributes[:seriestype] = :bar
|
||||
plotattributes[:fillrange] = plotattributes[:fillrange] == nothing ? 0.0 : plotattributes[:fillrange]
|
||||
plotattributes[:fillrange] = plotattributes[:fillrange] === nothing ? 0.0 : plotattributes[:fillrange]
|
||||
plotattributes
|
||||
end
|
||||
|
||||
@@ -38,7 +38,7 @@ function barHack(; kw...)
|
||||
plotattributes = KW(kw)
|
||||
midpoints = plotattributes[:x]
|
||||
heights = plotattributes[:y]
|
||||
fillrange = plotattributes[:fillrange] == nothing ? 0.0 : plotattributes[:fillrange]
|
||||
fillrange = plotattributes[:fillrange] === nothing ? 0.0 : plotattributes[:fillrange]
|
||||
|
||||
# estimate the edges
|
||||
dists = diff(midpoints) * 0.5
|
||||
@@ -81,7 +81,7 @@ function sticksHack(; kw...)
|
||||
# these are the line vertices
|
||||
x = Float64[]
|
||||
y = Float64[]
|
||||
fillrange = plotattributesLine[:fillrange] == nothing ? 0.0 : plotattributesLine[:fillrange]
|
||||
fillrange = plotattributesLine[:fillrange] === nothing ? 0.0 : plotattributesLine[:fillrange]
|
||||
|
||||
# calculate the vertices
|
||||
yScatter = plotattributesScatter[:y]
|
||||
@@ -194,7 +194,7 @@ end
|
||||
|
||||
function iter_segments(series::Series)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
if x == nothing
|
||||
if x === nothing
|
||||
return UnitRange{Int}[]
|
||||
elseif has_attribute_segments(series)
|
||||
if series[:seriestype] in (:scatter, :scatter3d)
|
||||
@@ -478,7 +478,7 @@ function make_fillrange_from_ribbon(kw::KW)
|
||||
rib1, rib2 = -first(rib), last(rib)
|
||||
# kw[:ribbon] = nothing
|
||||
kw[:fillrange] = make_fillrange_side(y, rib1), make_fillrange_side(y, rib2)
|
||||
(get(kw, :fillalpha, nothing) == nothing) && (kw[:fillalpha] = 0.5)
|
||||
(get(kw, :fillalpha, nothing) === nothing) && (kw[:fillalpha] = 0.5)
|
||||
end
|
||||
|
||||
#turn tuple of fillranges to one path
|
||||
@@ -529,7 +529,7 @@ function get_clims(sp::Subplot)
|
||||
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
||||
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing, Real})
|
||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals.surf)...)
|
||||
elseif (vals != nothing) && (eltype(vals) <: Union{Missing, Real})
|
||||
elseif (vals !== nothing) && (eltype(vals) <: Union{Missing, Real})
|
||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals)...)
|
||||
end
|
||||
end
|
||||
@@ -602,7 +602,7 @@ for comp in (:line, :fill, :marker)
|
||||
function $get_compcolor(series, cmin::Real, cmax::Real, i::Int = 1)
|
||||
c = series[$Symbol($compcolor)]
|
||||
z = series[$Symbol($comp_z)]
|
||||
if z == nothing
|
||||
if z === nothing
|
||||
isa(c, ColorGradient) ? c : plot_color(_cycle(c, i))
|
||||
else
|
||||
grad = isa(c, ColorGradient) ? c : cgrad()
|
||||
@@ -613,7 +613,7 @@ for comp in (:line, :fill, :marker)
|
||||
$get_compcolor(series, clims, i::Int = 1) = $get_compcolor(series, clims[1], clims[2], i)
|
||||
|
||||
function $get_compcolor(series, i::Int = 1)
|
||||
if series[$Symbol($comp_z)] == nothing
|
||||
if series[$Symbol($comp_z)] === nothing
|
||||
$get_compcolor(series, 0, 1, i)
|
||||
else
|
||||
$get_compcolor(series, get_clims(series[:subplot]), i)
|
||||
@@ -650,7 +650,7 @@ function has_attribute_segments(series::Series)
|
||||
for letter in (:x, :y, :z)
|
||||
# If we have NaNs in the data they define the segments and
|
||||
# SegmentsIterator is used
|
||||
series[letter] != nothing && NaN in collect(series[letter]) && return false
|
||||
series[letter] !== nothing && NaN in collect(series[letter]) && return false
|
||||
end
|
||||
series[:seriestype] == :shape && return false
|
||||
# ... else we check relevant attributes if they have multiple inputs
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
|
||||
|
||||
# now we have the fn (if any)... do the comparison
|
||||
# @show reffn
|
||||
if reffn == nothing
|
||||
if reffn === nothing
|
||||
reffn = newfn
|
||||
end
|
||||
# @show reffn
|
||||
@@ -98,7 +98,7 @@ function image_comparison_facts(pkg::Symbol;
|
||||
tol = 1e-2) # acceptable error (percent)
|
||||
for i in 1:length(Plots._examples)
|
||||
i in skip && continue
|
||||
if only == nothing || i in only
|
||||
if only === nothing || i in only
|
||||
@test image_comparison_tests(pkg, i, debug=debug, sigma=sigma, tol=tol) |> success == true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,6 +63,13 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
@testset "EmptyAnim" begin
|
||||
anim = @animate for i in []
|
||||
end
|
||||
|
||||
@test_throws ArgumentError gif(anim)
|
||||
end
|
||||
|
||||
@testset "Segments" begin
|
||||
function segments(args...)
|
||||
segs = UnitRange{Int}[]
|
||||
|
||||
Reference in New Issue
Block a user