388 lines
12 KiB
Julia
388 lines
12 KiB
Julia
# https://github.com/sisl/PGFPlots.jl
|
||
|
||
supportedArgs(::PGFPlotsBackend) = [
|
||
# :annotation,
|
||
:aspect_ratio,
|
||
# :axis,
|
||
:background_color,
|
||
# :color_palette,
|
||
# :fillrange,
|
||
:fillcolor,
|
||
:fillalpha,
|
||
# :foreground_color,
|
||
# :group,
|
||
:label,
|
||
# :layout,
|
||
:legend,
|
||
:seriescolor, :seriesalpha,
|
||
:linecolor,
|
||
:linestyle,
|
||
:linetype,
|
||
:linewidth,
|
||
:linealpha,
|
||
:markershape,
|
||
:markercolor,
|
||
:markersize,
|
||
:markeralpha,
|
||
# :markerstrokewidth,
|
||
:markerstrokecolor,
|
||
:markerstrokestyle,
|
||
# :n,
|
||
# :bins,
|
||
# :nc,
|
||
# :nr,
|
||
# :pos,
|
||
# :smooth,
|
||
# :show,
|
||
# :size,
|
||
:title,
|
||
# :windowtitle,
|
||
:x,
|
||
:xlabel,
|
||
:xlims,
|
||
# :xticks,
|
||
:y,
|
||
:ylabel,
|
||
:ylims,
|
||
# :yrightlabel,
|
||
# :yticks,
|
||
:xscale,
|
||
:yscale,
|
||
:xflip,
|
||
:yflip,
|
||
:z,
|
||
:zscale,
|
||
# :tickfont,
|
||
# :guidefont,
|
||
# :legendfont,
|
||
:grid,
|
||
# :surface
|
||
# :levels,
|
||
]
|
||
supportedAxes(::PGFPlotsBackend) = [:auto, :left]
|
||
supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :line, :steppre, :stepmid, :steppost, :hist, :bar, :hist2d, :sticks, :ysticks, :xsticks, :contour] # :hexbin, :hline, :vline,]
|
||
supportedStyles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||
supportedMarkers(::PGFPlotsBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
|
||
supportedScales(::PGFPlotsBackend) = [:identity, :log, :ln, :log2, :log10] # :asinh, :sqrt]
|
||
subplotSupported(::PGFPlotsBackend) = false
|
||
|
||
|
||
# --------------------------------------------------------------------------------------
|
||
|
||
|
||
function _initialize_backend(::PGFPlotsBackend; kw...)
|
||
@eval begin
|
||
import PGFPlots
|
||
export PGFPlots
|
||
# TODO: other initialization that needs to be eval-ed
|
||
end
|
||
# TODO: other initialization
|
||
end
|
||
const _pgfplots_linestyles = KW(
|
||
:solid => "solid",
|
||
:dash => "dashed",
|
||
:dot => "dotted",
|
||
:dashdot => "dashdotted",
|
||
:dashdotdot => "dashdotdotted"
|
||
)
|
||
|
||
const _pgfplots_markers = KW(
|
||
:none => "mark = none,",
|
||
:cross => "mark = +,",
|
||
:xcross => "mark = x,",
|
||
:utriangle => "mark = triangle*,",
|
||
:dtriangle => "mark = triangle*,",
|
||
:ellipse => "mark = o*,",
|
||
:rect => "mark = square*,",
|
||
:star5 => "mark = star,",
|
||
:star6 => "mark = asterisk,",
|
||
:diamond => "mark = diamond*,",
|
||
:pentagon => "mark = pentagon*,"
|
||
)
|
||
|
||
const _pgfplots_legend_pos = KW(
|
||
:bottomleft => "south west",
|
||
:bottomright => "south east",
|
||
:topright => "north east",
|
||
:topleft => "north west"
|
||
)
|
||
|
||
function _pgfplots_get_color(kwargs, symb)
|
||
c = typeof(kwargs[symb]) == Symbol ? convertColor(kwargs[symb]) : kwargs[symb].c
|
||
# We need to convert to decimals here because pgfplot will error
|
||
# for colors in engineering notation
|
||
r_str = @sprintf("%.8f", float(c.r))
|
||
g_str = @sprintf("%.8f", float(c.g))
|
||
b_str = @sprintf("%.8f", float(c.b))
|
||
"{rgb,1:red,$(r_str);green,$(g_str);blue,$(b_str)}"
|
||
end
|
||
|
||
function _pgfplots_get_linestyle!(kwargs, plt)
|
||
ls = plt[:linestyle]
|
||
if haskey(_pgfplots_linestyles, ls)
|
||
kwargs[:style] *= _pgfplots_linestyles[ls]*","
|
||
end
|
||
|
||
kwargs[:style] *= "line width = $(plt[:linewidth]) pt"*","
|
||
end
|
||
|
||
|
||
function _pgfplots_get_marker!(kwargs, plt)
|
||
# Control marker shape
|
||
mark = plt[:markershape]
|
||
kwargs[:style] *= _pgfplots_markers[mark]
|
||
|
||
# Control marker size
|
||
kwargs[:style] *= "mark size = $(plt[:markersize]/2),"
|
||
|
||
# Control marker colors and alphas
|
||
α = plt[:markeralpha] == nothing ? 1.0 : plt[:markeralpha]
|
||
kwargs[:style] *= "mark options = {color=$(_pgfplots_get_color(plt, :markerstrokecolor)),"
|
||
kwargs[:style] *= mark == :dtriangle ? "rotate=180," : ""
|
||
kwargs[:style] *= "fill=$(_pgfplots_get_color(plt, :markercolor)),"
|
||
kwargs[:style] *= "fill opacity = $α,"
|
||
markstrokestyle = plt[:markerstrokestyle]
|
||
if haskey(_pgfplots_linestyles, markstrokestyle)
|
||
kwargs[:style] *= _pgfplots_linestyles[markstrokestyle]
|
||
end
|
||
kwargs[:style] *= "},"
|
||
end
|
||
|
||
function _pgfplots_get_series_color!(kwargs, plt)
|
||
α = plt[:seriesalpha] == nothing ? 1.0 : plt[:seriesalpha]
|
||
kwargs[:style] *= "color=$(_pgfplots_get_color(plt, :seriescolor)),"
|
||
kwargs[:style] *= "draw opacity = $α,"
|
||
end
|
||
|
||
function _pgfplots_get_line_color!(kwargs, plt)
|
||
α = plt[:linealpha] == nothing ? 1.0 : plt[:linealpha]
|
||
kwargs[:style] *= "color=$(_pgfplots_get_color(plt, :linecolor)),"
|
||
kwargs[:style] *= "draw opacity = $α,"
|
||
end
|
||
|
||
function _pgfplots_get_fill_color!(kwargs, plt)
|
||
α = plt[:fillalpha] == nothing ? 1.0 : plt[:fillalpha]
|
||
kwargs[:style] *= "fill=$(_pgfplots_get_color(plt, :fillcolor)),"
|
||
kwargs[:style] *= "fill opacity = $α,"
|
||
end
|
||
|
||
function _pgfplots_get_label!(kwargs, plt)
|
||
if plt[:label] != nothing && plt[:legend] != :none
|
||
kwargs[:legendentry] = plt[:label]
|
||
end
|
||
end
|
||
|
||
|
||
function _pgfplots_get_plot_kwargs(plt)
|
||
kwargs = KW()
|
||
kwargs[:style] = ""
|
||
_pgfplots_get_linestyle!(kwargs, plt)
|
||
_pgfplots_get_marker!(kwargs, plt)
|
||
_pgfplots_get_series_color!(kwargs, plt)
|
||
_pgfplots_get_label!(kwargs, plt)
|
||
kwargs
|
||
end
|
||
|
||
function _pgfplots_axis(plt_series)
|
||
line_type = plt_series[:linetype]
|
||
plt_kwargs = _pgfplots_get_plot_kwargs(plt_series)
|
||
if line_type == :path
|
||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :path3d
|
||
PGFPlots.Linear3(plt_series[:x], plt_series[:y], plt_series[:z]; plt_kwargs...)
|
||
elseif line_type == :scatter
|
||
PGFPlots.Scatter(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :steppre
|
||
plt_kwargs[:style] *= "const plot mark right,"
|
||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :stepmid
|
||
plt_kwargs[:style] *= "const plot mark mid,"
|
||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :steppost
|
||
plt_kwargs[:style] *= "const plot,"
|
||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :hist
|
||
#TODO patch this in PGFPlots.jl instead; the problem is that PGFPlots will
|
||
# save _all_ data points in the figure which can be quite heavy
|
||
plt_hist = hist(plt_series[:y])
|
||
plt_kwargs[:style] *= "ybar interval,"
|
||
_pgfplots_get_line_color!(plt_kwargs, plt_series)
|
||
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
|
||
PGFPlots.Linear(plt_hist[1][1:end-1]+plt_hist[1].step/2, plt_hist[2]; plt_kwargs...)
|
||
elseif line_type == :hist2d
|
||
PGFPlots.Histogram2(plt_series[:x], plt_series[:y])
|
||
elseif line_type == :bar
|
||
plt_kwargs[:style] *= "ybar,"
|
||
_pgfplots_get_line_color!(plt_kwargs, plt_series)
|
||
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
|
||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :sticks || line_type == :ysticks
|
||
plt_kwargs[:style] *= "ycomb"
|
||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :xsticks
|
||
plt_kwargs[:style] *= "xcomb"
|
||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||
elseif line_type == :contour
|
||
PGFPlots.Contour(plt_series[:z].surf, plt_series[:x], plt_series[:y])
|
||
end
|
||
end
|
||
|
||
# ---------------------------------------------------------------------------
|
||
|
||
function _create_plot(pkg::PGFPlotsBackend, d::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, KW[])
|
||
end
|
||
|
||
|
||
function _add_series(::PGFPlotsBackend, plt::Plot, d::KW)
|
||
# TODO: add one series to the underlying package
|
||
push!(plt.seriesargs, d)
|
||
plt
|
||
end
|
||
|
||
function _add_annotations{X,Y,V}(plt::Plot{PGFPlotsBackend}, 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{PGFPlotsBackend})
|
||
end
|
||
|
||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||
function _update_plot(plt::Plot{PGFPlotsBackend}, d::KW)
|
||
end
|
||
|
||
function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW)
|
||
end
|
||
|
||
# ----------------------------------------------------------------
|
||
|
||
# accessors for x/y data
|
||
|
||
# function getxy(plt::Plot{PGFPlotsBackend}, i::Int)
|
||
# d = plt.seriesargs[i]
|
||
# d[:x], d[:y]
|
||
# end
|
||
#
|
||
# function setxy!{X,Y}(plt::Plot{PGFPlotsBackend}, xy::Tuple{X,Y}, i::Integer)
|
||
# d = plt.seriesargs[i]
|
||
# d[:x], d[:y] = xy
|
||
# plt
|
||
# end
|
||
|
||
# ----------------------------------------------------------------
|
||
|
||
function _create_subplot(subplt::Subplot{PGFPlotsBackend}, 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{PGFPlotsBackend}, isx::Bool)
|
||
# TODO: call expand limits for each plot data
|
||
end
|
||
|
||
function _remove_axis(plt::Plot{PGFPlotsBackend}, isx::Bool)
|
||
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
|
||
end
|
||
|
||
# ----------------------------------------------------------------
|
||
|
||
function _pgfplots_get_axis_kwargs(d)
|
||
axisargs = KW()
|
||
for arg in (:xlabel, :ylabel, :zlabel, :title)
|
||
axisargs[arg] = d[arg]
|
||
end
|
||
axisargs[:style] = ""
|
||
axisargs[:style] *= d[:xflip] == true ? "x dir=reverse," : ""
|
||
axisargs[:style] *= d[:yflip] == true ? "y dir=reverse," : ""
|
||
if d[:xscale] in (:log, :log2, :ln, :log10)
|
||
axisargs[:xmode] = "log"
|
||
if d[:xscale] == :log2
|
||
axisargs[:style] *= "log basis x=2,"
|
||
elseif d[:xscale] in (:log, :log10)
|
||
axisargs[:style] *= "log basis x=10,"
|
||
end
|
||
end
|
||
if d[:yscale] in (:log, :log2, :ln, :log10)
|
||
axisargs[:ymode] = "log"
|
||
if d[:yscale] == :log2
|
||
axisargs[:style] *= "log basis y=2,"
|
||
elseif d[:yscale] in (:log, :log10)
|
||
axisargs[:style] *= "log basis x=10,"
|
||
end
|
||
end
|
||
if d[:zscale] in (:log, :log2, :ln, :log10)
|
||
axisargs[:zmode] = "log"
|
||
if d[:zscale] == :log2
|
||
axisargs[:style] *= "log basis z=2,"
|
||
elseif d[:zscale] in (:log, :log10)
|
||
axisargs[:style] *= "log basis x=10,"
|
||
end
|
||
end
|
||
|
||
# Control background color
|
||
axisargs[:style] *= "axis background/.style={fill=$(_pgfplots_get_color(d, :background_color))},"
|
||
# Control x/y-limits
|
||
if d[:xlims] !== :auto
|
||
axisargs[:xmin] = d[:xlims][1]
|
||
axisargs[:xmax] = d[:xlims][2]
|
||
end
|
||
if d[:ylims] !== :auto
|
||
axisargs[:ymin] = d[:ylims][1]
|
||
axisargs[:ymax] = d[:ylims][2]
|
||
end
|
||
if d[:grid] == true
|
||
axisargs[:style] *= "grid = major,"
|
||
elseif d[:grid] == false
|
||
|
||
end
|
||
|
||
if d[:aspect_ratio] == :equal || d[:aspect_ratio] == 1
|
||
axisargs[:axisEqual] = "true"
|
||
end
|
||
|
||
if ((d[:legend] != :none) || (d[:legend] != :best)) && (d[:legend] in keys(_pgfplots_legend_pos))
|
||
axisargs[:legendPos] = _pgfplots_legend_pos[d[:legend]]
|
||
end
|
||
axisargs
|
||
end
|
||
|
||
# ----------------------------------------------------------------
|
||
|
||
################# This is the important method to implement!!! #################
|
||
function _make_pgf_plot(plt::Plot{PGFPlotsBackend})
|
||
os = Any[]
|
||
# We need to send the :legend KW to the axis
|
||
for plt_series in plt.seriesargs
|
||
plt_series[:legend] = plt.plotargs[:legend]
|
||
push!(os, _pgfplots_axis(plt_series))
|
||
end
|
||
axisargs =_pgfplots_get_axis_kwargs(plt.plotargs)
|
||
plt.o = PGFPlots.Axis([os...]; axisargs...)
|
||
end
|
||
|
||
function Base.writemime(io::IO, mime::MIME"image/svg+xml", plt::AbstractPlot{PGFPlotsBackend})
|
||
plt.o = _make_pgf_plot(plt)
|
||
writemime(io, mime, plt.o)
|
||
end
|
||
|
||
# function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PGFPlotsBackend})
|
||
# end
|
||
|
||
function Base.display(::PlotsDisplay, plt::AbstractPlot{PGFPlotsBackend})
|
||
plt.o = _make_pgf_plot(plt)
|
||
display(plt.o)
|
||
end
|
||
|
||
# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsBackend})
|
||
# # TODO: display/show the subplot
|
||
# end
|