diff --git a/src/Plots.jl b/src/Plots.jl index 5dcd596d..5c85dad6 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -18,8 +18,6 @@ export plot, plot!, - # plot_display, - # plot_display!, subplot, subplot!, @@ -57,6 +55,8 @@ export wireframe!, path3d, path3d!, + plot3d, + plot3d!, scatter3d, scatter3d!, abline!, @@ -80,6 +80,7 @@ export backend, backends, + backend_name, aliases, dataframes, @@ -132,7 +133,7 @@ include("types.jl") include("utils.jl") include("colors.jl") include("components.jl") -include("plotter.jl") +include("plotter2.jl") include("args.jl") include("plot.jl") include("subplot.jl") @@ -173,6 +174,8 @@ wireframe(args...; kw...) = plot(args...; kw..., linetype = :wireframe) wireframe!(args...; kw...) = plot!(args...; kw..., linetype = :wireframe) path3d(args...; kw...) = plot(args...; kw..., linetype = :path3d) path3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d) +plot3d(args...; kw...) = plot(args...; kw..., linetype = :path3d) +plot3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d) scatter3d(args...; kw...) = plot(args...; kw..., linetype = :scatter3d) scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d) diff --git a/src/backends/bokeh.jl b/src/backends/bokeh.jl index c6b678df..72b899c5 100644 --- a/src/backends/bokeh.jl +++ b/src/backends/bokeh.jl @@ -1,6 +1,14 @@ # https://github.com/bokeh/Bokeh.jl + +function _initialize_backend(::BokehPackage; kw...) + @eval begin + import Bokeh + export Bokeh + end +end + # make255(x) = round(Int, 255 * x) # function bokehcolor(c::Colorant) diff --git a/src/backends/gadfly.jl b/src/backends/gadfly.jl index 7fd43b5d..4a72205e 100644 --- a/src/backends/gadfly.jl +++ b/src/backends/gadfly.jl @@ -1,6 +1,15 @@ # https://github.com/dcjones/Gadfly.jl + +function _initialize_backend(::GadflyPackage; kw...) + @eval begin + import Gadfly, Compose + export Gadfly, Compose + include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl")) + end +end + # --------------------------------------------------------------------------- # immutable MissingVec <: AbstractVector{Float64} end @@ -656,7 +665,7 @@ setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getplotargs(subplt, # ------------------------------------------------------------------------- -function dowritemime{P<:GadflyOrImmerse}(io::IO, func, plt::PlottingObject{P}) +function dowritemime{P<:Union{GadflyPackage,ImmersePackage}}(io::IO, func, plt::PlottingObject{P}) gplt = getGadflyContext(plt) setGadflyDisplaySize(plt) Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt) @@ -671,7 +680,7 @@ getGadflyWriteFunc(::MIME"application/x-tex") = Gadfly.PGF getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m) for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript", MIME"application/x-tex") - @eval function Base.writemime{P<:GadflyOrImmerse}(io::IO, ::$mime, plt::PlottingObject{P}) + @eval function Base.writemime{P<:Union{GadflyPackage,ImmersePackage}}(io::IO, ::$mime, plt::PlottingObject{P}) func = getGadflyWriteFunc($mime()) dowritemime(io, func, plt) end diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index 10c59756..ec11607c 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -2,6 +2,13 @@ # [WEBSITE] +function _initialize_backend(::GLVisualizePackage; kw...) + @eval begin + import GLVisualize + export GLVisualize + end +end + # --------------------------------------------------------------------------- immutable GLScreenWrapper diff --git a/src/backends/gr.jl b/src/backends/gr.jl index c4010d45..3b1488b2 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -1,6 +1,15 @@ # https://github.com/jheinen/GR.jl + +function _initialize_backend(::GRPackage; kw...) + @eval begin + import GR + export GR + end +end + + const gr_linetype = Dict( :auto => 1, :solid => 1, :dash => 2, :dot => 3, :dashdot => 4, :dashdotdot => -1 ) diff --git a/src/backends/immerse.jl b/src/backends/immerse.jl index ee199f8e..3eb6cfba 100644 --- a/src/backends/immerse.jl +++ b/src/backends/immerse.jl @@ -1,6 +1,13 @@ # https://github.com/JuliaGraphics/Immerse.jl +function _initialize_backend(::ImmersePackage; kw...) + @eval begin + import Immerse, Gadfly, Compose, Gtk + export Immerse, Gadfly, Compose, Gtk + include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl")) + end +end function createImmerseFigure(d::Dict) w,h = d[:size] diff --git a/src/backends/pgfplots.jl b/src/backends/pgfplots.jl new file mode 100644 index 00000000..f76cb33b --- /dev/null +++ b/src/backends/pgfplots.jl @@ -0,0 +1,107 @@ + +# https://github.com/sisl/PGFPlots.jl + +function _initialize_backend(::PGFPlotsPackage; kw...) + @eval begin + import PGFPlots + export PGFPlots + # TODO: other initialization that needs to be eval-ed + end + # TODO: other initialization +end + +# --------------------------------------------------------------------------- + +function _create_plot(pkg::PGFPlotsPackage; kw...) + d = Dict{Symbol,Any}(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[]) +end + + +function _add_series(::PGFPlotsPackage, plt::Plot; kw...) + d = Dict{Symbol,Any}(kw) + # TODO: add one series to the underlying package + push!(plt.seriesargs, d) + plt +end + +function _add_annotations{X,Y,V}(plt::Plot{PGFPlotsPackage}, anns::AVec{@compat(Tuple{X,Y,V})}) + # set or add to the annotation_list + if haskey(plt.plotargs, :annotation_list) + append!(plt.plotargs[:annotation_list], anns) + else + plt.plotargs[:annotation_list] = anns + end +end + +# ---------------------------------------------------------------- + +function _before_update_plot(plt::Plot{PGFPlotsPackage}) +end + +# TODO: override this to update plot items (title, xlabel, etc) after creation +function _update_plot(plt::Plot{PGFPlotsPackage}, d::Dict) +end + +function _update_plot_pos_size(plt::PlottingObject{PGFPlotsPackage}, d::Dict) +end + +# ---------------------------------------------------------------- + +# accessors for x/y data + +function Base.getindex(plt::Plot{PGFPlotsPackage}, i::Int) + d = plt.seriesargs[i] + d[:x], d[:y] +end + +function Base.setindex!(plt::Plot{PGFPlotsPackage}, xy::Tuple, i::Integer) + d = plt.seriesargs[i] + d[:x], d[:y] = xy + plt +end + +# ---------------------------------------------------------------- + +function _create_subplot(subplt::Subplot{PGFPlotsPackage}, isbefore::Bool) + # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example + true +end + +function _expand_limits(lims, plt::Plot{PGFPlotsPackage}, isx::Bool) + # TODO: call expand limits for each plot data +end + +function _remove_axis(plt::Plot{PGFPlotsPackage}, isx::Bool) + # TODO: if plot is inner subplot, might need to remove ticks or axis labels +end + +# ---------------------------------------------------------------- + + +# ---------------------------------------------------------------- + +################# This is the important method to implement!!! ################# +function _make_pgf_plot(plt::Plot{PGFPlotsPackage}) + # TODO: convert plt.plotargs and plt.seriesargs into PGFPlots calls + # TODO: return the PGFPlots object +end + +function Base.writemime(io::IO, mime::MIME"image/png", plt::PlottingObject{PGFPlotsPackage}) + plt.o = _make_pgf_plot(plt) + writemime(io, mime, plt.o) +end + +# function Base.writemime(io::IO, ::MIME"text/html", plt::PlottingObject{PGFPlotsPackage}) +# end + +function Base.display(::PlotsDisplay, plt::PlottingObject{PGFPlotsPackage}) + plt.o = _make_pgf_plot(plt) + display(plt.o) +end + +# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsPackage}) +# # TODO: display/show the subplot +# end diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index aa1141cb..9672638d 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -1,6 +1,41 @@ # https://plot.ly/javascript/getting-started +function _initialize_backend(::PlotlyPackage; kw...) + @eval begin + import JSON + JSON._print(io::IO, state::JSON.State, dt::Union{Date,DateTime}) = print(io, '"', dt, '"') + + ############################ + # borrowed from https://github.com/spencerlyon2/Plotlyjs.jl/blob/master/src/display.jl + _js_path = joinpath(Pkg.dir("Plots"), "deps", "plotly-latest.min.js") + + # if we're in IJulia call setupnotebook to load js and css + if isijulia() + # the first script is some hack I needed to do in order for the notebook + # to not complain about Plotly being undefined + display("text/html", """ + + + """) + # display("text/html", "

Plotly javascript loaded.

") + end + # end borrowing (thanks :) + ########################### + + try + include(joinpath(Pkg.dir("Plots"), "src", "backends", "plotly_blink.jl")) + catch err + warn("Error including PlotlyJS: $err\n Note: Will fall back to built-in display.") + end + end + # TODO: other initialization +end + # --------------------------------------------------------------------------- function _create_plot(pkg::PlotlyPackage; kw...) diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 5621bf31..80772df7 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -1,6 +1,32 @@ # https://github.com/stevengj/PyPlot.jl +function _initialize_backend(::PyPlotPackage) + @eval begin + import PyPlot + export PyPlot + const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors")) + const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path")) + const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d")) + # const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar")) + end + + if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay) + PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia + + # # TODO: how the hell can I use PyQt4?? + # "pyqt4"=>:qt_pyqt4 + # PyPlot.backend[1] = "pyqt4" + # PyPlot.gui[1] = :qt_pyqt4 + # PyPlot.switch_backend("Qt4Agg") + + # only turn on the gui if we want it + if PyPlot.gui != :none + PyPlot.pygui(true) + end + end +end + # ------------------------------- # convert colorant to 4-tuple RGBA @@ -413,9 +439,6 @@ function Base.getindex(plt::Plot{PyPlotPackage}, i::Integer) xy = series[:get_offsets]() return vec(xy[:,1]), vec(xy[:,2]) end - # series[:relim]() - # mapping = getGadflyMappings(plt, i)[1] - # mapping[:x], mapping[:y] end function Base.setindex!{X,Y}(plt::Plot{PyPlotPackage}, xy::Tuple{X,Y}, i::Integer) diff --git a/src/backends/qwt.jl b/src/backends/qwt.jl index 922f691d..e4101a9f 100644 --- a/src/backends/qwt.jl +++ b/src/backends/qwt.jl @@ -1,6 +1,12 @@ # https://github.com/tbreloff/Qwt.jl +function _initialize_backend(::QwtPackage; kw...) + @eval begin + import Qwt + export Qwt + end +end # ------------------------------- diff --git a/src/backends/template.jl b/src/backends/template.jl index 450f264f..b8148da0 100644 --- a/src/backends/template.jl +++ b/src/backends/template.jl @@ -3,6 +3,15 @@ # [WEBSITE] +function _initialize_backend(::[PkgName]Package; kw...) + @eval begin + import [PkgName] + export [PkgName] + # TODO: other initialization that needs to be eval-ed + end + # TODO: other initialization +end + # --------------------------------------------------------------------------- function _create_plot(pkg::[PkgName]Package; kw...) @@ -43,13 +52,11 @@ end # accessors for x/y data function Base.getindex(plt::Plot{[PkgName]Package}, i::Int) - series = plt.o.lines[i] - series.x, series.y + # TODO: return a tuple of (x, y) vectors end function Base.setindex!(plt::Plot{[PkgName]Package}, xy::Tuple, i::Integer) - series = plt.o.lines[i] - series.x, series.y = xy + # TODO: set the plot data from the (x,y) tuple plt end diff --git a/src/backends/unicodeplots.jl b/src/backends/unicodeplots.jl index 23c0d591..6c959241 100644 --- a/src/backends/unicodeplots.jl +++ b/src/backends/unicodeplots.jl @@ -1,6 +1,13 @@ # https://github.com/Evizero/UnicodePlots.jl +function _initialize_backend(::UnicodePlotsPackage; kw...) + @eval begin + import UnicodePlots + export UnicodePlots + end +end + # ------------------------------- diff --git a/src/backends/winston.jl b/src/backends/winston.jl index b9b71a98..1e12f7ad 100644 --- a/src/backends/winston.jl +++ b/src/backends/winston.jl @@ -3,6 +3,13 @@ # credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation +function _initialize_backend(::WinstonPackage; kw...) + @eval begin + ENV["WINSTON_OUTPUT"] = "gtk" + import Winston, Gtk + export Winston, Gtk + end +end # --------------------------------------------------------------------------- diff --git a/src/plotter.jl b/src/plotter.jl index ebf3295e..ee62107b 100644 --- a/src/plotter.jl +++ b/src/plotter.jl @@ -83,11 +83,11 @@ subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, s const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly, :gr] -const INITIALIZED_BACKENDS = Set{Symbol}() +const _initialized_backends = Set{Symbol}() backends() = BACKENDS -function backendInstance(sym::Symbol) +function _backend_instance(sym::Symbol) sym == :qwt && return QwtPackage() sym == :gadfly && return GadflyPackage() sym == :unicodeplots && return UnicodePlotsPackage() @@ -107,7 +107,7 @@ type CurrentBackend sym::Symbol pkg::PlottingPackage end -CurrentBackend(sym::Symbol) = CurrentBackend(sym, backendInstance(sym)) +CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym)) # --------------------------------------------------------- @@ -134,7 +134,7 @@ function backend() end currentBackendSymbol = CURRENT_BACKEND.sym - if !(currentBackendSymbol in INITIALIZED_BACKENDS) + if !(currentBackendSymbol in _initialized_backends) # initialize println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym) @@ -283,7 +283,7 @@ function backend() else error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS") end - push!(INITIALIZED_BACKENDS, currentBackendSymbol) + push!(_initialized_backends, currentBackendSymbol) end CURRENT_BACKEND.pkg diff --git a/src/plotter2.jl b/src/plotter2.jl new file mode 100644 index 00000000..30bdc665 --- /dev/null +++ b/src/plotter2.jl @@ -0,0 +1,119 @@ + +immutable NoPackage <: PlottingPackage end + +const _backendType = Dict{Symbol, DataType}(:none => NoPackage) +const _backendSymbol = Dict{DataType, Symbol}(NoPackage => :none) +const _backends = Symbol[] +const _initialized_backends = Set{Symbol}() + +backends() = _backends +backend_name() = CURRENT_BACKEND.sym +_backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym") + +macro init_plotting_pkg(s) + str = lowercase(string(s)) + sym = symbol(str) + T = symbol(string(s) * "Package") + esc(quote + immutable $T <: PlottingPackage end + export $sym + $sym(; kw...) = (default(; kw...); backend(symbol($str))) + backend_name(::$T) = symbol($str) + push!(_backends, symbol($str)) + _backendType[symbol($str)] = $T + _backendSymbol[$T] = symbol($str) + include("backends/" * $str * ".jl") + end) +end + +@init_plotting_pkg Immerse +@init_plotting_pkg Gadfly +@init_plotting_pkg PyPlot +@init_plotting_pkg Qwt +@init_plotting_pkg UnicodePlots +@init_plotting_pkg Winston +@init_plotting_pkg Bokeh +@init_plotting_pkg Plotly +@init_plotting_pkg GR +@init_plotting_pkg GLVisualize +@init_plotting_pkg PGFPlots + +include("backends/supported.jl") + +# --------------------------------------------------------- + + +plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented") +plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented") +_update_plot(pkg::PlottingPackage, plt::Plot, d::Dict) = error("_update_plot($pkg, plt, d) is not implemented") +_update_plot_pos_size{P<:PlottingPackage}(plt::PlottingObject{P}, d::Dict) = nothing +subplot(pkg::PlottingPackage; kw...) = error("subplot($pkg; kw...) is not implemented") +subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented") + +# --------------------------------------------------------- + + +type CurrentBackend + sym::Symbol + pkg::PlottingPackage +end +CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym)) + +# --------------------------------------------------------- + +function pickDefaultBackend() + for pkgstr in ("PyPlot", "Immerse", "Qwt", "Gadfly", "GR", "UnicodePlots", "Bokeh", "GLVisualize") + if Pkg.installed(pkgstr) != nothing + return backend(symbol(lowercase(pkgstr))) + end + end + + # the default if nothing else is installed + backend(:plotly) +end + + +# --------------------------------------------------------- + +""" +Returns the current plotting package name. Initializes package on first call. +""" +function backend() + + global CURRENT_BACKEND + if CURRENT_BACKEND.sym == :none + pickDefaultBackend() + end + + sym = CURRENT_BACKEND.sym + if !(sym in _initialized_backends) + + # initialize + println("[Plots.jl] Initializing backend: ", sym) + + inst = _backend_instance(sym) + try + _initialize_backend(inst) + catch err + warn("Couldn't initialize $sym. (might need to install it?)") + rethrow(err) + end + + push!(_initialized_backends, sym) + + end + CURRENT_BACKEND.pkg +end + +""" +Set the plot backend. +""" +function backend(pkg::PlottingPackage) + CURRENT_BACKEND.sym = backend_name(pkg) + CURRENT_BACKEND.pkg = pkg +end + +function backend(modname::Symbol) + CURRENT_BACKEND.sym = modname + CURRENT_BACKEND.pkg = _backend_instance(modname) +end diff --git a/src/subplot.jl b/src/subplot.jl index dee0a7cd..07b56500 100644 --- a/src/subplot.jl +++ b/src/subplot.jl @@ -135,7 +135,7 @@ convertSeriesIndex(subplt::Subplot, n::Int) = ceil(Int, n / subplt.p) function validateSubplotSupported() if !subplotSupported() - error(CURRENT_BACKEND.sym, " does not support the subplot/subplot! commands at this time. Try one of: ", join(filter(pkg->subplotSupported(backendInstance(pkg)), backends()),", ")) + error(CURRENT_BACKEND.sym, " does not support the subplot/subplot! commands at this time. Try one of: ", join(filter(pkg->subplotSupported(_backend_instance(pkg)), backends()),", ")) end end diff --git a/src/utils.jl b/src/utils.jl index 90431042..68d27cb3 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -416,7 +416,7 @@ function supportGraph(allvals, func) y = ASCIIString[] for val in vals for b in bs - supported = func(Plots.backendInstance(b)) + supported = func(Plots._backend_instance(b)) if val in supported push!(x, string(b)) push!(y, string(val)) diff --git a/test/refimg/gadfly/ref13.png b/test/refimg/gadfly/ref13.png index caf2109c..e533d428 100644 Binary files a/test/refimg/gadfly/ref13.png and b/test/refimg/gadfly/ref13.png differ diff --git a/test/refimg/gadfly/ref15.png b/test/refimg/gadfly/ref15.png index 785bea7a..a7847442 100644 Binary files a/test/refimg/gadfly/ref15.png and b/test/refimg/gadfly/ref15.png differ diff --git a/test/refimg/pyplot/ref13.png b/test/refimg/pyplot/ref13.png index e2273cd9..bc7a7265 100644 Binary files a/test/refimg/pyplot/ref13.png and b/test/refimg/pyplot/ref13.png differ