diff --git a/NEWS.md b/NEWS.md index c1c98498..2598bd5f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,14 +3,47 @@ #### notes on release changes, ongoing development, and future planned work -- All new development should target 0.9! -- Minor version 0.8 is the last one to support Julia 0.4!! +- All new development should target 0.12! +- Minor version 0.11 is the last one to support Julia 0.5!! - Critical bugfixes only - `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 @@ -331,7 +364,7 @@ - z-axis keywords - 3D indexing overhaul: `push!`, `append!` support - 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 - `normalize` and `weights` keywords - background/foreground subcategories for fine-tuning of looks diff --git a/README.md b/README.md index a47da192..b88fe561 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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) diff --git a/REQUIRE b/REQUIRE index 9cced79d..e3d740a9 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,9 +1,10 @@ julia 0.5 RecipesBase -PlotUtils -PlotThemes +PlotUtils 0.4.1 +PlotThemes 0.1.3 Reexport Measures Showoff +StatsBase 0.14.0 StaticArrays diff --git a/src/Plots.jl b/src/Plots.jl index 7522d28b..180dedf0 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -9,6 +9,7 @@ using Base.Meta @reexport using PlotUtils @reexport using PlotThemes import Showoff +import StatsBase export grid, @@ -99,13 +100,15 @@ export center, P2, P3, - BezierCurve + BezierCurve, + + plotattr # --------------------------------------------------------- import Measures 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 # --------------------------------------------------------- @@ -127,6 +130,7 @@ include("animation.jl") include("output.jl") include("examples.jl") include("arg_desc.jl") +include("plotattr.jl") # --------------------------------------------------------- @@ -145,6 +149,9 @@ end @shorthands bar @shorthands barh @shorthands histogram +@shorthands barhist +@shorthands stephist +@shorthands scatterhist @shorthands histogram2d @shorthands density @shorthands heatmap diff --git a/src/animation.jl b/src/animation.jl index ee7428df..55a9fe53 100644 --- a/src/animation.jl +++ b/src/animation.jl @@ -80,7 +80,8 @@ function buildanimation(animdir::AbstractString, fn::AbstractString; catch 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...)""") # low quality diff --git a/src/arg_desc.jl b/src/arg_desc.jl index 4efbf1af..cea77071 100644 --- a/src/arg_desc.jl +++ b/src/arg_desc.jl @@ -21,7 +21,7 @@ const _arg_desc = KW( :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`.", :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?", :group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.", :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.", :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.", -: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.", :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`.", diff --git a/src/args.jl b/src/args.jl index 8f82fec0..b93e2d98 100644 --- a/src/args.jl +++ b/src/args.jl @@ -35,7 +35,9 @@ const _3dTypes = [ ] const _allTypes = vcat([ :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 ], _3dTypes) @@ -65,6 +67,7 @@ const _typeAliases = Dict{Symbol,Symbol}( :polygon => :shape, :box => :boxplot, :velocity => :quiver, + :vectorfield => :quiver, :gradient => :quiver, :img => :image, :imshow => :image, @@ -77,7 +80,7 @@ const _typeAliases = Dict{Symbol,Symbol}( 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_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 _logScales = [:ln, :log2, :log10] +const _logScaleBases = Dict(:ln => e, :log2 => 2.0, :log10 => 10.0) const _scaleAliases = Dict{Symbol,Symbol}( :none => :identity, :log => :log10, @@ -180,7 +185,7 @@ const _series_defaults = KW( :markerstrokewidth => 1, :markerstrokecolor => :match, :markerstrokealpha => nothing, - :bins => 30, # number of bins for hists + :bins => :auto, # number of bins for hists :smooth => false, # regression line? :group => nothing, # groupby vector :x => nothing, @@ -445,7 +450,7 @@ add_aliases(:color_palette, :palette) add_aliases(:overwrite_figure, :clf, :clearfig, :overwrite, :reuse) add_aliases(:xerror, :xerr, :xerrorbar) 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(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio) add_aliases(:match_dimensions, :transpose, :transpose_z) @@ -1260,7 +1265,7 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int) end # 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 if d[:markershape] == :none d[:markershape] = :circle diff --git a/src/axes.jl b/src/axes.jl index b9840e7a..9a39ca2e 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -156,6 +156,30 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing) scale = axis[: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 scaled_ticks = if ticks == nothing optimize_ticks( @@ -214,7 +238,7 @@ function get_ticks(axis::Axis) # @show ticks dvals cv dv # 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)] cv[rng], dv[rng] else diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index f1e40181..f053dfb7 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -1,4 +1,4 @@ -``#= +#= TODO * move all gl_ methods to GLPlot * integrate GLPlot UI @@ -7,7 +7,6 @@ TODO * polar plots * labes and axis * 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([ @@ -134,11 +133,6 @@ function empty_screen!(screen) end nothing 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 = []) for elem in list @@ -155,19 +149,20 @@ function get_plot_screen(screen, name, result = []) end function create_window(plt::Plot{GLVisualizeBackend}, visible) - name = Symbol("Plots.jl") + name = Symbol("__Plots.jl") # make sure we have any screen open if isempty(GLVisualize.get_screens()) # create a fresh, new screen parent_screen = GLVisualize.glscreen( - "Plot", + "Plots", resolution = plt[:size], visible = visible ) @async GLWindow.waiting_renderloop(parent_screen) + GLVisualize.add_screen(parent_screen) end # 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` parent = GLVisualize.current_screen() screen = GLWindow.Screen( @@ -183,7 +178,7 @@ function create_window(plt::Plot{GLVisualizeBackend}, visible) else # 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 -.-. - 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 # Since we own this window, we can do deep cleansing empty_screen!(screen) @@ -1141,8 +1136,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true) vis = gl_bar(d, kw_args) elseif st == :image extract_extrema(d, kw_args) - z = transpose_z(series, d[:z].surf, false) - vis = GL.gl_image(z, kw_args) + vis = GL.gl_image(d[:z].surf, kw_args) elseif st == :boxplot extract_c(d, kw_args, :fill) vis = gl_boxplot(d, kw_args) @@ -1182,7 +1176,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true) if _3d GLAbstraction.center!(sp_screen) end - Reactive.post_empty() + GLAbstraction.post_empty() yield() end end @@ -1422,6 +1416,8 @@ function label_scatter(d, w, ho) color = get(kw, :color, nothing) kw[:color] = isa(color, Array) ? first(color) : color 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) if isa(p, GLNormalMesh) bb = GeometryTypes.AABB{Float32}(GeometryTypes.vertices(p)) @@ -1436,6 +1432,9 @@ function label_scatter(d, w, ho) kw[:scale] = Vec3f0(w/2) delete!(kw, :offset) end + if isa(p, Array) + kw[:primitive] = GeometryTypes.Circle + end GL.gl_scatter(Point2f0[(w/2, ho)], kw) end diff --git a/src/backends/gr.jl b/src/backends/gr.jl index b092edd7..697e2471 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -172,6 +172,8 @@ function gr_polyline(x, y, func = GR.polyline; arrowside=:none) end end +gr_inqtext(x, y, s::Symbol) = gr_inqtext(x, y, string(s)) + function gr_inqtext(x, y, s) if length(s) >= 2 && s[1] == '$' && s[end] == '$' GR.inqtextext(x, y, s[2:end-1]) @@ -182,6 +184,8 @@ function gr_inqtext(x, y, s) end end +gr_text(x, y, s::Symbol) = gr_text(x, y, string(s)) + function gr_text(x, y, s) if length(s) >= 2 && s[1] == '$' && s[end] == '$' GR.mathtex(x, y, s[2:end-1]) @@ -283,7 +287,8 @@ end # draw ONE symbol marker function gr_draw_marker(xi, yi, msize::Number, shape::Symbol) 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]) 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.setlinewidth(w) + w, h = gr_plot_size + GR.setlinewidth(max(0, lw / ((w + h) * 0.001))) gr_set_linecolor(c) #, a) end @@ -421,7 +427,7 @@ end function gr_colorbar(sp::Subplot) if sp[:colorbar] != :none gr_set_viewport_cmap(sp) - GR.colormap() + GR.colorbar() gr_set_viewport_plotarea() end end @@ -545,6 +551,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) end if st == :heatmap 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 @@ -653,6 +663,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) flip = sp[:yaxis][:flip] mirror = sp[:xaxis][:mirror] gr_set_font(sp[:xaxis][:tickfont], + halign = (:left, :hcenter, :right)[sign(sp[:xaxis][:rotation]) + 2], valign = (mirror ? :bottom : :top), color = sp[:xaxis][:foreground_color_axis], rotation = sp[:xaxis][:rotation]) @@ -670,6 +681,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) mirror = sp[:yaxis][:mirror] gr_set_font(sp[:yaxis][:tickfont], halign = (mirror ? :left : :right), + valign = (:top, :vcenter, :bottom)[sign(sp[:yaxis][:rotation]) + 2], color = sp[:yaxis][:foreground_color_axis], rotation = sp[:yaxis][:rotation]) for (cv, dv) in zip(yticks...) @@ -757,10 +769,6 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) # recompute data 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)) elseif ispolar(sp) 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[2]) && (zmax = clims[2]) end + GR.setspace(zmin, zmax, 0, 90) if typeof(series[:levels]) <: Array h = series[:levels] else h = linspace(zmin, zmax, series[:levels]) end - GR.setspace(zmin, zmax, 0, 90) if series[:fillrange] != nothing GR.surface(x, y, z, GR.OPTION_CELL_ARRAY) else @@ -848,6 +856,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) isfinite(clims[1]) && (zmin = clims[1]) isfinite(clims[2]) && (zmax = clims[2]) end + GR.setspace(zmin, zmax, 0, 90) grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad() colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z] 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 z = transpose_z(series, series[:z].surf, true) - h, w = size(z) + w, h = size(z) if eltype(z) <: Colors.AbstractGray grey = round(UInt8, float(z) * 255) 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 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 if typeof(series[:label]) <: Array @@ -1101,7 +1110,7 @@ function _display(plt::Plot{GRBackend}) ENV["GKS_FILEPATH"] = filepath gr_display(plt) 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) rm(filepath) else diff --git a/src/backends/inspectdr.jl b/src/backends/inspectdr.jl index fea44064..c58ef884 100644 --- a/src/backends/inspectdr.jl +++ b/src/backends/inspectdr.jl @@ -38,7 +38,7 @@ const _inspectdr_attr = merge_with_base_supported([ # :ribbon, :quiver, :arrow, # :orientation, :overwrite_figure, -# :polar, + :polar, # :normalize, :weights, # :contours, :aspect_ratio, :match_dimensions, @@ -66,6 +66,9 @@ const _inspectdr_scale = [:identity, :ln, :log2, :log10] 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? function _inspectdr_mapglyph(s::Symbol) 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 + kwargs = yaxis? (:tgtmajor=>8, :tgtminor=>2): () #More grid lines on y-axis if :log2 == s - return InspectDR.AxisScale(:log2) + return InspectDR.AxisScale(:log2; kwargs...) elseif :log10 == s - return InspectDR.AxisScale(:log10) + return InspectDR.AxisScale(:log10; kwargs...) elseif :ln == s - return InspectDR.AxisScale(:ln) + return InspectDR.AxisScale(:ln; kwargs...) else #identity - return InspectDR.AxisScale(:lin) + return InspectDR.AxisScale(:lin; kwargs...) end end @@ -209,14 +213,10 @@ end # Set up the subplot within the backend object. function _initialize_subplot(plt::Plot{InspectDRBackend}, sp::Subplot{InspectDRBackend}) plot = sp.o - #Don't do anything without a "subplot" object: Will process later. if nothing == plot; return; end plot.data = [] - plot.markers = [] #Clear old markers - plot.atext = [] #Clear old annotation - plot.apline = [] #Clear old poly lines - + plot.userannot = [] #Clear old markers/text annotation/polyline "annotation" return plot 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 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): nx = length(x); ny = length(y) if nx < ny @@ -267,7 +273,7 @@ For st in :shape: apline = InspectDR.PolylineAnnotation( x[rng], y[rng], line=line, fillcolor=fillcolor ) - push!(plot.apline, apline) + InspectDR.add(plot, apline) end end @@ -328,23 +334,35 @@ end # --------------------------------------------------------------------------- function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend}) - const gridon = InspectDR.grid(vmajor=true, hmajor=true) - const gridoff = InspectDR.grid() + const gridon = InspectDR.GridRect(vmajor=true, hmajor=true) + const gridoff = InspectDR.GridRect() 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] - xscale = _inspectdr_getscale(xaxis[:scale]) - yscale = _inspectdr_getscale(yaxis[:scale]) - plot.axes = InspectDR.AxesRect(xscale, yscale) + plot.xscale = _inspectdr_getscale(xaxis[:scale], false) + strip.yscale = _inspectdr_getscale(yaxis[:scale], true) xmin, xmax = axis_limits(xaxis) ymin, ymax = axis_limits(yaxis) - plot.ext = InspectDR.PExtents2D() #reset - plot.ext_full = InspectDR.PExtents2D(xmin, xmax, ymin, ymax) + if ispolar(sp) + #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.title = sp[:title] - a.xlabel = xaxis[:guide]; a.ylabel = yaxis[:guide] + a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]] l = plot.layout + l.frame.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot]) l.framedata.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside]) l.framedata.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis]) l.fnttitle = InspectDR.Font(sp[:titlefont].family, @@ -360,8 +378,6 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend}) _inspectdr_mapptsize(xaxis[:tickfont].pointsize), color = _inspectdr_mapcolor(xaxis[:foreground_color_text]) ) - #No independent control of grid??? - l.grid = sp[:grid]? gridon: gridoff leg = l.legend leg.enabled = (sp[:legend] != :none) #leg.width = 150 #TODO: compute??? @@ -378,6 +394,13 @@ function _before_layout_calcs(plt::Plot{InspectDRBackend}) const mplot = _inspectdr_getmplot(plt.o) 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)) nsubplots = length(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() end sp.o = mplot.subplots[i] + plot = sp.o _initialize_subplot(plt, sp) _inspectdr_setupsubplot(sp) - sp.o.layout.frame.fillcolor = - _inspectdr_mapcolor(plt[:background_color_outside]) - # add the annotations for ann in sp[:annotations] - _inspectdr_add_annotations(mplot.subplots[i], ann...) + _inspectdr_add_annotations(plot, ann...) end end @@ -422,8 +443,19 @@ end # Set the (left, top, right, bottom) minimum padding around the plot area # to fit ticks, tick labels, guides, colorbars, etc. function _update_min_padding!(sp::Subplot{InspectDRBackend}) - sp.minpad = (20mm, 5mm, 2mm, 10mm) - #TODO: Add support for padding. + plot = sp.o + 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 # ---------------------------------------------------------------- @@ -432,6 +464,13 @@ end function _update_plot_object(plt::Plot{InspectDRBackend}) mplot = _inspectdr_getmplot(plt.o) 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) if nothing == gplot; return; end @@ -452,19 +491,21 @@ const _inspectdr_mimeformats_nodpi = Dict( # "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface "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")) -_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 @eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend}) 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 for (mime, fmt) in _inspectdr_mimeformats_nodpi @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 _show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show diff --git a/src/backends/pgfplots.jl b/src/backends/pgfplots.jl index b42e8629..c44c10ae 100644 --- a/src/backends/pgfplots.jl +++ b/src/backends/pgfplots.jl @@ -3,7 +3,7 @@ # significant contributions by: @pkofod const _pgfplots_attr = merge_with_base_supported([ - # :annotations, + :annotations, # :background_color_legend, :background_color_inside, # :background_color_outside, @@ -22,17 +22,17 @@ const _pgfplots_attr = merge_with_base_supported([ :guide, :lims, :ticks, :scale, :flip, :rotation, :tickfont, :guidefont, :legendfont, :grid, :legend, - # :colorbar, - # :marker_z, :levels, + :colorbar, + :marker_z, #:levels, # :ribbon, :quiver, :arrow, # :orientation, # :overwrite_figure, - # :polar, + :polar, # :normalize, :weights, :contours, :aspect_ratio, # :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_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape) const _pgfplots_scale = [:identity, :ln, :log2, :log10] @@ -98,14 +98,35 @@ const _pgf_series_extrastyle = KW( :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 -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, alpha(c) 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) cstr,a = pgf_color(d[:fillcolor]) "fill = $cstr, fill opacity=$a" @@ -136,6 +157,19 @@ function pgf_marker(d::KW) }""" 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) @@ -143,11 +177,10 @@ function pgf_series(sp::Subplot, series::Series) st = d[:seriestype] style = [] kw = KW() - push!(style, pgf_linestyle(d)) push!(style, pgf_marker(d)) - if d[:fillrange] != nothing + if d[:fillrange] != nothing || st in (:shape,) push!(style, pgf_fillstyle(d)) end @@ -163,6 +196,10 @@ function pgf_series(sp::Subplot, series::Series) d[:z].surf, d[:x], d[:y] elseif is3d(st) 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 d[:x], d[:y] end @@ -211,6 +248,9 @@ function pgf_axis(sp::Subplot, letter) # axis guide kw[Symbol(letter,:label)] = axis[:guide] + # Add ticklabel rotations + push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}") + # flip/reverse? axis[:flip] && push!(style, "$letter dir=reverse") @@ -249,8 +289,12 @@ end function _update_plot_object(plt::Plot{PGFPlotsBackend}) 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 - # first build the PGFPlots.Axis object + # first build the PGFPlots.Axis object style = ["unbounded coords=jump"] kw = KW() @@ -265,10 +309,12 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend}) # bounding box values are in mm # 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) push!(style, """ 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])} """) kw[:width] = "$(width(bb).value)mm" @@ -288,19 +334,62 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend}) kw[:legendPos] = _pgfplots_legend_pos[legpos] 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 for series in series_list(sp) 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 + # add the annotations + for ann in sp[:annotations] + pgf_add_annotation!(o,ann...) + end + + # add the PGFPlots.Axis to the list push!(plt.o, o) end end - function _show(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend}) show(io, mime, plt.o) end diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index bbdd29b9..814e4775 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -19,7 +19,7 @@ const _plotly_attr = merge_with_base_supported([ :window_title, :guide, :lims, :ticks, :scale, :flip, :rotation, :tickfont, :guidefont, :legendfont, - :grid, :legend, :colorbar, + :grid, :legend, :colorbar, :colorbar_title, :marker_z, :fill_z, :levels, :ribbon, :quiver, :orientation, @@ -31,6 +31,7 @@ const _plotly_attr = merge_with_base_supported([ :hover, :inset_subplots, :bar_width, + :clims, ]) const _plotly_seriestype = [ @@ -268,7 +269,7 @@ function plotly_layout(plt::Plot) w, h = plt[:size] d_out[:width], d_out[:height] = w, h 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[] @@ -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::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) function plotly_series(plt::Plot, series::Series) st = series[:seriestype] @@ -438,6 +443,13 @@ function plotly_series(plt::Plot, series::Series) 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" if st in (:path, :scatter, :scattergl) d_out[:type] = st==:scattergl ? "scattergl" : "scatter" @@ -533,9 +545,10 @@ function plotly_series(plt::Plot, series::Series) rgba_string(series[:markercolor]) else # grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha]) - grad = series[:markercolor] + grad = as_gradient(series[:markercolor], series[:markeralpha]) 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 diff --git a/src/backends/plotlyjs.jl b/src/backends/plotlyjs.jl index 6d4ad145..0a2ad219 100644 --- a/src/backends/plotlyjs.jl +++ b/src/backends/plotlyjs.jl @@ -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"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}) - 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 diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 84ea6718..df426bbb 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -55,6 +55,10 @@ function add_backend_string(::PyPlotBackend) withenv("PYTHON" => "") do Pkg.build("PyPlot") end + import Conda + Conda.add("qt=4.8.5") + + # now restart julia! """ end @@ -217,6 +221,12 @@ function py_stepstyle(seriestype::Symbol) return "default" 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 # function py_font(font::Font) # pyfont.pymember("FontProperties")( @@ -669,6 +679,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series) end z = transpose_z(series, z) 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 # the surface colors are different than z-value 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) end - handle = ax[f](args...; + handle = ax[f](args..., trues(n), false, py_fillstepstyle(st); zorder = series[:series_plotindex], facecolor = py_fillcolor(series), linewidths = 0 @@ -1045,7 +1060,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) end if sp[:grid] fgcolor = py_color(sp[:foreground_color_grid]) - pyaxis[:grid](true, color = fgcolor) + pyaxis[:grid](true, color = fgcolor, linestyle = ":") ax[:set_axisbelow](true) end py_set_axis_colors(ax, axis) @@ -1061,7 +1076,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) py_add_legend(plt, sp, ax) # 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 py_drawfig(fig) end @@ -1093,7 +1108,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend}) # optionally add the width of colorbar labels and colorbar to rightpad if haskey(sp.attr, :cbar_ax) 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] end diff --git a/src/backends/web.jl b/src/backends/web.jl index 2fd4ae6e..b0be7a3e 100644 --- a/src/backends/web.jl +++ b/src/backends/web.jl @@ -23,11 +23,11 @@ function open_browser_window(filename::AbstractString) @static if is_apple() return run(`open $(filename)`) 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)`) end @static if is_windows() - return run(`$(ENV["COMSPEC"]) /c start $(filename)`) + return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`) end warn("Unknown OS... cannot open browser window.") end diff --git a/src/components.jl b/src/components.jl index 1c6a55cc..24e938d5 100644 --- a/src/components.jl +++ b/src/components.jl @@ -1,7 +1,7 @@ -typealias P2 StaticArrays.SVector{2,Float64} -typealias P3 StaticArrays.SVector{3,Float64} +const P2 = StaticArrays.SVector{2,Float64} +const P3 = StaticArrays.SVector{3,Float64} 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)) @@ -174,7 +174,7 @@ function center(shape::Shape) Cx / 6A, Cy / 6A 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) cx, cy = c for i=1:length(sx) @@ -521,7 +521,7 @@ Base.Array(surf::Surface) = surf.surf for f in (:length, :size) @eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...) 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) function expand_extrema!(a::Axis, surf::Surface) diff --git a/src/deprecated/series_args.jl b/src/deprecated/series_args.jl index 7d491290..2d7536f4 100644 --- a/src/deprecated/series_args.jl +++ b/src/deprecated/series_args.jl @@ -5,7 +5,7 @@ # 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 -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)) diff --git a/src/examples.jl b/src/examples.jl index 96872851..9f54509d 100644 --- a/src/examples.jl +++ b/src/examples.jl @@ -172,6 +172,24 @@ PlotExample("", 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", "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 diff --git a/src/output.jl b/src/output.jl index ee9972ab..0482f325 100644 --- a/src/output.jl +++ b/src/output.jl @@ -271,7 +271,7 @@ function setup_ijulia() show(io, MIME("text/html"), plt) end end - set_ijulia_output("text/html") + @eval set_ijulia_output("text/html") end end @@ -302,6 +302,10 @@ function setup_atom() Media.render(pane, Atom.div(".fill", Atom.HTML(stringmime(MIME("text/html"), plt)))) plt[:size] = sz end + # special handling for PlotlyJS + function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend}) + display(Plots.PlotsDisplay(), plt) + end else # function Media.render(pane::Atom.PlotPane, plt::Plot) @@ -314,14 +318,8 @@ function setup_atom() # special handling for plotly... use PlotsDisplay function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend}) 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))) 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 diff --git a/src/pipeline.jl b/src/pipeline.jl index 6c2e05c3..70644055 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -277,6 +277,13 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW}) attr[Symbol(letter,k)] = v 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 sp_attrs[sp] = attr end @@ -357,7 +364,7 @@ function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol) expand_extrema!(sp[:xaxis], (0,w)) expand_extrema!(sp[:yaxis], (0,h)) sp[:yaxis].d[:flip] = true - elseif !(st in (:pie, :histogram, :histogram2d)) + elseif !(st in (:pie, :histogram, :bins2d, :histogram2d)) expand_extrema!(sp, d) end end diff --git a/src/plotattr.jl b/src/plotattr.jl new file mode 100644 index 00000000..cc8d053b --- /dev/null +++ b/src/plotattr.jl @@ -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 diff --git a/src/recipes.jl b/src/recipes.jl index 156bda3e..1bc4cad5 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -323,10 +323,11 @@ end # create a bar plot as a filled step function @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] - cv = [discrete_value!(axis, xi)[1] for xi=x] - x = if nx == ny + cv = [discrete_value!(axis, xi)[1] for xi=procx] + procx = if nx == ny cv elseif nx == ny + 1 0.5diff(cv) + cv[1:end-1] @@ -337,9 +338,9 @@ end # compute half-width of bars bw = d[:bar_width] hw = if bw == nothing - 0.5mean(diff(x)) + 0.5mean(diff(procx)) else - Float64[0.5cycle(bw,i) for i=1:length(x)] + Float64[0.5cycle(bw,i) for i=1:length(procx)] end # make fillto a vector... default fills to 0 @@ -347,16 +348,21 @@ end if fillto == nothing fillto = 0 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 xseg, yseg = Segments(), Segments() for i=1:ny - center = x[i] - hwi = cycle(hw,i) - yi = y[i] - fi = cycle(fillto,i) - push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi) - push!(yseg, yi, fi, fi, yi, yi) + yi = procy[i] + if !isnan(yi) + center = procx[i] + hwi = cycle(hw,i) + fi = cycle(fillto,i) + push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi) + push!(yseg, yi, fi, fi, yi, yi) + end end # widen limits out a bit @@ -378,106 +384,327 @@ end end @deps bar shape + # --------------------------------------------------------------------------- # Histograms -# edges from number of bins -function calc_edges(v, bins::Integer) - vmin, vmax = extrema(v) - linspace(vmin, vmax, bins+1) +_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2 + +_is_positive(x) = (x > 0) && !(x ≈ 0) + +_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 -# just pass through arrays -calc_edges(v, bins::AVec) = bins -# find the bucket index of this value -function bucket_index(vi, edges) - for (i,e) in enumerate(edges) - if vi <= e - return max(1,i-1) +function _binbarlike_baseline{T<:Real}(min_value::T, scale::Symbol) + if (scale in _logScales) + !isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3) + else + 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 + + 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 - return length(edges)-1 + if (last_w != baseline) + push!(x, a) + push!(y, baseline) + end + + (x, y) end -function my_hist(v, bins; normed = false, weights = nothing) - edges = calc_edges(v, bins) - counts = zeros(length(edges)-1) - # add a weighted count - for (i,vi) in enumerate(v) - idx = bucket_index(vi, edges) - counts[idx] += (weights == nothing ? 1.0 : weights[i]) +@recipe function f(::Type{Val{:stepbins}}, x, y, z) + axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis] + + 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 - # normalize by bar area? - norm_denom = normed ? sum(diff(edges) .* counts) : 1.0 - if norm_denom == 0 - norm_denom = 1.0 + # create a secondary series for the markers + if d[:markershape] != :none + @series begin + seriestype := :scatter + x := _bin_centers(edge) + y := weights + fillrange := nothing + label := "" + primary := false + () + end + markershape := :none + xerror := :none + yerror := :none 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 # Freedman–Diaconis 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 @recipe function f(::Type{Val{:histogram}}, x, y, z) - edges, counts = my_hist(y, d[:bins], - normed = d[:normalize], - weights = d[:weights]) - x := edges - y := counts - seriestype := :bar + seriestype := :barhist () 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 -# if tuple, map out bins, otherwise use the same for both -calc_edges_2d(x, y, bins) = calc_edges(x, bins), calc_edges(y, bins) -calc_edges_2d{X,Y}(x, y, bins::Tuple{X,Y}) = calc_edges(x, bins[1]), calc_edges(y, bins[2]) +@recipe function f(::Type{Val{:bins2d}}, x, y, z) + edge_x, edge_y, weights = x, y, z.surf -# the 2D version -function my_hist_2d(x, y, bins; normed = false, weights = nothing) - xedges, yedges = calc_edges_2d(x, y, bins) - 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]) + float_weights = float(weights) + if is(float_weights, weights) + float_weights = deepcopy(float_weights) end - - # 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) + for (i, c) in enumerate(float_weights) if c == 0 - counts[i] = NaN + float_weights[i] = NaN end end - x := centers(xedges) - y := centers(yedges) - z := Surface(counts) - linewidth := 0 + + x := Plots._bin_centers(edge_x) + y := Plots._bin_centers(edge_y) + z := Surface(float_weights) + + match_dimensions := true seriestype := :heatmap () 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 -@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int,dt), dt -> string(convert(Date,dt))) -@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int,dt), dt -> string(convert(DateTime,dt))) +dateformatter(dt) = string(convert(Date, 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 -@userplot ComplexPlot -@recipe function f(cp::ComplexPlot) - xguide --> "Real Part" - yguide --> "Imaginary Part" - seriestype --> :scatter - real(cp.args[1]), imag(cp.args[1]) +@recipe function f{T<:Number}(A::Array{Complex{T}}) + xguide --> "Re(x)" + yguide --> "Im(x)" + real.(A), imag.(A) +end + +# 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 diff --git a/src/subplots.jl b/src/subplots.jl index 3400874c..924cc6c0 100644 --- a/src/subplots.jl +++ b/src/subplots.jl @@ -39,7 +39,7 @@ series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot] function should_add_to_legend(series::Series) series.d[:primary] && series.d[:label] != "" && !(series.d[:seriestype] in ( - :hexbin,:histogram2d,:hline,:vline, + :hexbin,:bins2d,:histogram2d,:hline,:vline, :contour,:contourf,:contour3d,:surface,:wireframe, :heatmap, :pie, :image )) diff --git a/src/themes.jl b/src/themes.jl index 3429585a..e7a39d02 100644 --- a/src/themes.jl +++ b/src/themes.jl @@ -2,7 +2,8 @@ function theme(s::Symbol; kw...) # reset? if s == :none || s == :default - PlotUtils._default_gradient[] = :inferno + PlotUtils.clibrary(:Plots) + PlotUtils.default_cgrad(default = :sequential, sequential = :inferno) default(; bg = :white, bglegend = :match, @@ -23,7 +24,8 @@ function theme(s::Symbol; kw...) # update the default gradient and other defaults thm = PlotThemes._themes[s] 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 default(; bg = thm.bg_secondary, diff --git a/src/types.jl b/src/types.jl index 0f1e4909..284b6534 100644 --- a/src/types.jl +++ b/src/types.jl @@ -2,9 +2,9 @@ # 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 -typealias AVec AbstractVector -typealias AMat AbstractMatrix -typealias KW Dict{Symbol,Any} +const AVec = AbstractVector +const AMat = AbstractMatrix +const KW = Dict{Symbol,Any} immutable PlotsDisplay <: Display end @@ -62,7 +62,7 @@ Extrema() = Extrema(Inf, -Inf) # ----------------------------------------------------------- -typealias SubplotMap Dict{Any, Subplot} +const SubplotMap = Dict{Any, Subplot} # ----------------------------------------------------------- diff --git a/test/imgcomp.jl b/test/imgcomp.jl index 71f94924..890c374b 100644 --- a/test/imgcomp.jl +++ b/test/imgcomp.jl @@ -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 # 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) diff --git a/test/runtests.jl b/test/runtests.jl index ca7f5e3e..953ad8fe 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,7 +23,7 @@ facts("PyPlot") do @fact pyplot() --> 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 facts("GR") do