From 7a5197df63d7b4b2f2902fed6fdae75be7521a17 Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Thu, 10 Mar 2016 22:43:02 -0500 Subject: [PATCH] started work on new FlexLayout, some reorg/cleaning --- src/Plots.jl | 4 +- src/layouts.jl | 120 +++++++++++++++++++++++++++++++++++++++++++++++++ src/subplot.jl | 85 ++--------------------------------- src/types.jl | 75 +++++++++++++++++++++---------- 4 files changed, 177 insertions(+), 107 deletions(-) create mode 100644 src/layouts.jl diff --git a/src/Plots.jl b/src/Plots.jl index 352d8aae..e107808f 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -14,6 +14,7 @@ export Subplot, SubplotLayout, GridLayout, + RowsLayout, FlexLayout, AVec, AMat, @@ -26,7 +27,7 @@ export current, default, with, - + scatter, scatter!, bar, @@ -138,6 +139,7 @@ include("plotter2.jl") include("args.jl") include("plot.jl") include("subplot.jl") +include("layouts.jl") include("recipes.jl") include("animation.jl") include("output.jl") diff --git a/src/layouts.jl b/src/layouts.jl new file mode 100644 index 00000000..f8952b86 --- /dev/null +++ b/src/layouts.jl @@ -0,0 +1,120 @@ + +# ----------------------------------------------------------- + +# we're taking in a nested structure of some kind... parse it out and build a FlexLayout +function subplotlayout(mat::AbstractVecOrMat; widths = nothing, heights = nothing) + n = 0 + nr, nc = size(mat) + grid = Array(IntOrFlex, nr, nc) + for i=1:nr, j=1:nc + v = mat[i,j] + + if isa(v, Integer) + grid[i,j] = Int(v) + n += 1 + + elseif isa(v, Tuple) + warn("need to handle tuples somehow... (idx, sizepct)") + grid[i,j] = nothing + + elseif v == nothing + grid[i,j] = nothing + + elseif isa(v, AbstractVecOrMat) + grid[i,j] = layout(v) + n += grid[i,j].n + + else + error("How do we process? $v") + end + end + + if widths == nothing + widths = ones(nc) ./ nc + end + if heights == nothing + heights = ones(nr) ./ nr + end + + FlexLayout(n, grid, widths, heights) +end + + +function subplotlayout(sz::Tuple{Int,Int}) + GridLayout(sz...) +end + +function subplotlayout(rowcounts::AVec{Int}) + RowsLayout(sum(rowcounts), rowcounts) +end + +function subplotlayout(numplts::Int, nr::Int, nc::Int) + + # figure out how many rows/columns we need + if nr == -1 + if nc == -1 + nr = round(Int, sqrt(numplts)) + nc = ceil(Int, numplts / nr) + else + nr = ceil(Int, numplts / nc) + end + else + nc = ceil(Int, numplts / nr) + end + + # if it's a perfect rectangle, just create a grid + if numplts == nr * nc + return GridLayout(nr, nc) + end + + # create the rowcounts vector + i = 0 + rowcounts = Int[] + for r in 1:nr + cnt = min(nc, numplts - i) + push!(rowcounts, cnt) + i += cnt + end + + RowsLayout(numplts, rowcounts) +end + + + +Base.length(layout::RowsLayout) = layout.numplts +Base.start(layout::RowsLayout) = 1 +Base.done(layout::RowsLayout, state) = state > length(layout) +function Base.next(layout::RowsLayout, state) + r = 1 + c = 0 + for i = 1:state + c += 1 + if c > layout.rowcounts[r] + r += 1 + c = 1 + end + end + (r,c), state + 1 +end + +nrows(layout::RowsLayout) = length(layout.rowcounts) +ncols(layout::RowsLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row]) + +# get the plot index given row and column +Base.getindex(layout::RowsLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c + +Base.length(layout::GridLayout) = layout.nr * layout.nc +Base.start(layout::GridLayout) = 1 +Base.done(layout::GridLayout, state) = state > length(layout) +function Base.next(layout::GridLayout, state) + r = div(state-1, layout.nc) + 1 + c = mod1(state, layout.nc) + (r,c), state + 1 +end + +nrows(layout::GridLayout) = layout.nr +ncols(layout::GridLayout) = layout.nc +ncols(layout::GridLayout, row::Int) = layout.nc + +# get the plot index given row and column +Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c diff --git a/src/subplot.jl b/src/subplot.jl index 07b56500..9b56bfbf 100644 --- a/src/subplot.jl +++ b/src/subplot.jl @@ -1,83 +1,4 @@ -function subplotlayout(sz::@compat(Tuple{Int,Int})) - GridLayout(sz...) -end - -function subplotlayout(rowcounts::AVec{Int}) - FlexLayout(sum(rowcounts), rowcounts) -end - -function subplotlayout(numplts::Int, nr::Int, nc::Int) - - # figure out how many rows/columns we need - if nr == -1 - if nc == -1 - nr = round(Int, sqrt(numplts)) - nc = ceil(Int, numplts / nr) - else - nr = ceil(Int, numplts / nc) - end - else - nc = ceil(Int, numplts / nr) - end - - # if it's a perfect rectangle, just create a grid - if numplts == nr * nc - return GridLayout(nr, nc) - end - - # create the rowcounts vector - i = 0 - rowcounts = Int[] - for r in 1:nr - cnt = min(nc, numplts - i) - push!(rowcounts, cnt) - i += cnt - end - - FlexLayout(numplts, rowcounts) -end - - - -Base.length(layout::FlexLayout) = layout.numplts -Base.start(layout::FlexLayout) = 1 -Base.done(layout::FlexLayout, state) = state > length(layout) -function Base.next(layout::FlexLayout, state) - r = 1 - c = 0 - for i = 1:state - c += 1 - if c > layout.rowcounts[r] - r += 1 - c = 1 - end - end - (r,c), state + 1 -end - -nrows(layout::FlexLayout) = length(layout.rowcounts) -ncols(layout::FlexLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row]) - -# get the plot index given row and column -Base.getindex(layout::FlexLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c - -Base.length(layout::GridLayout) = layout.nr * layout.nc -Base.start(layout::GridLayout) = 1 -Base.done(layout::GridLayout, state) = state > length(layout) -function Base.next(layout::GridLayout, state) - r = div(state-1, layout.nc) + 1 - c = mod1(state, layout.nc) - (r,c), state + 1 -end - -nrows(layout::GridLayout) = layout.nr -ncols(layout::GridLayout) = layout.nc -ncols(layout::GridLayout, row::Int) = layout.nc - -# get the plot index given row and column -Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c - Base.getindex(subplt::Subplot, args...) = subplt.plts[subplt.layout[args...]] # handle "linking" the subplot axes together @@ -308,7 +229,7 @@ function subplot!(subplt::Subplot, args...; kw...) # create the underlying object (each backend will do this differently) # note: we call it once before doing the individual plots, and once after - # this is because some backends need to set up the subplots and then plot, + # this is because some backends need to set up the subplots and then plot, # and others need to do it the other way around if !subplt.initialized subplt.initialized = _create_subplot(subplt, true) @@ -343,7 +264,7 @@ function subplot!(subplt::Subplot, args...; kw...) end dumpdict(di, "subplot! kwList $i") dumpdict(plt.plotargs, "plt.plotargs before plotting") - + _add_series_subplot(plt; di...) end @@ -366,7 +287,7 @@ function _add_series_subplot(plt::Plot, args...; kw...) setTicksFromStringVector(d, d, :y, :yticks) _add_series(plt.backend, plt; d...) - + _add_annotations(plt, d) warnOnUnsupportedScales(plt.backend, d) end diff --git a/src/types.jl b/src/types.jl index 68cf57bb..493e049f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -3,47 +3,74 @@ typealias AVec AbstractVector typealias AMat AbstractMatrix immutable PlotsDisplay <: Display end - + abstract PlottingPackage abstract PlottingObject{T<:PlottingPackage} -type Plot{T<:PlottingPackage} <: PlottingObject{T} - o # the underlying object - backend::T - n::Int # number of series +# ----------------------------------------------------------- +# Plot +# ----------------------------------------------------------- - # store these just in case - plotargs::Dict - seriesargs::Vector{Dict} # args for each series +type Plot{T<:PlottingPackage} <: PlottingObject{T} + o # the backend's plot object + backend::T # the backend type + n::Int # number of series + plotargs::Dict # arguments for the whole plot + seriesargs::Vector{Dict} # arguments for each series end +# ----------------------------------------------------------- +# Layouts +# ----------------------------------------------------------- abstract SubplotLayout +# ----------------------------------------------------------- + +"Simple grid, indices are row-major." immutable GridLayout <: SubplotLayout - nr::Int - nc::Int + nr::Int + nc::Int end +# ----------------------------------------------------------- + +"Number of plots per row" +immutable RowsLayout <: SubplotLayout + numplts::Int + rowcounts::AbstractVector{Int} +end + +# ----------------------------------------------------------- + +"Flexible, nested layout with optional size percentages." immutable FlexLayout <: SubplotLayout - numplts::Int - rowcounts::AbstractVector{Int} + n::Int + grid::Matrix # Nested layouts. Each position + # can be a plot index or another FlexLayout + widths::Vector{Float64} + heights::Vector{Float64} end +typealias IntOrFlex Union{Int,FlexLayout} + +# ----------------------------------------------------------- +# Subplot +# ----------------------------------------------------------- type Subplot{T<:PlottingPackage, L<:SubplotLayout} <: PlottingObject{T} - o # the underlying object - plts::Vector{Plot{T}} # the individual plots - backend::T - p::Int # number of plots - n::Int # number of series - layout::L - # plotargs::Vector{Dict} - plotargs::Dict - initialized::Bool - linkx::Bool - linky::Bool - linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y + o # the underlying object + plts::Vector{Plot{T}} # the individual plots + backend::T + p::Int # number of plots + n::Int # number of series + layout::L + # plotargs::Vector{Dict} + plotargs::Dict + initialized::Bool + linkx::Bool + linky::Bool + linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y end # -----------------------------------------------------------------------