diff --git a/src/args.jl b/src/args.jl index f13d4398..07991160 100644 --- a/src/args.jl +++ b/src/args.jl @@ -584,6 +584,10 @@ function preprocessArgs!(d::KW) processLineArg(d, arg) end + if haskey(d, :linetype) && haskey(_typeAliases, d[:linetype]) + d[:linetype] = _typeAliases[d[:linetype]] + end + # handle marker args... default to ellipse if shape not set anymarker = false for arg in wraptuple(get(d, :marker, ())) @@ -689,6 +693,22 @@ function extractGroupArgs{T, V<:AVec{Int}}(idxmap::Dict{T,V}, args...) GroupBy(groupLabels, groupIds) end +filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter] +filter_data(v, idxfilter) = v + +function filter_data!(d::KW, idxfilter) + for s in (:x, :y, :z) + d[s] = filter_data(get(d, s, nothing), idxfilter) + end +end + +function _filter_input_data!(d::KW) + idxfilter = pop!(d, :idxfilter, nothing) + if idxfilter != nothing + filter_data!(d, idxfilter) + end +end + # ----------------------------------------------------------------------------- diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 661f649a..8e68e678 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -338,7 +338,6 @@ end function fix_xy_lengths!(plt::Plot{PyPlotBackend}, d::KW) x, y = d[:x], d[:y] - @show x, y nx, ny = length(x), length(y) if !isa(get(d, :z, nothing), Surface) && nx != ny if nx < ny diff --git a/src/plot.jl b/src/plot.jl index 92014cd4..c6bf3030 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -93,9 +93,20 @@ function _plot!(plt::Plot, d::KW, args...) # we simply add the GroupBy object to the front of the args list to allow # the recipe to be applied if haskey(d, :group) - args = vcat(extractGroupArgs(d[:group], args...), args) + args = tuple(extractGroupArgs(d[:group], args...), args...) end + # initialize the annotations list with what was passed in + # TODO: there must be cleaner way to handle this! + anns = annotations(get(d, :annotation, NTuple{3}[])) + if typeof(anns) <: AVec{PlotText} + anns = NTuple{3}[] + else + delete!(d, :annotation) + end + # anns = annotations(pop!(d, :annotation, []) + + # for plotting recipes, swap out the args and update the parameter dictionary # we are keeping a queue of series that still need to be processed. # each pass through the loop, we pop one off and apply the recipe. @@ -109,8 +120,9 @@ function _plot!(plt::Plot, d::KW, args...) series_list = RecipesBase.apply_recipe(next_series.d, next_series.args...) for series in series_list if isempty(series.args) - # apply markershape_to_add and then warn if there's anything left unsupported + # finish processing and add to the kw_list _add_markershape(series.d) + _filter_input_data!(series.d) warnOnUnsupportedArgs(plt.backend, series.d) warnOnUnsupportedScales(plt.backend, series.d) push!(kw_list, series.d) @@ -130,22 +142,27 @@ function _plot!(plt::Plot, d::KW, args...) # nothing # end + + + + # now include any annotations which were added during recipes + # anns = NTuple{3,Any}[] + for kw in kw_list + append!(anns, annotations(pop!(kw, :annotation, []))) + end + # @show anns + + # TODO: why do i need to check for the subplot key? # merge plot args if !haskey(d, :subplot) # merge the plot args from the recipes, then update the plot colors for kw in vcat(kw_list, d) + # @show kw + # append!(anns, annotations(pop!(kw, :annotation, []))) _add_plotargs!(plt, kw) end - # for k in keys(_plotDefaults) - # for kw in kw_list - # if haskey(kw, k) - # plt.plotargs[k] = pop!(kw, k) - # end - # end - # end - # merge!(plt.plotargs, d) handlePlotColors(plt.backend, plt.plotargs) end @@ -176,18 +193,18 @@ function _plot!(plt::Plot, d::KW, args...) # merge!(plt.plotargs, plotarg_overrides) # end - dumpdict(kw, "before add defaults", true) + # dumpdict(kw, "before add defaults", true) _add_defaults!(kw, plt, i) - dumpdict(kw, "after add defaults", true) + # dumpdict(kw, "after add defaults", true) # getSeriesArgs(plt.backend, getplotargs(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n) + # TODO: apply idxfilter to x/y/z + _replace_linewidth(kw) _add_series(plt.backend, plt, kw) end - - # _add_annotations(plt, d) # TODO - + _add_annotations(plt, anns) # add title, axis labels, ticks, etc if !haskey(d, :subplot) @@ -220,14 +237,6 @@ end # end # end -filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter] -filter_data(v, idxfilter) = v - -function filter_data!(d::KW, idxfilter) - for s in (:x, :y, :z) - d[s] = filter_data(get(d, s, nothing), idxfilter) - end -end function _replace_linewidth(d::KW) # get a good default linewidth... 0 for surface and heatmaps @@ -372,6 +381,17 @@ annotations(v::AVec) = map(PlotText, v) annotations(anns) = error("Expecting a tuple (or vector of tuples) for annotations: ", "(x, y, annotation)\n got: $(typeof(anns))") +function annotations(plt::Plot, anns) + anns = annotations(anns) + # if we just have a list of PlotText objects, then create (x,y,text) tuples + if typeof(anns) <: AVec{PlotText} + x, y = plt[plt.n] + anns = Tuple{Float64,Float64,PlotText}[(x[i], y[i], t) for (i,t) in enumerate(anns)] + end + anns +end + + function _add_annotations(plt::Plot, d::KW) anns = annotations(get(d, :annotation, nothing)) if !isempty(anns) diff --git a/src/recipes.jl b/src/recipes.jl index cd290435..b3c8cc13 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -44,6 +44,11 @@ if is_installed("DataFrames") end end + function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...) + extractGroupArgs(collect(df[group])) + end + + function handle_group(df::DataFrames.AbstractDataFrame, d::KW) if haskey(d, :group) g = d[:group] diff --git a/src/series_new.jl b/src/series_new.jl index 9982fe21..bb8dbae8 100644 --- a/src/series_new.jl +++ b/src/series_new.jl @@ -25,9 +25,9 @@ function _add_defaults!(d::KW, plt::Plot, commandIndex::Int) # end # end - if haskey(_typeAliases, d[:linetype]) - d[:linetype] = _typeAliases[d[:linetype]] - end + # if haskey(_typeAliases, d[:linetype]) + # d[:linetype] = _typeAliases[d[:linetype]] + # end aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex) aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex) @@ -87,10 +87,12 @@ end # instead of process_inputs: +# ensure we dispatch to the slicer +immutable SliceIt end + # the catch-all recipes -# @recipe function f(x, y, z) -function slice_and_dice(d, x, y, z) - @show "HERE", typeof((x,y,z)) +@recipe function f(::Type{SliceIt}, x, y, z) + # @show "HERE", typeof((x,y,z)) xs, _ = convertToAnyVector(x, d) ys, _ = convertToAnyVector(y, d) zs, _ = convertToAnyVector(z, d) @@ -111,22 +113,23 @@ function slice_and_dice(d, x, y, z) # add a new series di = copy(d) xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)] - @show i, typeof((xi, yi, zi)) + # @show i, typeof((xi, yi, zi)) di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi) # handle fillrange fr = fillranges[mod1(i,mf)] - d[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr + di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr - @show i, di[:x], di[:y], di[:z] + # @show i, di[:x], di[:y], di[:z] push!(series_list, RecipeData(di, ())) end nothing # don't add a series for the main block end -@recipe f(x, y, z) = slice_and_dice(d, x, y, z) -@recipe f(x, y) = x, y, nothing -@recipe f(y) = nothing, y, nothing +# pass these through to the slicer +@recipe f(x, y, z) = SliceIt, x, y, z +@recipe f(x, y) = SliceIt, x, y, nothing +@recipe f(y) = SliceIt, nothing, y, nothing # # -------------------------------------------------------------------- @@ -312,7 +315,7 @@ end elseif !(lt in _3dTypes) d[:linetype] = :path3d end - slice_and_dice(d, x, y, z) + SliceIt, x, y, z end # @@ -328,7 +331,7 @@ end @recipe function f{X,Y}(x::AVec{X}, y::AVec{Y}, zf::Function) x = TX <: Number ? sort(x) : x y = TY <: Number ? sort(y) : y - slice_and_dice(d, x, y, Surface(zf, x, y)) # TODO: replace with SurfaceFunction when supported + SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported end # @@ -353,7 +356,7 @@ end if !like_surface(get(d, :linetype, :none)) d[:linetype] = :contour end - slice_and_dice(d, x, y, Surface{Matrix{Z}}(z)) + SliceIt, x, y, Surface{Matrix{Z}}(z) end # @@ -390,9 +393,9 @@ end # process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)) # process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, linspace(umin, umax, numPoints)) -@recipe f(f::FuncOrFuncs, xmin::Number, xmax::Number) = linspace(xmin, xmax, 100), f -@recipe f{T<:Number}(fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u) -@recipe f{T<:Number}(u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u) +@recipe f(f::FuncOrFuncs, xmin::Number, xmax::Number) = linspace(xmin, xmax, 100), f +@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u) +# @recipe f(u::AVec, fx::FuncOrFuncs, fy::FuncOrFuncs) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u) @recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n) # @@ -401,12 +404,12 @@ end # process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)) # process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, fz, linspace(umin, umax, numPoints)) -@recipe function f{T<:Number}(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) - mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u) -end -@recipe function f{T<:Number}(u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) +@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec) mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u) end +# @recipe function f(u::AVec, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) +# mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u) +# end @recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPointsn = 200) fx, fy, fz, linspace(umin, umax, numPoints) end @@ -434,7 +437,7 @@ end # end @recipe f{R1<:Number,R2<:Number}(xy::AVec{Tuple{R1,R2}}) = unzip(xy) -@recipe f{R1<:Number,R2<:Number}(xy::Tuple{R1,R2}) = [xy[1]], [xy[2]] +@recipe f{R1<:Number,R2<:Number}(xy::Tuple{R1,R2}) = [xy[1]], [xy[2]] # # # (x,y,z) tuples @@ -446,7 +449,7 @@ end # end @recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::AVec{Tuple{R1,R2,R3}}) = unzip(xyz) -@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::Tuple{R1,R2,R3}) = [xyz[1]], [xyz[2]], [xyz[3]] +@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::Tuple{R1,R2,R3}) = [xyz[1]], [xyz[2]], [xyz[3]] # # # 2D FixedSizeArrays @@ -458,7 +461,7 @@ end # end @recipe f{T<:Number}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = unzip(xy) -@recipe f{T<:Number}(xy::FixedSizeArrays.Vec{2,T}) = [xy[1]], [xy[2]] +@recipe f{T<:Number}(xy::FixedSizeArrays.Vec{2,T}) = [xy[1]], [xy[2]] # # # 3D FixedSizeArrays @@ -470,7 +473,7 @@ end # end @recipe f{T<:Number}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = unzip(xyz) -@recipe f{T<:Number}(xyz::FixedSizeArrays.Vec{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]] +@recipe f{T<:Number}(xyz::FixedSizeArrays.Vec{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]] # # # -------------------------------------------------------------------- @@ -495,8 +498,8 @@ end # create a new series, with the label of the group, and an idxfilter (to be applied in slice_and_dice) # TODO: use @series instead di = copy(d) - label --> string(glab) - idxfilter --> groupby.groupIds[i] + get!(di, :label, string(glab)) + get!(di, :idxfilter, groupby.groupIds[i]) push!(series_list, RecipeData(di, args)) end nothing