diff --git a/src/Plots.jl b/src/Plots.jl index 167353df..93e313f2 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -82,7 +82,8 @@ include("plot.jl") include("subplot.jl") -# const LINE_TYPES = (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hist, :bar) +# --------------------------------------------------------- + scatter(args...; kw...) = plot(args...; kw..., linetype = :none, marker = :hexagon) scatter!(args...; kw...) = plot!(args...; kw..., linetype = :none, marker = :hexagon) bar(args...; kw...) = plot(args...; kw..., linetype = :bar) @@ -95,13 +96,6 @@ heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap) # --------------------------------------------------------- -# # TODO: how do we handle NA values in dataframes? -# function plot!(plt::Plot, df::DataFrame; kw...) # one line per DataFrame column, labels == names(df) -# end - -# function plot!(plt::Plot, df::DataFrame, columns; kw...) # one line per column, but on a subset of column names -# end - savepng(args...; kw...) = savepng(currentPlot(), args...; kw...) savepng(plt::Plot, args...; kw...) = savepng(plt.plotter, plt, args...; kw...) diff --git a/src/plot.jl b/src/plot.jl index 3950d5c1..322f748a 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -241,6 +241,8 @@ function createKWargsList(plt::PlottingObject, n::Integer; kw...) ret end +# TODO: handle DataFrames (might have NAs!) + # ------------------------- # most calls should flow through here now... we create a Dict with the keyword args for each series, and plot them diff --git a/src/subplot.jl b/src/subplot.jl index 7cf50848..43ac4138 100644 --- a/src/subplot.jl +++ b/src/subplot.jl @@ -1,31 +1,51 @@ -abstract SubPlotLayout - -type AutoGridLayout - maxplts::Int - maxrows::Int - maxcols::Int +type SubPlotLayout + numplts::Int + rowcounts::AbstractVector{Int} end -# create a grid structure that optimally fits in numplts, optionally fixing the numrows/numcols -function AutoGridLayout(numplts::Int; numrows::Int = -1, numcols::Int = -1) +# create a layout directly +SubPlotLayout(rowcounts::AbstractVector{Int}) = SubPlotLayout(sum(rowcounts), rowcounts) + +# create a layout given counts... numrows/numcols == -1 implies we figure out a good number automatically +function SubPlotLayout(numplts::Int, numrows::Int, numcols::Int) + + # figure out how many rows/columns we need + if numrows == -1 + if numcols == -1 + numrows = round(Int, sqrt(numplts)) + numcols = ceil(Int, numplts / numrows) + else + numrows = ceil(Int, numplts / numcols) + end + else + numcols = ceil(Int, numplts / numrows) + end + + # create the rowcounts vector + i = 0 + rowcounts = Int[] + for r in 1:numrows + cnt = min(numcols, numplts - i) + push!(rowcounts, cnt) + i += cnt + end + + SubPlotLayout(numplts, rowcounts) end +Base.length(layout::SubPlotLayout) = layout.numplts + + # ------------------------------------------------------------ -doc""" -y = rand(100,3) -subplot(y; layout=(2,2), kw...) # creates 3 lines going into 3 separate plots, laid out on a 2x2 grid (last row is filled with plot #3) -subplot(y; layout=(1,3), kw...) # again 3 plots, all in the same row -subplot(y; layout=[1,[2,3]]) # pass a nested Array to fully specify the layout. here the first plot will take up the first row, - # and the others will share the second row -""" type SubPlot <: PlottingObject - plts::Vector{Plot} # the underlying object + o # the underlying object + plts::Vector{Plot} # the individual plots plotter::PlottingPackage - p::Int # number of plots - n::Int # number of series + p::Int # number of plots + n::Int # number of series layout::SubPlotLayout end @@ -38,18 +58,37 @@ getplot(subplt::SubPlot, i::Int) = subplt.plts[mod1(i, subplt.p)] # ------------------------------------------------------------ +doc""" +y = rand(100,3) +subplot(y; n = 3) # create an automatic grid, and let it figure out the numrows/numcols... will put plots 1 and 2 on the first row, and plot 3 by itself on the 2nd row +subplot(y; n = 3, numrows = 1) # create an automatic grid, but fix the number of rows to 1 (so there are n columns) +subplot(y; n = 3, numcols = 1) # create an automatic grid, but fix the number of columns to 1 (so there are n rows) +subplot(y; layout = [1, 2]) # explicit layout by row... plot #1 goes by itself in the first row, plots 2 and 3 split the 2nd row (note the n kw is unnecessary) +""" function subplot(args...; kw...) - subplt = SubPlot(Plot[], plotter(), 0, 0) - d = Dict(kw) - # figure out the layouts - if !haskey(d, :layout) || d[:layout] == :auto - # do an automatic grid layout - + # figure out the layout + if !haskey(d, :layout) + layout = FixedLayout(d[:layout]) else - layout = d[:layout] + if !haskey(d, :n) + error("You must specify either layout or n when creating a subplot: ", d) + end + layout = AutoGridLayout(d[:n], get(d, :numrows, -1), get(d, :numcols, -1)) end + + # initialize the individual plots + pkg = plotter() + kw0 = getPlotKeywordArgs(kw, 1, 0) + plts = Plot[plot(pkg; kw0..., show=false) for i in 1:length(layout)] + + # create the underlying object (each backend will do this differently) + o = buildSubplotObject(plts, pkg, layout) + + # create the object and do the plotting + subplt = SubPlot(o, plts, pkg, length(layout), 0, layout) + subplot!(subplt, args...; kw...) end