Compare commits

...

23 Commits

Author SHA1 Message Date
Michael Krabbe Borregaard
8eac99a389
Merge pull request #2469 from JuliaPlots/bbs/remove-defaults
remove default recipes
2020-03-16 22:48:38 +01:00
Simon Christ
a65d41ca39 move Volume 2020-03-15 21:48:51 +01:00
Simon Christ
dd25893a70 move Surface and trueOrAllTrue 2020-03-15 21:22:59 +01:00
Simon Christ
52c081758d move Formatted and import 2020-03-15 21:14:16 +01:00
Simon Christ
7ed9fbd7fc factor out SliceIt dependencies 2020-03-15 18:11:46 +01:00
Simon Christ
6bb53b5181 remove default recipes 2020-03-15 16:53:54 +01:00
Simon Christ
9d57817bdf
Update plot.jl (#2467)
* implement the recipe_pipeline

* delete _plot!

* Update plot.jl

fix: https://github.com/mkborregaard/RecipePipeline.jl/issues/7
2020-03-15 15:40:49 +01:00
Anshul Singhvi
4b94a8c2f6 Fix un-imported symbol (#2468)
ensure that the pipeline works
2020-03-15 13:36:51 +01:00
Anshul Singhvi
aafc1e1989 change the is_st_supported signature (#2466) 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu
4e6c64ebcc Add finalize_subplot! 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu
cb103598d7 Fix typo
_process_userrecipes now calls the correct _process_userrecipe
2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu
5728e9fb46 rename 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu
74a6520960 Rename _typeAliases to type_aliases 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu
f557ca3b74 Disable precompile file for now 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu
bb08d61709 Add _recipe_before_series 2020-03-15 13:36:51 +01:00
Simon Christ
857d37c380 [skip ci]
more bangs
2020-03-15 13:36:51 +01:00
Simon Christ
261f47fb6c delete _plot! 2020-03-15 13:36:51 +01:00
Simon Christ
476769fab6 add bangs 2020-03-15 13:36:51 +01:00
Simon Christ
f7282dd49a implement the recipe_pipeline 2020-03-15 13:36:51 +01:00
Michael Krabbe Borregaard
5f09d97ca0 add function stubs from recipeutils 2020-03-15 13:36:51 +01:00
Michael Krabbe Borregaard
e4754f3aeb Move out three functions 2020-03-15 13:36:51 +01:00
Anshul Singhvi
43fd0e76be Manually add the unregistered package in Travis 2020-03-15 13:36:51 +01:00
Michael Krabbe Borregaard
3ed781ac2d depend on recipeutils 2020-03-15 13:36:51 +01:00
8 changed files with 58 additions and 355 deletions

View File

@ -35,4 +35,4 @@ notifications:
script: script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi - if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
- $TESTCMD -e 'using Pkg; Pkg.build(); Pkg.test(coverage=true)' - $TESTCMD -e 'using Pkg; pkg"add https://github.com/mkborregaard/RecipeUtils.jl"; Pkg.build(); Pkg.test(coverage=true)'

View File

@ -21,6 +21,7 @@ PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RecipePipeline = "01d81517-befc-4cb6-b9ec-a95719d0359c"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Requires = "ae029012-a4dd-5104-9daa-d747884805df" Requires = "ae029012-a4dd-5104-9daa-d747884805df"

View File

@ -18,6 +18,31 @@ using Base.Meta
import Showoff import Showoff
import StatsBase import StatsBase
import JSON import JSON
import RecipePipeline:
_process_userrecipe,
_process_plotrecipe,
_process_seriesrecipe,
_preprocess_args,
preprocessArgs!,
is_st_supported,
finalize_subplot!,
recipe_pipeline!,
_recipe_init!,
_recipe_after_user!,
_recipe_after_plot!,
_recipe_before_series!,
_recipe_finish!,
is_st_supported,
Formatted,
SliceIt,
FuncOrFuncs,
MaybeNumber,
MaybeString,
DataPoint,
trueOrAllTrue,
Surface,
AbstractSurface,
Volume
using Requires using Requires
@ -222,7 +247,7 @@ end
const CURRENT_BACKEND = CurrentBackend(:none) const CURRENT_BACKEND = CurrentBackend(:none)
include("precompile.jl") # include("precompile.jl")
_precompile_() # _precompile_()
end # module end # module

View File

@ -650,23 +650,6 @@ end
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
abstract type AbstractSurface end
"represents a contour or surface mesh"
struct Surface{M<:AMat} <: AbstractSurface
surf::M
end
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for yi in y, xi in x])
Base.Array(surf::Surface) = surf.surf
for f in (:length, :size)
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
end
Base.copy(surf::Surface) = Surface(copy(surf.surf))
Base.eltype(surf::Surface{T}) where {T} = eltype(T)
function expand_extrema!(a::Axis, surf::Surface) function expand_extrema!(a::Axis, surf::Surface)
ex = a[:extrema] ex = a[:extrema]
for vi in surf.surf for vi in surf.surf
@ -686,30 +669,6 @@ end
# # I don't want to clash with ValidatedNumerics, but this would be nice: # # I don't want to clash with ValidatedNumerics, but this would be nice:
# ..(a::T, b::T) = (a,b) # ..(a::T, b::T) = (a,b)
struct Volume{T}
v::Array{T,3}
x_extents::Tuple{T,T}
y_extents::Tuple{T,T}
z_extents::Tuple{T,T}
end
default_extents(::Type{T}) where {T} = (zero(T), one(T))
function Volume(v::Array{T,3},
x_extents = default_extents(T),
y_extents = default_extents(T),
z_extents = default_extents(T)) where T
Volume(v, x_extents, y_extents, z_extents)
end
Base.Array(vol::Volume) = vol.v
for f in (:length, :size)
@eval Base.$f(vol::Volume, args...) = $f(vol.v, args...)
end
Base.copy(vol::Volume{T}) where {T} = Volume{T}(copy(vol.v), vol.x_extents, vol.y_extents, vol.z_extents)
Base.eltype(vol::Volume{T}) where {T} = T
# -----------------------------------------------------------------------
# style is :open or :closed (for now) # style is :open or :closed (for now)
struct Arrow struct Arrow
@ -771,13 +730,6 @@ function add_arrows(func::Function, x::AVec, y::AVec)
end end
end end
# -----------------------------------------------------------------------
"Represents data values with formatting that should apply to the tick labels."
struct Formatted{T}
data::T
formatter::Function
end
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
"create a BezierCurve for plotting" "create a BezierCurve for plotting"

View File

@ -1,5 +1,13 @@
function finalize_subplot!(plt::Plot, st, plotattributes::AKW)
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)
end
# Override the RecipesPipeline `is_st_supported` for Plots.
is_st_supported(::Plot, st::Symbol) = is_seriestype_supported(st)
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# preprocessing # preprocessing
@ -59,39 +67,6 @@ end
# user recipes # user recipes
function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
still_to_process = RecipeData[]
args = _preprocess_args(plotattributes, args, still_to_process)
# for plotting recipes, swap out the args and update the parameter dictionary
# we are keeping a stack of series that still need to be processed.
# each pass through the loop, we pop one off and apply the recipe.
# the recipe will return a list a Series objects... the ones that are
# finished (no more args) get added to the kw_list, the ones that are not
# are placed on top of the stack and are then processed further.
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.
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
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...)
prepend!(still_to_process,rd_list)
end
end
# don't allow something else to handle it
plotattributes[:smooth] = false
kw_list
end
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData) 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 # when the arg tuple is empty, that means there's nothing left to recursively
# process... finish up and add to the kw_list # process... finish up and add to the kw_list
@ -168,39 +143,6 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
end end
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# plot recipes
# Grab the first in line to be processed and pass it through apply_recipe
# 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})
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)
return
end
try
st = kw[:seriestype]
st = kw[:seriestype] = get(_typeAliases, st, st)
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
for data in datalist
preprocessArgs!(data.plotattributes)
if data.plotattributes[:seriestype] == st
error("Plot recipe $st returned the same seriestype: $(data.plotattributes)")
end
push!(still_to_process, data.plotattributes)
end
catch err
if isa(err, MethodError)
push!(kw_list, kw)
else
rethrow()
end
end
return
end
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# setup plot and subplot # setup plot and subplot
@ -382,47 +324,3 @@ function _add_the_series(plt, sp, plotattributes)
push!(sp.series_list, series) push!(sp.series_list, series)
_series_added(plt, series) _series_added(plt, series)
end 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)
# shapes shouldn't have fillrange set
if plotattributes[:seriestype] == :shape
plotattributes[:fillrange] = nothing
end
# 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)
else
# get a sub list of series for this seriestype
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z])
# assuming there was no error, recursively apply the series recipes
for data in datalist
if isa(data, RecipeData)
preprocessArgs!(data.plotattributes)
if data.plotattributes[:seriestype] == st
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
end
_process_seriesrecipe(plt, data.plotattributes)
else
@warn("Unhandled recipe: $(data)")
break
end
end
end
nothing
end

View File

@ -54,7 +54,7 @@ function plot(args...; kw...)
# create an empty Plot then process # create an empty Plot then process
plt = Plot() plt = Plot()
# plt.user_attr = plotattributes # plt.user_attr = plotattributes
_plot!(plt, plotattributes, args) recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
end end
# build a new plot from existing plots # build a new plot from existing plots
@ -155,7 +155,7 @@ function plot!(plt::Plot, args...; kw...)
plotattributes = KW(kw) plotattributes = KW(kw)
preprocessArgs!(plotattributes) preprocessArgs!(plotattributes)
# merge!(plt.user_attr, plotattributes) # merge!(plt.user_attr, plotattributes)
_plot!(plt, plotattributes, args) recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
end end
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
@ -163,78 +163,41 @@ end
# this is the core plotting function. recursively apply recipes to build # this is the core plotting function. recursively apply recipes to build
# a list of series KW dicts. # a list of series KW dicts.
# note: at entry, we only have those preprocessed args which were passed in... no default values yet # note: at entry, we only have those preprocessed args which were passed in... no default values yet
function _plot!(plt::Plot, plotattributes::AKW, args::Tuple)
## here we implement the recipe interface
function _recipe_init!(plt::Plot, plotattributes::AKW, args::Tuple)
plotattributes[:plot_object] = plt plotattributes[:plot_object] = plt
if !isempty(args) && !isdefined(Main, :StatsPlots) && if !isempty(args) && !isdefined(Main, :StatsPlots) &&
first(split(string(typeof(args[1])), ".")) == "DataFrames" first(split(string(typeof(args[1])), ".")) == "DataFrames"
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots") @warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots")
end end
end
# -------------------------------- function _recipe_after_plot!(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
# "USER RECIPES"
# --------------------------------
kw_list = _process_userrecipes(plt, plotattributes, args)
# @info(1)
# map(DD, kw_list)
# --------------------------------
# "PLOT RECIPES"
# --------------------------------
# "plot recipe", which acts like a series type, and is processed before
# 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)
next_kw = popfirst!(still_to_process)
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
end
# @info(2)
# map(DD, kw_list)
# -------------------------------- # --------------------------------
# Plot/Subplot/Layout setup # Plot/Subplot/Layout setup
# -------------------------------- # --------------------------------
_plot_setup(plt, plotattributes, kw_list) _plot_setup(plt, plotattributes, kw_list)
_subplot_setup(plt, plotattributes, kw_list) _subplot_setup(plt, plotattributes, kw_list)
end
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!! function _recipe_after_user!(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!! # don't allow something else to handle it
plotattributes[:smooth] = false
end
# -------------------------------- function _recipe_before_series!(plt::Plot, kw, kw_list)
# "SERIES RECIPES"
# --------------------------------
# @info(3)
# map(DD, kw_list)
for kw in kw_list
sp::Subplot = kw[:subplot] sp::Subplot = kw[:subplot]
# in series attributes given as vector with one element per series, # in series attributes given as vector with one element per series,
# select the value for current series # select the value for current series
_slice_series_args!(kw, plt, sp, series_idx(kw_list,kw)) _slice_series_args!(kw, plt, sp, series_idx(kw_list,kw))
series_attr = Attr(kw, _series_defaults) series_attr = Attr(kw, _series_defaults)
# now we have a fully specified series, with colors chosen. we must recursively handle end
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
# we check for a recipe that will convert that series type into one made up of lower-level components.
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
# be able to support step, bar, and histogram plots (and any recipes that use those components).
_process_seriesrecipe(plt, series_attr)
end
# --------------------------------
function _recipe_finish!(plt::Plot, plotattributes::AKW, args::Tuple)
current(plt) current(plt)
# do we want to force display? # do we want to force display?
@ -246,7 +209,6 @@ function _plot!(plt::Plot, plotattributes::AKW, args::Tuple)
plt plt
end end
# we're getting ready to display/output. prep for layout calcs, then update # we're getting ready to display/output. prep for layout calcs, then update
# the plot object after # the plot object after
function prepare_output(plt::Plot) function prepare_output(plt::Plot)

View File

@ -1,71 +1,4 @@
# create a new "build_series_args" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
# Special handling for: no args, xmin/xmax, parametric, dataframes
# Then once inputs have been converted, build the series args, map functions, etc.
# This should cut down on boilerplate code and allow more focused dispatch on type
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
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(a::AbstractArray{<:MaybeNumber}) = replace!(
x -> ismissing(x) || isinf(x) ? NaN : x,
map(float,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)
# default: assume x represents a single series
convertToAnyVector(x, plotattributes) = Any[prepareSeriesData(x)]
# fixed number of blank series
convertToAnyVector(n::Integer, plotattributes) = Any[zeros(0) for i in 1:n]
# vector of data points is a single series
convertToAnyVector(v::AVec{<:DataPoint}, plotattributes) = Any[prepareSeriesData(v)]
# list of things (maybe other vectors, functions, or something else)
function convertToAnyVector(v::AVec, plotattributes)
if all(x -> x isa MaybeNumber, v)
convertToAnyVector(Vector{MaybeNumber}(v), plotattributes)
elseif all(x -> x isa MaybeString, v)
convertToAnyVector(Vector{MaybeString}(v), plotattributes)
else
vcat((convertToAnyVector(vi, plotattributes) for vi in v)...)
end
end
# Matrix is split into columns
function convertToAnyVector(v::AMat{<:DataPoint}, plotattributes)
if all3D(plotattributes)
Any[prepareSeriesData(Surface(v))]
else
Any[prepareSeriesData(v[:, i]) for i in axes(v, 2)]
end
end
# --------------------------------------------------------------------
# Fillranges & ribbons
process_fillrange(range::Number, plotattributes) = [range]
process_fillrange(range, plotattributes) = convertToAnyVector(range, plotattributes)
process_ribbon(ribbon::Number, plotattributes) = [ribbon]
process_ribbon(ribbon, plotattributes) = convertToAnyVector(ribbon, plotattributes)
# ribbon as a tuple: (lower_ribbons, upper_ribbons)
process_ribbon(ribbon::Tuple{Any,Any}, plotattributes) = collect(zip(convertToAnyVector(ribbon[1], plotattributes),
convertToAnyVector(ribbon[2], plotattributes)))
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array # TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
@ -107,70 +40,6 @@ compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all no
# we are going to build recipes to do the processing and splitting of the args # we are going to build recipes to do the processing and splitting of the args
# ensure we dispatch to the slicer
struct SliceIt end
# the catch-all recipes
@recipe function f(::Type{SliceIt}, x, y, z)
# handle data with formatting attached
if typeof(x) <: Formatted
xformatter := x.formatter
x = x.data
end
if typeof(y) <: Formatted
yformatter := y.formatter
y = y.data
end
if typeof(z) <: Formatted
zformatter := z.formatter
z = z.data
end
xs = convertToAnyVector(x, plotattributes)
ys = convertToAnyVector(y, plotattributes)
zs = convertToAnyVector(z, plotattributes)
fr = pop!(plotattributes, :fillrange, nothing)
fillranges = process_fillrange(fr, plotattributes)
mf = length(fillranges)
rib = pop!(plotattributes, :ribbon, nothing)
ribbons = process_ribbon(rib, plotattributes)
mr = length(ribbons)
# @show zs
mx = length(xs)
my = length(ys)
mz = length(zs)
if mx > 0 && my > 0 && mz > 0
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)]
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
# handle fillrange
fr = fillranges[mod1(i,mf)]
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
# handle ribbons
rib = ribbons[mod1(i,mr)]
di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib
push!(series_list, RecipeData(di, ()))
end
end
nothing # don't add a series for the main block
end
# this is the default "type recipe"... just pass the object through
@recipe f(::Type{T}, v::T) where {T<:Any} = 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.")
_apply_type_recipe(plotattributes, v) = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1] _apply_type_recipe(plotattributes, v) = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
@ -278,7 +147,6 @@ end
@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 (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(plotattributes, :seriestype, :none))
# return a surface if this is a 3d plot, otherwise let it be sliced up # return a surface if this is a 3d plot, otherwise let it be sliced up
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat,Missing} @recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat,Missing}

View File

@ -717,9 +717,6 @@ makekw(; kw...) = KW(kw)
wraptuple(x::Tuple) = x wraptuple(x::Tuple) = x
wraptuple(x) = (x,) wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
trueOrAllTrue(f::Function, x) = f(x)
allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg) allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg) allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
allShapes(arg) = trueOrAllTrue(a -> is_marker_supported(get(_markerAliases, a, a)), arg) || allShapes(arg) = trueOrAllTrue(a -> is_marker_supported(get(_markerAliases, a, a)), arg) ||