diff --git a/src/backends/gr.jl b/src/backends/gr.jl new file mode 100644 index 000000000..6f1916d12 --- /dev/null +++ b/src/backends/gr.jl @@ -0,0 +1,232 @@ + +# https://github.com/jheinen/GR.jl + +fig = Dict() + +fig[:size] = [500, 500] + +const gr_linetype = Dict( + :auto => 0, :solid => 1, :dash => 2, :dot => 3, :dashdot => 4, + :dashdotdot => -1 ) + +const gr_markertype = Dict( + :none => 1, :ellipse => -1, :rect => -7, :diamond => -13, + :utriangle => -3, :dtriangle => -5, :pentagon => -14, + :cross => 2, :xcross => 5, :star5 => 3 ) + +function _create_plot(pkg::GRPackage; kw...) + global fig + d = Dict(kw) + fig[:size] = d[:size] + Plot(nothing, pkg, 0, d, Dict[]) +end + +function _add_series(::GRPackage, plt::Plot; kw...) + d = Dict(kw) + push!(plt.seriesargs, d) + plt +end + +function _add_annotations{X,Y,V}(plt::Plot{GRPackage}, anns::AVec{@compat(Tuple{X,Y,V})}) + for ann in anns + # TODO: add the annotation to the plot + end +end + +# ---------------------------------------------------------------- + +function _before_update_plot(plt::Plot{GRPackage}) +end + +function _update_plot(plt::Plot{GRPackage}, d::Dict) + global fig + + GR.clearws() + + mwidth, mheight, width, height = GR.inqdspsize() + w, h = fig[:size] + if w > h + ratio = float(h) / w + size = mwidth * w / width + GR.setwsviewport(0, size, 0, size * ratio) + GR.setwswindow(0, 1, 0, ratio) + viewport = [0.1, 0.95, 0.1 * ratio, 0.95 * ratio] + else + ratio = float(w) / h + size = mheight * h / height + GR.setwsviewport(0, size * ratio, 0, size) + GR.setwswindow(0, ratio, 0, 1) + viewport = [0.1 * ratio, 0.95 * ratio, 0.1, 0.95] + end + + xmin = ymin = typemax(Float64) + xmax = ymax = typemin(Float64) + for p in plt.seriesargs + x, y = p[:x], p[:y] + xmin = min(minimum(x), xmin) + xmax = max(maximum(x), xmax) + ymin = min(minimum(y), ymin) + ymax = max(maximum(y), ymax) + end + + scale = 0 + d[:xscale] == :log10 && (scale |= GR.OPTION_X_LOG) + d[:yscale] == :log10 && (scale |= GR.OPTION_Y_LOG) + get(d, :xflip, false) && (scale |= GR.OPTION_FLIP_X) + get(d, :yflip, false) && (scale |= GR.OPTION_FLIP_Y) + + if scale & GR.OPTION_X_LOG == 0 + xmin, xmax = GR.adjustlimits(xmin, xmax) + majorx = 5 + xtick = GR.tick(xmin, xmax) / majorx + else + xtick = majorx = 1 + end + if scale & GR.OPTION_Y_LOG == 0 + ymin, ymax = GR.adjustlimits(ymin, ymax) + majory = 5 + ytick = GR.tick(ymin, ymax) / majory + else + ytick = majory = 1 + end + if scale & GR.OPTION_FLIP_X == 0 + xorg = (xmin, xmax) + else + xorg = (xmax, xmin) + end + if scale & GR.OPTION_FLIP_Y == 0 + yorg = (ymin, ymax) + else + yorg = (ymax, ymin) + end + + GR.setviewport(viewport[1], viewport[2], viewport[3], viewport[4]) + GR.setwindow(xmin, xmax, ymin, ymax) + GR.setscale(scale) + + charheight = 0.03 * (viewport[4] - viewport[3]) + GR.setcharheight(charheight) + GR.grid(xtick, ytick, 0, 0, majorx, majory) + ticksize = 0.0125 * (viewport[2] - viewport[1]) + GR.axes(xtick, ytick, xorg[1], yorg[1], majorx, majory, ticksize) + GR.axes(xtick, ytick, xorg[2], yorg[2], -majorx, -majory, -ticksize) + + if haskey(d, :title) + GR.savestate() + GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP) + GR.text(0.5, min(ratio, 1), d[:title]) + GR.restorestate() + end + if haskey(d, :xlabel) + GR.savestate() + GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM) + GR.text(0.5, 0, d[:xlabel]) + GR.restorestate() + end + if haskey(d, :ylabel) + GR.savestate() + GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP) + GR.setcharup(-1, 0) + GR.text(0, 0.5 * (viewport[3] + viewport[4]), d[:ylabel]) + GR.restorestate() + end + + GR.savestate() + haskey(d, :linewidth) && GR.setlinewidth(d[:linewidth]) + haskey(d, :linestyle) && GR.setlinetype(gr_linetype[d[:linestyle]]) + haskey(d, :markersize) && GR.setmarkersize(d[:markersize]) + haskey(d, :markershape) && GR.setmarkertype(gr_markertype[d[:markershape]]) + GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF) + + for p in plt.seriesargs + GR.uselinespec("") + if p[:linetype] == :path + GR.polyline(p[:x], p[:y]) + elseif p[:linetype] == :scatter + GR.polymarker(p[:x], p[:y]) + end + end + + if plt.plotargs[:legend] + GR.selntran(0) + GR.setscale(0) + w = 0 + for p in plt.seriesargs + tbx, tby = GR.inqtext(0, 0, p[:label]) + w = max(w, tbx[3]) + end + px = viewport[2] - 0.05 - w + py = viewport[4] - 0.06 + GR.setfillintstyle(GR.INTSTYLE_SOLID) + GR.setfillcolorind(0) + GR.fillrect(px - 0.06, px + w + 0.02, py + 0.03, py - 0.03 * length(plt.seriesargs)) + GR.setlinecolorind(1) + GR.setlinewidth(1) + GR.drawrect(px - 0.06, px + w + 0.02, py + 0.03, py - 0.03 * length(plt.seriesargs)) + haskey(d, :linewidth) && GR.setlinewidth(d[:linewidth]) + for p in plt.seriesargs + GR.uselinespec("") + if p[:linetype] == :path + GR.polyline([px - 0.05, px - 0.01], [py, py]) + elseif p[:linetype] == :scatter + GR.polymarker([px - 0.05, px - 0.01], [py, py]) + end + GR.text(px, py, p[:label]) + py -= 0.03 + end + GR.selntran(1) + end + + GR.restorestate() + GR.updatews() +end + +function _update_plot_pos_size(plt::PlottingObject{GRPackage}, d::Dict) + global fig + if haskey(d, :size) + fig[:size] = d[:size] + end +end + +# ---------------------------------------------------------------- + +# accessors for x/y data + +function Base.getindex(plt::Plot{GRPackage}, i::Int) + series = plt.o.lines[i] + series.x, series.y +end + +function Base.setindex!(plt::Plot{GRPackage}, xy::Tuple, i::Integer) + series = plt.o.lines[i] + series.x, series.y = xy + plt +end + +# ---------------------------------------------------------------- + +function _create_subplot(subplt::Subplot{GRPackage}, isbefore::Bool) + # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example +end + +function _expand_limits(lims, plt::Plot{GRPackage}, isx::Bool) + # TODO: call expand limits for each plot data +end + +function _remove_axis(plt::Plot{GRPackage}, isx::Bool) + # TODO: if plot is inner subplot, might need to remove ticks or axis labels +end + +# ---------------------------------------------------------------- + +function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{GRPackage}) + # TODO: write a png to io +end + +function Base.display(::PlotsDisplay, plt::Plot{GRPackage}) + # TODO: display/show the plot +end + +function Base.display(::PlotsDisplay, plt::Subplot{GRPackage}) + # TODO: display/show the subplot +end diff --git a/src/backends/supported.jl b/src/backends/supported.jl index 04a4318e7..1fdea5862 100644 --- a/src/backends/supported.jl +++ b/src/backends/supported.jl @@ -174,6 +174,78 @@ subplotSupported(::PyPlotPackage) = true +supportedArgs(::GRPackage) = [ + :annotation, + :axis, + :background_color, + :linecolor, + :color_palette, + :fillrange, + :fillcolor, + :foreground_color, + :group, + :label, + :layout, + :legend, + :linestyle, + :linetype, + :linewidth, + :markershape, + :markercolor, + :markersize, + :markerstrokewidth, + :markerstrokecolor, + # :markerstrokestyle, + :n, + :nbins, + :nc, + :nr, + # :pos, + :smooth, + # :ribbon, + :show, + :size, + :title, + :windowtitle, + :x, + :xlabel, + :xlims, + :xticks, + :y, + :ylabel, + :ylims, + :yrightlabel, + :yticks, + :xscale, + :yscale, + :xflip, + :yflip, + :z, + :zcolor, # only supported for scatter/scatter3d + :tickfont, + :guidefont, + :legendfont, + :grid, + # :surface, + :nlevels, + :fillalpha, + :linealpha, + :markeralpha, + ] +supportedAxes(::GRPackage) = _allAxes +supportedTypes(::GRPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, + :scatter, :heatmap, :hexbin, :hist, :density, :bar, + :hline, :vline, :contour, :path3d, :scatter3d, :surface, :wireframe] +supportedStyles(::GRPackage) = [:auto, :solid, :dash, :dot, :dashdot] +supportedMarkers(::GRPackage) = [:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :pentagon, :cross, :xcross, :star5] +supportedScales(::GRPackage) = [:identity, :log10] +subplotSupported(::GRPackage) = true + + +# -------------------------------------------------------------------------------------- + + + supportedArgs(::QwtPackage) = [ :annotation, # :args, diff --git a/src/plotter.jl b/src/plotter.jl index 6d84da53c..6cd8bb32d 100644 --- a/src/plotter.jl +++ b/src/plotter.jl @@ -8,6 +8,7 @@ immutable UnicodePlotsPackage <: PlottingPackage end immutable WinstonPackage <: PlottingPackage end immutable BokehPackage <: PlottingPackage end immutable PlotlyPackage <: PlottingPackage end +immutable GRPackage <: PlottingPackage end immutable GLVisualizePackage <: PlottingPackage end immutable NoPackage <: PlottingPackage end @@ -21,6 +22,7 @@ export unicodeplots, bokeh, plotly, + gr, glvisualize # winston @@ -31,6 +33,7 @@ qwt() = backend(:qwt) unicodeplots() = backend(:unicodeplots) bokeh() = backend(:bokeh) plotly() = backend(:plotly) +gr() = backend(:gr) glvisualize() = backend(:glvisualize) # winston() = backend(:winston) @@ -41,6 +44,7 @@ backend_name(::UnicodePlotsPackage) = :unicodeplots backend_name(::QwtPackage) = :qwt backend_name(::BokehPackage) = :bokeh backend_name(::PlotlyPackage) = :plotly +backend_name(::GRPackage) = :gr backend_name(::GLVisualizePackage) = :glvisualize backend_name(::NoPackage) = :none @@ -56,6 +60,7 @@ include("backends/winston.jl") include("backends/web.jl") include("backends/bokeh.jl") include("backends/plotly.jl") +include("backends/gr.jl") include("backends/glvisualize.jl") @@ -77,7 +82,7 @@ subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, s # --------------------------------------------------------- -const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly] +const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly, :gr] const INITIALIZED_BACKENDS = Set{Symbol}() backends() = BACKENDS @@ -91,6 +96,7 @@ function backendInstance(sym::Symbol) sym == :winston && return WinstonPackage() sym == :bokeh && return BokehPackage() sym == :plotly && return PlotlyPackage() + sym == :gr && return GRPackage() sym == :glvisualize && return GLVisualizePackage() sym == :none && return NoPackage() error("Unsupported backend $sym") @@ -106,7 +112,7 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, backendInstance(sym)) # --------------------------------------------------------- function pickDefaultBackend() - for pkgstr in ("PyPlot", "Immerse", "Qwt", "Gadfly", "UnicodePlots", "Bokeh", "GLVisualize") + for pkgstr in ("PyPlot", "Gr", "Immerse", "Qwt", "Gadfly", "UnicodePlots", "Bokeh", "GLVisualize") if Pkg.installed(pkgstr) != nothing return backend(symbol(lowercase(pkgstr))) end @@ -247,7 +253,14 @@ function backend() rethrow(err) end - elseif currentBackendSymbol == :glvisualize + elseif currentBackendSymbol == :gr + try + @eval import GR + catch err + warn("Couldn't import GR. Install it with: Pkg.add(\"GR\").") + end + + elseif currentBackendSymbol == :glvisualize try @eval import GLVisualize @eval export GLVisualize @@ -303,7 +316,9 @@ function backend(modname) CURRENT_BACKEND.pkg = BokehPackage() elseif modname == :plotly CURRENT_BACKEND.pkg = PlotlyPackage() -elseif modname == :glvisualize + elseif modname == :gr + CURRENT_BACKEND.pkg = GRPackage() + elseif modname == :glvisualize CURRENT_BACKEND.pkg = GLVisualizePackage() else error("Unknown backend $modname. Choose from: $BACKENDS")