major overhaul to backend definition and initialization; added pgfplots template; some new reference images

This commit is contained in:
Thomas Breloff 2016-02-04 12:29:56 -05:00
parent 96df75cc05
commit d388036601
20 changed files with 373 additions and 19 deletions

View File

@ -18,8 +18,6 @@ export
plot, plot,
plot!, plot!,
# plot_display,
# plot_display!,
subplot, subplot,
subplot!, subplot!,
@ -57,6 +55,8 @@ export
wireframe!, wireframe!,
path3d, path3d,
path3d!, path3d!,
plot3d,
plot3d!,
scatter3d, scatter3d,
scatter3d!, scatter3d!,
abline!, abline!,
@ -80,6 +80,7 @@ export
backend, backend,
backends, backends,
backend_name,
aliases, aliases,
dataframes, dataframes,
@ -132,7 +133,7 @@ include("types.jl")
include("utils.jl") include("utils.jl")
include("colors.jl") include("colors.jl")
include("components.jl") include("components.jl")
include("plotter.jl") include("plotter2.jl")
include("args.jl") include("args.jl")
include("plot.jl") include("plot.jl")
include("subplot.jl") include("subplot.jl")
@ -173,6 +174,8 @@ wireframe(args...; kw...) = plot(args...; kw..., linetype = :wireframe)
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)
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)
scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d) scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d)

View File

@ -1,6 +1,14 @@
# https://github.com/bokeh/Bokeh.jl # 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) # make255(x) = round(Int, 255 * x)
# function bokehcolor(c::Colorant) # function bokehcolor(c::Colorant)

View File

@ -1,6 +1,15 @@
# https://github.com/dcjones/Gadfly.jl # 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 # 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) gplt = getGadflyContext(plt)
setGadflyDisplaySize(plt) setGadflyDisplaySize(plt)
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt) 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) 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") 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()) func = getGadflyWriteFunc($mime())
dowritemime(io, func, plt) dowritemime(io, func, plt)
end end

View File

@ -2,6 +2,13 @@
# [WEBSITE] # [WEBSITE]
function _initialize_backend(::GLVisualizePackage; kw...)
@eval begin
import GLVisualize
export GLVisualize
end
end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
immutable GLScreenWrapper immutable GLScreenWrapper

View File

@ -1,6 +1,15 @@
# https://github.com/jheinen/GR.jl # https://github.com/jheinen/GR.jl
function _initialize_backend(::GRPackage; kw...)
@eval begin
import GR
export GR
end
end
const gr_linetype = Dict( const gr_linetype = Dict(
:auto => 1, :solid => 1, :dash => 2, :dot => 3, :dashdot => 4, :auto => 1, :solid => 1, :dash => 2, :dot => 3, :dashdot => 4,
:dashdotdot => -1 ) :dashdotdot => -1 )

View File

@ -1,6 +1,13 @@
# https://github.com/JuliaGraphics/Immerse.jl # 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) function createImmerseFigure(d::Dict)
w,h = d[:size] w,h = d[:size]

107
src/backends/pgfplots.jl Normal file
View File

@ -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

View File

@ -1,6 +1,41 @@
# https://plot.ly/javascript/getting-started # 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", """
<script type="text/javascript">
require=requirejs=define=undefined;
</script>
<script type="text/javascript">
$(open(readall, _js_path, "r"))
</script>
""")
# display("text/html", "<p>Plotly javascript loaded.</p>")
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...) function _create_plot(pkg::PlotlyPackage; kw...)

View File

@ -1,6 +1,32 @@
# https://github.com/stevengj/PyPlot.jl # 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 # convert colorant to 4-tuple RGBA
@ -413,9 +439,6 @@ function Base.getindex(plt::Plot{PyPlotPackage}, i::Integer)
xy = series[:get_offsets]() xy = series[:get_offsets]()
return vec(xy[:,1]), vec(xy[:,2]) return vec(xy[:,1]), vec(xy[:,2])
end end
# series[:relim]()
# mapping = getGadflyMappings(plt, i)[1]
# mapping[:x], mapping[:y]
end end
function Base.setindex!{X,Y}(plt::Plot{PyPlotPackage}, xy::Tuple{X,Y}, i::Integer) function Base.setindex!{X,Y}(plt::Plot{PyPlotPackage}, xy::Tuple{X,Y}, i::Integer)

View File

@ -1,6 +1,12 @@
# https://github.com/tbreloff/Qwt.jl # https://github.com/tbreloff/Qwt.jl
function _initialize_backend(::QwtPackage; kw...)
@eval begin
import Qwt
export Qwt
end
end
# ------------------------------- # -------------------------------

View File

@ -3,6 +3,15 @@
# [WEBSITE] # [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...) function _create_plot(pkg::[PkgName]Package; kw...)
@ -43,13 +52,11 @@ end
# accessors for x/y data # accessors for x/y data
function Base.getindex(plt::Plot{[PkgName]Package}, i::Int) function Base.getindex(plt::Plot{[PkgName]Package}, i::Int)
series = plt.o.lines[i] # TODO: return a tuple of (x, y) vectors
series.x, series.y
end end
function Base.setindex!(plt::Plot{[PkgName]Package}, xy::Tuple, i::Integer) function Base.setindex!(plt::Plot{[PkgName]Package}, xy::Tuple, i::Integer)
series = plt.o.lines[i] # TODO: set the plot data from the (x,y) tuple
series.x, series.y = xy
plt plt
end end

View File

@ -1,6 +1,13 @@
# https://github.com/Evizero/UnicodePlots.jl # https://github.com/Evizero/UnicodePlots.jl
function _initialize_backend(::UnicodePlotsPackage; kw...)
@eval begin
import UnicodePlots
export UnicodePlots
end
end
# ------------------------------- # -------------------------------

View File

@ -3,6 +3,13 @@
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation # 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
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@ -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 BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly, :gr]
const INITIALIZED_BACKENDS = Set{Symbol}() const _initialized_backends = Set{Symbol}()
backends() = BACKENDS backends() = BACKENDS
function backendInstance(sym::Symbol) function _backend_instance(sym::Symbol)
sym == :qwt && return QwtPackage() sym == :qwt && return QwtPackage()
sym == :gadfly && return GadflyPackage() sym == :gadfly && return GadflyPackage()
sym == :unicodeplots && return UnicodePlotsPackage() sym == :unicodeplots && return UnicodePlotsPackage()
@ -107,7 +107,7 @@ type CurrentBackend
sym::Symbol sym::Symbol
pkg::PlottingPackage pkg::PlottingPackage
end end
CurrentBackend(sym::Symbol) = CurrentBackend(sym, backendInstance(sym)) CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
# --------------------------------------------------------- # ---------------------------------------------------------
@ -134,7 +134,7 @@ function backend()
end end
currentBackendSymbol = CURRENT_BACKEND.sym currentBackendSymbol = CURRENT_BACKEND.sym
if !(currentBackendSymbol in INITIALIZED_BACKENDS) if !(currentBackendSymbol in _initialized_backends)
# initialize # initialize
println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym) println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym)
@ -283,7 +283,7 @@ function backend()
else else
error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS") error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS")
end end
push!(INITIALIZED_BACKENDS, currentBackendSymbol) push!(_initialized_backends, currentBackendSymbol)
end end
CURRENT_BACKEND.pkg CURRENT_BACKEND.pkg

119
src/plotter2.jl Normal file
View File

@ -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

View File

@ -135,7 +135,7 @@ convertSeriesIndex(subplt::Subplot, n::Int) = ceil(Int, n / subplt.p)
function validateSubplotSupported() function validateSubplotSupported()
if !subplotSupported() 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
end end

View File

@ -416,7 +416,7 @@ function supportGraph(allvals, func)
y = ASCIIString[] y = ASCIIString[]
for val in vals for val in vals
for b in bs for b in bs
supported = func(Plots.backendInstance(b)) supported = func(Plots._backend_instance(b))
if val in supported if val in supported
push!(x, string(b)) push!(x, string(b))
push!(y, string(val)) push!(y, string(val))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB