From eecb5c3754bacd6562cc81fe118fa788ff3087ae Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Thu, 17 Mar 2016 13:38:22 -0400 Subject: [PATCH] started series reorg --- src/args.jl | 4 +- src/backends/bokeh.jl | 4 +- src/backends/gadfly.jl | 4 +- src/backends/glvisualize.jl | 4 +- src/backends/gr.jl | 4 +- src/backends/immerse.jl | 4 +- src/backends/plotlyjs.jl | 4 +- src/backends/pyplot.jl | 4 +- src/backends/qwt.jl | 4 +- src/backends/template.jl | 4 +- src/backends/unicodeplots.jl | 4 +- src/backends/winston.jl | 4 +- src/plot.jl | 4 +- src/series_args.jl | 417 +++++++++++++++++++---------------- src/subplot.jl | 10 +- src/types.jl | 1 + src/utils.jl | 10 +- 17 files changed, 270 insertions(+), 220 deletions(-) diff --git a/src/args.jl b/src/args.jl index c8438cd9..8bb2d815 100644 --- a/src/args.jl +++ b/src/args.jl @@ -713,7 +713,7 @@ convertLegendValue(val::Bool) = val ? :best : :none # build the argument dictionary for the plot function getPlotArgs(pkg::AbstractBackend, kw, idx::Int; set_defaults = true) - kwdict = Dict(kw) + kwdict = KW(kw) d = Dict() # add defaults? @@ -750,7 +750,7 @@ end # build the argument dictionary for a series function getSeriesArgs(pkg::AbstractBackend, plotargs::Dict, kw, commandIndex::Int, plotIndex::Int, globalIndex::Int) # TODO, pass in plotargs, not plt - kwdict = Dict(kw) + kwdict = KW(kw) d = Dict() # add defaults? diff --git a/src/backends/bokeh.jl b/src/backends/bokeh.jl index a6e74fb0..3bf86195 100644 --- a/src/backends/bokeh.jl +++ b/src/backends/bokeh.jl @@ -65,7 +65,7 @@ end # --------------------------------------------------------------------------- function _create_plot(pkg::BokehBackend; kw...) - d = Dict(kw) + d = KW(kw) # dumpdict(d, "plot", true) @@ -89,7 +89,7 @@ end function _add_series(::BokehBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) # dumpdict(d, "plot!", true) diff --git a/src/backends/gadfly.jl b/src/backends/gadfly.jl index 1d036b50..b2d4da35 100644 --- a/src/backends/gadfly.jl +++ b/src/backends/gadfly.jl @@ -508,7 +508,7 @@ end # create a blank Gadfly.Plot object function _create_plot(pkg::GadflyBackend; kw...) - d = Dict(kw) + d = KW(kw) gplt = createGadflyPlotObject(d) Plot(gplt, pkg, 0, d, Dict[]) end @@ -522,7 +522,7 @@ function _add_series(::GadflyBackend, plt::Plot; kw...) gplt.layers = gplt.layers[2:end] end - d = Dict(kw) + d = KW(kw) addGadflySeries!(plt, d) push!(plt.seriesargs, d) plt diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index 61639575..590c83f0 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -16,7 +16,7 @@ immutable GLScreenWrapper end function _create_plot(pkg::GLVisualizeBackend; kw...) - d = Dict(kw) + d = KW(kw) # TODO: create the window/canvas/context that is the plot within the backend (call it `o`) # TODO: initialize the plot... title, xlabel, bgcolor, etc @@ -28,7 +28,7 @@ end function _add_series(::GLVisualizeBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) # TODO: add one series to the underlying package push!(plt.seriesargs, d) # TODO: this should be moved to the display method? diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 6b07f5a1..1c2e5065 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -702,12 +702,12 @@ function gr_display(subplt::Subplot{GRBackend}) end function _create_plot(pkg::GRBackend; kw...) - d = Dict(kw) + d = KW(kw) Plot(nothing, pkg, 0, d, Dict[]) end function _add_series(::GRBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) push!(plt.seriesargs, d) plt end diff --git a/src/backends/immerse.jl b/src/backends/immerse.jl index fcc210b9..b921a89a 100644 --- a/src/backends/immerse.jl +++ b/src/backends/immerse.jl @@ -20,7 +20,7 @@ end # create a blank Gadfly.Plot object function _create_plot(pkg::ImmerseBackend; kw...) - d = Dict(kw) + d = KW(kw) # create the underlying Gadfly.Plot object gplt = createGadflyPlotObject(d) @@ -32,7 +32,7 @@ end # plot one data series function _add_series(::ImmerseBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) addGadflySeries!(plt, d) push!(plt.seriesargs, d) plt diff --git a/src/backends/plotlyjs.jl b/src/backends/plotlyjs.jl index ca972d98..64cd0cf2 100644 --- a/src/backends/plotlyjs.jl +++ b/src/backends/plotlyjs.jl @@ -20,7 +20,7 @@ end # --------------------------------------------------------------------------- function _create_plot(pkg::PlotlyJSBackend; kw...) - d = Dict(kw) + d = KW(kw) # TODO: create the window/canvas/context that is the plot within the backend (call it `o`) # TODO: initialize the plot... title, xlabel, bgcolor, etc # o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(), @@ -34,7 +34,7 @@ end function _add_series(::PlotlyJSBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) syncplot = plt.o # dumpdict(d, "addseries", true) diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 8a2d225a..aee0a3b8 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -269,7 +269,7 @@ end function _create_plot(pkg::PyPlotBackend; kw...) # create the figure - d = Dict(kw) + d = KW(kw) # standalone plots will create a figure, but not if part of a subplot (do it later) if haskey(d, :subplot) @@ -290,7 +290,7 @@ end function _add_series(pkg::PyPlotBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) # 3D plots have a different underlying Axes object in PyPlot lt = d[:linetype] diff --git a/src/backends/qwt.jl b/src/backends/qwt.jl index e512009c..466b5620 100644 --- a/src/backends/qwt.jl +++ b/src/backends/qwt.jl @@ -39,7 +39,7 @@ function replaceQwtAliases(d, s) end function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...) - d = Dict(kw) + d = KW(kw) lt = d[:linetype] if lt == :scatter d[:linetype] = :none @@ -78,7 +78,7 @@ function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...) end function _create_plot(pkg::QwtBackend; kw...) - d = Dict(kw) + d = KW(kw) fixcolors(d) dumpdict(d,"\n\n!!! plot") o = Qwt.plot(zeros(0,0); d..., show=false) diff --git a/src/backends/template.jl b/src/backends/template.jl index d82bcbd1..48edf7cf 100644 --- a/src/backends/template.jl +++ b/src/backends/template.jl @@ -15,7 +15,7 @@ end # --------------------------------------------------------------------------- function _create_plot(pkg::[PkgName]AbstractBackend; kw...) - d = Dict(kw) + d = KW(kw) # TODO: create the window/canvas/context that is the plot within the backend (call it `o`) # TODO: initialize the plot... title, xlabel, bgcolor, etc Plot(nothing, pkg, 0, d, Dict[]) @@ -23,7 +23,7 @@ end function _add_series(::[PkgName]AbstractBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) # TODO: add one series to the underlying package push!(plt.seriesargs, d) plt diff --git a/src/backends/unicodeplots.jl b/src/backends/unicodeplots.jl index 38fd348e..c62456f5 100644 --- a/src/backends/unicodeplots.jl +++ b/src/backends/unicodeplots.jl @@ -120,7 +120,7 @@ end function _create_plot(pkg::UnicodePlotsBackend; kw...) - plt = Plot(nothing, pkg, 0, Dict(kw), Dict[]) + plt = Plot(nothing, pkg, 0, KW(kw), Dict[]) # do we want to give a new default size? if !haskey(plt.plotargs, :size) || plt.plotargs[:size] == _plotDefaults[:size] @@ -131,7 +131,7 @@ function _create_plot(pkg::UnicodePlotsBackend; kw...) end function _add_series(::UnicodePlotsBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) if d[:linetype] in (:sticks, :bar) d = barHack(; d...) elseif d[:linetype] == :hist diff --git a/src/backends/winston.jl b/src/backends/winston.jl index 687a0ddd..bea15099 100644 --- a/src/backends/winston.jl +++ b/src/backends/winston.jl @@ -41,7 +41,7 @@ end function _create_plot(pkg::WinstonBackend; kw...) - d = Dict(kw) + d = KW(kw) wplt = Winston.FramedPlot(title = d[:title], xlabel = d[:xlabel], ylabel = d[:ylabel]) Plot(wplt, pkg, 0, d, Dict[]) @@ -65,7 +65,7 @@ function getWinstonItems(plt::Plot) end function _add_series(::WinstonBackend, plt::Plot; kw...) - d = Dict(kw) + d = KW(kw) window, canvas, wplt = getWinstonItems(plt) diff --git a/src/plot.jl b/src/plot.jl index bf4b2338..daa991fb 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -44,7 +44,7 @@ When you pass in matrices, it splits by columns. See the documentation for more # this creates a new plot with args/kw and sets it to be the current plot function plot(args...; kw...) pkg = backend() - d = Dict(kw) + d = KW(kw) preprocessArgs!(d) dumpdict(d, "After plot preprocessing") @@ -71,7 +71,7 @@ end # this adds to a specific plot... most plot commands will flow through here function plot!(plt::Plot, args...; kw...) - d = Dict(kw) + d = KW(kw) preprocessArgs!(d) # for plotting recipes, swap out the args and update the parameter dictionary diff --git a/src/series_args.jl b/src/series_args.jl index 511e4f8e..5e0e1bd3 100644 --- a/src/series_args.jl +++ b/src/series_args.jl @@ -16,13 +16,13 @@ convertToAnyVector(v::@compat(Void), d::Dict) = Any[nothing], nothing convertToAnyVector(n::Integer, d::Dict) = Any[zeros(0) for i in 1:n], nothing # numeric vector -convertToAnyVector{T<:Real}(v::AVec{T}, d::Dict) = Any[v], nothing +convertToAnyVector{T<:Number}(v::AVec{T}, d::Dict) = Any[v], nothing # string vector convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::Dict) = Any[v], nothing # numeric matrix -function convertToAnyVector{T<:Real}(v::AMat{T}, d::Dict) +function convertToAnyVector{T<:Number}(v::AMat{T}, d::Dict) if all3D(d) Any[Surface(v)] else @@ -44,7 +44,7 @@ convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::Dict) = Any[dts], n # list of things (maybe other vectors, functions, or something else) function convertToAnyVector(v::AVec, d::Dict) - if all(x -> typeof(x) <: Real, v) + if all(x -> typeof(x) <: Number, v) # all real numbers wrap the whole vector as one item Any[convert(Vector{Float64}, v)], nothing else @@ -57,38 +57,58 @@ end # -------------------------------------------------------------------- -# in computeXandY, we take in any of the possible items, convert into proper x/y vectors, then return. -# this is also where all the "set x to 1:length(y)" happens, and also where we assert on lengths. -computeX(x::@compat(Void), y) = 1:size(y,1) -computeX(x, y) = copy(x) -computeY(x, y::Function) = map(y, x) -computeY(x, y) = copy(y) -function computeXandY(x, y) - if x == nothing && isa(y, Function) - error("If you want to plot the function `$y`, you need to define the x values somehow!") - end - x, y = computeX(x,y), computeY(x,y) - # @assert length(x) == length(y) - x, y -end +# # in computeXandY, we take in any of the possible items, convert into proper x/y vectors, then return. +# # this is also where all the "set x to 1:length(y)" happens, and also where we assert on lengths. +# computeX(x::@compat(Void), y) = 1:size(y,1) +# computeX(x, y) = copy(x) +# computeY(x, y::Function) = map(y, x) +# computeY(x, y) = copy(y) +# function computeXandY(x, y) +# if x == nothing && isa(y, Function) +# error("If you want to plot the function `$y`, you need to define the x values somehow!") +# end +# x, y = computeX(x,y), computeY(x,y) +# # @assert length(x) == length(y) +# x, y +# end +compute_x(x::Void, y::Void, z) = 1:size(z,1) +compute_x(x::Void, y, z) = 1:size(y,1) +compute_x(x::Function, y, z) = map(x, y) +compute_x(x, y, z) = x + +compute_y(x::Void, y::Void, z) = 1:size(z,2) +compute_y(x::Void, y, z) = 1:size(x,1) +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) = Surface(z) + +compute_xyz(x, y, z) = compute_x(x,y,z), compute_y(x,y,z), compute_z(x,y,z) + +# not allowed +compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!") +compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!") +compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!") # -------------------------------------------------------------------- # create n=max(mx,my) series arguments. the shorter list is cycled through # note: everything should flow through this -function build_series_args(plt::AbstractPlot, x, y; kw...) - kwdict = Dict(kw) - xs, xmeta = convertToAnyVector(x, kwdict) - ys, ymeta = convertToAnyVector(y, kwdict) +function build_series_args(plt::AbstractPlot, kw::KW) + xs, xmeta = convertToAnyVector(pop!(kw, :x, nothing), kw) + ys, ymeta = convertToAnyVector(pop!(kw, :y, nothing), kw) + zs, zmeta = convertToAnyVector(pop!(kw, :z, nothing), kw) mx = length(xs) my = length(ys) + mz = length(zs) ret = Any[] - for i in 1:max(mx, my) + for i in 1:max(mx, my, mz) # try to set labels using ymeta - d = copy(kwdict) + d = copy(kw) if !haskey(d, :label) && ymeta != nothing if isa(ymeta, Symbol) d[:label] = string(ymeta) @@ -98,25 +118,27 @@ function build_series_args(plt::AbstractPlot, x, y; kw...) end # build the series arg dict - numUncounted = get(d, :numUncounted, 0) + numUncounted = pop!(d, :numUncounted, 0) n = plt.n + i + numUncounted dumpdict(d, "before getSeriesArgs") d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, i + numUncounted, convertSeriesIndex(plt, n), n) dumpdict(d, "after getSeriesArgs") - d[:x], d[:y] = computeXandY(xs[mod1(i,mx)], ys[mod1(i,my)]) + d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]) - lt = d[:linetype] - if isa(d[:y], Surface) - if lt in (:contour, :heatmap, :surface, :wireframe) - z = d[:y] - d[:y] = 1:size(z,2) - d[:z] = z - end - end + # # NOTE: this should be handled by the time it gets here + # lt = d[:linetype] + # if isa(d[:y], Surface) + # if lt in (:contour, :heatmap, :surface, :wireframe) + # z = d[:y] + # d[:y] = 1:size(z,2) + # d[:z] = z + # end + # end if haskey(d, :idxfilter) - d[:x] = d[:x][d[:idxfilter]] - d[:y] = d[:y][d[:idxfilter]] + idxfilter = pop!(d, :idxfilter) + d[:x] = d[:x][idxfilter] + d[:y] = d[:y][idxfilter] end # for linetype `line`, need to sort by x values @@ -137,9 +159,10 @@ function build_series_args(plt::AbstractPlot, x, y; kw...) end # cleanup those fields that were used only for generating kw args - for k in (:idxfilter, :numUncounted, :dataframe) - delete!(d, k) - end + delete!(d, :dataframe) + # for k in (:idxfilter, :numUncounted, :dataframe) + # delete!(d, k) + # end # add it to our series list push!(ret, d) @@ -148,90 +171,58 @@ function build_series_args(plt::AbstractPlot, x, y; kw...) ret, xmeta, ymeta end -# handle grouping -function build_series_args(plt::AbstractPlot, groupby::GroupBy, args...; kw...) - ret = Any[] - for (i,glab) in enumerate(groupby.groupLabels) - # TODO: don't automatically overwrite labels - kwlist, xmeta, ymeta = build_series_args(plt, args...; kw..., - idxfilter = groupby.groupIds[i], - label = string(glab), - numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i - append!(ret, kwlist) - end - ret, nothing, nothing # TODO: handle passing meta through + +# -------------------------------------------------------------------- +# 0 arguments +# -------------------------------------------------------------------- + +# # TODO: all methods should probably do this... check for (and pop!) x/y/z values if they exist +# +# function build_series_args(plt::AbstractPlot, d::KW) +# +# build_series_args(plt, d, pop!(d, :x, nothing), +# pop!(d, :y, nothing), +# pop!(d, :z, nothing); d...) +# end + + +# d = KW(kw) +# if !haskey(d, :y) +# # assume we just want to create an empty plot object which can be added to later +# return [], nothing, nothing +# # error("Called plot/subplot without args... must set y in the keyword args. Example: plot(; y=rand(10))") +# end +# +# if haskey(d, :x) +# return build_series_args(plt, d, d[:x], d[:y]) +# else +# return build_series_args(plt, d, d[:y]) +# end +# end + +# -------------------------------------------------------------------- +# 1 argument +# -------------------------------------------------------------------- + +# no special handling... assume x and z are nothing +function build_series_args(plt::AbstractPlot, d::KW, y) + build_series_args(plt, d, nothing, y, nothing) end -# pass it off to the x/y version -function build_series_args(plt::AbstractPlot, y; kw...) - build_series_args(plt, nothing, y; kw...) -end - -# 3d line or scatter -function build_series_args(plt::AbstractPlot, x::AVec, y::AVec, zvec::AVec; kw...) - d = Dict(kw) - if !(get(d, :linetype, :none) in _3dTypes) - d[:linetype] = :path3d - end - build_series_args(plt, x, y; z=zvec, d...) -end - -function build_series_args{T<:Real}(plt::AbstractPlot, z::AMat{T}; kw...) - d = Dict(kw) +# matrix... is it z or y? +function build_series_args{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T}) if all3D(d) - n,m = size(z) - build_series_args(plt, 1:n, 1:m, z; kw...) + n,m = size(mat) + build_series_args(plt, d, 1:n, 1:m, mat) else - build_series_args(plt, nothing, z; kw...) + build_series_args(plt, d, nothing, mat, nothing) end end -# contours or surfaces... function grid -function build_series_args(plt::AbstractPlot, x::AVec, y::AVec, zf::Function; kw...) - # only allow sorted x/y for now - # TODO: auto sort x/y/z properly - @assert x == sort(x) - @assert y == sort(y) - surface = Float64[zf(xi, yi) for xi in x, yi in y] - build_series_args(plt, x, y, surface; kw...) # passes it to the zmat version -end - -# contours or surfaces... matrix grid -function build_series_args{T<:Real}(plt::AbstractPlot, x::AVec, y::AVec, zmat::AMat{T}; kw...) - # only allow sorted x/y for now - # TODO: auto sort x/y/z properly - @assert x == sort(x) - @assert y == sort(y) - @assert size(zmat) == (length(x), length(y)) - # surf = Surface(convert(Matrix{Float64}, zmat)) - # surf = Array(Any,1,1) - # surf[1,1] = convert(Matrix{Float64}, zmat) - d = Dict(kw) - d[:z] = Surface(convert(Matrix{Float64}, zmat)) - if !(get(d, :linetype, :none) in (:contour, :heatmap, :surface, :wireframe)) - d[:linetype] = :contour - end - build_series_args(plt, x, y; d...) #, z = surf) -end - -# contours or surfaces... general x, y grid -function build_series_args{T<:Real}(plt::AbstractPlot, x::AMat{T}, y::AMat{T}, zmat::AMat{T}; kw...) - @assert size(zmat) == size(x) == size(y) - surf = Surface(convert(Matrix{Float64}, zmat)) - # surf = Array(Any,1,1) - # surf[1,1] = convert(Matrix{Float64}, zmat) - d = Dict(kw) - d[:z] = Surface(convert(Matrix{Float64}, zmat)) - if !(get(d, :linetype, :none) in (:contour, :heatmap, :surface, :wireframe)) - d[:linetype] = :contour - end - build_series_args(plt, Any[x], Any[y]; d...) #kw..., z = surf, linetype = :contour) -end - # plotting arbitrary shapes/polygons -function build_series_args(plt::AbstractPlot, shape::Shape; kw...) +function build_series_args(plt::AbstractPlot, d::KW, shape::Shape) x, y = unzip(shape.vertices) - build_series_args(plt, x, y; linetype = :shape, kw...) + build_series_args(plt, d, x, y; linetype = :shape) end function shape_coords(shapes::AVec{Shape}) @@ -246,106 +237,162 @@ function shape_coords(shapes::AVec{Shape}) x, y end -function build_series_args(plt::AbstractPlot, shapes::AVec{Shape}; kw...) +function build_series_args(plt::AbstractPlot, d::KW, shapes::AVec{Shape}) x, y = shape_coords(shapes) - build_series_args(plt, x, y; linetype = :shape, kw...) + build_series_args(plt, d, x, y; linetype = :shape) end -function build_series_args(plt::AbstractPlot, shapes::AMat{Shape}; kw...) +function build_series_args(plt::AbstractPlot, d::KW, shapes::AMat{Shape}) x, y = [], [] for j in 1:size(shapes, 2) tmpx, tmpy = shape_coords(vec(shapes[:,j])) push!(x, tmpx) push!(y, tmpy) end - build_series_args(plt, x, y; linetype = :shape, kw...) + build_series_args(plt, d, x, y; linetype = :shape) end -function build_series_args(plt::AbstractPlot, surf::Surface; kw...) - build_series_args(plt, 1:size(surf.surf,1), 1:size(surf.surf,2), convert(Matrix{Float64}, surf.surf); kw...) -end - -function build_series_args(plt::AbstractPlot, x::AVec, y::AVec, surf::Surface; kw...) - build_series_args(plt, x, y, convert(Matrix{Float64}, surf.surf); kw...) -end - -function build_series_args(plt::AbstractPlot, f::FuncOrFuncs; kw...) - build_series_args(plt, f, xmin(plt), xmax(plt); kw...) -end - -# list of functions -function build_series_args(plt::AbstractPlot, f::FuncOrFuncs, x; kw...) - @assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here - build_series_args(plt, x, f; kw...) -end - -# special handling... xmin/xmax with function(s) -function build_series_args(plt::AbstractPlot, f::FuncOrFuncs, xmin::Real, xmax::Real; kw...) - width = get(plt.plotargs, :size, (100,))[1] - x = collect(linspace(xmin, xmax, width)) # we don't need more than the width - build_series_args(plt, x, f; kw...) -end - -mapFuncOrFuncs(f::Function, u::AVec) = map(f, u) -mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs] - -# special handling... xmin/xmax with parametric function(s) -build_series_args{T<:Real}(plt::AbstractPlot, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}; kw...) = build_series_args(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...) -build_series_args{T<:Real}(plt::AbstractPlot, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs; kw...) = build_series_args(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...) -build_series_args(plt::AbstractPlot, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Real, umax::Real, numPoints::Int = 1000; kw...) = build_series_args(plt, fx, fy, linspace(umin, umax, numPoints); kw...) - -# special handling... 3D parametric function(s) -build_series_args{T<:Real}(plt::AbstractPlot, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}; kw...) = build_series_args(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u); kw...) -build_series_args{T<:Real}(plt::AbstractPlot, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs; kw...) = build_series_args(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u); kw...) -build_series_args(plt::AbstractPlot, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Real, umax::Real, numPoints::Int = 1000; kw...) = build_series_args(plt, fx, fy, fz, linspace(umin, umax, numPoints); kw...) - -# (x,y) tuples -function build_series_args{R1<:Real,R2<:Real}(plt::AbstractPlot, xy::AVec{Tuple{R1,R2}}; kw...) - build_series_args(plt, unzip(xy)...; kw...) -end -function build_series_args{R1<:Real,R2<:Real}(plt::AbstractPlot, xy::Tuple{R1,R2}; kw...) - build_series_args(plt, [xy[1]], [xy[2]]; kw...) -end - - - -# special handling... no args... 1 series -function build_series_args(plt::AbstractPlot; kw...) - d = Dict(kw) - if !haskey(d, :y) - # assume we just want to create an empty plot object which can be added to later - return [], nothing, nothing - # error("Called plot/subplot without args... must set y in the keyword args. Example: plot(; y=rand(10))") - end - - if haskey(d, :x) - return build_series_args(plt, d[:x], d[:y]; kw...) - else - return build_series_args(plt, d[:y]; kw...) - end +function build_series_args(plt::AbstractPlot, d::KW, f::FuncOrFuncs) + build_series_args(plt, d, f, xmin(plt), xmax(plt)) end # -------------------------------------------------------------------- +# 2 arguments +# -------------------------------------------------------------------- + +function build_series_args(plt::AbstractPlot, d::KW, x, y) + build_series_args(plt, d, x, y, nothing) +end + +# list of functions +function build_series_args(plt::AbstractPlot, d::KW, f::FuncOrFuncs, x) + @assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here + build_series_args(plt, d, x, f) +end + +# -------------------------------------------------------------------- +# 3 arguments +# -------------------------------------------------------------------- + +# 3d line or scatter +function build_series_args(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zvec::AVec) + d = KW(kw) + if !(get(d, :linetype, :none) in _3dTypes) + d[:linetype] = :path3d + end + build_series_args(plt, d, x, y; z=zvec, d...) +end + +# contours or surfaces... function grid +function build_series_args(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zf::Function) + # only allow sorted x/y for now + # TODO: auto sort x/y/z properly + @assert x == sort(x) + @assert y == sort(y) + surface = Float64[zf(xi, yi) for xi in x, yi in y] + build_series_args(plt, d, x, y, surface) # passes it to the zmat version +end + +# contours or surfaces... matrix grid +function build_series_args{T<:Number}(plt::AbstractPlot, x::AVec, y::AVec, zmat::AMat{T}) + # only allow sorted x/y for now + # TODO: auto sort x/y/z properly + @assert x == sort(x) + @assert y == sort(y) + @assert size(zmat) == (length(x), length(y)) + d = KW(kw) + d[:z] = Surface(convert(Matrix{Float64}, zmat)) + if !(get(d, :linetype, :none) in (:contour, :heatmap, :surface, :wireframe)) + d[:linetype] = :contour + end + build_series_args(plt, d, x, y; d...) #, z = surf) +end + +# contours or surfaces... general x, y grid +function build_series_args{T<:Number}(plt::AbstractPlot, x::AMat{T}, y::AMat{T}, zmat::AMat{T}) + @assert size(zmat) == size(x) == size(y) + surf = Surface(convert(Matrix{Float64}, zmat)) + d = KW(kw) + d[:z] = Surface(convert(Matrix{Float64}, zmat)) + if !(get(d, :linetype, :none) in (:contour, :heatmap, :surface, :wireframe)) + d[:linetype] = :contour + end + build_series_args(plt, d, Any[x], Any[y]; d...) #kw..., z = surf, linetype = :contour) +end + + +# -------------------------------------------------------------------- +# Parametric functions +# -------------------------------------------------------------------- + +# special handling... xmin/xmax with function(s) +function build_series_args(plt::AbstractPlot, d::KW, f::FuncOrFuncs, xmin::Number, xmax::Number) + width = get(plt.plotargs, :size, (100,))[1] + x = collect(linspace(xmin, xmax, width)) # we don't need more than the width + build_series_args(plt, d, x, f) +end + + +# special handling... xmin/xmax with parametric function(s) +build_series_args{T<:Number}(plt::AbstractPlot, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = build_series_args(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)) +build_series_args{T<:Number}(plt::AbstractPlot, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = build_series_args(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)) +build_series_args(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = build_series_args(plt, d, fx, fy, linspace(umin, umax, numPoints)) + +# special handling... 3D parametric function(s) +build_series_args{T<:Number}(plt::AbstractPlot, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) = build_series_args(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)) +build_series_args{T<:Number}(plt::AbstractPlot, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = build_series_args(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)) +build_series_args(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = build_series_args(plt, d, fx, fy, fz, linspace(umin, umax, numPoints)) + + +# -------------------------------------------------------------------- +# Lists of tuples and FixedSizeArrays +# -------------------------------------------------------------------- + +# (x,y) tuples +function build_series_args{R1<:Number,R2<:Number}(plt::AbstractPlot, xy::AVec{Tuple{R1,R2}}) + build_series_args(plt, d, unzip(xy)...) +end +function build_series_args{R1<:Number,R2<:Number}(plt::AbstractPlot, xy::Tuple{R1,R2}) + build_series_args(plt, d, [xy[1]], [xy[2]]) +end + unzip{T}(x::AVec{FixedSizeArrays.Vec{2,T}}) = T[xi[1] for xi in x], T[xi[2] for xi in x] unzip{T}(x::FixedSizeArrays.Vec{2,T}) = T[x[1]], T[x[2]] -function build_series_args{T<:Real}(plt::AbstractPlot, xy::AVec{FixedSizeArrays.Vec{2,T}}; kw...) - build_series_args(plt, unzip(xy)...; kw...) +function build_series_args{T<:Number}(plt::AbstractPlot, xy::AVec{FixedSizeArrays.Vec{2,T}}) + build_series_args(plt, d, unzip(xy)...) end -function build_series_args{T<:Real}(plt::AbstractPlot, xy::FixedSizeArrays.Vec{2,T}; kw...) - build_series_args(plt, [xy[1]], [xy[2]]; kw...) +function build_series_args{T<:Number}(plt::AbstractPlot, xy::FixedSizeArrays.Vec{2,T}) + build_series_args(plt, d, [xy[1]], [xy[2]]) end +# -------------------------------------------------------------------- +# handle grouping # -------------------------------------------------------------------- +function build_series_args(plt::AbstractPlot, d::KW, groupby::GroupBy, args...) + ret = Any[] + for (i,glab) in enumerate(groupby.groupLabels) + # TODO: don't automatically overwrite labels + kwlist, xmeta, ymeta = build_series_args(plt, d, args..., + idxfilter = groupby.groupIds[i], + label = string(glab), + numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i + append!(ret, kwlist) + end + ret, nothing, nothing # TODO: handle passing meta through +end + +# -------------------------------------------------------------------- # For DataFrame support. Imports DataFrames and defines the necessary methods which support them. +# -------------------------------------------------------------------- function setup_dataframes() @require DataFrames begin - function build_series_args(plt::AbstractPlot, df::DataFrames.AbstractDataFrame, args...; kw...) - build_series_args(plt, args...; kw..., dataframe = df) + function build_series_args(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...) + build_series_args(plt, d, args..., dataframe = df) end # expecting the column name of a dataframe that was passed in... anything else should error diff --git a/src/subplot.jl b/src/subplot.jl index d6ca237e..bbe4e244 100644 --- a/src/subplot.jl +++ b/src/subplot.jl @@ -39,7 +39,7 @@ Create a series of plots: """ function subplot(args...; kw...) validateSubplotSupported() - d = Dict(kw) + d = KW(kw) preprocessArgs!(d) # for plotting recipes, swap out the args and update the parameter dictionary @@ -80,7 +80,7 @@ end # grid layout function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...) - d = Dict(kw) + d = KW(kw) layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1)) subplot(vcat(plt1, plts...), layout, d) end @@ -88,7 +88,7 @@ end # explicit layout function subplot{P,I<:Integer}(pltsPerRow::AVec{I}, plt1::Plot{P}, plts::Plot{P}...; kw...) layout = subplotlayout(pltsPerRow) - subplot(vcat(plt1, plts...), layout, Dict(kw)) + subplot(vcat(plt1, plts...), layout, KW(kw)) end # this will be called internally @@ -189,7 +189,7 @@ end function subplot!(subplt::Subplot, args...; kw...) # validateSubplotSupported() - d = Dict(kw) + d = KW(kw) args = _preprocess_subplot(subplt, d, args) # create the underlying object (each backend will do this differently) @@ -251,7 +251,7 @@ end function _add_series_subplot(plt::Plot, args...; kw...) - d = Dict(kw) + d = KW(kw) setTicksFromStringVector(d, d, :x, :xticks) setTicksFromStringVector(d, d, :y, :yticks) diff --git a/src/types.jl b/src/types.jl index b50c0fe3..8597027b 100644 --- a/src/types.jl +++ b/src/types.jl @@ -7,6 +7,7 @@ immutable PlotsDisplay <: Display end abstract AbstractBackend abstract AbstractPlot{T<:AbstractBackend} +typealias KW Dict{Symbol,Any} # ----------------------------------------------------------- # Plot # ----------------------------------------------------------- diff --git a/src/utils.jl b/src/utils.jl index 30ea6f99..b75f93cc 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -19,7 +19,7 @@ A hacky replacement for a histogram when the backend doesn't support histograms Convert it into a bar chart with the appropriate x/y values. """ function histogramHack(; kw...) - d = Dict(kw) + d = KW(kw) # we assume that the y kwarg is set with the data to be binned, and nbins is also defined edges, midpoints, buckets, counts = binData(d[:y], d[:nbins]) @@ -35,7 +35,7 @@ A hacky replacement for a bar graph when the backend doesn't support bars direct Convert it into a line chart with fillrange set. """ function barHack(; kw...) - d = Dict(kw) + d = KW(kw) midpoints = d[:x] heights = d[:y] fillrange = d[:fillrange] == nothing ? 0.0 : d[:fillrange] @@ -75,7 +75,7 @@ A hacky replacement for a sticks graph when the backend doesn't support sticks d Convert it into a line chart that traces the sticks, and a scatter that sets markers at the points. """ function sticksHack(; kw...) - dLine = Dict(kw) + dLine = KW(kw) dScatter = copy(dLine) # these are the line vertices @@ -130,6 +130,8 @@ makevec{T}(v::T) = T[v] maketuple(x::Real) = (x,x) maketuple{T,S}(x::@compat(Tuple{T,S})) = x +mapFuncOrFuncs(f::Function, u::AVec) = map(f, u) +mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs] unzip{T,S}(v::AVec{@compat(Tuple{T,S})}) = [vi[1] for vi in v], [vi[2] for vi in v] @@ -260,7 +262,7 @@ end function with(f::Function, args...; kw...) # dict to store old and new keyword args for anything that changes - newdefs = Dict(kw) + newdefs = KW(kw) olddefs = Dict() for k in keys(newdefs) olddefs[k] = default(k)