Merge 2b4f9f15ff7e9592b6d9163dc36e5e0e5a2472e5 into 25bf71aff229af4bd01d9fa4c94bc8d2bd0a3e6f

This commit is contained in:
Michael Krabbe Borregaard 2017-05-16 07:37:55 +00:00 committed by GitHub
commit d360b12508
28 changed files with 817 additions and 208 deletions

41
NEWS.md
View File

@ -3,14 +3,47 @@
#### notes on release changes, ongoing development, and future planned work #### notes on release changes, ongoing development, and future planned work
- All new development should target 0.9! - All new development should target 0.12!
- Minor version 0.8 is the last one to support Julia 0.4!! - Minor version 0.11 is the last one to support Julia 0.5!!
- Critical bugfixes only - Critical bugfixes only
- `backports` branch is for Julia 0.4 - `backports` branch is for Julia 0.4
--- ---
## 0.9 (current master/dev) ## 0.11 (current master/dev)
#### 0.11.0
- julia 0.6 compatibility
- matplotlib 0.2.0 compatibility
- add inspectdr backend
- improved histogram functionality:
- added a `:stephist` and `:scatterhist` series type as well as ``:barhist` (the default)
- support for log scale axes with histograms
- support for plotting `StatsBase.Histogram`
- allowing bins to be specified as `:sturges`, `:rice`, `:scott` or :fd
- allow `normalization` to be specified as :density (for unequal bins) or :pdf (sum to 1)
- add a `plotattr` function to access documentation for Plots attribute
- add `fill_z` attribute for pyplot
- add colorbar_title to plotlyjs
- enable standalone window for plotlyjs
- improved support for pgfplots, ticks rotation, clims, series_annotations
- restore colorbars for GR
- better axis labels for heatmap in GR
- better marker sizes in GR
- fix color representation in GR
- update GR legend
- fix image bug on GR
- fix glvisualize dependencies
- set dotted grid lines for pyplot
- several improvements to inspectdr
- improved tick positions for TimeType x axes
- support for improved color gradient capability in PlotUtils
- add a showlibrary recipe to display color libraries
- add a showgradient recipe to display color gradients
- add `vectorfield` as an alias for `quiver`
- use `PlotUtils.adaptedgrid` for functions
#### 0.9.5 #### 0.9.5
@ -331,7 +364,7 @@
- z-axis keywords - z-axis keywords
- 3D indexing overhaul: `push!`, `append!` support - 3D indexing overhaul: `push!`, `append!` support
- matplotlib colormap constants (`:inferno` is the new default colormap for Plots) - matplotlib colormap constants (`:inferno` is the new default colormap for Plots)
- `typealias KW Dict{Symbol,Any}` used in place of splatting in many places - `const KW = Dict{Symbol,Any}` used in place of splatting in many places
- png generation for plotly backend using wkhtmltoimage - png generation for plotly backend using wkhtmltoimage
- `normalize` and `weights` keywords - `normalize` and `weights` keywords
- background/foreground subcategories for fine-tuning of looks - background/foreground subcategories for fine-tuning of looks

View File

@ -1,6 +1,6 @@
# Plots # Plots
[![Build Status](https://travis-ci.org/tbreloff/Plots.jl.svg?branch=master)](https://travis-ci.org/tbreloff/Plots.jl) [![Build Status](https://travis-ci.org/JuliaPlots/Plots.jl.svg?branch=master)](https://travis-ci.org/JuliaPlots/Plots.jl)
[![Join the chat at https://gitter.im/tbreloff/Plots.jl](https://badges.gitter.im/tbreloff/Plots.jl.svg)](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/tbreloff/Plots.jl](https://badges.gitter.im/tbreloff/Plots.jl.svg)](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.3.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.3) --> <!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.3.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
<!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.4.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.4) --> <!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.4.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->

View File

@ -1,9 +1,10 @@
julia 0.5 julia 0.5
RecipesBase RecipesBase
PlotUtils PlotUtils 0.4.1
PlotThemes PlotThemes 0.1.3
Reexport Reexport
Measures Measures
Showoff Showoff
StatsBase 0.14.0
StaticArrays StaticArrays

View File

@ -9,6 +9,7 @@ using Base.Meta
@reexport using PlotUtils @reexport using PlotUtils
@reexport using PlotThemes @reexport using PlotThemes
import Showoff import Showoff
import StatsBase
export export
grid, grid,
@ -99,13 +100,15 @@ export
center, center,
P2, P2,
P3, P3,
BezierCurve BezierCurve,
plotattr
# --------------------------------------------------------- # ---------------------------------------------------------
import Measures import Measures
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
typealias BBox Measures.Absolute2DBox const BBox = Measures.Absolute2DBox
export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h
# --------------------------------------------------------- # ---------------------------------------------------------
@ -127,6 +130,7 @@ include("animation.jl")
include("output.jl") include("output.jl")
include("examples.jl") include("examples.jl")
include("arg_desc.jl") include("arg_desc.jl")
include("plotattr.jl")
# --------------------------------------------------------- # ---------------------------------------------------------
@ -145,6 +149,9 @@ end
@shorthands bar @shorthands bar
@shorthands barh @shorthands barh
@shorthands histogram @shorthands histogram
@shorthands barhist
@shorthands stephist
@shorthands scatterhist
@shorthands histogram2d @shorthands histogram2d
@shorthands density @shorthands density
@shorthands heatmap @shorthands heatmap

View File

@ -80,7 +80,8 @@ function buildanimation(animdir::AbstractString, fn::AbstractString;
catch err catch err
warn("""Tried to create gif using convert (ImageMagick), but got error: $err warn("""Tried to create gif using convert (ImageMagick), but got error: $err
ImageMagick can be installed by executing `Pkg.add("ImageMagick")` ImageMagick can be installed by executing `Pkg.add("ImageMagick")`.
You may also need to install the imagemagick c++ library through your operating system.
Will try ffmpeg, but it's lower quality...)""") Will try ffmpeg, but it's lower quality...)""")
# low quality # low quality

View File

@ -21,7 +21,7 @@ const _arg_desc = KW(
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)", :markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.", :markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.", :markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
:bins => "Integer, NTuple{2,Integer}, AbstractVector. For histogram-types, defines the number of bins, or the edges, of the histogram.", :bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto. For histogram-types, defines the number of bins, or the edges, of the histogram, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd)",
:smooth => "Bool. Add a regression line?", :smooth => "Bool. Add a regression line?",
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.", :group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
:x => "Various. Input data. First Dimension", :x => "Various. Input data. First Dimension",
@ -40,7 +40,7 @@ const _arg_desc = KW(
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.", :ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.", :quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.", :arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.",
:normalize => "Bool. Should normalize histogram types? Trying for area == 1.", :normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a PDF with integral of 1) and :density (only normalize in respect to bin sizes).",
:weights => "AbstractVector. Used in histogram types for weighted counts.", :weights => "AbstractVector. Used in histogram types for weighted counts.",
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.", :contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.", :match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",

View File

@ -35,7 +35,9 @@ const _3dTypes = [
] ]
const _allTypes = vcat([ const _allTypes = vcat([
:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :none, :line, :path, :steppre, :steppost, :sticks, :scatter,
:heatmap, :hexbin, :histogram, :histogram2d, :histogram3d, :density, :bar, :hline, :vline, :heatmap, :hexbin, :barbins, :barhist, :histogram, :scatterbins,
:scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :histogram3d,
:density, :bar, :hline, :vline,
:contour, :pie, :shape, :image :contour, :pie, :shape, :image
], _3dTypes) ], _3dTypes)
@ -65,6 +67,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
:polygon => :shape, :polygon => :shape,
:box => :boxplot, :box => :boxplot,
:velocity => :quiver, :velocity => :quiver,
:vectorfield => :quiver,
:gradient => :quiver, :gradient => :quiver,
:img => :image, :img => :image,
:imshow => :image, :imshow => :image,
@ -77,7 +80,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
add_non_underscore_aliases!(_typeAliases) add_non_underscore_aliases!(_typeAliases)
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density) like_histogram(seriestype::Symbol) = seriestype in (:histogram, :barhist, :barbins)
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost) like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image) like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image)
@ -153,6 +156,8 @@ const _markerAliases = Dict{Symbol,Symbol}(
) )
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt] const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
const _logScales = [:ln, :log2, :log10]
const _logScaleBases = Dict(:ln => e, :log2 => 2.0, :log10 => 10.0)
const _scaleAliases = Dict{Symbol,Symbol}( const _scaleAliases = Dict{Symbol,Symbol}(
:none => :identity, :none => :identity,
:log => :log10, :log => :log10,
@ -180,7 +185,7 @@ const _series_defaults = KW(
:markerstrokewidth => 1, :markerstrokewidth => 1,
:markerstrokecolor => :match, :markerstrokecolor => :match,
:markerstrokealpha => nothing, :markerstrokealpha => nothing,
:bins => 30, # number of bins for hists :bins => :auto, # number of bins for hists
:smooth => false, # regression line? :smooth => false, # regression line?
:group => nothing, # groupby vector :group => nothing, # groupby vector
:x => nothing, :x => nothing,
@ -445,7 +450,7 @@ add_aliases(:color_palette, :palette)
add_aliases(:overwrite_figure, :clf, :clearfig, :overwrite, :reuse) add_aliases(:overwrite_figure, :clf, :clearfig, :overwrite, :reuse)
add_aliases(:xerror, :xerr, :xerrorbar) add_aliases(:xerror, :xerr, :xerrorbar)
add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar) add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar)
add_aliases(:quiver, :velocity, :quiver2d, :gradient) add_aliases(:quiver, :velocity, :quiver2d, :gradient, :vectorfield)
add_aliases(:normalize, :norm, :normed, :normalized) add_aliases(:normalize, :norm, :normed, :normalized)
add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio) add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio)
add_aliases(:match_dimensions, :transpose, :transpose_z) add_aliases(:match_dimensions, :transpose, :transpose_z)
@ -1260,7 +1265,7 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
end end
# scatter plots don't have a line, but must have a shape # scatter plots don't have a line, but must have a shape
if d[:seriestype] in (:scatter, :scatter3d) if d[:seriestype] in (:scatter, :scatterbins, :scatterhist, :scatter3d)
d[:linewidth] = 0 d[:linewidth] = 0
if d[:markershape] == :none if d[:markershape] == :none
d[:markershape] = :circle d[:markershape] = :circle

View File

@ -156,6 +156,30 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
scale = axis[:scale] scale = axis[:scale]
sf = scalefunc(scale) sf = scalefunc(scale)
# If the axis input was a Date or DateTime use a special logic to find
# "round" Date(Time)s as ticks
# This bypasses the rest of optimal_ticks_and_labels, because
# optimize_datetime_ticks returns ticks AND labels: the label format (Date
# or DateTime) is chosen based on the time span between amin and amax
# rather than on the input format
# TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime
if ticks == nothing && scale == :identity
if axis[:formatter] == dateformatter
# optimize_datetime_ticks returns ticks and labels(!) based on
# integers/floats corresponding to the DateTime type. Thus, the axes
# limits, which resulted from converting the Date type to integers,
# are converted to 'DateTime integers' (actually floats) before
# being passed to optimize_datetime_ticks.
# (convert(Int, convert(DateTime, convert(Date, i))) == 87600000*i)
ticks, labels = optimize_datetime_ticks(864e5 * amin, 864e5 * amax;
k_min = 2, k_max = 4)
# Now the ticks are converted back to floats corresponding to Dates.
return ticks / 864e5, labels
elseif axis[:formatter] == datetimeformatter
return optimize_datetime_ticks(amin, amax; k_min = 2, k_max = 4)
end
end
# get a list of well-laid-out ticks # get a list of well-laid-out ticks
scaled_ticks = if ticks == nothing scaled_ticks = if ticks == nothing
optimize_ticks( optimize_ticks(
@ -214,7 +238,7 @@ function get_ticks(axis::Axis)
# @show ticks dvals cv dv # @show ticks dvals cv dv
# TODO: better/smarter cutoff values for sampling ticks # TODO: better/smarter cutoff values for sampling ticks
if length(cv) > 30 if length(cv) > 30 && ticks == :auto
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)] rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
cv[rng], dv[rng] cv[rng], dv[rng]
else else

View File

@ -1,4 +1,4 @@
``#= #=
TODO TODO
* move all gl_ methods to GLPlot * move all gl_ methods to GLPlot
* integrate GLPlot UI * integrate GLPlot UI
@ -7,7 +7,6 @@ TODO
* polar plots * polar plots
* labes and axis * labes and axis
* fix units in all visuals (e.g dotted lines, marker scale, surfaces) * fix units in all visuals (e.g dotted lines, marker scale, surfaces)
* why is there so little unicode supported in the font!??!?
=# =#
const _glvisualize_attr = merge_with_base_supported([ const _glvisualize_attr = merge_with_base_supported([
@ -134,11 +133,6 @@ function empty_screen!(screen)
end end
nothing nothing
end end
function poll_reactive()
# run_till_now blocks when message queue is empty!
Base.n_avail(Reactive._messages) > 0 && Reactive.run_till_now()
end
function get_plot_screen(list::Vector, name, result = []) function get_plot_screen(list::Vector, name, result = [])
for elem in list for elem in list
@ -155,19 +149,20 @@ function get_plot_screen(screen, name, result = [])
end end
function create_window(plt::Plot{GLVisualizeBackend}, visible) function create_window(plt::Plot{GLVisualizeBackend}, visible)
name = Symbol("Plots.jl") name = Symbol("__Plots.jl")
# make sure we have any screen open # make sure we have any screen open
if isempty(GLVisualize.get_screens()) if isempty(GLVisualize.get_screens())
# create a fresh, new screen # create a fresh, new screen
parent_screen = GLVisualize.glscreen( parent_screen = GLVisualize.glscreen(
"Plot", "Plots",
resolution = plt[:size], resolution = plt[:size],
visible = visible visible = visible
) )
@async GLWindow.waiting_renderloop(parent_screen) @async GLWindow.waiting_renderloop(parent_screen)
GLVisualize.add_screen(parent_screen)
end end
# now lets get ourselves a permanent Plotting screen # now lets get ourselves a permanent Plotting screen
plot_screens = get_plot_screen(GLVisualize.get_screens(), name) plot_screens = get_plot_screen(GLVisualize.current_screen(), name)
screen = if isempty(plot_screens) # no screen with `name` screen = if isempty(plot_screens) # no screen with `name`
parent = GLVisualize.current_screen() parent = GLVisualize.current_screen()
screen = GLWindow.Screen( screen = GLWindow.Screen(
@ -183,7 +178,7 @@ function create_window(plt::Plot{GLVisualizeBackend}, visible)
else else
# okay this is silly! Lets see if we can. There is an ID we could use # okay this is silly! Lets see if we can. There is an ID we could use
# will not be fine for more than 255 screens though -.-. # will not be fine for more than 255 screens though -.-.
error("multiple Plot screens. Please don't use any screen with the name Plots.jl") error("multiple Plot screens. Please don't use any screen with the name $name")
end end
# Since we own this window, we can do deep cleansing # Since we own this window, we can do deep cleansing
empty_screen!(screen) empty_screen!(screen)
@ -1141,8 +1136,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
vis = gl_bar(d, kw_args) vis = gl_bar(d, kw_args)
elseif st == :image elseif st == :image
extract_extrema(d, kw_args) extract_extrema(d, kw_args)
z = transpose_z(series, d[:z].surf, false) vis = GL.gl_image(d[:z].surf, kw_args)
vis = GL.gl_image(z, kw_args)
elseif st == :boxplot elseif st == :boxplot
extract_c(d, kw_args, :fill) extract_c(d, kw_args, :fill)
vis = gl_boxplot(d, kw_args) vis = gl_boxplot(d, kw_args)
@ -1182,7 +1176,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
if _3d if _3d
GLAbstraction.center!(sp_screen) GLAbstraction.center!(sp_screen)
end end
Reactive.post_empty() GLAbstraction.post_empty()
yield() yield()
end end
end end
@ -1422,6 +1416,8 @@ function label_scatter(d, w, ho)
color = get(kw, :color, nothing) color = get(kw, :color, nothing)
kw[:color] = isa(color, Array) ? first(color) : color kw[:color] = isa(color, Array) ? first(color) : color
end end
strcolor = get(kw, :stroke_color, RGBA{Float32}(0,0,0,0))
kw[:stroke_color] = isa(strcolor, Array) ? first(strcolor) : strcolor
p = get(kw, :primitive, GeometryTypes.Circle) p = get(kw, :primitive, GeometryTypes.Circle)
if isa(p, GLNormalMesh) if isa(p, GLNormalMesh)
bb = GeometryTypes.AABB{Float32}(GeometryTypes.vertices(p)) bb = GeometryTypes.AABB{Float32}(GeometryTypes.vertices(p))
@ -1436,6 +1432,9 @@ function label_scatter(d, w, ho)
kw[:scale] = Vec3f0(w/2) kw[:scale] = Vec3f0(w/2)
delete!(kw, :offset) delete!(kw, :offset)
end end
if isa(p, Array)
kw[:primitive] = GeometryTypes.Circle
end
GL.gl_scatter(Point2f0[(w/2, ho)], kw) GL.gl_scatter(Point2f0[(w/2, ho)], kw)
end end

View File

@ -172,6 +172,8 @@ function gr_polyline(x, y, func = GR.polyline; arrowside=:none)
end end
end end
gr_inqtext(x, y, s::Symbol) = gr_inqtext(x, y, string(s))
function gr_inqtext(x, y, s) function gr_inqtext(x, y, s)
if length(s) >= 2 && s[1] == '$' && s[end] == '$' if length(s) >= 2 && s[1] == '$' && s[end] == '$'
GR.inqtextext(x, y, s[2:end-1]) GR.inqtextext(x, y, s[2:end-1])
@ -182,6 +184,8 @@ function gr_inqtext(x, y, s)
end end
end end
gr_text(x, y, s::Symbol) = gr_text(x, y, string(s))
function gr_text(x, y, s) function gr_text(x, y, s)
if length(s) >= 2 && s[1] == '$' && s[end] == '$' if length(s) >= 2 && s[1] == '$' && s[end] == '$'
GR.mathtex(x, y, s[2:end-1]) GR.mathtex(x, y, s[2:end-1])
@ -283,7 +287,8 @@ end
# draw ONE symbol marker # draw ONE symbol marker
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol) function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
GR.setmarkertype(gr_markertype[shape]) GR.setmarkertype(gr_markertype[shape])
GR.setmarkersize(0.3msize) w, h = gr_plot_size
GR.setmarkersize(0.3msize / ((w + h) * 0.001))
GR.polymarker([xi], [yi]) GR.polymarker([xi], [yi])
end end
@ -330,9 +335,10 @@ end
# --------------------------------------------------------- # ---------------------------------------------------------
function gr_set_line(w, style, c) #, a) function gr_set_line(lw, style, c) #, a)
GR.setlinetype(gr_linetype[style]) GR.setlinetype(gr_linetype[style])
GR.setlinewidth(w) w, h = gr_plot_size
GR.setlinewidth(max(0, lw / ((w + h) * 0.001)))
gr_set_linecolor(c) #, a) gr_set_linecolor(c) #, a)
end end
@ -421,7 +427,7 @@ end
function gr_colorbar(sp::Subplot) function gr_colorbar(sp::Subplot)
if sp[:colorbar] != :none if sp[:colorbar] != :none
gr_set_viewport_cmap(sp) gr_set_viewport_cmap(sp)
GR.colormap() GR.colorbar()
gr_set_viewport_plotarea() gr_set_viewport_plotarea()
end end
end end
@ -545,6 +551,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end end
if st == :heatmap if st == :heatmap
outside_ticks = true outside_ticks = true
x, y = heatmap_edges(series[:x]), heatmap_edges(series[:y])
expand_extrema!(sp[:xaxis], x)
expand_extrema!(sp[:yaxis], y)
data_lims = gr_xy_axislims(sp)
end end
end end
@ -653,6 +663,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
flip = sp[:yaxis][:flip] flip = sp[:yaxis][:flip]
mirror = sp[:xaxis][:mirror] mirror = sp[:xaxis][:mirror]
gr_set_font(sp[:xaxis][:tickfont], gr_set_font(sp[:xaxis][:tickfont],
halign = (:left, :hcenter, :right)[sign(sp[:xaxis][:rotation]) + 2],
valign = (mirror ? :bottom : :top), valign = (mirror ? :bottom : :top),
color = sp[:xaxis][:foreground_color_axis], color = sp[:xaxis][:foreground_color_axis],
rotation = sp[:xaxis][:rotation]) rotation = sp[:xaxis][:rotation])
@ -670,6 +681,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
mirror = sp[:yaxis][:mirror] mirror = sp[:yaxis][:mirror]
gr_set_font(sp[:yaxis][:tickfont], gr_set_font(sp[:yaxis][:tickfont],
halign = (mirror ? :left : :right), halign = (mirror ? :left : :right),
valign = (:top, :vcenter, :bottom)[sign(sp[:yaxis][:rotation]) + 2],
color = sp[:yaxis][:foreground_color_axis], color = sp[:yaxis][:foreground_color_axis],
rotation = sp[:yaxis][:rotation]) rotation = sp[:yaxis][:rotation])
for (cv, dv) in zip(yticks...) for (cv, dv) in zip(yticks...)
@ -757,10 +769,6 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# recompute data # recompute data
if typeof(z) <: Surface if typeof(z) <: Surface
# if st == :heatmap
# expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
# expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
# end
z = vec(transpose_z(series, z.surf, false)) z = vec(transpose_z(series, z.surf, false))
elseif ispolar(sp) elseif ispolar(sp)
if frng != nothing if frng != nothing
@ -805,12 +813,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
isfinite(clims[1]) && (zmin = clims[1]) isfinite(clims[1]) && (zmin = clims[1])
isfinite(clims[2]) && (zmax = clims[2]) isfinite(clims[2]) && (zmax = clims[2])
end end
GR.setspace(zmin, zmax, 0, 90)
if typeof(series[:levels]) <: Array if typeof(series[:levels]) <: Array
h = series[:levels] h = series[:levels]
else else
h = linspace(zmin, zmax, series[:levels]) h = linspace(zmin, zmax, series[:levels])
end end
GR.setspace(zmin, zmax, 0, 90)
if series[:fillrange] != nothing if series[:fillrange] != nothing
GR.surface(x, y, z, GR.OPTION_CELL_ARRAY) GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
else else
@ -848,6 +856,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
isfinite(clims[1]) && (zmin = clims[1]) isfinite(clims[1]) && (zmin = clims[1])
isfinite(clims[2]) && (zmax = clims[2]) isfinite(clims[2]) && (zmax = clims[2])
end end
GR.setspace(zmin, zmax, 0, 90)
grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad() grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z] colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z]
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 + rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
@ -942,7 +951,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
elseif st == :image elseif st == :image
z = transpose_z(series, series[:z].surf, true) z = transpose_z(series, series[:z].surf, true)
h, w = size(z) w, h = size(z)
if eltype(z) <: Colors.AbstractGray if eltype(z) <: Colors.AbstractGray
grey = round(UInt8, float(z) * 255) grey = round(UInt8, float(z) * 255)
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey) rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
@ -1015,7 +1024,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end end
if series[:markershape] != :none if series[:markershape] != :none
gr_draw_markers(series, xpos-[0.06,0.02], [ypos,ypos], 10, nothing) gr_draw_markers(series, xpos - .035, ypos, 6, nothing)
end end
if typeof(series[:label]) <: Array if typeof(series[:label]) <: Array
@ -1101,7 +1110,7 @@ function _display(plt::Plot{GRBackend})
ENV["GKS_FILEPATH"] = filepath ENV["GKS_FILEPATH"] = filepath
gr_display(plt) gr_display(plt)
GR.emergencyclosegks() GR.emergencyclosegks()
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(readbytes, filepath)), "\a") content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(read, filepath)), "\a")
println(content) println(content)
rm(filepath) rm(filepath)
else else

View File

@ -38,7 +38,7 @@ const _inspectdr_attr = merge_with_base_supported([
# :ribbon, :quiver, :arrow, # :ribbon, :quiver, :arrow,
# :orientation, # :orientation,
:overwrite_figure, :overwrite_figure,
# :polar, :polar,
# :normalize, :weights, # :normalize, :weights,
# :contours, :aspect_ratio, # :contours, :aspect_ratio,
:match_dimensions, :match_dimensions,
@ -66,6 +66,9 @@ const _inspectdr_scale = [:identity, :ln, :log2, :log10]
is_marker_supported(::InspectDRBackend, shape::Shape) = true is_marker_supported(::InspectDRBackend, shape::Shape) = true
_inspectdr_to_pixels(bb::BoundingBox) =
InspectDR.BoundingBox(to_pixels(left(bb)), to_pixels(right(bb)), to_pixels(top(bb)), to_pixels(bottom(bb)))
#Do we avoid Map to avoid possible pre-comile issues? #Do we avoid Map to avoid possible pre-comile issues?
function _inspectdr_mapglyph(s::Symbol) function _inspectdr_mapglyph(s::Symbol)
s == :rect && return :square s == :rect && return :square
@ -125,16 +128,17 @@ end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
function _inspectdr_getscale(s::Symbol) function _inspectdr_getscale(s::Symbol, yaxis::Bool)
#TODO: Support :asinh, :sqrt #TODO: Support :asinh, :sqrt
kwargs = yaxis? (:tgtmajor=>8, :tgtminor=>2): () #More grid lines on y-axis
if :log2 == s if :log2 == s
return InspectDR.AxisScale(:log2) return InspectDR.AxisScale(:log2; kwargs...)
elseif :log10 == s elseif :log10 == s
return InspectDR.AxisScale(:log10) return InspectDR.AxisScale(:log10; kwargs...)
elseif :ln == s elseif :ln == s
return InspectDR.AxisScale(:ln) return InspectDR.AxisScale(:ln; kwargs...)
else #identity else #identity
return InspectDR.AxisScale(:lin) return InspectDR.AxisScale(:lin; kwargs...)
end end
end end
@ -209,14 +213,10 @@ end
# Set up the subplot within the backend object. # Set up the subplot within the backend object.
function _initialize_subplot(plt::Plot{InspectDRBackend}, sp::Subplot{InspectDRBackend}) function _initialize_subplot(plt::Plot{InspectDRBackend}, sp::Subplot{InspectDRBackend})
plot = sp.o plot = sp.o
#Don't do anything without a "subplot" object: Will process later. #Don't do anything without a "subplot" object: Will process later.
if nothing == plot; return; end if nothing == plot; return; end
plot.data = [] plot.data = []
plot.markers = [] #Clear old markers plot.userannot = [] #Clear old markers/text annotation/polyline "annotation"
plot.atext = [] #Clear old annotation
plot.apline = [] #Clear old poly lines
return plot return plot
end end
@ -237,6 +237,12 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series)
_vectorize(v) = isa(v, Vector)? v: collect(v) #InspectDR only supports vectors _vectorize(v) = isa(v, Vector)? v: collect(v) #InspectDR only supports vectors
x = _vectorize(series[:x]); y = _vectorize(series[:y]) x = _vectorize(series[:x]); y = _vectorize(series[:y])
#No support for polar grid... but can still perform polar transformation:
if ispolar(sp)
Θ = x; r = y
x = r.*cos(Θ); y = r.*sin(Θ)
end
# doesn't handle mismatched x/y - wrap data (pyplot behaviour): # doesn't handle mismatched x/y - wrap data (pyplot behaviour):
nx = length(x); ny = length(y) nx = length(x); ny = length(y)
if nx < ny if nx < ny
@ -267,7 +273,7 @@ For st in :shape:
apline = InspectDR.PolylineAnnotation( apline = InspectDR.PolylineAnnotation(
x[rng], y[rng], line=line, fillcolor=fillcolor x[rng], y[rng], line=line, fillcolor=fillcolor
) )
push!(plot.apline, apline) InspectDR.add(plot, apline)
end end
end end
@ -328,23 +334,35 @@ end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend}) function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
const gridon = InspectDR.grid(vmajor=true, hmajor=true) const gridon = InspectDR.GridRect(vmajor=true, hmajor=true)
const gridoff = InspectDR.grid() const gridoff = InspectDR.GridRect()
const plot = sp.o const plot = sp.o
const strip = plot.strips[1] #Only 1 strip supported with Plots.jl
#No independent control of grid???
strip.grid = sp[:grid]? gridon: gridoff
xaxis = sp[:xaxis]; yaxis = sp[:yaxis] xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
xscale = _inspectdr_getscale(xaxis[:scale]) plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
yscale = _inspectdr_getscale(yaxis[:scale]) strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
plot.axes = InspectDR.AxesRect(xscale, yscale)
xmin, xmax = axis_limits(xaxis) xmin, xmax = axis_limits(xaxis)
ymin, ymax = axis_limits(yaxis) ymin, ymax = axis_limits(yaxis)
plot.ext = InspectDR.PExtents2D() #reset if ispolar(sp)
plot.ext_full = InspectDR.PExtents2D(xmin, xmax, ymin, ymax) #Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
rmax = max(abs(ymin), abs(ymax))
xmin, xmax = -rmax, rmax
ymin, ymax = -rmax, rmax
end
plot.xext = InspectDR.PExtents1D() #reset
strip.yext = InspectDR.PExtents1D() #reset
plot.xext_full = InspectDR.PExtents1D(xmin, xmax)
strip.yext_full = InspectDR.PExtents1D(ymin, ymax)
a = plot.annotation a = plot.annotation
a.title = sp[:title] a.title = sp[:title]
a.xlabel = xaxis[:guide]; a.ylabel = yaxis[:guide] a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]]
l = plot.layout l = plot.layout
l.frame.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
l.framedata.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside]) l.framedata.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
l.framedata.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis]) l.framedata.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
l.fnttitle = InspectDR.Font(sp[:titlefont].family, l.fnttitle = InspectDR.Font(sp[:titlefont].family,
@ -360,8 +378,6 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
_inspectdr_mapptsize(xaxis[:tickfont].pointsize), _inspectdr_mapptsize(xaxis[:tickfont].pointsize),
color = _inspectdr_mapcolor(xaxis[:foreground_color_text]) color = _inspectdr_mapcolor(xaxis[:foreground_color_text])
) )
#No independent control of grid???
l.grid = sp[:grid]? gridon: gridoff
leg = l.legend leg = l.legend
leg.enabled = (sp[:legend] != :none) leg.enabled = (sp[:legend] != :none)
#leg.width = 150 #TODO: compute??? #leg.width = 150 #TODO: compute???
@ -378,6 +394,13 @@ function _before_layout_calcs(plt::Plot{InspectDRBackend})
const mplot = _inspectdr_getmplot(plt.o) const mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end if nothing == mplot; return; end
mplot.title = plt[:plot_title]
if "" == mplot.title
#Don't use window_title... probably not what you want.
#mplot.title = plt[:window_title]
end
mplot.frame.fillcolor = _inspectdr_mapcolor(plt[:background_color_outside])
resize!(mplot.subplots, length(plt.subplots)) resize!(mplot.subplots, length(plt.subplots))
nsubplots = length(plt.subplots) nsubplots = length(plt.subplots)
for (i, sp) in enumerate(plt.subplots) for (i, sp) in enumerate(plt.subplots)
@ -385,15 +408,13 @@ function _before_layout_calcs(plt::Plot{InspectDRBackend})
mplot.subplots[i] = InspectDR.Plot2D() mplot.subplots[i] = InspectDR.Plot2D()
end end
sp.o = mplot.subplots[i] sp.o = mplot.subplots[i]
plot = sp.o
_initialize_subplot(plt, sp) _initialize_subplot(plt, sp)
_inspectdr_setupsubplot(sp) _inspectdr_setupsubplot(sp)
sp.o.layout.frame.fillcolor =
_inspectdr_mapcolor(plt[:background_color_outside])
# add the annotations # add the annotations
for ann in sp[:annotations] for ann in sp[:annotations]
_inspectdr_add_annotations(mplot.subplots[i], ann...) _inspectdr_add_annotations(plot, ann...)
end end
end end
@ -422,8 +443,19 @@ end
# Set the (left, top, right, bottom) minimum padding around the plot area # Set the (left, top, right, bottom) minimum padding around the plot area
# to fit ticks, tick labels, guides, colorbars, etc. # to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot{InspectDRBackend}) function _update_min_padding!(sp::Subplot{InspectDRBackend})
sp.minpad = (20mm, 5mm, 2mm, 10mm) plot = sp.o
#TODO: Add support for padding. if !isa(plot, InspectDR.Plot2D); return sp.minpad; end
#Computing plotbounds with 0-BoundingBox returns required padding:
bb = InspectDR.plotbounds(plot.layout, InspectDR.BoundingBox(0,0,0,0))
#NOTE: plotbounds always pads for titles, legends, etc. even if not in use.
#TODO: possibly zero-out items not in use??
# add in the user-specified margin to InspectDR padding:
leftpad = abs(bb.xmin)*px + sp[:left_margin]
toppad = abs(bb.ymin)*px + sp[:top_margin]
rightpad = abs(bb.xmax)*px + sp[:right_margin]
bottompad = abs(bb.ymax)*px + sp[:bottom_margin]
sp.minpad = (leftpad, toppad, rightpad, bottompad)
end end
# ---------------------------------------------------------------- # ----------------------------------------------------------------
@ -432,6 +464,13 @@ end
function _update_plot_object(plt::Plot{InspectDRBackend}) function _update_plot_object(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o) mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end if nothing == mplot; return; end
for (i, sp) in enumerate(plt.subplots)
graphbb = _inspectdr_to_pixels(plotarea(sp))
plot = mplot.subplots[i]
plot.plotbb = InspectDR.plotbounds(plot.layout, graphbb)
end
gplot = _inspectdr_getgui(plt.o) gplot = _inspectdr_getgui(plt.o)
if nothing == gplot; return; end if nothing == gplot; return; end
@ -452,19 +491,21 @@ const _inspectdr_mimeformats_nodpi = Dict(
# "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface # "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface
"application/pdf" => "pdf" "application/pdf" => "pdf"
) )
_inspectdr_show(io::IO, mime::MIME, ::Void) = _inspectdr_show(io::IO, mime::MIME, ::Void, w, h) =
throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated")) throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated"))
_inspectdr_show(io::IO, mime::MIME, mplot) = show(io, mime, mplot) function _inspectdr_show(io::IO, mime::MIME, mplot, w, h)
InspectDR._show(io, mime, mplot, Float64(w), Float64(h))
end
for (mime, fmt) in _inspectdr_mimeformats_dpi for (mime, fmt) in _inspectdr_mimeformats_dpi
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend}) @eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend})
dpi = plt[:dpi]#TODO: support dpi = plt[:dpi]#TODO: support
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o)) _inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
end end
end end
for (mime, fmt) in _inspectdr_mimeformats_nodpi for (mime, fmt) in _inspectdr_mimeformats_nodpi
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend}) @eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend})
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o)) _inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
end end
end end
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show _show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show

View File

@ -3,7 +3,7 @@
# significant contributions by: @pkofod # significant contributions by: @pkofod
const _pgfplots_attr = merge_with_base_supported([ const _pgfplots_attr = merge_with_base_supported([
# :annotations, :annotations,
# :background_color_legend, # :background_color_legend,
:background_color_inside, :background_color_inside,
# :background_color_outside, # :background_color_outside,
@ -22,17 +22,17 @@ const _pgfplots_attr = merge_with_base_supported([
:guide, :lims, :ticks, :scale, :flip, :rotation, :guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont, :tickfont, :guidefont, :legendfont,
:grid, :legend, :grid, :legend,
# :colorbar, :colorbar,
# :marker_z, :levels, :marker_z, #:levels,
# :ribbon, :quiver, :arrow, # :ribbon, :quiver, :arrow,
# :orientation, # :orientation,
# :overwrite_figure, # :overwrite_figure,
# :polar, :polar,
# :normalize, :weights, :contours, # :normalize, :weights, :contours,
:aspect_ratio, :aspect_ratio,
# :match_dimensions, # :match_dimensions,
]) ])
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour] const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape]
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape) const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
const _pgfplots_scale = [:identity, :ln, :log2, :log10] const _pgfplots_scale = [:identity, :ln, :log2, :log10]
@ -98,14 +98,35 @@ const _pgf_series_extrastyle = KW(
:xsticks => "xcomb", :xsticks => "xcomb",
) )
# PGFPlots uses the anchors to define orientations for example to align left
# one needs to use the right edge as anchor
const _pgf_annotation_halign = KW(
:center => "",
:left => "right",
:right => "left"
)
# -------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------
# takes in color,alpha, and returns color and alpha appropriate for pgf style # takes in color,alpha, and returns color and alpha appropriate for pgf style
function pgf_color(c) function pgf_color(c::Colorant)
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c)) cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c))
cstr, alpha(c) cstr, alpha(c)
end end
function pgf_color(grad::ColorGradient)
# Can't handle ColorGradient here, fallback to defaults.
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", 0.0, 0.60560316,0.97868012)
cstr, 1
end
# Generates a colormap for pgfplots based on a ColorGradient
function pgf_colormap(grad::ColorGradient)
join(map(grad.colors) do c
@sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c),blue(c))
end,", ")
end
function pgf_fillstyle(d::KW) function pgf_fillstyle(d::KW)
cstr,a = pgf_color(d[:fillcolor]) cstr,a = pgf_color(d[:fillcolor])
"fill = $cstr, fill opacity=$a" "fill = $cstr, fill opacity=$a"
@ -136,6 +157,19 @@ function pgf_marker(d::KW)
}""" }"""
end end
function pgf_add_annotation!(o,x,y,val)
# Construct the style string.
# Currently supports color and orientation
cstr,a = pgf_color(val.font.color)
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
x, y,
style="""
$(get(_pgf_annotation_halign,val.font.halign,"")),
color=$cstr, draw opacity=$(convert(Float16,a)),
rotate=$(val.font.rotation)
"""))
end
# -------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------
function pgf_series(sp::Subplot, series::Series) function pgf_series(sp::Subplot, series::Series)
@ -143,11 +177,10 @@ function pgf_series(sp::Subplot, series::Series)
st = d[:seriestype] st = d[:seriestype]
style = [] style = []
kw = KW() kw = KW()
push!(style, pgf_linestyle(d)) push!(style, pgf_linestyle(d))
push!(style, pgf_marker(d)) push!(style, pgf_marker(d))
if d[:fillrange] != nothing if d[:fillrange] != nothing || st in (:shape,)
push!(style, pgf_fillstyle(d)) push!(style, pgf_fillstyle(d))
end end
@ -163,6 +196,10 @@ function pgf_series(sp::Subplot, series::Series)
d[:z].surf, d[:x], d[:y] d[:z].surf, d[:x], d[:y]
elseif is3d(st) elseif is3d(st)
d[:x], d[:y], d[:z] d[:x], d[:y], d[:z]
elseif d[:marker_z] != nothing
# If a marker_z is used pass it as third coordinate to a 2D plot.
# See "Scatter Plots" in PGFPlots documentation
d[:x], d[:y], d[:marker_z]
else else
d[:x], d[:y] d[:x], d[:y]
end end
@ -211,6 +248,9 @@ function pgf_axis(sp::Subplot, letter)
# axis guide # axis guide
kw[Symbol(letter,:label)] = axis[:guide] kw[Symbol(letter,:label)] = axis[:guide]
# Add ticklabel rotations
push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}")
# flip/reverse? # flip/reverse?
axis[:flip] && push!(style, "$letter dir=reverse") axis[:flip] && push!(style, "$letter dir=reverse")
@ -249,8 +289,12 @@ end
function _update_plot_object(plt::Plot{PGFPlotsBackend}) function _update_plot_object(plt::Plot{PGFPlotsBackend})
plt.o = PGFPlots.Axis[] plt.o = PGFPlots.Axis[]
# Obtain the total height of the plot by extracting the maximal bottom
# coordinate from the bounding box.
total_height = bottom(bbox(plt.layout))
for sp in plt.subplots for sp in plt.subplots
# first build the PGFPlots.Axis object # first build the PGFPlots.Axis object
style = ["unbounded coords=jump"] style = ["unbounded coords=jump"]
kw = KW() kw = KW()
@ -265,10 +309,12 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
# bounding box values are in mm # bounding box values are in mm
# note: bb origin is top-left, pgf is bottom-left # note: bb origin is top-left, pgf is bottom-left
# A round on 2 decimal places should be enough precision for 300 dpi
# plots.
bb = bbox(sp) bb = bbox(sp)
push!(style, """ push!(style, """
xshift = $(left(bb).value)mm, xshift = $(left(bb).value)mm,
yshift = $((height(bb) - (bottom(bb))).value)mm, yshift = $(round((total_height - (bottom(bb))).value,2))mm,
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])} axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
""") """)
kw[:width] = "$(width(bb).value)mm" kw[:width] = "$(width(bb).value)mm"
@ -288,19 +334,62 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
kw[:legendPos] = _pgfplots_legend_pos[legpos] kw[:legendPos] = _pgfplots_legend_pos[legpos]
end end
o = PGFPlots.Axis(; style = style, kw...) axisf = PGFPlots.Axis
if sp[:projection] == :polar
axisf = PGFPlots.PolarAxis
end
# Search series for any gradient. In case one series uses a gradient set
# the colorbar and colomap.
# The reasoning behind doing this on the axis level is that pgfplots
# colorbar seems to only works on axis level and needs the proper colormap for
# correctly displaying it.
# It's also possible to assign the colormap to the series itself but
# then the colormap needs to be added twice, once for the axis and once for the
# series.
# As it is likely that all series within the same axis use the same
# colormap this should not cause any problem.
for series in series_list(sp)
for col in (:markercolor, :fillcolor)
if typeof(series.d[col]) == ColorGradient
push!(style,"colormap={plots}{$(pgf_colormap(series.d[col]))}")
if sp[:colorbar] == :none
kw[:colorbar] = "false"
else
kw[:colorbar] = "true"
end
# goto is needed to break out of col and series for
@goto colorbar_end
end
end
end
@label colorbar_end
o = axisf(; style = style, kw...)
# add the series object to the PGFPlots.Axis # add the series object to the PGFPlots.Axis
for series in series_list(sp) for series in series_list(sp)
push!(o, pgf_series(sp, series)) push!(o, pgf_series(sp, series))
# add series annotations
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt))
end
end end
# add the annotations
for ann in sp[:annotations]
pgf_add_annotation!(o,ann...)
end
# add the PGFPlots.Axis to the list # add the PGFPlots.Axis to the list
push!(plt.o, o) push!(plt.o, o)
end end
end end
function _show(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend}) function _show(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
show(io, mime, plt.o) show(io, mime, plt.o)
end end

View File

@ -19,7 +19,7 @@ const _plotly_attr = merge_with_base_supported([
:window_title, :window_title,
:guide, :lims, :ticks, :scale, :flip, :rotation, :guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont, :tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar, :grid, :legend, :colorbar, :colorbar_title,
:marker_z, :fill_z, :levels, :marker_z, :fill_z, :levels,
:ribbon, :quiver, :ribbon, :quiver,
:orientation, :orientation,
@ -31,6 +31,7 @@ const _plotly_attr = merge_with_base_supported([
:hover, :hover,
:inset_subplots, :inset_subplots,
:bar_width, :bar_width,
:clims,
]) ])
const _plotly_seriestype = [ const _plotly_seriestype = [
@ -268,7 +269,7 @@ function plotly_layout(plt::Plot)
w, h = plt[:size] w, h = plt[:size]
d_out[:width], d_out[:height] = w, h d_out[:width], d_out[:height] = w, h
d_out[:paper_bgcolor] = rgba_string(plt[:background_color_outside]) d_out[:paper_bgcolor] = rgba_string(plt[:background_color_outside])
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>20) d_out[:margin] = KW(:l=>0, :b=>20, :r=>0, :t=>20)
d_out[:annotations] = KW[] d_out[:annotations] = KW[]
@ -408,6 +409,10 @@ plotly_surface_data(series::Series, a::AbstractVector) = a
plotly_surface_data(series::Series, a::AbstractMatrix) = transpose_z(series, a, false) plotly_surface_data(series::Series, a::AbstractMatrix) = transpose_z(series, a, false)
plotly_surface_data(series::Series, a::Surface) = plotly_surface_data(series, a.surf) plotly_surface_data(series::Series, a::Surface) = plotly_surface_data(series, a.surf)
#ensures that a gradient is called if a single color is supplied where a gradient is needed (e.g. if a series recipe defines marker_z)
as_gradient(grad::ColorGradient, α) = grad
as_gradient(grad, α) = cgrad(alpha = α)
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict) # get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
function plotly_series(plt::Plot, series::Series) function plotly_series(plt::Plot, series::Series)
st = series[:seriestype] st = series[:seriestype]
@ -438,6 +443,13 @@ function plotly_series(plt::Plot, series::Series)
end end
end end
d_out[:colorbar] = KW(:title => sp[:colorbar_title])
clims = sp[:clims]
if is_2tuple(clims)
d_out[:zmin], d_out[:zmax] = clims
end
# set the "type" # set the "type"
if st in (:path, :scatter, :scattergl) if st in (:path, :scatter, :scattergl)
d_out[:type] = st==:scattergl ? "scattergl" : "scatter" d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
@ -533,9 +545,10 @@ function plotly_series(plt::Plot, series::Series)
rgba_string(series[:markercolor]) rgba_string(series[:markercolor])
else else
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha]) # grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
grad = series[:markercolor] grad = as_gradient(series[:markercolor], series[:markeralpha])
zmin, zmax = extrema(series[:marker_z]) zmin, zmax = extrema(series[:marker_z])
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]] zrange = zmax == zmin ? 1 : zmax - zmin # if all marker_z values are the same, plot all markers same color (avoids division by zero in next line)
[rgba_string(grad[(zi - zmin) / zrange]) for zi in series[:marker_z]]
end end
end end

View File

@ -102,8 +102,18 @@ _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hac
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "pdf") _show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "pdf")
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "eps") _show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "eps")
function write_temp_html(plt::Plot{PlotlyJSBackend})
filename = string(tempname(), ".html")
savefig(plt, filename)
filename
end
function _display(plt::Plot{PlotlyJSBackend}) function _display(plt::Plot{PlotlyJSBackend})
display(plt.o) if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
display(plt.o)
else
standalone_html_window(plt)
end
end end

View File

@ -55,6 +55,10 @@ function add_backend_string(::PyPlotBackend)
withenv("PYTHON" => "") do withenv("PYTHON" => "") do
Pkg.build("PyPlot") Pkg.build("PyPlot")
end end
import Conda
Conda.add("qt=4.8.5")
# now restart julia!
""" """
end end
@ -217,6 +221,12 @@ function py_stepstyle(seriestype::Symbol)
return "default" return "default"
end end
function py_fillstepstyle(seriestype::Symbol)
seriestype == :steppost && return "post"
seriestype == :steppre && return "pre"
return nothing
end
# # untested... return a FontProperties object from a Plots.Font # # untested... return a FontProperties object from a Plots.Font
# function py_font(font::Font) # function py_font(font::Font)
# pyfont.pymember("FontProperties")( # pyfont.pymember("FontProperties")(
@ -669,6 +679,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end end
z = transpose_z(series, z) z = transpose_z(series, z)
if st == :surface if st == :surface
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
if series[:fill_z] != nothing if series[:fill_z] != nothing
# the surface colors are different than z-value # the surface colors are different than z-value
extrakw[:facecolors] = py_shading(series[:fillcolor], transpose_z(series, series[:fill_z].surf)) extrakw[:facecolors] = py_shading(series[:fillcolor], transpose_z(series, series[:fill_z].surf))
@ -859,7 +874,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
dim1, expand_data(fillrange[1], n), expand_data(fillrange[2], n) dim1, expand_data(fillrange[1], n), expand_data(fillrange[2], n)
end end
handle = ax[f](args...; handle = ax[f](args..., trues(n), false, py_fillstepstyle(st);
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
facecolor = py_fillcolor(series), facecolor = py_fillcolor(series),
linewidths = 0 linewidths = 0
@ -1045,7 +1060,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
end end
if sp[:grid] if sp[:grid]
fgcolor = py_color(sp[:foreground_color_grid]) fgcolor = py_color(sp[:foreground_color_grid])
pyaxis[:grid](true, color = fgcolor) pyaxis[:grid](true, color = fgcolor, linestyle = ":")
ax[:set_axisbelow](true) ax[:set_axisbelow](true)
end end
py_set_axis_colors(ax, axis) py_set_axis_colors(ax, axis)
@ -1061,7 +1076,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
py_add_legend(plt, sp, ax) py_add_legend(plt, sp, ax)
# this sets the bg color inside the grid # this sets the bg color inside the grid
ax[:set_axis_bgcolor](py_color(sp[:background_color_inside])) ax[:set_facecolor](py_color(sp[:background_color_inside]))
end end
py_drawfig(fig) py_drawfig(fig)
end end
@ -1093,7 +1108,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
# optionally add the width of colorbar labels and colorbar to rightpad # optionally add the width of colorbar labels and colorbar to rightpad
if haskey(sp.attr, :cbar_ax) if haskey(sp.attr, :cbar_ax)
bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]()) bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]())
sp.attr[:cbar_width] = _cbar_width + width(bb) + 1mm + (sp[:colorbar_title] == "" ? 0px : 30px) sp.attr[:cbar_width] = _cbar_width + width(bb) + 2.3mm + (sp[:colorbar_title] == "" ? 0px : 30px)
rightpad = rightpad + sp.attr[:cbar_width] rightpad = rightpad + sp.attr[:cbar_width]
end end

View File

@ -23,11 +23,11 @@ function open_browser_window(filename::AbstractString)
@static if is_apple() @static if is_apple()
return run(`open $(filename)`) return run(`open $(filename)`)
end end
@static if is_linux() @static if is_linux() || is_bsd() # is_bsd() addition is as yet untested, but based on suggestion in https://github.com/JuliaPlots/Plots.jl/issues/681
return run(`xdg-open $(filename)`) return run(`xdg-open $(filename)`)
end end
@static if is_windows() @static if is_windows()
return run(`$(ENV["COMSPEC"]) /c start $(filename)`) return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`)
end end
warn("Unknown OS... cannot open browser window.") warn("Unknown OS... cannot open browser window.")
end end

View File

@ -1,7 +1,7 @@
typealias P2 StaticArrays.SVector{2,Float64} const P2 = StaticArrays.SVector{2,Float64}
typealias P3 StaticArrays.SVector{3,Float64} const P3 = StaticArrays.SVector{3,Float64}
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b)) nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b)) nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
@ -174,7 +174,7 @@ function center(shape::Shape)
Cx / 6A, Cy / 6A Cx / 6A, Cy / 6A
end end
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape)) function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
sx, sy = coords(shape) sx, sy = coords(shape)
cx, cy = c cx, cy = c
for i=1:length(sx) for i=1:length(sx)
@ -521,7 +521,7 @@ Base.Array(surf::Surface) = surf.surf
for f in (:length, :size) for f in (:length, :size)
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...) @eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
end end
Base.copy(surf::Surface) = Surface{typeof(surf.surf)}(copy(surf.surf)) Base.copy(surf::Surface) = Surface(copy(surf.surf))
Base.eltype{T}(surf::Surface{T}) = eltype(T) Base.eltype{T}(surf::Surface{T}) = eltype(T)
function expand_extrema!(a::Axis, surf::Surface) function expand_extrema!(a::Axis, surf::Surface)

View File

@ -5,7 +5,7 @@
# This should cut down on boilerplate code and allow more focused dispatch on type # This should cut down on boilerplate code and allow more focused dispatch on type
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now # note: returns meta information... mainly for use with automatic labeling from DataFrames for now
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}}) const FuncOrFuncs = @compat(Union{Function, AVec{Function}})
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none)) all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))

View File

@ -172,6 +172,24 @@ PlotExample("",
end)] end)]
), ),
PlotExample("Animation with subplots",
"The `layout` macro can be used to create an animation with subplots.",
[:(begin
l = @layout([[a; b] c])
p = plot(plot([sin,cos],1,leg=false),
scatter([atan,cos],1,leg=false),
plot(log,1,xlims=(1,10π),ylims=(0,5),leg=false),layout=l)
anim = Animation()
for x = linspace(1,10π,100)
plot(push!(p,x,Float64[sin(x),cos(x),atan(x),cos(x),log(x)]))
frame(anim)
end
end)]
),
PlotExample("Open/High/Low/Close", PlotExample("Open/High/Low/Close",
"Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y` argument. This uses recipes to first convert the tuples to OHLC objects, and subsequently create a :path series with the appropriate line segments.", "Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y` argument. This uses recipes to first convert the tuples to OHLC objects, and subsequently create a :path series with the appropriate line segments.",
[:(begin [:(begin

View File

@ -271,7 +271,7 @@ function setup_ijulia()
show(io, MIME("text/html"), plt) show(io, MIME("text/html"), plt)
end end
end end
set_ijulia_output("text/html") @eval set_ijulia_output("text/html")
end end
end end
@ -302,6 +302,10 @@ function setup_atom()
Media.render(pane, Atom.div(".fill", Atom.HTML(stringmime(MIME("text/html"), plt)))) Media.render(pane, Atom.div(".fill", Atom.HTML(stringmime(MIME("text/html"), plt))))
plt[:size] = sz plt[:size] = sz
end end
# special handling for PlotlyJS
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
display(Plots.PlotsDisplay(), plt)
end
else else
# #
function Media.render(pane::Atom.PlotPane, plt::Plot) function Media.render(pane::Atom.PlotPane, plt::Plot)
@ -314,14 +318,8 @@ function setup_atom()
# special handling for plotly... use PlotsDisplay # special handling for plotly... use PlotsDisplay
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend}) function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend})
display(Plots.PlotsDisplay(), plt) display(Plots.PlotsDisplay(), plt)
s = "PlotPane turned off. The plotly and plotlyjs backends cannot render in the PlotPane due to javascript issues." s = "PlotPane turned off. The plotly backend cannot render in the PlotPane due to javascript issues. Plotlyjs is similar to plotly and is compatible with the plot pane."
Media.render(pane, Atom.div(Atom.HTML(s))) Media.render(pane, Atom.div(Atom.HTML(s)))
end end
# special handling for PlotlyJS to pass through to that render method
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
Plots.prepare_output(plt)
Media.render(pane, plt.o)
end
end end
end end

View File

@ -277,6 +277,13 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
attr[Symbol(letter,k)] = v attr[Symbol(letter,k)] = v
end end
end end
for k in (:scale,), letter in (:x,:y,:z)
# Series recipes may need access to this information
lk = Symbol(letter,k)
if haskey(attr, lk)
kw[lk] = attr[lk]
end
end
end end
sp_attrs[sp] = attr sp_attrs[sp] = attr
end end
@ -357,7 +364,7 @@ function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
expand_extrema!(sp[:xaxis], (0,w)) expand_extrema!(sp[:xaxis], (0,w))
expand_extrema!(sp[:yaxis], (0,h)) expand_extrema!(sp[:yaxis], (0,h))
sp[:yaxis].d[:flip] = true sp[:yaxis].d[:flip] = true
elseif !(st in (:pie, :histogram, :histogram2d)) elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
expand_extrema!(sp, d) expand_extrema!(sp, d)
end end
end end

56
src/plotattr.jl Normal file
View File

@ -0,0 +1,56 @@
const _attribute_defaults = Dict(:Series => _series_defaults,
:Subplot => _subplot_defaults,
:Plot => _plot_defaults,
:Axis => _axis_defaults)
attrtypes() = join(keys(_attribute_defaults), ", ")
attributes(attrtype::Symbol) = sort(collect(keys(_attribute_defaults[attrtype])))
function lookup_aliases(attrtype, attribute)
attribute = Symbol(attribute)
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
in(attribute, keys(_attribute_defaults[attrtype])) && return attribute
error("There is no attribute named $attribute in $attrtype")
end
function plotattr()
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
end
function plotattr(attrtype::Symbol)
in(attrtype, keys(_attribute_defaults)) || error("Viable options are $(attrtypes())")
println("Defined $attrtype attributes are:\n$(join(attributes(attrtype), ", "))")
end
function plotattr(attribute::AbstractString)
attribute = Symbol(attribute)
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
for (k, v) in _attribute_defaults
if in(attribute, keys(v))
return plotattr(k, "$attribute")
end
end
error("There is no attribute named $attribute")
end
function plotattr(attrtype::Symbol, attribute::AbstractString)
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
attribute = Symbol(lookup_aliases(attrtype, attribute))
desc = get(_arg_desc, attribute, "")
first_period_idx = findfirst(desc, '.')
typedesc = desc[1:first_period_idx-1]
desc = strip(desc[first_period_idx+1:end])
als = keys(filter((_,v)->v==attribute, _keyAliases)) |> collect |> sort
als = join(map(string,als), ", ")
def = _attribute_defaults[attrtype][attribute]
# Looks up the different elements and plots them
println("$attribute ", typedesc == "" ? "" : "{$typedesc}", "\n",
als == "" ? "" : "$als\n",
"\n$desc\n",
"$(attrtype) attribute, ", def == "" ? "" : " default: $def")
end

View File

@ -323,10 +323,11 @@ end
# create a bar plot as a filled step function # create a bar plot as a filled step function
@recipe function f(::Type{Val{:bar}}, x, y, z) @recipe function f(::Type{Val{:bar}}, x, y, z)
nx, ny = length(x), length(y) procx, procy, xscale, yscale, baseline = _preprocess_barlike(d, x, y)
nx, ny = length(procx), length(procy)
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis] axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
cv = [discrete_value!(axis, xi)[1] for xi=x] cv = [discrete_value!(axis, xi)[1] for xi=procx]
x = if nx == ny procx = if nx == ny
cv cv
elseif nx == ny + 1 elseif nx == ny + 1
0.5diff(cv) + cv[1:end-1] 0.5diff(cv) + cv[1:end-1]
@ -337,9 +338,9 @@ end
# compute half-width of bars # compute half-width of bars
bw = d[:bar_width] bw = d[:bar_width]
hw = if bw == nothing hw = if bw == nothing
0.5mean(diff(x)) 0.5mean(diff(procx))
else else
Float64[0.5cycle(bw,i) for i=1:length(x)] Float64[0.5cycle(bw,i) for i=1:length(procx)]
end end
# make fillto a vector... default fills to 0 # make fillto a vector... default fills to 0
@ -347,16 +348,21 @@ end
if fillto == nothing if fillto == nothing
fillto = 0 fillto = 0
end end
if (yscale in _logScales) && !all(_is_positive, fillto)
fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto)
end
# create the bar shapes by adding x/y segments # create the bar shapes by adding x/y segments
xseg, yseg = Segments(), Segments() xseg, yseg = Segments(), Segments()
for i=1:ny for i=1:ny
center = x[i] yi = procy[i]
hwi = cycle(hw,i) if !isnan(yi)
yi = y[i] center = procx[i]
fi = cycle(fillto,i) hwi = cycle(hw,i)
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi) fi = cycle(fillto,i)
push!(yseg, yi, fi, fi, yi, yi) push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
push!(yseg, yi, fi, fi, yi, yi)
end
end end
# widen limits out a bit # widen limits out a bit
@ -378,106 +384,327 @@ end
end end
@deps bar shape @deps bar shape
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Histograms # Histograms
# edges from number of bins _bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
function calc_edges(v, bins::Integer)
vmin, vmax = extrema(v) _is_positive(x) = (x > 0) && !(x 0)
linspace(vmin, vmax, bins+1)
_positive_else_nan{T}(::Type{T}, x::Real) = _is_positive(x) ? T(x) : T(NaN)
function _scale_adjusted_values{T<:AbstractFloat}(::Type{T}, V::AbstractVector, scale::Symbol)
if scale in _logScales
[_positive_else_nan(T, x) for x in V]
else
[T(x) for x in V]
end
end end
# just pass through arrays
calc_edges(v, bins::AVec) = bins
# find the bucket index of this value function _binbarlike_baseline{T<:Real}(min_value::T, scale::Symbol)
function bucket_index(vi, edges) if (scale in _logScales)
for (i,e) in enumerate(edges) !isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
if vi <= e else
return max(1,i-1) zero(T)
end
end
function _preprocess_binbarlike_weights{T<:AbstractFloat}(::Type{T}, w, wscale::Symbol)
w_adj = _scale_adjusted_values(T, w, wscale)
w_min = minimum(w_adj)
w_max = maximum(w_adj)
baseline = _binbarlike_baseline(w_min, wscale)
w_adj, baseline
end
function _preprocess_barlike(d, x, y)
xscale = get(d, :xscale, :identity)
yscale = get(d, :yscale, :identity)
weights, baseline = _preprocess_binbarlike_weights(float(eltype(y)), y, yscale)
x, weights, xscale, yscale, baseline
end
function _preprocess_binlike(d, x, y)
xscale = get(d, :xscale, :identity)
yscale = get(d, :yscale, :identity)
T = float(promote_type(eltype(x), eltype(y)))
edge = T.(x)
weights, baseline = _preprocess_binbarlike_weights(T, y, yscale)
edge, weights, xscale, yscale, baseline
end
@recipe function f(::Type{Val{:barbins}}, x, y, z)
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
if (d[:bar_width] == nothing)
bar_width := diff(edge)
end
x := _bin_centers(edge)
y := weights
seriestype := :bar
()
end
@deps barbins bar
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
xerror := diff(edge)/2
x := _bin_centers(edge)
y := weights
seriestype := :scatter
()
end
@deps scatterbins scatter
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
log_scale_x = xscale in _logScales
log_scale_y = yscale in _logScales
nbins = length(linearindices(weights))
if length(linearindices(edge)) != nbins + 1
error("Edge vector must be 1 longer than weight vector")
end
x = eltype(edge)[]
y = eltype(weights)[]
it_e, it_w = start(edge), start(weights)
a, it_e = next(edge, it_e)
last_w = eltype(weights)(NaN)
i = 1
while (!done(edge, it_e) && !done(edge, it_e))
b, it_e = next(edge, it_e)
w, it_w = next(weights, it_w)
if (log_scale_x && a 0)
a = b/_logScaleBases[xscale]^3
end end
if isnan(w)
if !isnan(last_w)
push!(x, a)
push!(y, baseline)
end
else
if isnan(last_w)
push!(x, a)
push!(y, baseline)
end
push!(x, a)
push!(y, w)
push!(x, b)
push!(y, w)
end
a = b
last_w = w
end end
return length(edges)-1 if (last_w != baseline)
push!(x, a)
push!(y, baseline)
end
(x, y)
end end
function my_hist(v, bins; normed = false, weights = nothing)
edges = calc_edges(v, bins)
counts = zeros(length(edges)-1)
# add a weighted count @recipe function f(::Type{Val{:stepbins}}, x, y, z)
for (i,vi) in enumerate(v) axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis]
idx = bucket_index(vi, edges)
counts[idx] += (weights == nothing ? 1.0 : weights[i]) edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
if !isvertical(d)
xpts, ypts = ypts, xpts
end end
# normalize by bar area? # create a secondary series for the markers
norm_denom = normed ? sum(diff(edges) .* counts) : 1.0 if d[:markershape] != :none
if norm_denom == 0 @series begin
norm_denom = 1.0 seriestype := :scatter
x := _bin_centers(edge)
y := weights
fillrange := nothing
label := ""
primary := false
()
end
markershape := :none
xerror := :none
yerror := :none
end end
edges, counts ./ norm_denom x := xpts
y := ypts
seriestype := :path
()
end
Plots.@deps stepbins path
function _auto_binning_nbins{N}(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto)
_cl(x) = max(ceil(Int, x), 1)
_iqr(v) = quantile(v, 0.75) - quantile(v, 0.25)
_span(v) = maximum(v) - minimum(v)
n_samples = length(linearindices(first(vs)))
# Estimator for number of samples in one row/column of bins along each axis:
n = max(1, n_samples^(1/N))
v = vs[dim]
if mode == :auto
30
elseif mode == :sqrt # Square-root choice
_cl(sqrt(n))
elseif mode == :sturges # Sturges' formula
_cl(log2(n)) + 1
elseif mode == :rice # Rice Rule
_cl(2 * n^(1/3))
elseif mode == :scott # Scott's normal reference rule
_cl(_span(v) / (3.5 * std(v) / n^(1/3)))
elseif mode == :fd # FreedmanDiaconis rule
_cl(_span(v) / (2 * _iqr(v) / n^(1/3)))
else
error("Unknown auto-binning mode $mode")
end::Int
end
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) = StatsBase.histrange(vs[dim], binning, :left)
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) = binning
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::NTuple{N}) =
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...))
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) =
map(dim -> _hist_edge(vs, dim, binning), (1:N...))
_hist_norm_mode(mode::Symbol) = mode
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
function _make_hist{N}(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing)
info("binning = $binning")
edges = _hist_edges(vs, binning)
h = float( weights == nothing ?
StatsBase.fit(StatsBase.Histogram, vs, edges, closed = :left) :
StatsBase.fit(StatsBase.Histogram, vs, weights, edges, closed = :left)
)
normalize!(h, mode = _hist_norm_mode(normed))
end end
@recipe function f(::Type{Val{:histogram}}, x, y, z) @recipe function f(::Type{Val{:histogram}}, x, y, z)
edges, counts = my_hist(y, d[:bins], seriestype := :barhist
normed = d[:normalize],
weights = d[:weights])
x := edges
y := counts
seriestype := :bar
() ()
end end
@deps histogram bar @deps histogram barhist
@recipe function f(::Type{Val{:barhist}}, x, y, z)
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.weights
seriestype := :barbins
()
end
@deps barhist barbins
@recipe function f(::Type{Val{:stephist}}, x, y, z)
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.weights
seriestype := :stepbins
()
end
@deps stephist stepbins
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.weights
seriestype := :scatterbins
()
end
@deps scatterhist scatterbins
@recipe function f{T, E}(h::StatsBase.Histogram{T, 1, E})
seriestype --> :barbins
st_map = Dict(
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
)
seriestype := get(st_map, d[:seriestype], d[:seriestype])
if d[:seriestype] == :scatterbins
# Workaround, error bars currently not set correctly by scatterbins
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, h.edges[1], h.weights)
xerror --> diff(h.edges[1])/2
seriestype := :scatter
(Plots._bin_centers(edge), weights)
else
(h.edges[1], h.weights)
end
end
@recipe function f{H <: StatsBase.Histogram}(hv::AbstractVector{H})
for h in hv
@series begin
h
end
end
end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Histogram 2D # Histogram 2D
# if tuple, map out bins, otherwise use the same for both @recipe function f(::Type{Val{:bins2d}}, x, y, z)
calc_edges_2d(x, y, bins) = calc_edges(x, bins), calc_edges(y, bins) edge_x, edge_y, weights = x, y, z.surf
calc_edges_2d{X,Y}(x, y, bins::Tuple{X,Y}) = calc_edges(x, bins[1]), calc_edges(y, bins[2])
# the 2D version float_weights = float(weights)
function my_hist_2d(x, y, bins; normed = false, weights = nothing) if is(float_weights, weights)
xedges, yedges = calc_edges_2d(x, y, bins) float_weights = deepcopy(float_weights)
counts = zeros(length(yedges)-1, length(xedges)-1)
# add a weighted count
for i=1:length(x)
r = bucket_index(y[i], yedges)
c = bucket_index(x[i], xedges)
counts[r,c] += (weights == nothing ? 1.0 : weights[i])
end end
for (i, c) in enumerate(float_weights)
# normalize to cubic area of the imaginary surface towers
norm_denom = normed ? sum((diff(yedges) * diff(xedges)') .* counts) : 1.0
if norm_denom == 0
norm_denom = 1.0
end
xedges, yedges, counts ./ norm_denom
end
centers(v::AVec) = 0.5 * (v[1:end-1] + v[2:end])
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
normed = d[:normalize],
weights = d[:weights])
for (i,c) in enumerate(counts)
if c == 0 if c == 0
counts[i] = NaN float_weights[i] = NaN
end end
end end
x := centers(xedges)
y := centers(yedges) x := Plots._bin_centers(edge_x)
z := Surface(counts) y := Plots._bin_centers(edge_y)
linewidth := 0 z := Surface(float_weights)
match_dimensions := true
seriestype := :heatmap seriestype := :heatmap
() ()
end end
@deps histogram2d heatmap Plots.@deps bins2d heatmap
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
h = _make_hist((x, y), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.edges[2]
z := Surface(h.weights)
seriestype := :bins2d
()
end
@deps histogram2d bins2d
@recipe function f{T, E}(h::StatsBase.Histogram{T, 2, E})
seriestype --> :bins2d
(h.edges[1], h.edges[2], Surface(h.weights))
end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -789,16 +1016,70 @@ abline!(args...; kw...) = abline!(current(), args...; kw...)
# ------------------------------------------------- # -------------------------------------------------
# Dates # Dates
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int,dt), dt -> string(convert(Date,dt))) dateformatter(dt) = string(convert(Date, dt))
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int,dt), dt -> string(convert(DateTime,dt))) datetimeformatter(dt) = string(convert(DateTime, dt))
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int, dt), dateformatter)
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int, dt), datetimeformatter)
# ------------------------------------------------- # -------------------------------------------------
# Complex Numbers # Complex Numbers
@userplot ComplexPlot @recipe function f{T<:Number}(A::Array{Complex{T}})
@recipe function f(cp::ComplexPlot) xguide --> "Re(x)"
xguide --> "Real Part" yguide --> "Im(x)"
yguide --> "Imaginary Part" real.(A), imag.(A)
seriestype --> :scatter end
real(cp.args[1]), imag(cp.args[1])
# Splits a complex matrix to its real and complex parts
# Reals defaults solid, imaginary defaults dashed
# Label defaults are changed to match the real-imaginary reference / indexing
@recipe function f{T<:Real,T2}(x::AbstractArray{T},y::Array{Complex{T2}})
ylabel --> "Re(y)"
zlabel --> "Im(y)"
x,real.(y),imag.(y)
end
# --------------------------------------------------
# Color Gradients
@userplot ShowLibrary
@recipe function f(cl::ShowLibrary)
if !(length(cl.args) == 1 && isa(cl.args[1], Symbol))
error("showlibrary takes the name of a color library as a Symbol")
end
library = PlotUtils.color_libraries[cl.args[1]]
z = sqrt.((1:15)*(1:20)')
seriestype := :heatmap
ticks := nothing
legend := false
layout --> length(library.lib)
i = 0
for grad in sort(collect(keys(library.lib)))
@series begin
seriescolor := cgrad(grad, cl.args[1])
title := string(grad)
subplot := i += 1
z
end
end
end
@userplot ShowGradient
@recipe function f(grad::ShowGradient)
if !(length(grad.args) == 1 && isa(grad.args[1], Symbol))
error("showgradient takes the name of a color gradient as a Symbol")
end
z = sqrt.((1:15)*(1:20)')
seriestype := :heatmap
ticks := nothing
legend := false
seriescolor := grad.args[1]
title := string(grad.args[1])
z
end end

View File

@ -39,7 +39,7 @@ series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot]
function should_add_to_legend(series::Series) function should_add_to_legend(series::Series)
series.d[:primary] && series.d[:label] != "" && series.d[:primary] && series.d[:label] != "" &&
!(series.d[:seriestype] in ( !(series.d[:seriestype] in (
:hexbin,:histogram2d,:hline,:vline, :hexbin,:bins2d,:histogram2d,:hline,:vline,
:contour,:contourf,:contour3d,:surface,:wireframe, :contour,:contourf,:contour3d,:surface,:wireframe,
:heatmap, :pie, :image :heatmap, :pie, :image
)) ))

View File

@ -2,7 +2,8 @@
function theme(s::Symbol; kw...) function theme(s::Symbol; kw...)
# reset? # reset?
if s == :none || s == :default if s == :none || s == :default
PlotUtils._default_gradient[] = :inferno PlotUtils.clibrary(:Plots)
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
default(; default(;
bg = :white, bg = :white,
bglegend = :match, bglegend = :match,
@ -23,7 +24,8 @@ function theme(s::Symbol; kw...)
# update the default gradient and other defaults # update the default gradient and other defaults
thm = PlotThemes._themes[s] thm = PlotThemes._themes[s]
if thm.gradient != nothing if thm.gradient != nothing
PlotUtils._default_gradient[] = PlotThemes.gradient_name(s) PlotUtils.clibrary(:misc)
PlotUtils.default_cgrad(default = :sequential, sequential = PlotThemes.gradient_name(s))
end end
default(; default(;
bg = thm.bg_secondary, bg = thm.bg_secondary,

View File

@ -2,9 +2,9 @@
# TODO: I declare lots of types here because of the lacking ability to do forward declarations in current Julia # TODO: I declare lots of types here because of the lacking ability to do forward declarations in current Julia
# I should move these to the relevant files when something like "extern" is implemented # I should move these to the relevant files when something like "extern" is implemented
typealias AVec AbstractVector const AVec = AbstractVector
typealias AMat AbstractMatrix const AMat = AbstractMatrix
typealias KW Dict{Symbol,Any} const KW = Dict{Symbol,Any}
immutable PlotsDisplay <: Display end immutable PlotsDisplay <: Display end
@ -62,7 +62,7 @@ Extrema() = Extrema(Inf, -Inf)
# ----------------------------------------------------------- # -----------------------------------------------------------
typealias SubplotMap Dict{Any, Subplot} const SubplotMap = Dict{Any, Subplot}
# ----------------------------------------------------------- # -----------------------------------------------------------

View File

@ -24,7 +24,7 @@ default(size=(500,300))
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that # TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
# is referenced in a button press callback (the button clicked callback will call notify() on that condition) # is referenced in a button press callback (the button clicked callback will call notify() on that condition)
const _current_plots_version = v"0.9.6" const _current_plots_version = v"0.11.2"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2) function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)

View File

@ -23,7 +23,7 @@ facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotBackend() @fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend() @fact backend() --> Plots.PyPlotBackend()
image_comparison_facts(:pyplot, skip=[25,30], eps=img_eps) image_comparison_facts(:pyplot, skip=[6,25,30], eps=img_eps)
end end
facts("GR") do facts("GR") do