diff --git a/Project.toml b/Project.toml index 41c40181..0f4fb1d8 100644 --- a/Project.toml +++ b/Project.toml @@ -44,7 +44,7 @@ PlotThemes = "1" PlotUtils = "0.6.5" RecipesBase = "1" Reexport = "0.2" -Requires = "0.5, 1.0" +Requires = "0.5, 1" Showoff = "0.3.1" StatsBase = "0.32, 0.33" julia = "1" diff --git a/src/Plots.jl b/src/Plots.jl index 5c960e19..494d1da9 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -226,7 +226,7 @@ end const CURRENT_BACKEND = CurrentBackend(:none) -include("precompile.jl") -_precompile_() +# include("precompile.jl") +# _precompile_() end # module diff --git a/src/args.jl b/src/args.jl index a24d7de6..000ba22e 100644 --- a/src/args.jl +++ b/src/args.jl @@ -685,7 +685,7 @@ end function default(; kw...) kw = KW(kw) - preprocessArgs!(kw) + preprocess_attributes!(kw) for (k,v) in kw default(k, v) end @@ -938,7 +938,7 @@ function _add_markershape(plotattributes::AKW) end "Handle all preprocessing of args... break out colors/sizes/etc and replace aliases." -function preprocessArgs!(plotattributes::AKW) +function preprocess_attributes!(plotattributes::AKW) replaceAliases!(plotattributes, _keyAliases) # handle axis args common to all axis @@ -1125,7 +1125,7 @@ end # this is when given a vector-type of values to group by -function extractGroupArgs(v::AVec, args...; legendEntry = string) +function _extract_group_attributes(v::AVec, args...; legendEntry = string) groupLabels = sort(collect(unique(v))) n = length(groupLabels) if n > 100 @@ -1138,24 +1138,24 @@ end legendEntryFromTuple(ns::Tuple) = join(ns, ' ') # this is when given a tuple of vectors of values to group by -function extractGroupArgs(vs::Tuple, args...) +function _extract_group_attributes(vs::Tuple, args...) isempty(vs) && return GroupBy([""], [axes(args[1],1)]) v = map(tuple, vs...) - extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple) + _extract_group_attributes(v, args...; legendEntry = legendEntryFromTuple) end # allow passing NamedTuples for a named legend entry legendEntryFromTuple(ns::NamedTuple) = join(["$k = $v" for (k, v) in pairs(ns)], ", ") -function extractGroupArgs(vs::NamedTuple, args...) +function _extract_group_attributes(vs::NamedTuple, args...) isempty(vs) && return GroupBy([""], [axes(args[1],1)]) v = map(NamedTuple{keys(vs)}∘tuple, values(vs)...) - extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple) + _extract_group_attributes(v, args...; legendEntry = legendEntryFromTuple) end # expecting a mapping of "group label" to "group indices" -function extractGroupArgs(idxmap::Dict{T,V}, args...) where {T, V<:AVec{Int}} +function _extract_group_attributes(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) @@ -1183,7 +1183,7 @@ end const _already_warned = Dict{Symbol,Set{Symbol}}() const _to_warn = Set{Symbol}() -function warnOnUnsupported_args(pkg::AbstractBackend, plotattributes) +function warn_on_unsupported_args(pkg::AbstractBackend, plotattributes) empty!(_to_warn) bend = backend_name(pkg) already_warned = get!(_already_warned, bend, Set{Symbol}()) @@ -1207,7 +1207,7 @@ end # _markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg) # _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes]) -function warnOnUnsupported(pkg::AbstractBackend, plotattributes) +function warn_on_unsupported(pkg::AbstractBackend, plotattributes) if !is_seriestype_supported(pkg, plotattributes[:seriestype]) @warn("seriestype $(plotattributes[:seriestype]) is unsupported with $pkg. Choose from: $(supported_seriestypes(pkg))") end @@ -1219,7 +1219,7 @@ function warnOnUnsupported(pkg::AbstractBackend, plotattributes) end end -function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::AKW) +function warn_on_unsupported_scales(pkg::AbstractBackend, plotattributes::AKW) for k in (:xscale, :yscale, :zscale, :scale) if haskey(plotattributes, k) v = plotattributes[k] diff --git a/src/axes.jl b/src/axes.jl index 822de875..15e93b1f 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -85,7 +85,7 @@ function attr!(axis::Axis, args...; kw...) end # then preprocess keyword arguments - preprocessArgs!(KW(kw)) + preprocess_attributes!(KW(kw)) # then override for any keywords... only those keywords that already exists in plotattributes for (k,v) in kw diff --git a/src/pipeline.jl b/src/pipeline.jl index e6256edc..8325d86a 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -38,7 +38,7 @@ function _expand_seriestype_array(plotattributes::AKW, args) rd = Vector{RecipeData}(undef, size(sts, 1)) for r in axes(sts, 1) dc = copy(plotattributes) - dc[:seriestype] = sts[r:r,:] + dc[:seriestype] = sts[r:r, :] rd[r] = RecipeData(dc, args) end rd @@ -52,7 +52,7 @@ function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{Re # we simply add the GroupBy object to the front of the args list to allow # the recipe to be applied if haskey(plotattributes, :group) - args = (extractGroupArgs(plotattributes[:group], args...), args...) + args = (_extract_group_attributes(plotattributes[:group], args...), args...) end # if we were passed a vector/matrix of seriestypes and there's more than one row, @@ -61,9 +61,10 @@ function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{Re append!(still_to_process, _expand_seriestype_array(plotattributes, args)) end - # remove subplot and axis args from plotattributes... they will be passed through in the kw_list + # remove subplot and axis args from plotattributes... + # they will be passed through in the kw_list if !isempty(args) - for (k,v) in plotattributes + for (k, v) in plotattributes if k in _all_subplot_args || k in _all_axis_args reset_kw!(plotattributes, k) end @@ -90,22 +91,21 @@ function _process_userrecipes(plt::Plot, plotattributes::AKW, args) kw_list = KW[] while !isempty(still_to_process) # grab the first in line to be processed and either add it to the kw_list or - # pass it through apply_recipe to generate a list of RecipeData objects (data + attributes) - # for further processing. + # pass it through apply_recipe to generate a list of RecipeData objects + # (data + attributes) for further processing. next_series = popfirst!(still_to_process) - # recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes + # recipedata should be of type RecipeData. + # if it's not then the inputs must not have been fully processed by recipes if !(typeof(next_series) <: RecipeData) error("Inputs couldn't be processed... expected RecipeData but got: $next_series") end if isempty(next_series.args) _process_userrecipe(plt, kw_list, next_series) else - rd_list = RecipesBase.apply_recipe( - next_series.plotattributes, - next_series.args... - ) + rd_list = + RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...) warn_on_recipe_aliases!(rd_list, :user, next_series.args...) - prepend!(still_to_process,rd_list) + prepend!(still_to_process, rd_list) end end @@ -118,9 +118,9 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD # 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 - preprocessArgs!(kw) + preprocess_attributes!(kw) _preprocess_userrecipe(kw) - warnOnUnsupported_scales(plt.backend, kw) + warn_on_unsupported_scales(plt.backend, kw) # add the plot index plt.n += 1 @@ -141,12 +141,14 @@ function _preprocess_userrecipe(kw::AKW) # map marker_z if it's a Function if isa(get(kw, :marker_z, nothing), Function) # TODO: should this take y and/or z as arguments? - kw[:marker_z] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) : map(kw[:marker_z], kw[:x], kw[:y], kw[:z]) + kw[:marker_z] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) : + map(kw[:marker_z], kw[:x], kw[:y], kw[:z]) end # map line_z if it's a Function if isa(get(kw, :line_z, nothing), Function) - kw[:line_z] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) : map(kw[:line_z], kw[:x], kw[:y], kw[:z]) + kw[:line_z] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) : + map(kw[:line_z], kw[:x], kw[:y], kw[:z]) end # convert a ribbon into a fillrange @@ -178,14 +180,20 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW) β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y) sx = [ignorenan_minimum(x), ignorenan_maximum(x)] sy = β .* sx .+ α - push!(kw_list, merge(copy(kw), KW( - :seriestype => :path, - :x => sx, - :y => sy, - :fillrange => nothing, - :label => "", - :primary => false, - ))) + push!( + kw_list, + merge( + copy(kw), + KW( + :seriestype => :path, + :x => sx, + :y => sy, + :fillrange => nothing, + :label => "", + :primary => false, + ), + ), + ) end end @@ -196,7 +204,12 @@ end # to generate a list of RecipeData objects (data + attributes). # If we applied a "plot recipe" without error, then add the returned datalist's KWs, # otherwise we just add the original KW. -function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_process::Vector{KW}) +function _process_plotrecipe( + plt::Plot, + kw::AKW, + kw_list::Vector{KW}, + still_to_process::Vector{KW}, +) if !isa(get(kw, :seriestype, nothing), Symbol) # seriestype was never set, or it's not a Symbol, so it can't be a plot recipe push!(kw_list, kw) @@ -208,7 +221,7 @@ function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_p datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) warn_on_recipe_aliases!(datalist, :plot, st) for data in datalist - preprocessArgs!(data.plotattributes) + preprocess_attributes!(data.plotattributes) if data.plotattributes[:seriestype] == st error("Plot recipe $st returned the same seriestype: $(data.plotattributes)") end @@ -230,7 +243,7 @@ end function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) # merge in anything meant for the Plot - for kw in kw_list, (k,v) in kw + for kw in kw_list, (k, v) in kw haskey(_plot_defaults, k) && (plotattributes[k] = pop!(kw, k)) end @@ -241,7 +254,7 @@ function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) # create the layout and subplots from the inputs plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr) - for (idx,sp) in enumerate(plt.subplots) + for (idx, sp) in enumerate(plt.subplots) sp.plt = plt sp.attr[:subplot_index] = idx end @@ -266,7 +279,7 @@ function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) else parent = plt.layout end - sp = Subplot(backend(), parent=parent) + sp = Subplot(backend(), parent = parent) sp.plt = plt push!(plt.subplots, sp) push!(plt.inset_subplots, sp) @@ -282,28 +295,34 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) # 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{Subplot, 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], series_idx(kw_list,kw))) + sp = get_subplot( + plt, + _cycle( + sps == :auto ? plt.subplots : plt.subplots[sps], + series_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) + for (k, v) in collect(kw) if is_subplot_attr(k) || is_axis_attr(k) attr[k] = pop!(kw, k) end if is_axis_attr_noletter(k) v = pop!(kw, k) - for letter in (:x,:y,:z) - attr[Symbol(letter,k)] = v + for letter in (:x, :y, :z) + attr[Symbol(letter, k)] = v end end - for k in (:scale,), letter in (:x,:y,:z) + for k in (:scale,), letter in (:x, :y, :z) # Series recipes may need access to this information - lk = Symbol(letter,k) + lk = Symbol(letter, k) if haskey(attr, lk) kw[lk] = attr[lk] end @@ -313,7 +332,7 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) end # override subplot/axis args. `sp_attrs` take precendence - for (idx,sp) in enumerate(plt.subplots) + for (idx, sp) in enumerate(plt.subplots) attr = if !haskey(plotattributes, :subplot) || plotattributes[:subplot] == idx merge(plotattributes, get(sp_attrs, sp, KW())) else @@ -328,7 +347,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::AKW) where T +function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T} st::Symbol = plotattributes[:seriestype] sp::Subplot{T} = plotattributes[:subplot] sp_idx = get_subplot_index(plt, sp) @@ -354,9 +373,10 @@ end function _override_seriestype_check(plotattributes::AKW, st::Symbol) # do we want to override the series type? - if !is3d(st) && !(st in (:contour,:contour3d)) + if !is3d(st) && !(st in (:contour, :contour3d)) z = plotattributes[:z] - if !isa(z, Nothing) && (size(plotattributes[:x]) == size(plotattributes[:y]) == size(z)) + if !isa(z, Nothing) && + (size(plotattributes[:x]) == size(plotattributes[:y]) == size(z)) st = (st == :scatter ? :scatter3d : :path3d) plotattributes[:seriestype] = st end @@ -364,27 +384,11 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol) st end -function _prepare_annotations(sp::Subplot, plotattributes::AKW) - # 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]) - # series_anns = annotations(pop!(plotattributes, :series_annotations, [])) - # if isa(series_anns, SeriesAnnotations) - # series_anns.x = plotattributes[:x] - # series_anns.y = plotattributes[:y] - # elseif length(series_anns) > 0 - # x, y = plotattributes[:x], plotattributes[:y] - # nx, ny, na = map(length, (x,y,series_anns)) - # n = max(nx, ny, na) - # series_anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(series_anns[mod1(i,na)])) for i=1:n] - # end - # sp.attr[:annotations] = vcat(sp_anns, series_anns) -end - function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol) # adjust extrema and discrete info if st == :image - xmin, xmax = ignorenan_extrema(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y]) + xmin, xmax = ignorenan_extrema(plotattributes[:x]) + ymin, ymax = ignorenan_extrema(plotattributes[:y]) expand_extrema!(sp[:xaxis], (xmin, xmax)) expand_extrema!(sp[:yaxis], (ymin, ymax)) elseif !(st in (:pie, :histogram, :bins2d, :histogram2d)) @@ -398,8 +402,8 @@ function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol) end function _add_the_series(plt, sp, plotattributes) - warnOnUnsupported_args(plt.backend, plotattributes) - warnOnUnsupported(plt.backend, plotattributes) + warn_on_unsupported_args(plt.backend, plotattributes) + warn_on_unsupported(plt.backend, plotattributes) series = Series(plotattributes) push!(plt.series_list, series) push!(sp.series_list, series) @@ -411,7 +415,6 @@ end # this method recursively applies series recipes when the seriestype is not supported # natively by the backend function _process_seriesrecipe(plt::Plot, plotattributes::AKW) - #println("process $(typeof(plotattributes))") # replace seriestype aliases st = Symbol(plotattributes[:seriestype]) st = plotattributes[:seriestype] = get(_typeAliases, st, st) @@ -421,10 +424,10 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW) plotattributes[:fillrange] = nothing end - # if it's natively supported, finalize processing and pass along to the backend, otherwise recurse + # if it's natively supported, finalize processing and pass along to the backend, + # otherwise recurse if is_seriestype_supported(st) sp = _prepare_subplot(plt, plotattributes) - _prepare_annotations(sp, plotattributes) _expand_subplot_extrema(sp, plotattributes, st) _update_series_attributes!(plotattributes, plt, sp) _add_the_series(plt, sp, plotattributes) @@ -438,7 +441,7 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW) # assuming there was no error, recursively apply the series recipes for data in datalist if isa(data, RecipeData) - preprocessArgs!(data.plotattributes) + preprocess_attributes!(data.plotattributes) if data.plotattributes[:seriestype] == st error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.") end diff --git a/src/plot.jl b/src/plot.jl index 7bd16693..13653fee 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -49,7 +49,7 @@ as a String to look up its docstring; e.g. `plotattr("seriestype")`. 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) + preprocess_attributes!(plotattributes) # create an empty Plot then process plt = Plot() @@ -61,7 +61,7 @@ end # note: we split into plt1 and plts_tail so we can dispatch correctly function plot(plt1::Plot, plts_tail::Plot...; kw...) plotattributes = KW(kw) - preprocessArgs!(plotattributes) + preprocess_attributes!(plotattributes) # build our plot vector from the args n = length(plts_tail) + 1 @@ -153,7 +153,7 @@ end # this adds to a specific plot... most plot commands will flow through here function plot!(plt::Plot, args...; kw...) plotattributes = KW(kw) - preprocessArgs!(plotattributes) + preprocess_attributes!(plotattributes) # merge!(plt.user_attr, plotattributes) _plot!(plt, plotattributes, args) end diff --git a/src/series.jl b/src/series.jl index ff3ce8a7..11cf7493 100644 --- a/src/series.jl +++ b/src/series.jl @@ -11,29 +11,32 @@ const MaybeNumber = Union{Number, Missing} const MaybeString = Union{AbstractString, Missing} const DataPoint = Union{MaybeNumber, MaybeString} -prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plotting") -prepareSeriesData(::Nothing) = nothing -prepareSeriesData(t::Tuple{T, T}) where {T<:Number} = t -prepareSeriesData(f::Function) = f -prepareSeriesData(ar::AbstractRange{<:Number}) = ar -function prepareSeriesData(a::AbstractArray{<:MaybeNumber}) +prepare_series_data(x) = error("Cannot convert $(typeof(x)) to series data for plotting") +prepare_series_data(::Nothing) = nothing +prepare_series_data(t::Tuple{T, T}) where {T <: Number} = t +prepare_series_data(f::Function) = f +prepare_series_data(ar::AbstractRange{<:Number}) = ar +function prepare_series_data(a::AbstractArray{<:MaybeNumber}) f = isimmutable(a) ? replace : replace! a = f(x -> ismissing(x) || isinf(x) ? NaN : x, map(float, a)) end -prepareSeriesData(a::AbstractArray{<:Missing}) = fill(NaN, axes(a)) -prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a) -prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf)) -prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image -prepareSeriesData(v::Volume) = Volume(prepareSeriesData(v.v), v.x_extents, v.y_extents, v.z_extents) +prepare_series_data(a::AbstractArray{<:Missing}) = fill(NaN, axes(a)) +prepare_series_data(a::AbstractArray{<:MaybeString}) = + replace(x -> ismissing(x) ? "" : x, a) +prepare_series_data(s::Surface{<:AMat{<:MaybeNumber}}) = + Surface(prepare_series_data(s.surf)) +prepare_series_data(s::Surface) = s # non-numeric Surface, such as an image +prepare_series_data(v::Volume) = + Volume(prepare_series_data(v.v), v.x_extents, v.y_extents, v.z_extents) # default: assume x represents a single series -series_vector(x, plotattributes) = [prepareSeriesData(x)] +series_vector(x, plotattributes) = [prepare_series_data(x)] # fixed number of blank series series_vector(n::Integer, plotattributes) = [zeros(0) for i in 1:n] # vector of data points is a single series -series_vector(v::AVec{<:DataPoint}, plotattributes) = [prepareSeriesData(v)] +series_vector(v::AVec{<:DataPoint}, plotattributes) = [prepare_series_data(v)] # list of things (maybe other vectors, functions, or something else) function series_vector(v::AVec, plotattributes) @@ -49,9 +52,9 @@ end # Matrix is split into columns function series_vector(v::AMat{<:DataPoint}, plotattributes) if all3D(plotattributes) - [prepareSeriesData(Surface(v))] + [prepare_series_data(Surface(v))] else - [prepareSeriesData(v[:, i]) for i in axes(v, 2)] + [prepare_series_data(v[:, i]) for i in axes(v, 2)] end end @@ -73,35 +76,37 @@ process_ribbon(ribbon::Tuple{S, T}, plotattributes) where {S, T} = collect(zip( # -------------------------------------------------------------------- -compute_x(x::Nothing, y::Nothing, z) = axes(z,1) -compute_x(x::Nothing, y, z) = axes(y,1) -compute_x(x::Function, y, z) = map(x, y) -compute_x(x, y, z) = x +compute_x(x::Nothing, y::Nothing, z) = axes(z, 1) +compute_x(x::Nothing, y, z) = axes(y, 1) +compute_x(x::Function, y, z) = map(x, y) +compute_x(x, y, z) = x -compute_y(x::Nothing, y::Nothing, z) = axes(z,2) -compute_y(x, y::Function, z) = map(y, x) -compute_y(x, y, z) = y +compute_y(x::Nothing, y::Nothing, z) = axes(z, 2) +compute_y(x, y::Function, z) = map(y, x) +compute_y(x, y, z) = y -compute_z(x, y, z::Function) = map(z, x, y) -compute_z(x, y, z::AbstractMatrix) = Surface(z) -compute_z(x, y, z::Nothing) = nothing -compute_z(x, y, z) = z +compute_z(x, y, z::Function) = map(z, x, y) +compute_z(x, y, z::AbstractMatrix) = Surface(z) +compute_z(x, y, z::Nothing) = nothing +compute_z(x, y, z) = z nobigs(v::AVec{BigFloat}) = map(Float64, v) nobigs(v::AVec{BigInt}) = map(Int64, v) nobigs(v) = v @noinline function compute_xyz(x, y, z) - x = compute_x(x,y,z) - y = compute_y(x,y,z) - z = compute_z(x,y,z) + x = compute_x(x, y, z) + y = compute_y(x, y, z) + z = compute_z(x, y, z) nobigs(x), nobigs(y), nobigs(z) end # not allowed -compute_xyz(x::Nothing, y::FuncOrFuncs{F}, z) where {F<:Function} = error("If you want to plot the function `$y`, you need to define the x values!") -compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs{F}) where {F<:Function} = error("If you want to plot the function `$z`, you need to define x and y values!") -compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!") +compute_xyz(x::Nothing, y::FuncOrFuncs{F}, z) where {F <: Function} = + error("If you want to plot the function `$y`, you need to define the x values!") +compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs{F}) where {F <: Function} = + error("If you want to plot the function `$z`, you need to define x and y values!") +compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!") # -------------------------------------------------------------------- @@ -153,15 +158,15 @@ struct SliceIt end for i in 1:max(mx, my, mz) # add a new series di = copy(plotattributes) - xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)] + xi, yi, zi = xs[mod1(i, mx)], ys[mod1(i, my)], zs[mod1(i, mz)] di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi) # handle fillrange - fr = fillranges[mod1(i,mf)] + fr = fillranges[mod1(i, mf)] di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr # handle ribbons - rib = ribbons[mod1(i,mr)] + rib = ribbons[mod1(i, mr)] di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib push!(series_list, RecipeData(di, ())) @@ -176,10 +181,11 @@ end # -------------------------------------------------------------------- # this is the default "type recipe"... just pass the object through -@recipe f(::Type{T}, v::T) where T = v +@recipe f(::Type{T}, v::T) where {T} = v # this should catch unhandled "series recipes" and error with a nice message -@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.") +@recipe f(::Type{V}, x, y, z) where {V <: Val} = + error("The backend must not support the series type $V, and there isn't a series recipe defined.") function _apply_type_recipe(plotattributes, v, letter) _preprocess_axis_args!(plotattributes, letter) @@ -206,9 +212,9 @@ function _apply_type_recipe(plotattributes, v::AbstractArray, letter) _postprocess_axis_args!(plotattributes, letter) if length(args) == 2 && all(arg -> arg isa Function, args) numfunc, formatter = args - return Formatted(map(numfunc, v), formatter) - else - return v + return Formatted(map(numfunc, v), formatter) + else + return v end end _postprocess_axis_args!(plotattributes, letter) @@ -317,13 +323,16 @@ end # so we'll apply_type_recipe to all of them @recipe function f(v1, v2, v3, v4, vrest...) did_replace = false - newargs = map(v -> begin - newv = _apply_type_recipe(plotattributes, v, :unknown) - if newv !== v - did_replace = true - end - newv - end, (v1, v2, v3, v4, vrest...)) + newargs = map( + v -> begin + newv = _apply_type_recipe(plotattributes, v, :unknown) + if newv !== v + did_replace = true + end + newv + end, + (v1, v2, v3, v4, vrest...), + ) if !did_replace error("Couldn't process recipe args: $(map(typeof, (v1, v2, v3, v4, vrest...)))") end @@ -352,7 +361,8 @@ end # 1 argument # -------------------------------------------------------------------- -@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing) +@recipe f(n::Integer) = is3d(get(plotattributes, :seriestype, :path)) ? (SliceIt, n, n, n) : + (SliceIt, n, n, nothing) all3D(plotattributes) = trueOrAllTrue( st -> st in ( @@ -416,13 +426,13 @@ end yflip --> true cbar --> false fillcolor --> ColorGradient([:black, :white]) - SliceIt, m, n, Surface(clamp!(convert(Matrix{Float64}, mat), 0., 1.)) + SliceIt, m, n, Surface(clamp!(convert(Matrix{Float64}, mat), 0.0, 1.0)) end end # images - colors -@recipe function f(mat::AMat{T}) where T<:Colorant - n, m = axes(mat) +@recipe function f(mat::AMat{T}) where {T <: Colorant} + n, m = axes(mat) if is_seriestype_supported(:image) seriestype := :image @@ -452,8 +462,8 @@ end @recipe function f(shapes::AMat{Shape}) seriestype --> :shape - for j in axes(shapes,2) - @series coords(vec(shapes[:,j])) + for j in axes(shapes, 2) + @series coords(vec(shapes[:, j])) end end @@ -463,7 +473,7 @@ end # function without range... use the current range of the x-axis -@recipe function f(f::FuncOrFuncs{F}) where F<:Function +@recipe function f(f::FuncOrFuncs{F}) where {F <: Function} plt = plotattributes[:plot_object] xmin, xmax = if haskey(plotattributes, :xlims) plotattributes[:xlims] @@ -472,8 +482,8 @@ end axis_limits(plt[1], :x) catch xinv = invscalefunc(get(plotattributes, :xscale, :identity)) - xm = PlotUtils.tryrange(f, xinv.([-5,-1,0,0.01])) - xm, PlotUtils.tryrange(f, filter(x->x>xm, xinv.([5,1,0.99, 0, -0.01]))) + xm = PlotUtils.tryrange(f, xinv.([-5, -1, 0, 0.01])) + xm, PlotUtils.tryrange(f, filter(x -> x > xm, xinv.([5, 1, 0.99, 0, -0.01]))) end end @@ -488,7 +498,7 @@ end # if functions come first, just swap the order (not to be confused with parametric # functions... as there would be more than one function passed in) -@recipe function f(f::FuncOrFuncs{F}, x) where F<:Function +@recipe function f(f::FuncOrFuncs{F}, x) where {F <: Function} F2 = typeof(x) @assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here x, f @@ -513,7 +523,7 @@ end end # images - grays -@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray +@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T <: Gray} if is_seriestype_supported(:image) seriestype := :image yflip --> true @@ -528,7 +538,7 @@ end end # images - colors -@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant +@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T <: Colorant} if is_seriestype_supported(:image) seriestype := :image yflip --> true @@ -549,27 +559,49 @@ end # special handling... xmin/xmax with parametric function(s) @recipe function f(f::Function, xmin::Number, xmax::Number) - xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)] + xscale, yscale = [get(plotattributes, sym, :identity) for sym in (:xscale, :yscale)] _scaled_adapted_grid(f, xscale, yscale, xmin, xmax) end -@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function - xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)] +@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where {F <: Function} + xscale, yscale = [get(plotattributes, sym, :identity) for sym in (:xscale, :yscale)] unzip(_scaled_adapted_grid.(fs, xscale, yscale, xmin, xmax)) end -@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u) -@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, range(umin, stop = umax, length = n) +@recipe f( + fx::FuncOrFuncs{F}, + fy::FuncOrFuncs{G}, + u::AVec, +) where {F <: Function, G <: Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u) +@recipe f( + fx::FuncOrFuncs{F}, + fy::FuncOrFuncs{G}, + umin::Number, + umax::Number, + n = 200, +) where {F <: Function, G <: Function} = fx, fy, range(umin, stop = umax, length = n) function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax) - (xf, xinv), (yf, yinv) = ((scalefunc(s),invscalefunc(s)) for s in (xscale,yscale)) - xs, ys = adapted_grid(yf∘f∘xinv, xf.((xmin, xmax))) + (xf, xinv), (yf, yinv) = ((scalefunc(s), invscalefunc(s)) for s in (xscale, yscale)) + xs, ys = adapted_grid(yf ∘ f ∘ xinv, xf.((xmin, xmax))) xinv.(xs), yinv.(ys) end # special handling... 3D parametric function(s) -@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function} +@recipe function f( + fx::FuncOrFuncs{F}, + fy::FuncOrFuncs{G}, + fz::FuncOrFuncs{H}, + u::AVec, +) where {F <: Function, G <: Function, H <: Function} mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u) end -@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200) where {F<:Function,G<:Function,H<:Function} +@recipe function f( + fx::FuncOrFuncs{F}, + fy::FuncOrFuncs{G}, + fz::FuncOrFuncs{H}, + umin::Number, + umax::Number, + numPoints = 200, +) where {F <: Function, G <: Function, H <: Function} fx, fy, fz, range(umin, stop = umax, length = numPoints) end @@ -578,13 +610,15 @@ end # Lists of tuples and GeometryTypes.Points # -------------------------------------------------------------------- -@recipe f(v::AVec{<:Tuple}) = unzip(v) +@recipe f(v::AVec{<:Tuple}) = unzip(v) @recipe f(v::AVec{<:GeometryTypes.Point}) = unzip(v) -@recipe f(tup::Tuple) = [tup] +@recipe f(tup::Tuple) = [tup] @recipe f(p::GeometryTypes.Point) = [p] # Special case for 4-tuples in :ohlc series -@recipe f(xyuv::AVec{<:Tuple{R1,R2,R3,R4}}) where {R1,R2,R3,R4} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv) +@recipe f(xyuv::AVec{<:Tuple{R1, R2, R3, R4}}) where {R1, R2, R3, R4} = + get(plotattributes, :seriestype, :path) == :ohlc ? OHLC[OHLC(t...) for t in xyuv] : + unzip(xyuv) # -------------------------------------------------------------------- @@ -592,11 +626,13 @@ end # -------------------------------------------------------------------- splittable_kw(key, val, lengthGroup) = false -splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && length(axes(val,1)) == lengthGroup +splittable_kw(key, val::AbstractArray, lengthGroup) = + !(key in (:group, :color_palette)) && length(axes(val, 1)) == lengthGroup splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, lengthGroup)) -splittable_kw(key, val::SeriesAnnotations, lengthGroup) = splittable_kw(key, val.strs, lengthGroup) +splittable_kw(key, val::SeriesAnnotations, lengthGroup) = + splittable_kw(key, val.strs, lengthGroup) -split_kw(key, val::AbstractArray, indices) = val[indices, fill(Colon(), ndims(val)-1)...] +split_kw(key, val::AbstractArray, indices) = val[indices, fill(Colon(), ndims(val) - 1)...] split_kw(key, val::Tuple, indices) = Tuple(split_kw(key, v, indices) for v in val) function split_kw(key, val::SeriesAnnotations, indices) split_strs = split_kw(key, val.strs, indices) @@ -604,7 +640,11 @@ function split_kw(key, val::SeriesAnnotations, indices) end function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1]) - y_mat = Array{promote_type(eltype(y), typeof(def_val))}(undef, length(keys(x_ind)), length(groupby.groupLabels)) + y_mat = Array{promote_type(eltype(y), typeof(def_val))}( + undef, + length(keys(x_ind)), + length(groupby.groupLabels), + ) fill!(y_mat, def_val) for i in eachindex(groupby.groupLabels) xi = x[groupby.groupIds[i]] @@ -614,7 +654,8 @@ function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1]) return y_mat end -groupedvec2mat(x_ind, x, y::Tuple, groupby) = Tuple(groupedvec2mat(x_ind, x, v, groupby) for v in y) +groupedvec2mat(x_ind, x, y::Tuple, groupby) = + Tuple(groupedvec2mat(x_ind, x, v, groupby) for v in y) group_as_matrix(t) = false @@ -622,11 +663,11 @@ group_as_matrix(t) = false @recipe function f(groupby::GroupBy, args...) lengthGroup = maximum(union(groupby.groupIds...)) if !(group_as_matrix(args[1])) - for (i,glab) in enumerate(groupby.groupLabels) + for (i, glab) in enumerate(groupby.groupLabels) @series begin - label --> string(glab) + label --> string(glab) idxfilter --> groupby.groupIds[i] - for (key,val) in plotattributes + for (key, val) in plotattributes if splittable_kw(key, val, lengthGroup) :($key) := split_kw(key, val, groupby.groupIds[i]) end @@ -648,12 +689,15 @@ group_as_matrix(t) = false end x_u = unique(sort(x)) x_ind = Dict(zip(x_u, eachindex(x_u))) - for (key,val) in plotattributes + for (key, val) in plotattributes if splittable_kw(key, val, lengthGroup) :($key) := groupedvec2mat(x_ind, x, val, groupby) end end label --> reshape(groupby.groupLabels, 1, :) - typeof(g)((x_u, (groupedvec2mat(x_ind, x, arg, groupby, NaN) for arg in last_args)...)) + typeof(g)(( + x_u, + (groupedvec2mat(x_ind, x, arg, groupby, NaN) for arg in last_args)..., + )) end end diff --git a/src/utils.jl b/src/utils.jl index 7d274961..42f905c6 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -838,7 +838,7 @@ end function attr!(series::Series; kw...) plotattributes = KW(kw) - preprocessArgs!(plotattributes) + preprocess_attributes!(plotattributes) for (k,v) in plotattributes if haskey(_series_defaults, k) series[k] = v @@ -852,7 +852,7 @@ end function attr!(sp::Subplot; kw...) plotattributes = KW(kw) - preprocessArgs!(plotattributes) + preprocess_attributes!(plotattributes) for (k,v) in plotattributes if haskey(_subplot_defaults, k) sp[k] = v