diff --git a/src/args.jl b/src/args.jl index 55f629a8..fbe47f4c 100644 --- a/src/args.jl +++ b/src/args.jl @@ -10,7 +10,7 @@ const _allAxes = [:auto, :left, :right] const _3dTypes = [:path3d, :scatter3d, :surface, :wireframe, :contour3d] const _allTypes = vcat([ :none, :line, :path, :steppre, :steppost, :sticks, :scatter, - :heatmap, :hexbin, :hist, :hist2d, :hist3d, :density, :bar, :hline, :vline, :ohlc, + :heatmap, :hexbin, :hist, :hist2d, :hist3d, :density, :bar, :hline, :vline, #:ohlc, :contour, :pie, :shape, :image #, :boxplot, :violin, :quiver, ], _3dTypes) @compat const _typeAliases = KW( diff --git a/src/backends/gadfly.jl b/src/backends/gadfly.jl index af342454..67f16c08 100644 --- a/src/backends/gadfly.jl +++ b/src/backends/gadfly.jl @@ -298,9 +298,9 @@ function addGadflySeries!(plt::Plot, d::KW) # special handling for ohlc and scatter st = d[:seriestype] - if st == :ohlc - error("Haven't re-implemented after refactoring") - elseif st in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction)) + # if st == :ohlc + # error("Haven't re-implemented after refactoring") + if st in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction)) push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p)))) elseif st == :scatter && d[:markershape] == :none d[:markershape] = :ellipse diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 58925375..ccccd028 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -40,7 +40,7 @@ supportedTypes(::GRBackend) = [ :none, :line, :path, :steppre, :steppost, :scatter, :hist2d, :hexbin, :hist, :density, :bar, :sticks, - :hline, :vline, :heatmap, :pie, :image, :ohlc, + :hline, :vline, :heatmap, :pie, :image, #:ohlc, :contour, :path3d, :scatter3d, :surface, :wireframe ] supportedStyles(::GRBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] @@ -261,8 +261,8 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true, end if st == :bar x, y = 1:length(p[:y]), p[:y] - elseif st == :ohlc - x, y = 1:size(p[:y], 1), p[:y] + # elseif st == :ohlc + # x, y = 1:size(p[:y], 1), p[:y] elseif st in [:hist, :density] x, y = Base.hist(p[:y], p[:bins]) elseif st in [:hist2d, :hexbin] @@ -295,15 +295,15 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true, if !(st in [:pie, :polar]) xmin = min(minimum(x), xmin) xmax = max(maximum(x), xmax) - if st == :ohlc - for val in y - ymin = min(val.open, val.high, val.low, val.close, ymin) - ymax = max(val.open, val.high, val.low, val.close, ymax) - end - else + # if st == :ohlc + # for val in y + # ymin = min(val.open, val.high, val.low, val.close, ymin) + # ymax = max(val.open, val.high, val.low, val.close, ymax) + # end + # else ymin = min(minimum(y), ymin) ymax = max(maximum(y), ymax) - end + # end if p[:xerror] != nothing || p[:yerror] != nothing dx = xmax - xmin xmin -= 0.02 * dx @@ -460,7 +460,7 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true, GR.savestate() xmin, xmax, ymin, ymax = extrema[gr_getaxisind(p),:] GR.setwindow(xmin, xmax, ymin, ymax) - if st in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :ohlc, :polar] + if st in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :polar] # :ohlc, :polar] GR.setlinetype(gr_linetype[p[:linestyle]]) GR.setlinewidth(p[:linewidth]) GR.setlinecolorind(gr_getcolorind(p[:linecolor])) @@ -675,15 +675,15 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true, GR.setcharheight(charheight) GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize) GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize) - elseif st == :ohlc - y = p[:y] - n = size(y, 1) - ticksize = 0.5 * (xmax - xmin) / n - for i in 1:n - GR.polyline([i-ticksize, i], [y[i].open, y[i].open]) - GR.polyline([i, i], [y[i].low, y[i].high]) - GR.polyline([i, i+ticksize], [y[i].close, y[i].close]) - end + # elseif st == :ohlc + # y = p[:y] + # n = size(y, 1) + # ticksize = 0.5 * (xmax - xmin) / n + # for i in 1:n + # GR.polyline([i-ticksize, i], [y[i].open, y[i].open]) + # GR.polyline([i, i], [y[i].low, y[i].high]) + # GR.polyline([i, i+ticksize], [y[i].close, y[i].close]) + # end elseif st == :pie GR.selntran(0) GR.setfillintstyle(GR.INTSTYLE_SOLID) diff --git a/src/components.jl b/src/components.jl index 25fc5382..52fc5034 100644 --- a/src/components.jl +++ b/src/components.jl @@ -374,15 +374,6 @@ end # ----------------------------------------------------------------------- -type OHLC{T<:Real} - open::T - high::T - low::T - close::T -end - -# ----------------------------------------------------------------------- - # style is :open or :closed (for now) immutable Arrow style::Symbol diff --git a/src/plot.jl b/src/plot.jl index a00fc897..4c604dd2 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -197,6 +197,12 @@ function _plot!(plt::Plot, d::KW, args...) next_series = pop!(still_to_process) series_list = RecipesBase.apply_recipe(next_series.d, next_series.args...) for series in series_list + + # series should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes + if !(typeof(series) <: RecipeData) + error("Inputs couldn't be processed... expected RecipeData but got: $series") + end + # @show series if isempty(series.args) # when the arg tuple is empty, that means there's nothing left to recursively diff --git a/src/recipes.jl b/src/recipes.jl index 0fb196c5..da4ff3b0 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -20,7 +20,7 @@ num_series(x) = 1 # _apply_recipe(d::KW, kw::KW) = () # if it's not a recipe, just do nothing and return the args -function RecipesBase.apply_recipe(d::KW, kw::KW, args...; issubplot=false) +function RecipesBase.apply_recipe(d::KW, args...; issubplot=false) if issubplot && !isempty(args) && !haskey(d, :n) && !haskey(d, :layout) # put in a sensible default d[:n] = maximum(map(num_series, args)) @@ -540,7 +540,65 @@ function getRecipeArgs(ep::EllipseRecipe) [(:line, (3, [:dot :solid], [:red :blue], :path))] end -# # ------------------------------------------------- +# ------------------------------------------------- + +# TODO: this should really be in another package... +type OHLC{T<:Real} + open::T + high::T + low::T + close::T +end +tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close) + +# get one OHLC path +function get_xy(o::OHLC, x, xdiff) + xl, xm, xr = x-xdiff, x, x+xdiff + ox = [xl, xm, NaN, + xm, xm, NaN, + xm, xr] + oy = [o.open, o.open, NaN, + o.low, o.high, NaN, + o.close, o.close] + ox, oy +end + +# get the joined vector +function get_xy(v::AVec{OHLC}, x = 1:length(v)) + xdiff = 0.3mean(abs(diff(x))) + x_out, y_out = zeros(0), zeros(0) + for (i,ohlc) in enumerate(v) + ox,oy = get_xy(ohlc, x[i], xdiff) + nanappend!(x_out, ox) + nanappend!(y_out, oy) + end + x_out, y_out +end + +# these are for passing in a vector of OHLC objects +# TODO: when I allow `@recipe f(::Type{T}, v::T) = ...` definitions to replace convertToAnyVector, +# then I should replace these with one definition to convert to a vector of 4-tuples + +# to squash ambiguity warnings... +@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error() +@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) = error() + +# this must be OHLC? +@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) = x, OHLC[OHLC(t...) for t in ohlc] + +@recipe function f(x::AVec, v::AVec{OHLC}) + d[:seriestype] = :path + get_xy(v, x) +end + +@recipe function f(v::AVec{OHLC}) + d[:seriestype] = :path + get_xy(v) +end + +# the series recipe, when passed vectors of 4-tuples + +# ------------------------------------------------- "Sparsity plot... heatmap of non-zero values of a matrix" diff --git a/src/series_args.jl b/src/series_args.jl index 9fd32046..b70392db 100644 --- a/src/series_args.jl +++ b/src/series_args.jl @@ -44,8 +44,8 @@ convertToAnyVector(f::Function, d::KW) = Any[f], nothing # surface convertToAnyVector(s::Surface, d::KW) = Any[s], nothing -# vector of OHLC -convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing +# # vector of OHLC +# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing # dates convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing diff --git a/src/series_new.jl b/src/series_new.jl index c93d6aca..6275d156 100644 --- a/src/series_new.jl +++ b/src/series_new.jl @@ -450,6 +450,11 @@ 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]] +# these might be points+velocity, or OHLC or something else +@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::AVec{Tuple{R1,R2,R3,R4}}) = get(d,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv) +@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::Tuple{R1,R2,R3,R4}) = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]] + + # # # 2D FixedSizeArrays # function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}}) diff --git a/src/utils.jl b/src/utils.jl index a4453cf0..3edd9f0a 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -156,13 +156,19 @@ 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}(xy::AVec{Tuple{T,S}}) = [x[1] for x in xy], [y[2] for y in xy] -unzip{T,S,R}(xyz::AVec{Tuple{T,S,R}}) = [x[1] for x in xyz], [y[2] for y in xyz], [z[3] for z in xyz] -unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[x[1] for x in xy], T[y[2] for y in xy] +unzip{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t in xy] +unzip{X,Y,Z}(xyz::AVec{Tuple{X,Y,Z}}) = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz] +unzip{X,Y,U,V}(xyuv::AVec{Tuple{X,Y,U,V}}) = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv] + +unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy] unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]] -unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[x[1] for x in xyz], T[y[2] for y in xyz], T[z[3] for z in xyz] + +unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz] unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]] +unzip{T}(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv] +unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]] + # given 2-element lims and a vector of data x, widen lims to account for the extrema of x function _expand_limits(lims, x) try