Plots.jl/src/backends/pgfplots.jl
2016-05-24 12:20:03 -04:00

389 lines
12 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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,
:seriestype,
:linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokestyle,
# :n,
# :bins,
# :nc,
# :nr,
# :pos,
# :smooth,
# :show,
:size,
:title,
# :window_title,
:x,
:xguide,
:xlims,
# :xticks,
:y,
:yguide,
: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 => "none",
:cross => "+",
:xcross => "x",
:utriangle => "triangle*",
:dtriangle => "triangle*",
:ellipse => "o*",
:rect => "square*",
:star5 => "star",
:star6 => "asterisk",
:diamond => "diamond*",
:pentagon => "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)
push!(kwargs[:style], _pgfplots_linestyles[ls])
end
push!(kwargs[:style], "line width = $(plt[:linewidth]) pt")
end
function _pgfplots_get_marker!(kwargs, plt)
# Control marker shape, size, colors, alphas, and stroke width
mark = plt[:markershape]
α = plt[:markeralpha] == nothing ? 1.0 : plt[:markeralpha]
push!(kwargs[:style], "mark = " * _pgfplots_markers[mark],
"mark size = $(plt[:markersize]/2)",
"mark options = {color=$(_pgfplots_get_color(plt, :markerstrokecolor))",
"fill=$(_pgfplots_get_color(plt, :markercolor))",
"fill opacity = $α",
"line width=$(plt[:markerstrokewidth])")
# Rotate the marker if :dtriangle was chosen
mark == :dtriangle && push!(kwargs[:style], "rotate=180")
# Apply marker stroke style if it is a valid PGFPlots stroke style
if haskey(_pgfplots_linestyles, plt[:markerstrokestyle])
push!(kwargs[:style], _pgfplots_linestyles[plt[:markerstrokestyle]])
end
# End the open mark options bracker
push!(kwargs[:style], "}")
end
function _pgfplots_get_series_color!(kwargs, plt)
α = plt[:seriesalpha] == nothing ? 1.0 : plt[:seriesalpha]
push!(kwargs[:style], "color=$(_pgfplots_get_color(plt, :seriescolor))",
"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))" *
", 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))" *
", 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[:style] = join(kwargs[:style], ',')
kwargs
end
function _pgfplots_axis(plt_series)
line_type = plt_series[:seriestype]
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 _series_added(::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.attr, :annotation_list)
append!(plt.attr[:annotation_list], anns)
else
plt.attr[: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 (:xguide, :yguide, :zguide, :title)
axisargs[arg] = d[arg]
end
axisargs[:style] = []
d[:xflip] == true && push!(axisargs[:style], "x dir=reverse")
d[:yflip] == true && push!(axisargs[:style], "y dir=reverse")
if d[:xscale] in (:log, :log2, :ln, :log10)
axisargs[:xmode] = "log"
if d[:xscale] == :log2
push!(axisargs[:style], "log basis x=2")
elseif d[:xscale] in (:log, :log10)
push!(axisargs[:style], "log basis x=10")
end
end
if d[:yscale] in (:log, :log2, :ln, :log10)
axisargs[:ymode] = "log"
if d[:yscale] == :log2
push!(axisargs[:style], "log basis y=2")
elseif d[:yscale] in (:log, :log10)
push!(axisargs[:style], "log basis x=10")
end
end
if d[:zscale] in (:log, :log2, :ln, :log10)
axisargs[:zmode] = "log"
if d[:zscale] == :log2
push!(axisargs[:style], "log basis z=2")
elseif d[:zscale] in (:log, :log10)
push!(axisargs[:style], "log basis x=10")
end
end
# Control background color
push!(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
d[:grid] == true && push!(axisargs[:style], "grid = major")
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[:style] = join(axisargs[:style], ',')
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.attr[:legend]
push!(os, _pgfplots_axis(plt_series))
end
axisargs =_pgfplots_get_axis_kwargs(plt.attr)
w, h = map(px2mm, plt.attr[:size])
plt.o = PGFPlots.Axis([os...]; width = "$w mm", height = "$h mm", 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