Compare commits

..

3 Commits

Author SHA1 Message Date
Tom Breloff 25bf71aff2 0.6 fixes 2017-02-21 12:10:38 -05:00
Tom Breloff 33d6c3426a inline docs for plot 2017-01-18 13:45:02 -05:00
Tom Breloff 9d038ea05e starting no-conditional reorg; replaced FixedSizeArrays with StaticArrays 2017-01-18 12:08:20 -05:00
40 changed files with 648 additions and 1908 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ os:
- linux
# - osx
julia:
- 0.6
- 0.5
matrix:
allow_failures:
- julia: nightly
+4 -51
View File
@@ -3,61 +3,14 @@
#### notes on release changes, ongoing development, and future planned work
- All new development should target 0.12!
- Minor version 0.11 is the last one to support Julia 0.5!!
- All new development should target 0.9!
- Minor version 0.8 is the last one to support Julia 0.4!!
- Critical bugfixes only
- `backports` branch is for Julia 0.4
---
## 0.12 (current master/dev)
- 0.6 only
#### 0.11.3
- add HDF5 backend
- GR replaces PyPlot as first-choice backend
- support for legend position in GR
- smaller markers in GR
- better viewport size in GR
- fix glvisualize support
- remove bug with three-argument method of `text`
- `legendtitle` attribute added
- add test for `spy`
#### 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 (current master/dev)
#### 0.9.5
@@ -378,7 +331,7 @@
- z-axis keywords
- 3D indexing overhaul: `push!`, `append!` support
- matplotlib colormap constants (`:inferno` is the new default colormap for Plots)
- `const KW = Dict{Symbol,Any}` used in place of splatting in many places
- `typealias 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
+1 -1
View File
@@ -1,6 +1,6 @@
# Plots
[![Build Status](https://travis-ci.org/JuliaPlots/Plots.jl.svg?branch=master)](https://travis-ci.org/JuliaPlots/Plots.jl)
[![Build Status](https://travis-ci.org/tbreloff/Plots.jl.svg?branch=master)](https://travis-ci.org/tbreloff/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)
<!-- [![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) -->
+5 -9
View File
@@ -1,13 +1,9 @@
julia 0.6-pre
julia 0.5
RecipesBase 0.2.0
PlotUtils 0.4.1
PlotThemes 0.1.3
RecipesBase
PlotUtils
PlotThemes
Reexport
FixedSizeArrays
FixedPointNumbers 0.3
Measures
Showoff
StatsBase 0.14.0
JSON
NaNMath
StaticArrays
+5 -6
View File
@@ -1,9 +1,9 @@
environment:
matrix:
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
- JULIAVERSION: "julialang/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
- JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe"
notifications:
- provider: Email
@@ -12,14 +12,13 @@ notifications:
on_build_status_changed: false
install:
- ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12"
# If there's a newer build queued for the same PR, cancel this one
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
# Download most recent Julia Windows binary
- ps: (new-object net.webclient).DownloadFile($env:JULIA_URL, "C:\projects\julia-binary.exe")
- ps: (new-object net.webclient).DownloadFile($("http://s3.amazonaws.com/"+$env:JULIAVERSION), "C:\projects\julia-binary.exe")
# Run installer silently, output to C:\projects\julia
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
+18 -37
View File
@@ -1,16 +1,14 @@
__precompile__(false)
__precompile__(true)
module Plots
using Reexport
using FixedSizeArrays
using StaticArrays
@reexport using RecipesBase
import RecipesBase: plot, animate
using Base.Meta
@reexport using PlotUtils
@reexport using PlotThemes
import Showoff
import StatsBase
export
grid,
@@ -31,6 +29,9 @@ export
with,
twinx,
@userplot,
@shorthands,
pie,
pie!,
plot3d,
@@ -98,39 +99,13 @@ export
center,
P2,
P3,
BezierCurve,
plotattr
# ---------------------------------------------------------
import NaNMath # define functions that ignores NaNs. To overcome the destructive effects of https://github.com/JuliaLang/julia/pull/12563
ignorenan_minimum{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.minimum(x)
ignorenan_minimum(x) = Base.minimum(x)
ignorenan_maximum{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.maximum(x)
ignorenan_maximum(x) = Base.maximum(x)
ignorenan_mean{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.mean(x)
ignorenan_mean(x) = Base.mean(x)
ignorenan_extrema{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.extrema(x)
ignorenan_extrema(x) = Base.extrema(x)
# ---------------------------------------------------------
# to cater for block matrices, Base.transpose is recursive.
# This makes it impossible to create row vectors of String and Symbol with the transpose operator.
# This solves this issue, internally in Plots at least.
# commented out on the insistence of the METADATA maintainers
#Base.transpose(x::Symbol) = x
#Base.transpose(x::String) = x
BezierCurve
# ---------------------------------------------------------
import Measures
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
const BBox = Measures.Absolute2DBox
typealias BBox Measures.Absolute2DBox
export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h
# ---------------------------------------------------------
@@ -140,6 +115,7 @@ include("utils.jl")
include("components.jl")
include("axes.jl")
include("args.jl")
include("backends.jl")
include("themes.jl")
include("plot.jl")
include("pipeline.jl")
@@ -151,19 +127,24 @@ include("animation.jl")
include("output.jl")
include("examples.jl")
include("arg_desc.jl")
include("plotattr.jl")
include("backends.jl")
# ---------------------------------------------------------
# define and export shorthand plotting method definitions
macro shorthands(funcname::Symbol)
funcname2 = Symbol(funcname, "!")
esc(quote
export $funcname, $funcname2
$funcname(args...; kw...) = plot(args...; kw..., seriestype = $(quot(funcname)))
$funcname2(args...; kw...) = plot!(args...; kw..., seriestype = $(quot(funcname)))
end)
end
@shorthands scatter
@shorthands bar
@shorthands barh
@shorthands histogram
@shorthands barhist
@shorthands stephist
@shorthands scatterhist
@shorthands histogram2d
@shorthands density
@shorthands heatmap
+1 -2
View File
@@ -80,8 +80,7 @@ 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")`.
You may also need to install the imagemagick c++ library through your operating system.
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`
Will try ffmpeg, but it's lower quality...)""")
# low quality
+2 -3
View File
@@ -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 or Symbol. Default is :auto. For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `linspace(extrema(x)..., 25)`",
:bins => "Integer, NTuple{2,Integer}, AbstractVector. For histogram-types, defines the number of bins, or the edges, of the histogram.",
: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 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).",
:normalize => "Bool. Should normalize histogram types? Trying for area == 1.",
: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`.",
@@ -80,7 +80,6 @@ const _arg_desc = KW(
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)",
:legendtitle => "String or nothing (default). Sets the legend title.",
:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)",
:clims => "`:auto` or NTuple{2,Number}. Fixes the limits of the colorbar.",
:legendfont => "Font. Font of legend items.",
+5 -12
View File
@@ -35,9 +35,7 @@ const _3dTypes = [
]
const _allTypes = vcat([
:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
:heatmap, :hexbin, :barbins, :barhist, :histogram, :scatterbins,
:scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :histogram3d,
:density, :bar, :hline, :vline,
:heatmap, :hexbin, :histogram, :histogram2d, :histogram3d, :density, :bar, :hline, :vline,
:contour, :pie, :shape, :image
], _3dTypes)
@@ -67,7 +65,6 @@ const _typeAliases = Dict{Symbol,Symbol}(
:polygon => :shape,
:box => :boxplot,
:velocity => :quiver,
:vectorfield => :quiver,
:gradient => :quiver,
:img => :image,
:imshow => :image,
@@ -80,7 +77,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
add_non_underscore_aliases!(_typeAliases)
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :barhist, :barbins)
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image)
@@ -156,8 +153,6 @@ 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,
@@ -185,7 +180,7 @@ const _series_defaults = KW(
:markerstrokewidth => 1,
:markerstrokecolor => :match,
:markerstrokealpha => nothing,
:bins => :auto, # number of bins for hists
:bins => 30, # number of bins for hists
:smooth => false, # regression line?
:group => nothing, # groupby vector
:x => nothing,
@@ -251,7 +246,6 @@ const _subplot_defaults = KW(
:foreground_color_title => :match, # title color
:color_palette => :auto,
:legend => :best,
:legendtitle => nothing,
:colorbar => :legend,
:clims => :auto,
:legendfont => font(8),
@@ -440,7 +434,6 @@ add_aliases(:zticks, :ztick)
add_aliases(:zrotation, :zrot, :zr)
add_aliases(:fill_z, :fillz, :fz, :surfacecolor, :surfacecolour, :sc, :surfcolor, :surfcolour)
add_aliases(:legend, :leg, :key)
add_aliases(:legendtitle, :legend_title, :labeltitle, :label_title, :leg_title, :key_title)
add_aliases(:colorbar, :cb, :cbar, :colorkey)
add_aliases(:clims, :clim, :cbarlims, :cbar_lims, :climits, :color_limits)
add_aliases(:smooth, :regression, :reg)
@@ -452,7 +445,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, :vectorfield)
add_aliases(:quiver, :velocity, :quiver2d, :gradient)
add_aliases(:normalize, :norm, :normed, :normalized)
add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio)
add_aliases(:match_dimensions, :transpose, :transpose_z)
@@ -1267,7 +1260,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, :scatterbins, :scatterhist, :scatter3d)
if d[:seriestype] in (:scatter, :scatter3d)
d[:linewidth] = 0
if d[:markershape] == :none
d[:markershape] = :circle
+13 -37
View File
@@ -118,7 +118,7 @@ Base.show(io::IO, axis::Axis) = dumpdict(axis.d, "Axis", true)
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.d, k)
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
ignorenan_extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
const _scale_funcs = Dict{Symbol,Function}(
@@ -156,30 +156,6 @@ 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(
@@ -229,7 +205,7 @@ function get_ticks(axis::Axis)
elseif typeof(ticks) <: AVec
# override ticks, but get the labels
optimal_ticks_and_labels(axis, ticks)
elseif typeof(ticks) <: NTuple{2, Any}
elseif typeof(ticks) <: NTuple{2}
# assuming we're passed (ticks, labels)
ticks
else
@@ -238,7 +214,7 @@ function get_ticks(axis::Axis)
# @show ticks dvals cv dv
# TODO: better/smarter cutoff values for sampling ticks
if length(cv) > 30 && ticks == :auto
if length(cv) > 30
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
cv[rng], dv[rng]
else
@@ -260,8 +236,8 @@ end
function expand_extrema!(ex::Extrema, v::Number)
ex.emin = NaNMath.min(v, ex.emin)
ex.emax = NaNMath.max(v, ex.emax)
ex.emin = min(v, ex.emin)
ex.emax = max(v, ex.emax)
ex
end
@@ -276,8 +252,8 @@ expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
ex = axis[:extrema]
ex.emin = NaNMath.min(v[1], ex.emin)
ex.emax = NaNMath.max(v[2], ex.emax)
ex.emin = min(v[1], ex.emin)
ex.emax = max(v[2], ex.emax)
ex
end
function expand_extrema!{N<:Number}(axis::Axis, v::AVec{N})
@@ -349,11 +325,11 @@ function expand_extrema!(sp::Subplot, d::KW)
bw = d[:bar_width]
if bw == nothing
bw = d[:bar_width] = ignorenan_mean(diff(data))
bw = d[:bar_width] = mean(diff(data))
end
axis = sp.attr[Symbol(dsym, :axis)]
expand_extrema!(axis, ignorenan_maximum(data) + 0.5maximum(bw))
expand_extrema!(axis, ignorenan_minimum(data) - 0.5minimum(bw))
expand_extrema!(axis, maximum(data) + 0.5maximum(bw))
expand_extrema!(axis, minimum(data) - 0.5minimum(bw))
end
end
@@ -368,8 +344,8 @@ end
# push the limits out slightly
function widen(lmin, lmax)
span = lmax - lmin
# eps = NaNMath.max(1e-16, min(1e-2span, 1e-10))
eps = NaNMath.max(1e-16, 0.03span)
# eps = max(1e-16, min(1e-2span, 1e-10))
eps = max(1e-16, 0.03span)
lmin-eps, lmax+eps
end
@@ -425,7 +401,7 @@ function discrete_value!(axis::Axis, dv)
# @show axis[:discrete_map], axis[:discrete_values], dv
if cv_idx == -1
ex = axis[:extrema]
cv = NaNMath.max(0.5, ex.emax + 1.0)
cv = max(0.5, ex.emax + 1.0)
expand_extrema!(axis, cv)
push!(axis[:discrete_values], dv)
push!(axis[:continuous_values], cv)
+54 -20
View File
@@ -19,10 +19,11 @@ macro init_backend(s)
export $sym
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
backend_name(::$T) = Symbol($str)
backend_package_name(::$T) = Symbol("Plots", $(string(s)))
push!(_backends, Symbol($str))
_backendType[Symbol($str)] = $T
_backendSymbol[$T] = Symbol($str)
include("backends/" * $str * ".jl")
# include("backends/" * $str * ".jl")
end)
end
@@ -148,7 +149,7 @@ function pickDefaultBackend()
# the ordering/inclusion of this package list is my semi-arbitrary guess at
# which one someone will want to use if they have the package installed...accounting for
# features, speed, and robustness
for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize")
for pkgstr in ("GR", "PlotlyJS", "PyPlot", "UnicodePlots")
if Pkg.installed(pkgstr) != nothing
return backend(Symbol(lowercase(pkgstr)))
end
@@ -263,24 +264,32 @@ function merge_with_base_supported(v::AVec)
end
# @init_backend Immerse
# @init_backend Gadfly
@init_backend PyPlot
# @init_backend Qwt
@init_backend UnicodePlots
# @init_backend Winston
# @init_backend Bokeh
# @init_backend PyPlot
# @init_backend UnicodePlots
@init_backend Plotly
@init_backend PlotlyJS
# @init_backend PlotlyJS
@init_backend GR
@init_backend GLVisualize
@init_backend PGFPlots
@init_backend InspectDR
@init_backend HDF5
# @init_backend GLVisualize
# @init_backend PGFPlots
# @init_backend InspectDR
_initialize_backend(::PlotlyBackend) = include("backends/plotly.jl")
function _initialize_backend(b::AbstractBackend)
bname = backend_name(b)
@eval import $(backend_package_name(b))
end
# ---------------------------------------------------------
const _attr = KW()
const _seriestype = KW()
const _marker = KW()
const _style = KW()
const _scale = KW()
using Base.Meta
# create the various `is_xxx_supported` and `supported_xxxs` methods
# by default they pass through to checking membership in `_gr_xxx`
for s in (:attr, :seriestype, :marker, :style, :scale)
@@ -295,13 +304,38 @@ for s in (:attr, :seriestype, :marker, :style, :scale)
for bend in backends()
bend_type = typeof(_backend_instance(bend))
v = Symbol("_", bend, "_", s)
# v = Symbol("_", bend, "_", s)
v = Symbol("_", s)
@eval begin
$f(::$bend_type, $s::Symbol) = $s in $v
$f2(::$bend_type) = $v
$f(::$bend_type, $s::Symbol) = $s in $v[$(quot(bend))]
$f2(::$bend_type) = $v[$(quot(bend))]
end
end
end
# is_subplot_supported(::AbstractBackend) = false
# is_subplot_supported() = is_subplot_supported(backend())
# ---------------------------------------------------------
# # create the various `is_xxx_supported` and `supported_xxxs` methods
# # by default they pass through to checking membership in `_gr_xxx`
# for s in (:attr, :seriestype, :marker, :style, :scale)
# f = Symbol("is_", s, "_supported")
# f2 = Symbol("supported_", s, "s")
# @eval begin
# $f(::AbstractBackend, $s) = false
# $f(bend::AbstractBackend, $s::AbstractVector) = all(v -> $f(bend, v), $s)
# $f($s) = $f(backend(), $s)
# $f2() = $f2(backend())
# end
#
# for bend in backends()
# bend_type = typeof(_backend_instance(bend))
# v = Symbol("_", bend, "_", s)
# @eval begin
# $f(::$bend_type, $s::Symbol) = $s in $v
# $f2(::$bend_type) = $v
# end
# end
# end
#
# # is_subplot_supported(::AbstractBackend) = false
# # is_subplot_supported() = is_subplot_supported(backend())
+85 -41
View File
@@ -1,4 +1,4 @@
#=
``#=
TODO
* move all gl_ methods to GLPlot
* integrate GLPlot UI
@@ -7,6 +7,7 @@ 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([
@@ -77,9 +78,14 @@ end
function add_backend_string(b::GLVisualizeBackend)
"""
if !Plots.is_installed("GLVisualize")
Pkg.add("GLVisualize")
end
For those incredibly brave souls who assume full responsibility for what happens next...
There's an easy way to get what you need for the GLVisualize backend to work (until Pkg3 is usable):
Pkg.clone("https://github.com/tbreloff/MetaPkg.jl")
using MetaPkg
meta_checkout("MetaGL")
See the MetaPkg readme for details...
"""
end
@@ -93,6 +99,46 @@ end
# end
const _glplot_deletes = []
function close_child_signals!(screen)
for child in screen.children
for (k, s) in child.inputs
empty!(s.actions)
end
for (k, cam) in child.cameras
for f in fieldnames(cam)
s = getfield(cam, f)
if isa(s, Signal)
close(s, false)
end
end
end
empty!(child.cameras)
close_child_signals!(child)
end
return
end
function empty_screen!(screen)
if isempty(_glplot_deletes)
close_child_signals!(screen)
empty!(screen)
empty!(screen.cameras)
for (k, s) in screen.inputs
empty!(s.actions)
end
empty!(screen)
else
for del_signal in _glplot_deletes
push!(del_signal, true) # trigger delete
end
empty!(_glplot_deletes)
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
@@ -109,36 +155,38 @@ 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(
"Plots",
"Plot",
resolution = plt[:size],
visible = visible
)
@async GLWindow.renderloop(parent_screen)
GLVisualize.add_screen(parent_screen)
@async GLWindow.waiting_renderloop(parent_screen)
end
# now lets get ourselves a permanent Plotting screen
plot_screens = get_plot_screen(GLVisualize.current_screen(), name)
plot_screens = get_plot_screen(GLVisualize.get_screens(), name)
screen = if isempty(plot_screens) # no screen with `name`
parent = GLVisualize.current_screen()
screen = GLWindow.Screen(
parent, area = map(GLWindow.zeroposition, parent.area),
name = name
)
for (k, s) in screen.inputs # copy signals, so we can clean them up better
screen.inputs[k] = map(identity, s)
end
screen
elseif length(plot_screens) == 1
plot_screens[1]
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 $name")
error("multiple Plot screens. Please don't use any screen with the name Plots.jl")
end
# Since we own this window, we can do deep cleansing
empty!(screen)
empty_screen!(screen)
plt.o = screen
GLWindow.set_visibility!(screen, visible)
resize!(screen, plt[:size]...)
@@ -304,7 +352,7 @@ function extract_any_color(d, kw_args)
kw_args[:color_norm] = Vec2f0(clims)
end
elseif clims == :auto
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(d[:y]))
kw_args[:color_norm] = Vec2f0(extrema(d[:y]))
end
end
else
@@ -315,7 +363,7 @@ function extract_any_color(d, kw_args)
kw_args[:color_norm] = Vec2f0(clims)
end
elseif clims == :auto
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(d[:y]))
kw_args[:color_norm] = Vec2f0(extrema(d[:y]))
else
error("Unsupported limits: $clims")
end
@@ -367,14 +415,14 @@ end
dist(a, b) = abs(a-b)
mindist(x, a, b) = NaNMath.min(dist(a, x), dist(b, x))
mindist(x, a, b) = min(dist(a, x), dist(b, x))
function gappy(x, ps)
n = length(ps)
x <= first(ps) && return first(ps) - x
for j=1:(n-1)
p0 = ps[j]
p1 = ps[NaNMath.min(j+1, n)]
p1 = ps[min(j+1, n)]
if p0 <= x && p1 >= x
return mindist(x, p0, p1) * (isodd(j) ? 1 : -1)
end
@@ -482,7 +530,7 @@ function hover(to_hover, to_display, window)
end
function extract_extrema(d, kw_args)
xmin, xmax = ignorenan_extrema(d[:x]); ymin, ymax = ignorenan_extrema(d[:y])
xmin, xmax = extrema(d[:x]); ymin, ymax = extrema(d[:y])
kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(xmin, ymin, xmax-xmin, ymax-ymin)
nothing
end
@@ -509,7 +557,7 @@ function extract_colornorm(d, kw_args)
else
d[:y]
end
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(z))
kw_args[:color_norm] = Vec2f0(extrema(z))
kw_args[:intensity] = map(Float32, collect(z))
end
end
@@ -649,7 +697,7 @@ function draw_ticks(
text, positions, offsets
end
function glvisualize_text(position, text, kw_args)
function text(position, text, kw_args)
text_align = alignment2num(text.font)
startpos = Vec2f0(position)
atlas = GLVisualize.get_texture_atlas()
@@ -726,7 +774,7 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
extract_font(font, kw)
t = PlotText(sp[:title], font)
push!(axis_vis, glvisualize_text(xy, t, kw))
push!(axis_vis, text(xy, t, kw))
end
if xaxis[:guide] != ""
tf = xaxis[:guidefont]; color = gl_color(xaxis[:foreground_color_guide])
@@ -735,7 +783,7 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
t = PlotText(xaxis[:guide], font)
extract_font(font, kw)
push!(axis_vis, glvisualize_text(xy, t, kw))
push!(axis_vis, text(xy, t, kw))
end
if yaxis[:guide] != ""
@@ -745,7 +793,7 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
t = PlotText(yaxis[:guide], font)
extract_font(font, kw)
push!(axis_vis, glvisualize_text(xy, t, kw))
push!(axis_vis, text(xy, t, kw))
end
axis_vis
@@ -781,9 +829,9 @@ function gl_bar(d, kw_args)
# compute half-width of bars
bw = nothing
hw = if bw == nothing
ignorenan_mean(diff(x))
mean(diff(x))
else
Float64[_cycle(bw,i)*0.5 for i=1:length(x)]
Float64[cycle(bw,i)*0.5 for i=1:length(x)]
end
# make fillto a vector... default fills to 0
@@ -797,7 +845,7 @@ function gl_bar(d, kw_args)
sx, sy = m[1,1], m[2,2]
for i=1:ny
center = x[i]
hwi = abs(_cycle(hw,i)); yi = y[i]; fi = _cycle(fillto,i)
hwi = abs(cycle(hw,i)); yi = y[i]; fi = cycle(fillto,i)
if Plots.isvertical(d)
sz = (hwi*sx, yi*sy)
else
@@ -833,7 +881,7 @@ function gl_boxplot(d, kw_args)
sx, sy = m[1,1], m[2,2]
for (i,glabel) in enumerate(glabels)
# filter y
values = y[filter(i -> _cycle(x,i) == glabel, 1:length(y))]
values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
# compute quantiles
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
# notch
@@ -846,7 +894,7 @@ function gl_boxplot(d, kw_args)
# make the shape
center = Plots.discrete_value!(d[:subplot][:xaxis], glabel)[1]
hw = d[:bar_width] == nothing ? Plots._box_halfwidth*2 : _cycle(d[:bar_width], i)
hw = d[:bar_width] == nothing ? Plots._box_halfwidth*2 : cycle(d[:bar_width], i)
l, m, r = center - hw/2, center, center + hw/2
# internal nodes for notches
@@ -864,7 +912,7 @@ function gl_boxplot(d, kw_args)
end
# change q1 and q5 to show outliers
# using maximum and minimum values inside the limits
q1, q5 = ignorenan_extrema(inside)
q1, q5 = extrema(inside)
end
# Box
if notch
@@ -945,7 +993,7 @@ function scale_for_annotations!(series::Series, scaletype::Symbol = :pixels)
msw, msh = anns.scalefactor
offsets = Array(Vec2f0, length(anns.strs))
series[:markersize] = map(1:length(anns.strs)) do i
str = _cycle(anns.strs, i)
str = cycle(anns.strs, i)
# get the width and height of the string (in mm)
sw, sh = text_size(str, anns.font.pointsize)
@@ -1058,7 +1106,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
kw = copy(kw_args)
fr = d[:fillrange]
ps = if all(x-> x >= 0, diff(d[:x])) # if is monotonic
vcat(points, Point2f0[(points[i][1], _cycle(fr, i)) for i=length(points):-1:1])
vcat(points, Point2f0[(points[i][1], cycle(fr, i)) for i=length(points):-1:1])
else
points
end
@@ -1093,7 +1141,8 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
vis = gl_bar(d, kw_args)
elseif st == :image
extract_extrema(d, kw_args)
vis = GL.gl_image(d[:z].surf, kw_args)
z = transpose_z(series, d[:z].surf, false)
vis = GL.gl_image(z, kw_args)
elseif st == :boxplot
extract_c(d, kw_args, :fill)
vis = gl_boxplot(d, kw_args)
@@ -1124,7 +1173,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
txt_args = Dict{Symbol, Any}(:model => eye(GLAbstraction.Mat4f0))
x, y = Reactive.value(model_m) * Vec{4, Float32}(x, y, 0, 1)
extract_font(font, txt_args)
t = glvisualize_text(Point2f0(x, y), PlotText(str, font), txt_args)
t = text(Point2f0(x, y), PlotText(str, font), txt_args)
GLVisualize._view(t, sp_screen, camera = :perspective)
end
@@ -1133,7 +1182,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
if _3d
GLAbstraction.center!(sp_screen)
end
GLAbstraction.post_empty()
Reactive.post_empty()
yield()
end
end
@@ -1231,7 +1280,7 @@ function gl_scatter(points, kw_args)
if haskey(kw_args, :stroke_width)
s = Reactive.value(kw_args[:scale])
sw = kw_args[:stroke_width]
if sw*5 > _cycle(Reactive.value(s), 1)[1] # restrict marker stroke to 1/10th of scale (and handle arrays of scales)
if sw*5 > cycle(Reactive.value(s), 1)[1] # restrict marker stroke to 1/10th of scale (and handle arrays of scales)
kw_args[:stroke_width] = s[1] / 5f0
end
end
@@ -1318,7 +1367,7 @@ function gl_contour(x, y, z, kw_args)
T = eltype(z)
levels = Contour.contours(map(T, x), map(T, y), z, h)
result = Point2f0[]
zmin, zmax = get(kw_args, :limits, Vec2f0(ignorenan_extrema(z)))
zmin, zmax = get(kw_args, :limits, Vec2f0(extrema(z)))
cmap = get(kw_args, :color_map, get(kw_args, :color, RGBA{Float32}(0,0,0,1)))
colors = RGBA{Float32}[]
for c in levels.contours
@@ -1339,7 +1388,7 @@ end
function gl_heatmap(x,y,z, kw_args)
get!(kw_args, :color_norm, Vec2f0(ignorenan_extrema(z)))
get!(kw_args, :color_norm, Vec2f0(extrema(z)))
get!(kw_args, :color_map, Plots.make_gradient(cgrad()))
delete!(kw_args, :intensity)
I = GLVisualize.Intensity{1, Float32}
@@ -1373,8 +1422,6 @@ 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))
@@ -1389,9 +1436,6 @@ 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
@@ -1436,7 +1480,7 @@ function make_label(sp, series, i)
kw = Dict(:model => text_model(font, xy), :scale_primitive=>false)
extract_font(font, kw)
t = PlotText(labeltext, font)
push!(result, glvisualize_text(xy, t, kw))
push!(result, text(xy, t, kw))
GLAbstraction.Context(result...), i
end
+69 -112
View File
@@ -20,7 +20,7 @@ const _gr_attr = merge_with_base_supported([
:title, :window_title,
:guide, :lims, :ticks, :scale, :flip,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :legendtitle, :colorbar,
:grid, :legend, :colorbar,
:marker_z, :levels,
:ribbon, :quiver,
:orientation,
@@ -76,8 +76,6 @@ const gr_markertype = KW(
:diamond => -13,
:utriangle => -3,
:dtriangle => -5,
:ltriangle => -18,
:rtriangle => -17,
:pentagon => -21,
:hexagon => -22,
:heptagon => -23,
@@ -124,10 +122,10 @@ function gr_getcolorind(c)
convert(Int, GR.inqcolorfromrgb(red(c), green(c), blue(c)))
end
gr_set_linecolor(c) = GR.setlinecolorind(gr_getcolorind(_cycle(c,1)))
gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(_cycle(c,1)))
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(_cycle(c,1)))
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(_cycle(c,1)))
gr_set_linecolor(c) = GR.setlinecolorind(gr_getcolorind(cycle(c,1)))
gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(cycle(c,1)))
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(cycle(c,1)))
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(cycle(c,1)))
# --------------------------------------------------------------------------------------
@@ -174,8 +172,6 @@ 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])
@@ -186,8 +182,6 @@ 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])
@@ -264,7 +258,7 @@ end
normalize_zvals(zv::Void) = zv
function normalize_zvals(zv::AVec)
vmin, vmax = ignorenan_extrema(zv)
vmin, vmax = extrema(zv)
if vmin == vmax
zeros(length(zv))
else
@@ -279,19 +273,17 @@ function gr_draw_marker(xi, yi, msize, shape::Shape)
sx, sy = coords(shape)
# convert to ndc coords (percentages of window)
GR.selntran(0)
w, h = gr_plot_size
f = msize / (w + h)
xi, yi = GR.wctondc(xi, yi)
GR.fillarea(xi .+ sx .* f,
yi .+ sy .* f)
ms_ndc_x, ms_ndc_y = gr_pixels_to_ndc(msize, msize)
GR.fillarea(xi .+ sx .* ms_ndc_x,
yi .+ sy .* ms_ndc_y)
GR.selntran(1)
end
# draw ONE symbol marker
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
GR.setmarkertype(gr_markertype[shape])
w, h = gr_plot_size
GR.setmarkersize(0.3msize / ((w + h) * 0.001))
GR.setmarkersize(0.3msize)
GR.polymarker([xi], [yi])
end
@@ -301,23 +293,23 @@ function gr_draw_markers(series::Series, x, y, msize, mz)
shapes = series[:markershape]
if shapes != :none
for i=1:length(x)
msi = _cycle(msize, i)
shape = _cycle(shapes, i)
msi = cycle(msize, i)
shape = cycle(shapes, i)
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
# draw a filled in shape, slightly bigger, to estimate a stroke
if series[:markerstrokewidth] > 0
cfunc(_cycle(series[:markerstrokecolor], i)) #, series[:markerstrokealpha])
cfunc(cycle(series[:markerstrokecolor], i)) #, series[:markerstrokealpha])
gr_draw_marker(x[i], y[i], msi + series[:markerstrokewidth], shape)
end
# draw the shape
if mz == nothing
cfunc(_cycle(series[:markercolor], i)) #, series[:markeralpha])
cfunc(cycle(series[:markercolor], i)) #, series[:markeralpha])
else
# pick a color from the pre-loaded gradient
ci = round(Int, 1000 + _cycle(mz, i) * 255)
ci = round(Int, 1000 + cycle(mz, i) * 255)
cfuncind(ci)
GR.settransparency(_gr_gradient_alpha[ci-999])
end
@@ -338,10 +330,9 @@ end
# ---------------------------------------------------------
function gr_set_line(lw, style, c) #, a)
function gr_set_line(w, style, c) #, a)
GR.setlinetype(gr_linetype[style])
w, h = gr_plot_size
GR.setlinewidth(max(0, lw / ((w + h) * 0.001)))
GR.setlinewidth(w)
gr_set_linecolor(c) #, a)
end
@@ -386,28 +377,20 @@ const viewport_plotarea = zeros(4)
# the size of the current plot in pixels
const gr_plot_size = zeros(2)
function gr_viewport_from_bbox(sp::Subplot{GRBackend}, bb::BoundingBox, w, h, viewport_canvas)
function gr_viewport_from_bbox(bb::BoundingBox, w, h, viewport_canvas)
viewport = zeros(4)
viewport[1] = viewport_canvas[2] * (left(bb) / w)
viewport[2] = viewport_canvas[2] * (right(bb) / w)
viewport[3] = viewport_canvas[4] * (1.0 - bottom(bb) / h)
viewport[4] = viewport_canvas[4] * (1.0 - top(bb) / h)
if is3d(sp)
vp = viewport[:]
extent = min(vp[2] - vp[1], vp[4] - vp[3])
viewport[1] = 0.5 * (vp[1] + vp[2] - extent)
viewport[2] = 0.5 * (vp[1] + vp[2] + extent)
viewport[3] = 0.5 * (vp[3] + vp[4] - extent)
viewport[4] = 0.5 * (vp[3] + vp[4] + extent)
end
viewport
end
# change so we're focused on the viewport area
function gr_set_viewport_cmap(sp::Subplot)
GR.setviewport(
viewport_plotarea[2] + (is3d(sp) ? 0.07 : 0.02),
viewport_plotarea[2] + (is3d(sp) ? 0.10 : 0.05),
viewport_plotarea[2] + (is3d(sp) ? 0.04 : 0.02),
viewport_plotarea[2] + (is3d(sp) ? 0.07 : 0.05),
viewport_plotarea[3],
viewport_plotarea[4]
)
@@ -428,7 +411,7 @@ function gr_set_viewport_polar()
ymax -= 0.05 * (xmax - xmin)
xcenter = 0.5 * (xmin + xmax)
ycenter = 0.5 * (ymin + ymax)
r = 0.5 * NaNMath.min(xmax - xmin, ymax - ymin)
r = 0.5 * min(xmax - xmin, ymax - ymin)
GR.setviewport(xcenter -r, xcenter + r, ycenter - r, ycenter + r)
GR.setwindow(-1, 1, -1, 1)
r
@@ -438,41 +421,23 @@ end
function gr_colorbar(sp::Subplot)
if sp[:colorbar] != :none
gr_set_viewport_cmap(sp)
GR.colorbar()
GR.colormap()
gr_set_viewport_plotarea()
end
end
gr_view_xcenter() = 0.5 * (viewport_plotarea[1] + viewport_plotarea[2])
gr_view_ycenter() = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
gr_view_xdiff() = viewport_plotarea[2] - viewport_plotarea[1]
gr_view_ydiff() = viewport_plotarea[4] - viewport_plotarea[3]
function gr_legend_pos(s::Symbol,w,h)
str = string(s)
if str == "best"
str = "topright"
end
if contains(str,"right")
xpos = viewport_plotarea[2] - 0.05 - w
elseif contains(str,"left")
xpos = viewport_plotarea[1] + 0.11
else
xpos = (viewport_plotarea[2]-viewport_plotarea[1])/2 - w/2 +.04
end
if contains(str,"top")
ypos = viewport_plotarea[4] - 0.06
elseif contains(str,"bottom")
ypos = viewport_plotarea[3] + h + 0.06
else
ypos = (viewport_plotarea[4]-viewport_plotarea[3])/2 + h/2
end
(xpos,ypos)
function gr_pixels_to_ndc(x_pixels, y_pixels)
w,h = gr_plot_size
totx = w * gr_view_xdiff()
toty = h * gr_view_ydiff()
x_pixels / totx, y_pixels / toty
end
function gr_legend_pos{S<:Real, T<:Real}(v::Tuple{S,T},w,h)
xpos = v[1] * (viewport_plotarea[2] - viewport_plotarea[1]) + viewport_plotarea[1]
ypos = v[2] * (viewport_plotarea[4] - viewport_plotarea[3]) + viewport_plotarea[3]
(xpos,ypos)
end
# --------------------------------------------------------------------------------------
@@ -533,15 +498,10 @@ function gr_display(plt::Plot)
end
function _update_min_padding!(sp::Subplot{GRBackend})
sp.minpad = (10mm,2mm,2mm,8mm)
end
function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# the viewports for this subplot
viewport_subplot = gr_viewport_from_bbox(sp, bbox(sp), w, h, viewport_canvas)
viewport_plotarea[:] = gr_viewport_from_bbox(sp, plotarea(sp), w, h, viewport_canvas)
viewport_subplot = gr_viewport_from_bbox(bbox(sp), w, h, viewport_canvas)
viewport_plotarea[:] = gr_viewport_from_bbox(plotarea(sp), w, h, viewport_canvas)
# get data limits
data_lims = gr_xy_axislims(sp)
@@ -585,10 +545,6 @@ 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
@@ -666,7 +622,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
elseif ispolar(sp)
r = gr_set_viewport_polar()
rmin, rmax = GR.adjustrange(ignorenan_minimum(r), ignorenan_maximum(r))
rmin, rmax = GR.adjustrange(minimum(r), maximum(r))
# rmin, rmax = axis_limits(sp[:yaxis])
gr_polaraxes(rmin, rmax)
@@ -690,24 +646,21 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# spine (border) and tick marks
gr_set_line(1, :solid, sp[:xaxis][:foreground_color_axis])
GR.setclip(0)
gr_polyline(coords(spine_segs)...)
GR.setclip(1)
if !(xticks in (nothing, false))
# x labels
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])
for (cv, dv) in zip(xticks...)
# use xor ($) to get the right y coords
xi, yi = GR.wctondc(cv, xor(flip, mirror) ? ymax : ymin)
xi, yi = GR.wctondc(cv, (flip $ mirror) ? ymax : ymin)
# @show cv dv ymin xi yi flip mirror (flip $ mirror)
gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3, string(dv))
gr_text(xi, yi + (mirror ? 1 : -1) * 2e-3, string(dv))
end
end
@@ -717,16 +670,28 @@ 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...)
# use xor ($) to get the right y coords
xi, yi = GR.wctondc(xor(flip, mirror) ? xmax : xmin, cv)
xi, yi = GR.wctondc((flip $ mirror) ? xmax : xmin, cv)
# @show cv dv xmin xi yi
gr_text(xi + (mirror ? 1 : -1) * 1e-2, yi, string(dv))
gr_text(xi + (mirror ? 1 : -1) * 2e-3, yi, string(dv))
end
end
# window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
# ticksize = 0.0075 * window_diag
# if outside_ticks
# ticksize = -ticksize
# end
# # TODO: this should be done for each axis separately
# gr_set_linecolor(xaxis[:foreground_color_axis])
# x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
# y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
# GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
# GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
end
# end
@@ -792,6 +757,10 @@ 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
@@ -808,9 +777,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
fr_from, fr_to = (is_2tuple(frng) ? frng : (y, frng))
for (i,rng) in enumerate(iter_segments(series[:x], series[:y]))
if length(rng) > 1
gr_set_fillcolor(_cycle(series[:fillcolor], i))
fx = _cycle(x, vcat(rng, reverse(rng)))
fy = vcat(_cycle(fr_from,rng), _cycle(fr_to,reverse(rng)))
gr_set_fillcolor(cycle(series[:fillcolor], i))
fx = cycle(x, vcat(rng, reverse(rng)))
fy = vcat(cycle(fr_from,rng), cycle(fr_to,reverse(rng)))
# @show i rng fx fy
GR.fillarea(fx, fy)
end
@@ -836,12 +805,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
@@ -851,7 +820,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# create the colorbar of contour levels
if sp[:colorbar] != :none
gr_set_viewport_cmap(sp)
l = round(Int32, 1000 + (h - ignorenan_minimum(h)) / (ignorenan_maximum(h) - ignorenan_minimum(h)) * 255)
l = round(Int32, 1000 + (h - minimum(h)) / (maximum(h) - minimum(h)) * 255)
GR.setwindow(xmin, xmax, zmin, zmax)
GR.cellarray(xmin, xmax, zmax, zmin, 1, length(l), l)
ztick = 0.5 * GR.tick(zmin, zmax)
@@ -879,7 +848,6 @@ 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 +
@@ -909,7 +877,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.selntran(0)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
xmin, xmax, ymin, ymax = viewport_plotarea
ymax -= 0.1 * (xmax - xmin)
ymax -= 0.05 * (xmax - xmin)
xcenter = 0.5 * (xmin + xmax)
ycenter = 0.5 * (ymin + ymax)
if xmax - xmin > ymax - ymin
@@ -962,19 +930,19 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
x, y = series[:x][rng], series[:y][rng]
# draw the interior
gr_set_fill(_cycle(series[:fillcolor], i))
gr_set_fill(cycle(series[:fillcolor], i))
GR.fillarea(x, y)
# draw the shapes
gr_set_line(series[:linewidth], :solid, _cycle(series[:linecolor], i))
gr_set_line(series[:linewidth], :solid, cycle(series[:linecolor], i))
GR.polyline(x, y)
end
end
elseif st == :image
z = transpose_z(series, series[:z].surf, true)'
w, h = size(z)
z = transpose_z(series, series[:z].surf, true)
h, w = 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)
@@ -1007,11 +975,6 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
w = 0
i = 0
n = 0
if sp[:legendtitle] != nothing
tbx, tby = gr_inqtext(0, 0, string(sp[:legendtitle]))
w = tbx[3] - tbx[1]
n += 1
end
for series in series_list(sp)
should_add_to_legend(series) || continue
n += 1
@@ -1025,9 +988,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
w = max(w, tbx[3] - tbx[1])
end
if w > 0
xpos = viewport_plotarea[2] - 0.05 - w
ypos = viewport_plotarea[4] - 0.06
dy = _gr_point_mult[1] * sp[:legendfont].pointsize * 1.75
h = dy*n
(xpos,ypos) = gr_legend_pos(sp[:legend],w,h)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
gr_set_fillcolor(sp[:background_color_legend])
GR.fillrect(xpos - 0.08, xpos + w + 0.02, ypos + dy, ypos - dy * n)
@@ -1035,19 +998,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.setlinewidth(1)
GR.drawrect(xpos - 0.08, xpos + w + 0.02, ypos + dy, ypos - dy * n)
i = 0
if sp[:legendtitle] != nothing
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_HALF)
gr_set_textcolor(sp[:foreground_color_legend])
gr_text(xpos - 0.03 + 0.5*w, ypos, string(sp[:legendtitle]))
ypos -= dy
end
for series in series_list(sp)
should_add_to_legend(series) || continue
st = series[:seriestype]
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
if st == :path && series[:fillrange] == nothing
if st == :path
GR.polyline([xpos - 0.07, xpos - 0.01], [ypos, ypos])
elseif st == :shape || series[:fillrange] != nothing
elseif st == :shape
gr_set_fill(series[:fillcolor]) #, series[:fillalpha])
l, r = xpos-0.07, xpos-0.01
b, t = ypos-0.4dy, ypos+0.4dy
@@ -1058,7 +1015,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
if series[:markershape] != :none
gr_draw_markers(series, xpos - .035, ypos, 6, nothing)
gr_draw_markers(series, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
end
if typeof(series[:label]) <: Array
@@ -1144,7 +1101,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(read, filepath)), "\a")
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(readbytes, filepath)), "\a")
println(content)
rm(filepath)
else
-655
View File
@@ -1,655 +0,0 @@
#HDF5 Plots: Save/replay plots to/from HDF5
#-------------------------------------------------------------------------------
#==Usage
===============================================================================
Write to .hdf5 file using:
p = plot(...)
Plots.hdf5plot_write(p, "plotsave.hdf5")
Read from .hdf5 file using:
pyplot() #Must first select backend
pread = Plots.hdf5plot_read("plotsave.hdf5")
display(pread)
==#
#==TODO
===============================================================================
1. Support more features
- SeriesAnnotations & GridLayout known to be missing.
3. Improve error handling.
- Will likely crash if file format is off.
2. Save data in a folder parallel to "plot".
- Will make it easier for users to locate data.
- Use HDF5 reference to link data?
3. Develop an actual versioned file format.
- Should have some form of backward compatibility.
- Should be reliable for archival purposes.
==#
import FixedPointNumbers: N0f8 #In core Julia
#Dispatch types:
immutable HDF5PlotNative; end #Indentifies a data element that can natively be handled by HDF5
immutable HDF5CTuple; end #Identifies a "complex" tuple structure
type HDF5Plot_PlotRef
ref::Union{Plot, Void}
end
#==Useful constants
===============================================================================#
const _hdf5_plotroot = "plot"
const _hdf5_dataroot = "data" #TODO: Eventually move data to different root (easier to locate)?
const _hdf5plot_datatypeid = "TYPE" #Attribute identifying type
const _hdf5plot_countid = "COUNT" #Attribute for storing count
#Dict has problems using "Types" as keys. Initialize in "_initialize_backend":
const HDF5PLOT_MAP_STR2TELEM = Dict{String, Type}()
const HDF5PLOT_MAP_TELEM2STR = Dict{Type, String}()
#Don't really like this global variable... Very hacky
const HDF5PLOT_PLOTREF = HDF5Plot_PlotRef(nothing)
#Simple sub-structures that can just be written out using _hdf5plot_gwritefields:
const HDF5PLOT_SIMPLESUBSTRUCT = Union{Font, BoundingBox,
GridLayout, RootLayout, ColorGradient, SeriesAnnotations, PlotText
}
#==
===============================================================================#
const _hdf5_attr = merge_with_base_supported([
:annotations,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
:label,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:fillrange, :fillcolor, :fillalpha,
:bins, :bar_width, :bar_edges, :bar_position,
:title, :title_location, :titlefont,
:window_title,
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
:marker_z, :line_z, :fill_z,
:levels,
:ribbon, :quiver, :arrow,
:orientation,
:overwrite_figure,
:polar,
:normalize, :weights,
:contours, :aspect_ratio,
:match_dimensions,
:clims,
:inset_subplots,
:dpi,
:colorbar_title,
])
const _hdf5_seriestype = [
:path, :steppre, :steppost, :shape,
:scatter, :hexbin, #:histogram2d, :histogram,
# :bar,
:heatmap, :pie, :image,
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
]
const _hdf5_style = [:auto, :solid, :dash, :dot, :dashdot]
const _hdf5_marker = vcat(_allMarkers, :pixel)
const _hdf5_scale = [:identity, :ln, :log2, :log10]
is_marker_supported(::HDF5Backend, shape::Shape) = true
function add_backend_string(::HDF5Backend)
"""
if !Plots.is_installed("HDF5")
Pkg.add("HDF5")
end
"""
end
#==Helper functions
===============================================================================#
_hdf5_plotelempath(subpath::String) = "$_hdf5_plotroot/$subpath"
_hdf5_datapath(subpath::String) = "$_hdf5_dataroot/$subpath"
_hdf5_map_str2telem(k::String) = HDF5PLOT_MAP_STR2TELEM[k]
_hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
function _hdf5_merge!(dest::Dict, src::Dict)
for (k, v) in src
if isa(v, Axis)
_hdf5_merge!(dest[k].d, v.d)
else
dest[k] = v
end
end
return
end
#==
===============================================================================#
function _initialize_backend(::HDF5Backend)
@eval begin
import HDF5
export HDF5
if length(HDF5PLOT_MAP_TELEM2STR) < 1
#Possible element types of high-level data types:
const telem2str = Dict{String, Type}(
"NATIVE" => HDF5PlotNative,
"VOID" => Void,
"BOOL" => Bool,
"SYMBOL" => Symbol,
"TUPLE" => Tuple,
"CTUPLE" => HDF5CTuple, #Tuple of complex structures
"RGBA" => ARGB{N0f8},
"EXTREMA" => Extrema,
"LENGTH" => Length,
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
#Sub-structure types:
"FONT" => Font,
"BOUNDINGBOX" => BoundingBox,
"GRIDLAYOUT" => GridLayout,
"ROOTLAYOUT" => RootLayout,
"SERIESANNOTATIONS" => SeriesAnnotations,
# "PLOTTEXT" => PlotText,
"COLORGRADIENT" => ColorGradient,
"AXIS" => Axis,
"SUBPLOT" => Subplot,
"NULLABLE" => Nullable,
)
merge!(HDF5PLOT_MAP_STR2TELEM, telem2str)
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
end
end
end
# ---------------------------------------------------------------------------
# Create the window/figure for this backend.
function _create_backend_figure(plt::Plot{HDF5Backend})
#Do nothing
end
# ---------------------------------------------------------------------------
# # this is called early in the pipeline, use it to make the plot current or something
# function _prepare_plot_object(plt::Plot{HDF5Backend})
# end
# ---------------------------------------------------------------------------
# Set up the subplot within the backend object.
function _initialize_subplot(plt::Plot{HDF5Backend}, sp::Subplot{HDF5Backend})
#Do nothing
end
# ---------------------------------------------------------------------------
# Add one series to the underlying backend object.
# Called once per series
# NOTE: Seems to be called when user calls plot()... even if backend
# plot, sp.o has not yet been constructed...
function _series_added(plt::Plot{HDF5Backend}, series::Series)
#Do nothing
end
# ---------------------------------------------------------------------------
# When series data is added/changed, this callback can do dynamic updates to the backend object.
# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
function _series_updated(plt::Plot{HDF5Backend}, series::Series)
#Do nothing
end
# ---------------------------------------------------------------------------
# called just before updating layout bounding boxes... in case you need to prep
# for the calcs
function _before_layout_calcs(plt::Plot{HDF5Backend})
#Do nothing
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{HDF5Backend})
#Do nothing
end
# ----------------------------------------------------------------
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
function _update_plot_object(plt::Plot{HDF5Backend})
#Do nothing
end
# ----------------------------------------------------------------
_show(io::IO, mime::MIME"text/plain", plt::Plot{HDF5Backend}) = nothing #Don't show
# ----------------------------------------------------------------
# Display/show the plot (open a GUI window, or browser page, for example).
function _display(plt::Plot{HDF5Backend})
msg = "HDF5 interface does not support `display()` function."
msg *= "\nUse `Plots.hdf5plot_write(::String)` method to write to .HDF5 \"plot\" file instead."
warn(msg)
return
end
#==HDF5 write functions
===============================================================================#
function _hdf5plot_writetype(grp, k::String, tstr::Array{String})
d = HDF5.d_open(grp, k)
HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype(grp, k::String, T::Type)
tstr = HDF5PLOT_MAP_TELEM2STR[T]
d = HDF5.d_open(grp, k)
HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_overwritetype(grp, k::String, T::Type)
tstr = HDF5PLOT_MAP_TELEM2STR[T]
d = HDF5.d_open(grp, k)
HDF5.a_delete(d, _hdf5plot_datatypeid)
HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype(grp, T::Type) #Write directly to group
tstr = HDF5PLOT_MAP_TELEM2STR[T]
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_overwritetype(grp, T::Type) #Write directly to group
tstr = HDF5PLOT_MAP_TELEM2STR[T]
HDF5.a_delete(grp, _hdf5plot_datatypeid)
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype{T<:Any}(grp, ::Type{Array{T}})
tstr = HDF5PLOT_MAP_TELEM2STR[Array] #ANY
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype{T<:BoundingBox}(grp, ::Type{T})
tstr = HDF5PLOT_MAP_TELEM2STR[BoundingBox]
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writecount(grp, n::Int) #Write directly to group
HDF5.a_write(grp, _hdf5plot_countid, n)
end
function _hdf5plot_gwritefields(grp, k::String, v)
grp = HDF5.g_create(grp, k)
for _k in fieldnames(v)
_v = getfield(v, _k)
kstr = string(_k)
_hdf5plot_gwrite(grp, kstr, _v)
end
_hdf5plot_writetype(grp, typeof(v))
return
end
# Write data
# ----------------------------------------------------------------
function _hdf5plot_gwrite(grp, k::String, v) #Default
grp[k] = v
_hdf5plot_writetype(grp, k, HDF5PlotNative)
end
function _hdf5plot_gwrite{T<:Number}(grp, k::String, v::Array{T}) #Default for arrays
grp[k] = v
_hdf5plot_writetype(grp, k, HDF5PlotNative)
end
#=
function _hdf5plot_gwrite(grp, k::String, v::Array{Any})
# @show grp, k
warn("Cannot write Array: $k=$v")
end
=#
function _hdf5plot_gwrite(grp, k::String, v::Void)
grp[k] = 0
_hdf5plot_writetype(grp, k, Void)
end
function _hdf5plot_gwrite(grp, k::String, v::Bool)
grp[k] = Int(v)
_hdf5plot_writetype(grp, k, Bool)
end
function _hdf5plot_gwrite(grp, k::String, v::Symbol)
grp[k] = string(v)
_hdf5plot_writetype(grp, k, Symbol)
end
function _hdf5plot_gwrite(grp, k::String, v::Tuple)
varr = [v...]
elt = eltype(varr)
# if isleaftype(elt)
_hdf5plot_gwrite(grp, k, varr)
if elt <: Number
#We just wrote a simple dataset
_hdf5plot_overwritetype(grp, k, Tuple)
else #Used a more complex scheme (using subgroups):
_hdf5plot_overwritetype(grp[k], HDF5CTuple)
end
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
end
function _hdf5plot_gwrite(grp, k::String, d::Dict)
# warn("Cannot write dict: $k=$d")
end
function _hdf5plot_gwrite(grp, k::String, v::Range)
_hdf5plot_gwrite(grp, k, collect(v)) #For now
end
function _hdf5plot_gwrite(grp, k::String, v::ARGB{N0f8})
grp[k] = [v.r.i, v.g.i, v.b.i, v.alpha.i]
_hdf5plot_writetype(grp, k, ARGB{N0f8})
end
function _hdf5plot_gwrite(grp, k::String, v::Colorant)
_hdf5plot_gwrite(grp, k, ARGB{N0f8}(v))
end
#Custom vector (when not using simple numeric type):
function _hdf5plot_gwritearray{T}(grp, k::String, v::Array{T})
if "annotations" == k;
return #Hack. Does not yet support annotations.
end
vgrp = HDF5.g_create(grp, k)
_hdf5plot_writetype(vgrp, Array) #ANY
sz = size(v)
for iter in eachindex(v)
coord = ind2sub(sz, iter)
elem = v[iter]
idxstr = join(coord, "_")
_hdf5plot_gwrite(vgrp, "v$idxstr", v[iter])
end
_hdf5plot_gwrite(vgrp, "dim", [sz...])
return
end
_hdf5plot_gwrite(grp, k::String, v::Array) =
_hdf5plot_gwritearray(grp, k, v)
function _hdf5plot_gwrite(grp, k::String, v::Extrema)
grp[k] = [v.emin, v.emax]
_hdf5plot_writetype(grp, k, Extrema)
end
function _hdf5plot_gwrite{T}(grp, k::String, v::Length{T})
grp[k] = v.value
_hdf5plot_writetype(grp, k, [HDF5PLOT_MAP_TELEM2STR[Length], string(T)])
end
# Write more complex structures:
# ----------------------------------------------------------------
function _hdf5plot_gwrite(grp, k::String, v::Plot)
#Don't write plot references
end
function _hdf5plot_gwrite(grp, k::String, v::HDF5PLOT_SIMPLESUBSTRUCT)
_hdf5plot_gwritefields(grp, k, v)
return
end
function _hdf5plot_gwrite(grp, k::String, v::Axis)
grp = HDF5.g_create(grp, k)
for (_k, _v) in v.d
kstr = string(_k)
_hdf5plot_gwrite(grp, kstr, _v)
end
_hdf5plot_writetype(grp, Axis)
return
end
#TODO: "Properly" support Nullable using _hdf5plot_writetype?
function _hdf5plot_gwrite(grp, k::String, v::Nullable)
if isnull(v)
_hdf5plot_gwrite(grp, k, nothing)
else
_hdf5plot_gwrite(grp, k, v.value)
end
return
end
function _hdf5plot_gwrite(grp, k::String, v::SeriesAnnotations)
#Currently no support for SeriesAnnotations
return
end
function _hdf5plot_gwrite(grp, k::String, v::Subplot)
grp = HDF5.g_create(grp, k)
_hdf5plot_gwrite(grp, "index", v[:subplot_index])
_hdf5plot_writetype(grp, Subplot)
return
end
function _hdf5plot_write(grp, d::Dict)
for (k, v) in d
kstr = string(k)
_hdf5plot_gwrite(grp, kstr, v)
end
return
end
# Write main plot structures:
# ----------------------------------------------------------------
function _hdf5plot_write(sp::Subplot{HDF5Backend}, subpath::String, f)
f = f::HDF5.HDF5File #Assert
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/attr"))
_hdf5plot_write(grp, sp.attr)
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/series_list"))
_hdf5plot_writecount(grp, length(sp.series_list))
for (i, series) in enumerate(sp.series_list)
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
_hdf5plot_write(grp, series.d)
end
return
end
function _hdf5plot_write(plt::Plot{HDF5Backend}, f)
f = f::HDF5.HDF5File #Assert
grp = HDF5.g_create(f, _hdf5_plotelempath("attr"))
_hdf5plot_write(grp, plt.attr)
grp = HDF5.g_create(f, _hdf5_plotelempath("subplots"))
_hdf5plot_writecount(grp, length(plt.subplots))
for (i, sp) in enumerate(plt.subplots)
_hdf5plot_write(sp, "subplots/subplot$i", f)
end
return
end
function hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString)
HDF5.h5open(path, "w") do file
_hdf5plot_write(plt, file)
end
end
hdf5plot_write(path::AbstractString) = hdf5plot_write(current(), path)
#==HDF5 playback (read) functions
===============================================================================#
function _hdf5plot_readcount(grp) #Read directly from group
return HDF5.a_read(grp, _hdf5plot_countid)
end
_hdf5plot_convert(T::Type{HDF5PlotNative}, v) = v
_hdf5plot_convert(T::Type{Void}, v) = nothing
_hdf5plot_convert(T::Type{Bool}, v) = (v!=0)
_hdf5plot_convert(T::Type{Symbol}, v) = Symbol(v)
_hdf5plot_convert(T::Type{Tuple}, v) = tuple(v...) #With Vector{T<:Number}
function _hdf5plot_convert(T::Type{ARGB{N0f8}}, v)
r, g, b, a = reinterpret(N0f8, v)
return Colors.ARGB{N0f8}(r, g, b, a)
end
_hdf5plot_convert(T::Type{Extrema}, v) = Extrema(v[1], v[2])
# Read data structures:
# ----------------------------------------------------------------
function _hdf5plot_read(grp, k::String, T::Type, dtid)
v = HDF5.d_read(grp, k)
return _hdf5plot_convert(T, v)
end
function _hdf5plot_read(grp, k::String, T::Type{Length}, dtid::Vector)
v = HDF5.d_read(grp, k)
TU = Symbol(dtid[2])
T = typeof(v)
return Length{TU,T}(v)
end
# Read more complex data structures:
# ----------------------------------------------------------------
function _hdf5plot_read(grp, k::String, T::Type{Font}, dtid)
grp = HDF5.g_open(grp, k)
family = _hdf5plot_read(grp, "family")
pointsize = _hdf5plot_read(grp, "pointsize")
halign = _hdf5plot_read(grp, "halign")
valign = _hdf5plot_read(grp, "valign")
rotation = _hdf5plot_read(grp, "rotation")
color = _hdf5plot_read(grp, "color")
return Font(family, pointsize, halign, valign, rotation, color)
end
function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
grp = HDF5.g_open(grp, k)
sz = _hdf5plot_read(grp, "dim")
if [0] == sz; return []; end
sz = tuple(sz...)
result = Array{Any}(sz)
for iter in eachindex(result)
coord = ind2sub(sz, iter)
idxstr = join(coord, "_")
result[iter] = _hdf5plot_read(grp, "v$idxstr")
end
#Hack: Implicitly make Julia detect element type.
# (Should probably write it explicitly to file)
result = [result[iter] for iter in eachindex(result)] #Potentially make more specific
return reshape(result, sz)
end
function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple}, dtid)
v = _hdf5plot_read(grp, k, Array, dtid)
return tuple(v...)
end
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient}, dtid)
grp = HDF5.g_open(grp, k)
colors = _hdf5plot_read(grp, "colors")
values = _hdf5plot_read(grp, "values")
return ColorGradient(colors, values)
end
function _hdf5plot_read(grp, k::String, T::Type{BoundingBox}, dtid)
grp = HDF5.g_open(grp, k)
x0 = _hdf5plot_read(grp, "x0")
a = _hdf5plot_read(grp, "a")
return BoundingBox(x0, a)
end
_hdf5plot_read(grp, k::String, T::Type{RootLayout}, dtid) = RootLayout()
function _hdf5plot_read(grp, k::String, T::Type{GridLayout}, dtid)
grp = HDF5.g_open(grp, k)
# parent = _hdf5plot_read(grp, "parent")
parent = RootLayout()
minpad = _hdf5plot_read(grp, "minpad")
bbox = _hdf5plot_read(grp, "bbox")
grid = _hdf5plot_read(grp, "grid")
widths = _hdf5plot_read(grp, "widths")
heights = _hdf5plot_read(grp, "heights")
attr = KW() #TODO support attr: _hdf5plot_read(grp, "attr")
return GridLayout(parent, minpad, bbox, grid, widths, heights, attr)
end
function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid)
grp = HDF5.g_open(grp, k)
kwlist = KW()
_hdf5plot_read(grp, kwlist)
return Axis([], kwlist)
end
function _hdf5plot_read(grp, k::String, T::Type{Subplot}, dtid)
grp = HDF5.g_open(grp, k)
idx = _hdf5plot_read(grp, "index")
return HDF5PLOT_PLOTREF.ref.subplots[idx]
end
function _hdf5plot_read(grp, k::String)
dtid = HDF5.a_read(grp[k], _hdf5plot_datatypeid)
T = _hdf5_map_str2telem(dtid) #expect exception
return _hdf5plot_read(grp, k, T, dtid)
end
#Read in values in group to populate d:
function _hdf5plot_read(grp, d::Dict)
gnames = names(grp)
for k in gnames
try
v = _hdf5plot_read(grp, k)
d[Symbol(k)] = v
catch e
@show e
@show grp
warn("Could not read field $k")
end
end
return
end
# Read main plot structures:
# ----------------------------------------------------------------
function _hdf5plot_read(sp::Subplot, subpath::String, f)
f = f::HDF5.HDF5File #Assert
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
kwlist = KW()
_hdf5plot_read(grp, kwlist)
_hdf5_merge!(sp.attr, kwlist)
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list"))
nseries = _hdf5plot_readcount(grp)
for i in 1:nseries
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
kwlist = KW()
_hdf5plot_read(grp, kwlist)
plot!(sp, kwlist[:x], kwlist[:y]) #Add data & create data structures
_hdf5_merge!(sp.series_list[end].d, kwlist)
end
return
end
function _hdf5plot_read(plt::Plot, f)
f = f::HDF5.HDF5File #Assert
#Assumpltion: subplots are already allocated (plt.subplots)
HDF5PLOT_PLOTREF.ref = plt #Used when reading "layout"
grp = HDF5.g_open(f, _hdf5_plotelempath("attr"))
_hdf5plot_read(grp, plt.attr)
for (i, sp) in enumerate(plt.subplots)
_hdf5plot_read(sp, "subplots/subplot$i", f)
end
return
end
function hdf5plot_read(path::AbstractString)
plt = nothing
HDF5.h5open(path, "r") do file
grp = HDF5.g_open(file, _hdf5_plotelempath("subplots"))
n = _hdf5plot_readcount(grp)
plt = plot(layout=n) #Get reference to a new plot
_hdf5plot_read(plt, file)
end
return plt
end
#Last line
+36 -77
View File
@@ -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,9 +66,6 @@ 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
@@ -128,17 +125,16 @@ end
# ---------------------------------------------------------------------------
function _inspectdr_getscale(s::Symbol, yaxis::Bool)
function _inspectdr_getscale(s::Symbol)
#TODO: Support :asinh, :sqrt
kwargs = yaxis? (:tgtmajor=>8, :tgtminor=>2): () #More grid lines on y-axis
if :log2 == s
return InspectDR.AxisScale(:log2; kwargs...)
return InspectDR.AxisScale(:log2)
elseif :log10 == s
return InspectDR.AxisScale(:log10; kwargs...)
return InspectDR.AxisScale(:log10)
elseif :ln == s
return InspectDR.AxisScale(:ln; kwargs...)
return InspectDR.AxisScale(:ln)
else #identity
return InspectDR.AxisScale(:lin; kwargs...)
return InspectDR.AxisScale(:lin)
end
end
@@ -213,10 +209,14 @@ 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.userannot = [] #Clear old markers/text annotation/polyline "annotation"
plot.markers = [] #Clear old markers
plot.atext = [] #Clear old annotation
plot.apline = [] #Clear old poly lines
return plot
end
@@ -237,12 +237,6 @@ 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
@@ -265,23 +259,23 @@ For st in :shape:
nmax = i
if length(rng) > 1
linewidth = series[:linewidth]
linecolor = _inspectdr_mapcolor(_cycle(series[:linecolor], i))
fillcolor = _inspectdr_mapcolor(_cycle(series[:fillcolor], i))
linecolor = _inspectdr_mapcolor(cycle(series[:linecolor], i))
fillcolor = _inspectdr_mapcolor(cycle(series[:fillcolor], i))
line = InspectDR.line(
style=:solid, width=linewidth, color=linecolor
)
apline = InspectDR.PolylineAnnotation(
x[rng], y[rng], line=line, fillcolor=fillcolor
)
InspectDR.add(plot, apline)
push!(plot.apline, apline)
end
end
i = (nmax >= 2? div(nmax, 2): nmax) #Must pick one set of colors for legend
if i > 1 #Add dummy waveform for legend entry:
linewidth = series[:linewidth]
linecolor = _inspectdr_mapcolor(_cycle(series[:linecolor], i))
fillcolor = _inspectdr_mapcolor(_cycle(series[:fillcolor], i))
linecolor = _inspectdr_mapcolor(cycle(series[:linecolor], i))
fillcolor = _inspectdr_mapcolor(cycle(series[:fillcolor], i))
wfrm = InspectDR.add(plot, Float64[], Float64[], id=series[:label])
wfrm.line = InspectDR.line(
style=:none, width=linewidth, #linewidth affects glyph
@@ -334,35 +328,23 @@ end
# ---------------------------------------------------------------------------
function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
const gridon = InspectDR.GridRect(vmajor=true, hmajor=true)
const gridoff = InspectDR.GridRect()
const gridon = InspectDR.grid(vmajor=true, hmajor=true)
const gridoff = InspectDR.grid()
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]
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
xscale = _inspectdr_getscale(xaxis[:scale])
yscale = _inspectdr_getscale(yaxis[:scale])
plot.axes = InspectDR.AxesRect(xscale, yscale)
xmin, xmax = axis_limits(xaxis)
ymin, ymax = axis_limits(yaxis)
if ispolar(sp)
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
rmax = NaNMath.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)
plot.ext = InspectDR.PExtents2D() #reset
plot.ext_full = InspectDR.PExtents2D(xmin, xmax, ymin, ymax)
a = plot.annotation
a.title = sp[:title]
a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]]
a.xlabel = xaxis[:guide]; a.ylabel = 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,
@@ -378,6 +360,8 @@ 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???
@@ -394,13 +378,6 @@ 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)
@@ -408,13 +385,15 @@ 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(plot, ann...)
_inspectdr_add_annotations(mplot.subplots[i], ann...)
end
end
@@ -443,19 +422,8 @@ 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})
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)
sp.minpad = (20mm, 5mm, 2mm, 10mm)
#TODO: Add support for padding.
end
# ----------------------------------------------------------------
@@ -464,13 +432,6 @@ 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
@@ -491,21 +452,19 @@ const _inspectdr_mimeformats_nodpi = Dict(
# "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface
"application/pdf" => "pdf"
)
_inspectdr_show(io::IO, mime::MIME, ::Void, w, h) =
_inspectdr_show(io::IO, mime::MIME, ::Void) =
throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated"))
function _inspectdr_show(io::IO, mime::MIME, mplot, w, h)
InspectDR._show(io, mime, mplot, Float64(w), Float64(h))
end
_inspectdr_show(io::IO, mime::MIME, mplot) = show(io, mime, mplot)
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), plt[:size]...)
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o))
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), plt[:size]...)
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o))
end
end
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show
+12 -101
View File
@@ -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, :shape]
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
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,35 +98,14 @@ 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::Colorant)
function pgf_color(c)
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"
@@ -157,19 +136,6 @@ 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)
@@ -177,10 +143,11 @@ 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 || st in (:shape,)
if d[:fillrange] != nothing
push!(style, pgf_fillstyle(d))
end
@@ -196,10 +163,6 @@ 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
@@ -248,9 +211,6 @@ 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")
@@ -289,12 +249,8 @@ 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()
@@ -309,12 +265,10 @@ 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 = $(round((total_height - (bottom(bb))).value,2))mm,
yshift = $((height(bb) - (bottom(bb))).value)mm,
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
""")
kw[:width] = "$(width(bb).value)mm"
@@ -334,62 +288,19 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
kw[:legendPos] = _pgfplots_legend_pos[legpos]
end
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...)
o = PGFPlots.Axis(; 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
+8 -21
View File
@@ -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, :colorbar_title,
:grid, :legend, :colorbar,
:marker_z, :fill_z, :levels,
:ribbon, :quiver,
:orientation,
@@ -31,7 +31,6 @@ const _plotly_attr = merge_with_base_supported([
:hover,
:inset_subplots,
:bar_width,
:clims,
])
const _plotly_seriestype = [
@@ -269,7 +268,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=>20, :r=>0, :t=>20)
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>20)
d_out[:annotations] = KW[]
@@ -409,10 +408,6 @@ 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]
@@ -443,13 +438,6 @@ 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"
@@ -545,10 +533,9 @@ function plotly_series(plt::Plot, series::Series)
rgba_string(series[:markercolor])
else
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
grad = as_gradient(series[:markercolor], series[:markeralpha])
zmin, zmax = ignorenan_extrema(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]]
grad = series[:markercolor]
zmin, zmax = extrema(series[:marker_z])
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]]
end
end
@@ -600,18 +587,18 @@ function plotly_series_shapes(plt::Plot, series::Series)
:x => vcat(x[rng], x[rng[1]]),
:y => vcat(y[rng], y[rng[1]]),
:fill => "tozeroy",
:fillcolor => rgba_string(_cycle(series[:fillcolor], i)),
:fillcolor => rgba_string(cycle(series[:fillcolor], i)),
))
if series[:markerstrokewidth] > 0
d_out[:line] = KW(
:color => rgba_string(_cycle(series[:linecolor], i)),
:color => rgba_string(cycle(series[:linecolor], i)),
:width => series[:linewidth],
:dash => string(series[:linestyle]),
)
end
d_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
plotly_polar!(d_out, series)
plotly_hover!(d_out, _cycle(series[:hover], i))
plotly_hover!(d_out, cycle(series[:hover], i))
push!(d_outs, d_out)
end
d_outs
+1 -11
View File
@@ -102,18 +102,8 @@ _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})
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
display(plt.o)
else
standalone_html_window(plt)
end
display(plt.o)
end
+25 -39
View File
@@ -17,7 +17,7 @@ const _pyplot_attr = merge_with_base_supported([
:window_title,
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :legendtitle, :colorbar,
:grid, :legend, :colorbar,
:marker_z, :line_z, :fill_z,
:levels,
:ribbon, :quiver, :arrow,
@@ -55,8 +55,6 @@ function add_backend_string(::PyPlotBackend)
withenv("PYTHON" => "") do
Pkg.build("PyPlot")
end
# now restart julia!
"""
end
@@ -82,10 +80,10 @@ function _initialize_backend(::PyPlotBackend)
const pytransforms = PyPlot.pywrap(PyPlot.pyimport("matplotlib.transforms"))
const pycollections = PyPlot.pywrap(PyPlot.pyimport("matplotlib.collections"))
const pyart3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d.art3d"))
# we don't want every command to update the figure
PyPlot.ioff()
end
# we don't want every command to update the figure
PyPlot.ioff()
end
# --------------------------------------------------------------------------------------
@@ -219,12 +217,6 @@ 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")(
@@ -495,10 +487,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
handle = if is3d(st)
for rng in iter_segments(x, y, z)
length(rng) < 2 && continue
push!(segments, [(_cycle(x,i),_cycle(y,i),_cycle(z,i)) for i in rng])
push!(segments, [(cycle(x,i),cycle(y,i),cycle(z,i)) for i in rng])
end
# for i=1:n
# segments[i] = [(_cycle(x,i), _cycle(y,i), _cycle(z,i)), (_cycle(x,i+1), _cycle(y,i+1), _cycle(z,i+1))]
# segments[i] = [(cycle(x,i), cycle(y,i), cycle(z,i)), (cycle(x,i+1), cycle(y,i+1), cycle(z,i+1))]
# end
lc = pyart3d.Line3DCollection(segments; kw...)
lc[:set_array](lz)
@@ -507,10 +499,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
else
for rng in iter_segments(x, y)
length(rng) < 2 && continue
push!(segments, [(_cycle(x,i),_cycle(y,i)) for i in rng])
push!(segments, [(cycle(x,i),cycle(y,i)) for i in rng])
end
# for i=1:n
# segments[i] = [(_cycle(x,i), _cycle(y,i)), (_cycle(x,i+1), _cycle(y,i+1))]
# segments[i] = [(cycle(x,i), cycle(y,i)), (cycle(x,i+1), cycle(y,i+1))]
# end
lc = pycollections.LineCollection(segments; kw...)
lc[:set_array](lz)
@@ -581,16 +573,16 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
lw = py_dpi_scale(plt, series[:markerstrokewidth])
for i=1:length(y)
extrakw[:c] = if series[:marker_z] == nothing
py_color_fix(py_color(_cycle(series[:markercolor],i)), x)
py_color_fix(py_color(cycle(series[:markercolor],i)), x)
else
extrakw[:c]
end
push!(handle, ax[:scatter](_cycle(x,i), _cycle(y,i);
push!(handle, ax[:scatter](cycle(x,i), cycle(y,i);
label = series[:label],
zorder = series[:series_plotindex] + 0.5,
marker = py_marker(_cycle(shapes,i)),
s = py_dpi_scale(plt, _cycle(series[:markersize],i) .^ 2),
marker = py_marker(cycle(shapes,i)),
s = py_dpi_scale(plt, cycle(series[:markersize],i) .^ 2),
edgecolors = msc,
linewidths = lw,
extrakw...
@@ -677,11 +669,6 @@ 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))
@@ -705,11 +692,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
# contours on the axis planes
if series[:contours]
for (zdir,mat) in (("x",x), ("y",y), ("z",z))
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat)
offset = (zdir == "y" ? maximum : minimum)(mat)
handle = ax[:contourf](x, y, z, levelargs...;
zdir = zdir,
cmap = py_fillcolormap(series),
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat) # where to draw the contour plane
offset = (zdir == "y" ? maximum : minimum)(mat) # where to draw the contour plane
)
push!(handles, handle)
needs_colorbar = true
@@ -778,7 +765,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end
clims = sp[:clims]
zmin, zmax = ignorenan_extrema(z)
zmin, zmax = extrema(z)
extrakw[:vmin] = (is_2tuple(clims) && isfinite(clims[1])) ? clims[1] : zmin
extrakw[:vmax] = (is_2tuple(clims) && isfinite(clims[2])) ? clims[2] : zmax
@@ -802,8 +789,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
path;
label = series[:label],
zorder = series[:series_plotindex],
edgecolor = py_color(_cycle(series[:linecolor], i)),
facecolor = py_color(_cycle(series[:fillcolor], i)),
edgecolor = py_color(cycle(series[:linecolor], i)),
facecolor = py_color(cycle(series[:fillcolor], i)),
linewidth = py_dpi_scale(plt, series[:linewidth]),
fill = true
)
@@ -872,7 +859,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..., trues(n), false, py_fillstepstyle(st);
handle = ax[f](args...;
zorder = series[:series_plotindex],
facecolor = py_fillcolor(series),
linewidths = 0
@@ -926,14 +913,14 @@ function py_compute_axis_minval(axis::Axis)
for series in series_list(sp)
v = series.d[axis[:letter]]
if !isempty(v)
minval = NaNMath.min(minval, ignorenan_minimum(abs(v)))
minval = min(minval, minimum(abs(v)))
end
end
end
# now if the axis limits go to a smaller abs value, use that instead
vmin, vmax = axis_limits(axis)
minval = NaNMath.min(minval, abs(vmin), abs(vmax))
minval = min(minval, abs(vmin), abs(vmax))
minval
end
@@ -954,7 +941,7 @@ function py_set_scale(ax, axis::Axis)
elseif scale == :log10
10
end
kw[Symbol(:linthresh,letter)] = NaNMath.max(1e-16, py_compute_axis_minval(axis))
kw[Symbol(:linthresh,letter)] = max(1e-16, py_compute_axis_minval(axis))
"symlog"
end
func(arg; kw...)
@@ -1058,7 +1045,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
end
if sp[:grid]
fgcolor = py_color(sp[:foreground_color_grid])
pyaxis[:grid](true, color = fgcolor, linestyle = ":")
pyaxis[:grid](true, color = fgcolor)
ax[:set_axisbelow](true)
end
py_set_axis_colors(ax, axis)
@@ -1074,7 +1061,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
py_add_legend(plt, sp, ax)
# this sets the bg color inside the grid
ax[:set_facecolor](py_color(sp[:background_color_inside]))
ax[:set_axis_bgcolor](py_color(sp[:background_color_inside]))
end
py_drawfig(fig)
end
@@ -1106,7 +1093,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) + 2.3mm + (sp[:colorbar_title] == "" ? 0px : 30px)
sp.attr[:cbar_width] = _cbar_width + width(bb) + 1mm + (sp[:colorbar_title] == "" ? 0px : 30px)
rightpad = rightpad + sp.attr[:cbar_width]
end
@@ -1166,7 +1153,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
# add a line/marker and a label
push!(handles, if series[:seriestype] == :shape
PyPlot.plt[:Line2D]((0,1),(0,0),
color = py_color(_cycle(series[:fillcolor],1)),
color = py_color(cycle(series[:fillcolor],1)),
linewidth = py_dpi_scale(plt, 4)
)
else
@@ -1187,7 +1174,6 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
# framealpha = 0.6
)
leg[:set_zorder](1000)
sp[:legendtitle] != nothing && leg[:set_title](sp[:legendtitle])
fgcolor = py_color(sp[:foreground_color_legend])
for txt in leg[:get_texts]()
+2 -2
View File
@@ -23,11 +23,11 @@ function open_browser_window(filename::AbstractString)
@static if is_apple()
return run(`open $(filename)`)
end
@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
@static if is_linux()
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
+14 -14
View File
@@ -1,7 +1,7 @@
const P2 = FixedSizeArrays.Vec{2,Float64}
const P3 = FixedSizeArrays.Vec{3,Float64}
typealias P2 StaticArrays.SVector{2,Float64}
typealias 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 scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
sx, sy = coords(shape)
cx, cy = c
for i=1:length(sx)
@@ -446,7 +446,7 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
msw,msh = anns.scalefactor
msize = Float64[]
shapes = Shape[begin
str = _cycle(anns.strs,i)
str = cycle(anns.strs,i)
# get the width and height of the string (in mm)
sw, sh = text_size(str, anns.font.pointsize)
@@ -462,7 +462,7 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
# and then re-scale a copy of baseshape to match the w/h ratio
maxscale = max(xscale, yscale)
push!(msize, maxscale)
baseshape = _cycle(get(anns.baseshape),i)
baseshape = cycle(get(anns.baseshape),i)
shape = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
end for i=1:length(anns.strs)]
series[:markershape] = shapes
@@ -479,13 +479,13 @@ end
Base.start(ea::EachAnn) = 1
Base.done(ea::EachAnn, i) = ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
function Base.next(ea::EachAnn, i)
tmp = _cycle(ea.anns.strs,i)
tmp = cycle(ea.anns.strs,i)
str,fnt = if isa(tmp, PlotText)
tmp.str, tmp.font
else
tmp, ea.anns.font
end
((_cycle(ea.x,i), _cycle(ea.y,i), str, fnt), i+1)
((cycle(ea.x,i), cycle(ea.y,i), str, fnt), i+1)
end
annotations(::Void) = []
@@ -501,13 +501,13 @@ immutable ZValues
zrange::Tuple{Float64,Float64}
end
function zvalues{T<:Real}(values::AVec{T}, zrange::Tuple{T,T} = (ignorenan_minimum(values), ignorenan_maximum(values)))
function zvalues{T<:Real}(values::AVec{T}, zrange::Tuple{T,T} = (minimum(values), maximum(values)))
ZValues(collect(float(values)), map(Float64, zrange))
end
# -----------------------------------------------------------------------
abstract type AbstractSurface end
abstract AbstractSurface
"represents a contour or surface mesh"
immutable Surface{M<:AMat} <: AbstractSurface
@@ -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(copy(surf.surf))
Base.copy(surf::Surface) = Surface{typeof(surf.surf)}(copy(surf.surf))
Base.eltype{T}(surf::Surface{T}) = eltype(T)
function expand_extrema!(a::Axis, surf::Surface)
@@ -632,7 +632,7 @@ end
# -----------------------------------------------------------------------
type BezierCurve{T <: FixedSizeArrays.Vec}
type BezierCurve{T <: StaticArrays.SVector}
control_points::Vector{T}
end
@@ -645,8 +645,8 @@ function (bc::BezierCurve)(t::Real)
p
end
# mean(x::Real, y::Real) = 0.5*(x+y) #commented out as I cannot see this used anywhere and it overwrites a Base method with different functionality
# mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps) # I also could not see this used anywhere, and it's type piracy - implementing a NaNMath version for this would just involve converting to a standard array
Base.mean(x::Real, y::Real) = 0.5*(x+y)
Base.mean{N,T<:Real}(ps::StaticArrays.SVector{N,T}...) = sum(ps) / length(ps)
@deprecate curve_points coords
@@ -659,7 +659,7 @@ function directed_curve(args...; kw...)
end
function extrema_plus_buffer(v, buffmult = 0.2)
vmin,vmax = ignorenan_extrema(v)
vmin,vmax = extrema(v)
vdiff = vmax-vmin
buffer = vdiff * buffmult
vmin - buffer, vmax + buffer
+1 -1
View File
@@ -84,7 +84,7 @@ function make_polygon(geom::ShapeGeometry, xs::AbstractArray, ys::AbstractArray,
x = Compose.x_measure(xs[mod1(i, length(xs))])
y = Compose.y_measure(ys[mod1(i, length(ys))])
r = rs[mod1(i, length(rs))]
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in _cycle(geom.vertices, i)]
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in cycle(geom.vertices, i)]
end
Gadfly.polygon(polys, geom.tag)
end
+1 -1
View File
@@ -1,5 +1,5 @@
abstract type ColorScheme end
abstract ColorScheme
Base.getindex(scheme::ColorScheme, i::Integer) = getColor(scheme, i)
+1 -1
View File
@@ -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
const FuncOrFuncs = @compat(Union{Function, AVec{Function}})
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
+9 -36
View File
@@ -41,7 +41,7 @@ PlotExample("Colors",
[:(begin
y = rand(100)
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=0, α=0.6)
scatter!(y, zcolor=abs.(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs.(y-0.5)+4, lab="grad")
scatter!(y, zcolor=abs(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs(y-0.5)+4, lab="grad")
end)]
),
@@ -115,22 +115,20 @@ PlotExample("Line types",
PlotExample("Line styles",
"",
[:(begin
styles = filter(s -> s in Plots.supported_styles(), [:solid, :dash, :dot, :dashdot, :dashdotdot])
styles = reshape(styles, 1, length(styles)) # Julia 0.6 unfortunately gives an error when transposing symbol vectors
styles = filter(s -> s in Plots.supported_styles(), [:solid, :dash, :dot, :dashdot, :dashdotdot])'
n = length(styles)
y = cumsum(randn(20,n),1)
plot(y, line = (5, styles), label = map(string,styles), legendtitle = "linestyle")
plot(y, line = (5, styles), label = map(string,styles))
end)]
),
PlotExample("Marker types",
"",
[:(begin
markers = filter(m -> m in Plots.supported_markers(), Plots._shape_keys)
markers = reshape(markers, 1, length(markers))
markers = filter(m -> m in Plots.supported_markers(), Plots._shape_keys)'
n = length(markers)
x = linspace(0,10,n+2)[2:end-1]
y = repmat(reshape(reverse(x),1,:), n, 1)
y = repmat(reverse(x)', n, 1)
scatter(x, y, m=(8,:auto), lab=map(string,markers), bg=:linen, xlim=(0,10), ylim=(0,10))
end)]
),
@@ -216,7 +214,7 @@ PlotExample("Contours",
x = 1:0.5:20
y = 1:0.5:10
f(x,y) = (3x+y^2)*abs(sin(x)+cos(y))
X = repmat(reshape(x,1,:), length(y), 1)
X = repmat(x', length(y), 1)
Y = repmat(y, 1, length(x))
Z = map(f, X, Y)
p1 = contour(x, y, f, fill=true)
@@ -270,7 +268,7 @@ PlotExample("Polar Plots",
"",
[:(begin
Θ = linspace(0,1.5π,100)
r = abs.(0.1randn(100)+sin.(3Θ))
r = abs(0.1randn(100)+sin(3Θ))
plot(Θ, r, proj=:polar, m=2)
end)]
),
@@ -280,7 +278,7 @@ PlotExample("Heatmap, categorical axes, and aspect_ratio",
[:(begin
xs = [string("x",i) for i=1:10]
ys = [string("y",i) for i=1:4]
z = float((1:4)*reshape(1:10,1,:))
z = float((1:4)*(1:10)')
heatmap(xs, ys, z, aspect_ratio=1)
end)]
),
@@ -302,32 +300,7 @@ PlotExample("Boxplot and Violin series recipes",
violin(singers, :VoicePart, :Height, line = 0, fill = (0.2, :blue))
boxplot!(singers, :VoicePart, :Height, line = (2,:black), fill = (0.3, :orange))
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("Spy",
"For a matrix `mat` with unique nonzeros `spy(mat)` returns a colorless plot. If `mat` has various different nonzero values, a colorbar is added. The colorbar can be disabled with `legend = nothing`. As always, the marker shape and size can be changed with `spy(mat, markersize = 3, markershape = :star)`",
[:(begin
a = spdiagm((ones(50), ones(49), ones(49), ones(40), ones(40)),(0, 1, -1, 10, -10))
b = spdiagm((1:50, 1:49, 1:49, 1:40, 1:40),(0, 1, -1, 10, -10))
plot(spy(a, markershape = :dtriangle), spy(b), markersize = 3, title = ["Unique nonzeros" "Different nonzeros"])
end)]
),
)
]
+3 -3
View File
@@ -9,8 +9,8 @@ to_pixels(m::AbsoluteLength) = m.value / 0.254
const _cbar_width = 5mm
Base.broadcast(::typeof(Base.:.*), m::Measure, n::Number) = m * n
Base.broadcast(::typeof(Base.:.*), m::Number, n::Measure) = m * n
Base.:.*(m::Measure, n::Number) = m * n
Base.:.*(n::Number, m::Measure) = m * n
Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
Base.zero(::Type{typeof(mm)}) = 0mm
@@ -704,7 +704,7 @@ function link_axes!(axes::Axis...)
a1 = axes[1]
for i=2:length(axes)
a2 = axes[i]
expand_extrema!(a1, ignorenan_extrema(a2))
expand_extrema!(a1, extrema(a2))
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
a2[k] = a1[k]
end
+8 -6
View File
@@ -271,7 +271,7 @@ function setup_ijulia()
show(io, MIME("text/html"), plt)
end
end
@eval set_ijulia_output("text/html")
set_ijulia_output("text/html")
end
end
@@ -302,10 +302,6 @@ 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)
@@ -318,8 +314,14 @@ 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 backend cannot render in the PlotPane due to javascript issues. Plotlyjs is similar to plotly and is compatible with the plot pane."
s = "PlotPane turned off. The plotly and plotlyjs backends cannot render in the PlotPane due to javascript issues."
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
+3 -10
View File
@@ -153,7 +153,7 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
if get(kw, :smooth, false)
x, y = kw[:x], kw[:y]
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
sx = [ignorenan_minimum(x), ignorenan_maximum(x)]
sx = [minimum(x), maximum(x)]
sy = β * sx + α
push!(kw_list, merge(copy(kw), KW(
:seriestype => :path,
@@ -262,7 +262,7 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
for kw in kw_list
# get the Subplot object to which the series belongs.
sps = get(kw, :subplot, :auto)
sp = get_subplot(plt, _cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
sp = get_subplot(plt, cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
kw[:subplot] = sp
# extract subplot/axis attributes from kw and add to sp_attr
@@ -277,13 +277,6 @@ 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
@@ -364,7 +357,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, :bins2d, :histogram2d))
elseif !(st in (:pie, :histogram, :histogram2d))
expand_extrema!(sp, d)
end
end
+5 -5
View File
@@ -29,12 +29,12 @@ convertSeriesIndex(plt::Plot, n::Int) = n
"""
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
The main plot command. Use `plot` to create a new plot object, and `plot!` to mutate an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the current
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
plot(args...; kw...) # creates a new Plot, and sets it to be the current
plot!(args...; kw...) # adds to the `current` Plot
plot!(plotobj, args...; kw...) # adds to the Plot `plotobj`
```
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
@@ -60,7 +60,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
# build our plot vector from the args
n = length(plts_tail) + 1
plts = Array{Plot}(n)
plts = Array(Plot, n)
plts[1] = plt1
for (i,plt) in enumerate(plts_tail)
plts[i+1] = plt
-56
View File
@@ -1,56 +0,0 @@
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
+158 -393
View File
@@ -1,4 +1,53 @@
"""
You can easily define your own plotting recipes with convenience methods:
```
@userplot type GroupHist
args
end
@recipe function f(gh::GroupHist)
# set some attributes, add some series, using gh.args as input
end
# now you can plot like:
grouphist(rand(1000,4))
```
"""
macro userplot(expr)
_userplot(expr)
end
function _userplot(expr::Expr)
if expr.head != :type
errror("Must call userplot on a type/immutable expression. Got: $expr")
end
typename = expr.args[2]
funcname = Symbol(lowercase(string(typename)))
funcname2 = Symbol(funcname, "!")
# return a code block with the type definition and convenience plotting methods
esc(quote
$expr
export $funcname, $funcname2
$funcname(args...; kw...) = plot($typename(args); kw...)
$funcname2(args...; kw...) = plot!($typename(args); kw...)
end)
end
function _userplot(sym::Symbol)
_userplot(:(type $sym
args
end))
end
# ----------------------------------------------------------------------------------
const _series_recipe_deps = Dict()
function series_recipe_dependencies(st::Symbol, deps::Symbol...)
@@ -47,7 +96,7 @@ end
num_series(x::AMat) = size(x,2)
num_series(x) = 1
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::AbstractPlot) = throw(MethodError("Unmatched plot recipe: $T"))
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T"))
# ---------------------------------------------------------------------------
@@ -176,14 +225,14 @@ end
fr = if yaxis[:scale] == :identity
0.0
else
NaNMath.min(axis_limits(yaxis)[1], ignorenan_minimum(y))
min(axis_limits(yaxis)[1], minimum(y))
end
end
newx, newy = zeros(3n), zeros(3n)
for i=1:n
rng = 3i-2:3i
newx[rng] = [x[i], x[i], NaN]
newy[rng] = [_cycle(fr,i), y[i], NaN]
newy[rng] = [cycle(fr,i), y[i], NaN]
end
x := newx
y := newy
@@ -235,16 +284,16 @@ end
for rng in iter_segments(args...)
length(rng) < 2 && continue
ts = linspace(0, 1, npoints)
nanappend!(newx, map(t -> bezier_value(_cycle(x,rng), t), ts))
nanappend!(newy, map(t -> bezier_value(_cycle(y,rng), t), ts))
nanappend!(newx, map(t -> bezier_value(cycle(x,rng), t), ts))
nanappend!(newy, map(t -> bezier_value(cycle(y,rng), t), ts))
if z != nothing
nanappend!(newz, map(t -> bezier_value(_cycle(z,rng), t), ts))
nanappend!(newz, map(t -> bezier_value(cycle(z,rng), t), ts))
end
if fr != nothing
nanappend!(newfr, map(t -> bezier_value(_cycle(fr,rng), t), ts))
nanappend!(newfr, map(t -> bezier_value(cycle(fr,rng), t), ts))
end
# if lz != nothing
# lzrng = _cycle(lz, rng) # the line_z's for this segment
# lzrng = cycle(lz, rng) # the line_z's for this segment
# push!(newlz, 0.0)
# append!(newlz, map(t -> lzrng[1+floor(Int, t * (length(rng)-1))], ts))
# end
@@ -274,11 +323,10 @@ end
# create a bar plot as a filled step function
@recipe function f(::Type{Val{:bar}}, x, y, z)
procx, procy, xscale, yscale, baseline = _preprocess_barlike(d, x, y)
nx, ny = length(procx), length(procy)
nx, ny = length(x), length(y)
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
cv = [discrete_value!(axis, xi)[1] for xi=procx]
procx = if nx == ny
cv = [discrete_value!(axis, xi)[1] for xi=x]
x = if nx == ny
cv
elseif nx == ny + 1
0.5diff(cv) + cv[1:end-1]
@@ -289,9 +337,9 @@ end
# compute half-width of bars
bw = d[:bar_width]
hw = if bw == nothing
0.5ignorenan_mean(diff(procx))
0.5mean(diff(x))
else
Float64[0.5_cycle(bw,i) for i=1:length(procx)]
Float64[0.5cycle(bw,i) for i=1:length(x)]
end
# make fillto a vector... default fills to 0
@@ -299,25 +347,20 @@ 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
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
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)
end
# widen limits out a bit
expand_extrema!(axis, widen(ignorenan_extrema(xseg.pts)...))
expand_extrema!(axis, widen(extrema(xseg.pts)...))
# switch back
if !isvertical(d)
@@ -335,326 +378,106 @@ end
end
@deps bar shape
# ---------------------------------------------------------------------------
# Histograms
_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
# edges from number of bins
function calc_edges(v, bins::Integer)
vmin, vmax = extrema(v)
linspace(vmin, vmax, bins+1)
end
# just pass through arrays
calc_edges(v, bins::AVec) = bins
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 = ignorenan_minimum(w_adj)
w_max = ignorenan_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
# 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)
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
if (last_w != baseline)
push!(x, a)
push!(y, baseline)
end
(x, y)
return length(edges)-1
end
function my_hist(v, bins; normed = false, weights = nothing)
edges = calc_edges(v, bins)
counts = zeros(length(edges)-1)
@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
# add a weighted count
for (i,vi) in enumerate(v)
idx = bucket_index(vi, edges)
counts[idx] += (weights == nothing ? 1.0 : weights[i])
end
# 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
# normalize by bar area?
norm_denom = normed ? sum(diff(edges) .* counts) : 1.0
if norm_denom == 0
norm_denom = 1.0
end
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) = ceil(Int, NaNMath.max(x, one(x)))
_iqr(v) = quantile(v, 0.75) - quantile(v, 0.25)
_span(v) = ignorenan_maximum(v) - ignorenan_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)
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))
edges, counts ./ norm_denom
end
@recipe function f(::Type{Val{:histogram}}, x, y, z)
seriestype := :barhist
edges, counts = my_hist(y, d[:bins],
normed = d[:normalize],
weights = d[:weights])
x := edges
y := counts
seriestype := :bar
()
end
@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
@deps histogram bar
# ---------------------------------------------------------------------------
# Histogram 2D
@recipe function f(::Type{Val{:bins2d}}, x, y, z)
edge_x, edge_y, weights = x, y, z.surf
# 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])
float_weights = float(weights)
if float_weights === weights
float_weights = deepcopy(float_weights)
# 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])
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
float_weights[i] = NaN
counts[i] = NaN
end
end
x := Plots._bin_centers(edge_x)
y := Plots._bin_centers(edge_y)
z := Surface(float_weights)
match_dimensions := true
x := centers(xedges)
y := centers(yedges)
z := Surface(counts)
linewidth := 0
seriestype := :heatmap
()
end
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
@deps histogram2d heatmap
# ---------------------------------------------------------------------------
@@ -706,9 +529,9 @@ function error_coords(xorig, yorig, ebar)
x, y = Array(float_extended_type(xorig), 0), Array(Float64, 0)
# for each point, create a line segment from the bottom to the top of the errorbar
for i = 1:max(length(xorig), length(yorig))
xi = _cycle(xorig, i)
yi = _cycle(yorig, i)
ebi = _cycle(ebar, i)
xi = cycle(xorig, i)
yi = cycle(yorig, i)
ebi = cycle(ebar, i)
nanappend!(x, [xi, xi])
e1, e2 = if istuple(ebi)
first(ebi), last(ebi)
@@ -761,11 +584,11 @@ function quiver_using_arrows(d::KW)
x, y = zeros(0), zeros(0)
for i = 1:max(length(xorig), length(yorig))
# get the starting position
xi = _cycle(xorig, i)
yi = _cycle(yorig, i)
xi = cycle(xorig, i)
yi = cycle(yorig, i)
# get the velocity
vi = _cycle(velocity, i)
vi = cycle(velocity, i)
vx, vy = if istuple(vi)
first(vi), last(vi)
elseif isscalar(vi)
@@ -798,12 +621,12 @@ function quiver_using_hack(d::KW)
for i = 1:max(length(xorig), length(yorig))
# get the starting position
xi = _cycle(xorig, i)
yi = _cycle(yorig, i)
xi = cycle(xorig, i)
yi = cycle(yorig, i)
p = P2(xi, yi)
# get the velocity
vi = _cycle(velocity, i)
vi = cycle(velocity, i)
vx, vy = if istuple(vi)
first(vi), last(vi)
elseif isscalar(vi)
@@ -870,7 +693,7 @@ end
# get the joined vector
function get_xy(v::AVec{OHLC}, x = 1:length(v))
xdiff = 0.3ignorenan_mean(abs.(diff(x)))
xdiff = 0.3mean(abs(diff(x)))
x_out, y_out = zeros(0), zeros(0)
for (i,ohlc) in enumerate(v)
ox,oy = get_xy(ohlc, x[i], xdiff)
@@ -911,7 +734,7 @@ end
# "Sparsity plot... heatmap of non-zero values of a matrix"
# function spy{T<:Real}(z::AMat{T}; kw...)
# mat = reshape(map(zi->float(zi!=0), z),1,:)
# mat = map(zi->float(zi!=0), z)'
# xn, yn = size(mat)
# heatmap(mat; leg=false, yflip=true, aspect_ratio=:equal,
# xlim=(0.5, xn+0.5), ylim=(0.5, yn+0.5),
@@ -927,9 +750,6 @@ end
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
seriestype := :spy
mat = g.args[1]
if length(unique(mat[mat .!= 0])) < 2
legend --> nothing
end
n,m = size(mat)
Plots.SliceIt, 1:m, 1:n, Surface(mat)
end
@@ -938,8 +758,8 @@ end
yflip := true
aspect_ratio := 1
rs, cs, zs = findnz(z.surf)
xlim := ignorenan_extrema(cs)
ylim := ignorenan_extrema(rs)
xlim := extrema(cs)
ylim := extrema(rs)
if d[:markershape] == :none
markershape := :circle
end
@@ -953,7 +773,6 @@ end
y := rs
z := nothing
seriestype := :scatter
grid --> false
()
end
@@ -961,7 +780,7 @@ end
"Adds a+bx... straight line over the current plot"
function abline!(plt::Plot, a, b; kw...)
plot!(plt, [ignorenan_extrema(plt)...], x -> b + a*x; kw...)
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
end
abline!(args...; kw...) = abline!(current(), args...; kw...)
@@ -970,70 +789,16 @@ abline!(args...; kw...) = abline!(current(), args...; kw...)
# -------------------------------------------------
# Dates
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)
@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)))
# -------------------------------------------------
# Complex Numbers
@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)*reshape(1:20,1,:))
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)*reshape(1:20,1,:))
seriestype := :heatmap
ticks := nothing
legend := false
seriescolor := grad.args[1]
title := string(grad.args[1])
z
@userplot ComplexPlot
@recipe function f(cp::ComplexPlot)
xguide --> "Real Part"
yguide --> "Imaginary Part"
seriestype --> :scatter
real(cp.args[1]), imag(cp.args[1])
end
+6 -7
View File
@@ -6,7 +6,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
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
typealias FuncOrFuncs{F} Union{F, Vector{F}, Matrix{F}}
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
@@ -317,10 +317,9 @@ end
# # images - colors
@recipe function f{T<:Colorant}(mat::AMat{T})
n, m = size(mat)
if is_seriestype_supported(:image)
seriestype := :image
n, m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
else
seriestype := :heatmap
@@ -483,13 +482,13 @@ end
#
# # 2D FixedSizeArrays
@recipe f{T<:Number}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = unzip(xy)
@recipe f{T<:Number}(xy::FixedSizeArrays.Vec{2,T}) = [xy[1]], [xy[2]]
@recipe f{T<:Number}(xy::AVec{StaticArrays.SVector{2,T}}) = unzip(xy)
@recipe f{T<:Number}(xy::StaticArrays.SVector{2,T}) = [xy[1]], [xy[2]]
#
# # 3D FixedSizeArrays
@recipe f{T<:Number}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = unzip(xyz)
@recipe f{T<:Number}(xyz::FixedSizeArrays.Vec{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]]
@recipe f{T<:Number}(xyz::AVec{StaticArrays.SVector{3,T}}) = unzip(xyz)
@recipe f{T<:Number}(xyz::StaticArrays.SVector{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]]
#
# # --------------------------------------------------------------------
+2 -2
View File
@@ -32,14 +32,14 @@ get_subplot(plt::Plot, k) = plt.spmap[k]
get_subplot(series::Series) = series.d[:subplot]
get_subplot_index(plt::Plot, idx::Integer) = Int(idx)
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(x -> x === sp, plt.subplots)
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(_ -> _ === sp, plt.subplots)
series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
function should_add_to_legend(series::Series)
series.d[:primary] && series.d[:label] != "" &&
!(series.d[:seriestype] in (
:hexbin,:bins2d,:histogram2d,:hline,:vline,
:hexbin,:histogram2d,:hline,:vline,
:contour,:contourf,:contour3d,:surface,:wireframe,
:heatmap, :pie, :image
))
+2 -4
View File
@@ -2,8 +2,7 @@
function theme(s::Symbol; kw...)
# reset?
if s == :none || s == :default
PlotUtils.clibrary(:Plots)
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
PlotUtils._default_gradient[] = :inferno
default(;
bg = :white,
bglegend = :match,
@@ -24,8 +23,7 @@ function theme(s::Symbol; kw...)
# update the default gradient and other defaults
thm = PlotThemes._themes[s]
if thm.gradient != nothing
PlotUtils.clibrary(:misc)
PlotUtils.default_cgrad(default = :sequential, sequential = PlotThemes.gradient_name(s))
PlotUtils._default_gradient[] = PlotThemes.gradient_name(s)
end
default(;
bg = thm.bg_secondary,
+8 -4
View File
@@ -2,12 +2,16 @@
# 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
const AVec = AbstractVector
const AMat = AbstractMatrix
const KW = Dict{Symbol,Any}
typealias AVec AbstractVector
typealias AMat AbstractMatrix
typealias KW Dict{Symbol,Any}
immutable PlotsDisplay <: Display end
abstract AbstractBackend
abstract AbstractPlot{T<:AbstractBackend}
abstract AbstractLayout
# -----------------------------------------------------------
immutable InputWrapper{T}
@@ -58,7 +62,7 @@ Extrema() = Extrema(Inf, -Inf)
# -----------------------------------------------------------
const SubplotMap = Dict{Any, Subplot}
typealias SubplotMap Dict{Any, Subplot}
# -----------------------------------------------------------
+37 -37
View File
@@ -3,7 +3,7 @@ calcMidpoints(edges::AbstractVector) = Float64[0.5 * (edges[i] + edges[i+1]) for
"Make histogram-like bins of data"
function binData(data, nbins)
lo, hi = ignorenan_extrema(data)
lo, hi = extrema(data)
edges = collect(linspace(lo, hi, nbins+1))
midpoints = calcMidpoints(edges)
buckets = Int[max(2, min(searchsortedfirst(edges, x), length(edges)))-1 for x in data]
@@ -109,7 +109,7 @@ function regressionXY(x, y)
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
# make a line segment
regx = [ignorenan_minimum(x), ignorenan_maximum(x)]
regx = [minimum(x), maximum(x)]
regy = β * regx + α
regx, regy
end
@@ -192,7 +192,7 @@ function iter_segments(args...)
end
# helpers to figure out if there are NaN values in a list of array types
anynan(i::Int, args::Tuple) = any(a -> !isfinite(_cycle(a,i)), args)
anynan(i::Int, args::Tuple) = any(a -> !isfinite(cycle(a,i)), args)
anynan(istart::Int, iend::Int, args::Tuple) = any(i -> anynan(i, args), istart:iend)
allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:iend)
@@ -243,19 +243,19 @@ notimpl() = error("This has not been implemented yet")
isnothing(x::Void) = true
isnothing(x) = false
_cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
_cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
_cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
_cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
_cycle(v, idx::Int) = v
cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
cycle(v, idx::Int) = v
_cycle(v::AVec, indices::AVec{Int}) = map(i -> _cycle(v,i), indices)
_cycle(v::AMat, indices::AVec{Int}) = map(i -> _cycle(v,i), indices)
_cycle(v, indices::AVec{Int}) = fill(v, length(indices))
cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
cycle(v, indices::AVec{Int}) = fill(v, length(indices))
_cycle(grad::ColorGradient, idx::Int) = _cycle(grad.colors, idx)
_cycle(grad::ColorGradient, indices::AVec{Int}) = _cycle(grad.colors, indices)
cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
makevec(v::AVec) = v
makevec{T}(v::T) = T[v]
@@ -271,28 +271,28 @@ unzip{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t
unzip{X,Y,Z}(xyz::AVec{Tuple{X,Y,Z}}) = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
unzip{X,Y,U,V}(xyuv::AVec{Tuple{X,Y,U,V}}) = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]]
unzip{T}(xy::AVec{StaticArrays.SVector{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
unzip{T}(xy::StaticArrays.SVector{2,T}) = T[xy[1]], T[xy[2]]
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
unzip{T}(xyz::AVec{StaticArrays.SVector{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
unzip{T}(xyz::StaticArrays.SVector{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
unzip{T}(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
unzip{T}(xyuv::AVec{StaticArrays.SVector{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
unzip{T}(xyuv::StaticArrays.SVector{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
function _expand_limits(lims, x)
try
e1, e2 = ignorenan_extrema(x)
lims[1] = NaNMath.min(lims[1], e1)
lims[2] = NaNMath.max(lims[2], e2)
e1, e2 = extrema(x)
lims[1] = min(lims[1], e1)
lims[2] = max(lims[2], e2)
# catch err
# warn(err)
end
nothing
end
expand_data(v, n::Integer) = [_cycle(v, i) for i=1:n]
expand_data(v, n::Integer) = [cycle(v, i) for i=1:n]
# if the type exists in a list, replace the first occurence. otherwise add it to the end
function addOrReplace(v::AbstractVector, t::DataType, args...; kw...)
@@ -324,7 +324,7 @@ function replaceAliases!(d::KW, aliases::Dict{Symbol,Symbol})
end
end
createSegments(z) = collect(repmat(reshape(z,1,:),2,1))[2:end]
createSegments(z) = collect(repmat(z',2,1))[2:end]
Base.first(c::Colorant) = c
Base.first(x::Symbol) = x
@@ -334,29 +334,29 @@ sortedkeys(d::Dict) = sort(collect(keys(d)))
"create an (n+1) list of the outsides of heatmap rectangles"
function heatmap_edges(v::AVec)
vmin, vmax = ignorenan_extrema(v)
vmin, vmax = extrema(v)
extra = 0.5 * (vmax-vmin) / (length(v)-1)
vcat(vmin-extra, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra)
end
function calc_r_extrema(x, y)
xmin, xmax = ignorenan_extrema(x)
ymin, ymax = ignorenan_extrema(y)
r = 0.5 * NaNMath.min(xmax - xmin, ymax - ymin)
ignorenan_extrema(r)
xmin, xmax = extrema(x)
ymin, ymax = extrema(y)
r = 0.5 * min(xmax - xmin, ymax - ymin)
extrema(r)
end
function convert_to_polar(x, y, r_extrema = calc_r_extrema(x, y))
rmin, rmax = r_extrema
phi, r = x, y
r = (r - rmin) / (rmax - rmin)
r = 0.5 * (r - rmin) / (rmax - rmin)
n = max(length(phi), length(r))
x = zeros(n)
y = zeros(n)
for i in 1:n
x[i] = _cycle(r,i) * cos.(_cycle(phi,i))
y[i] = _cycle(r,i) * sin.(_cycle(phi,i))
x[i] = cycle(r,i) * cos(cycle(phi,i))
y[i] = cycle(r,i) * sin(cycle(phi,i))
end
x, y
end
@@ -469,7 +469,7 @@ ok(tup::Tuple) = ok(tup...)
# compute one side of a fill range from a ribbon
function make_fillrange_side(y, rib)
frs = zeros(length(y))
for (i, (yi, ri)) in enumerate(zip(y, Base.Iterators.cycle(rib)))
for (i, (yi, ri)) in enumerate(zip(y, Base.cycle(rib)))
frs[i] = yi + ri
end
frs
@@ -645,7 +645,7 @@ end
# used in updating an existing series
extendSeriesByOne(v::UnitRange{Int}, n::Int = 1) = isempty(v) ? (1:n) : (minimum(v):maximum(v)+n)
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + ignorenan_maximum(v))
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + maximum(v))
extendSeriesData{T}(v::Range{T}, z::Real) = extendSeriesData(float(collect(v)), z)
extendSeriesData{T}(v::Range{T}, z::AVec) = extendSeriesData(float(collect(v)), z)
extendSeriesData{T}(v::AVec{T}, z::Real) = (push!(v, convert(T, z)); v)
@@ -871,9 +871,9 @@ mm2px(mm::Real) = float(px / MM_PER_PX)
"Smallest x in plot"
xmin(plt::Plot) = ignorenan_minimum([ignorenan_minimum(series.d[:x]) for series in plt.series_list])
xmin(plt::Plot) = minimum([minimum(series.d[:x]) for series in plt.series_list])
"Largest x in plot"
xmax(plt::Plot) = ignorenan_maximum([ignorenan_maximum(series.d[:x]) for series in plt.series_list])
xmax(plt::Plot) = maximum([maximum(series.d[:x]) for series in plt.series_list])
"Extrema of x-values in plot"
ignorenan_extrema(plt::Plot) = (xmin(plt), xmax(plt))
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))
+2 -2
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
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
const _current_plots_version = v"0.12.0"
const _current_plots_version = v"0.9.6"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
@@ -99,7 +99,7 @@ function image_comparison_facts(pkg::Symbol;
for i in 1:length(Plots._examples)
i in skip && continue
if only == nothing || i in only
@fact @eval(image_comparison_tests(Symbol(String(Symbol($pkg))[7:end]), $i, debug=$debug, sigma=$sigma, eps=$eps)) |> success --> true
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
end
end
end
+3 -5
View File
@@ -5,11 +5,9 @@ set -ex
sudo apt-get -qq update
# sudo apt-get install -y wkhtmltopdf
sudo apt-get install -y xfonts-75dpi xfonts-base
wget https://downloads.wkhtmltopdf.org/0.12/0.12.2/wkhtmltox-0.12.2_linux-precise-amd64.deb
sudo dpkg -i wkhtmltox-0.12.2_linux-precise-amd64.deb
# wget http://download.gna.org/wkhtmltopdf/0.12/0.12.2/wkhtmltox-0.12.2_linux-trusty-amd64.deb
# sudo dpkg -i wkhtmltox-0.12.2_linux-trusty-amd64.deb
sudo apt-get install -y xfonts-75dpi
wget http://download.gna.org/wkhtmltopdf/0.12/0.12.2/wkhtmltox-0.12.2_linux-trusty-amd64.deb
sudo dpkg -i wkhtmltox-0.12.2_linux-trusty-amd64.deb
wkhtmltoimage http://www.google.com test.png
ls
+32 -74
View File
@@ -7,78 +7,41 @@ srand(1234)
default(show=false, reuse=true)
img_eps = isinteractive() ? 1e-2 : 10e-2
facts("GR") do
@fact gr() --> Plots.GRBackend()
@fact backend() --> Plots.GRBackend()
image_comparison_facts(:gr, eps=img_eps)
end
# facts("Gadfly") do
# @fact gadfly() --> Plots.GadflyBackend()
# @fact backend() --> Plots.GadflyBackend()
#
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
# @fact plot(Int[1,2,3], rand(3)) --> not(nothing)
# @fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
# @fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
#
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], eps=img_eps)
# end
facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend()
image_comparison_facts(:pyplot, eps=img_eps)
image_comparison_facts(:pyplot, skip=[25,30], eps=img_eps)
end
facts("UnicodePlots") do
@fact unicodeplots() --> Plots.UnicodePlotsBackend()
@fact backend() --> Plots.UnicodePlotsBackend()
facts("GR") do
@fact gr() --> Plots.GRBackend()
@fact backend() --> Plots.GRBackend()
# lets just make sure it runs without error
@fact isa(plot(rand(10)), Plots.Plot) --> true
if is_linux() && isinteractive()
image_comparison_facts(:gr, skip=[2,25,30], eps=img_eps)
end
end
# The plotlyjs testimages return a connection error on travis:
# connect: connection refused (ECONNREFUSED)
facts("Plotly") do
@fact plotly() --> Plots.PlotlyBackend()
@fact backend() --> Plots.PlotlyBackend()
# facts("PlotlyJS") do
# @fact plotlyjs() --> Plots.PlotlyJSBackend()
# @fact backend() --> Plots.PlotlyJSBackend()
#
# if is_linux() && isinteractive()
# image_comparison_facts(:plotlyjs,
# skip=[
# 2, # animation (skipped for speed)
# 27, # (polar plots) takes very long / not working
# 31, # animation (skipped for speed)
# ],
# eps=img_eps)
# end
# end
# InspectDR returns that error on travis:
# ERROR: LoadError: InitError: Cannot open display:
# in Gtk.GLib.GError(::Gtk.##229#230) at /home/travis/.julia/v0.5/Gtk/src/GLib/gerror.jl:17
# facts("InspectDR") do
# @fact inspectdr() --> Plots.InspectDRBackend()
# @fact backend() --> Plots.InspectDRBackend()
#
# image_comparison_facts(:inspectdr,
# skip=[
# 2, # animation
# 6, # heatmap not defined
# 10, # heatmap not defined
# 22, # contour not defined
# 23, # pie not defined
# 27, # polar plot not working
# 28, # heatmap not defined
# 31, # animation
# ],
# eps=img_eps)
# end
# facts("Plotly") do
# @fact plotly() --> Plots.PlotlyBackend()
# @fact backend() --> Plots.PlotlyBackend()
#
# # # until png generation is reliable on OSX, just test on linux
# # @static is_linux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
# end
# # until png generation is reliable on OSX, just test on linux
# @static is_linux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
end
# facts("Immerse") do
@@ -99,18 +62,13 @@ end
# end
# facts("Gadfly") do
# @fact gadfly() --> Plots.GadflyBackend()
# @fact backend() --> Plots.GadflyBackend()
#
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
# @fact plot(Int[1,2,3], rand(3)) --> not(nothing)
# @fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
# @fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
#
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], eps=img_eps)
# end
facts("UnicodePlots") do
@fact unicodeplots() --> Plots.UnicodePlotsBackend()
@fact backend() --> Plots.UnicodePlotsBackend()
# lets just make sure it runs without error
@fact isa(plot(rand(10)), Plots.Plot) --> true
end
@@ -120,12 +78,12 @@ facts("Axes") do
@fact typeof(axis) --> Plots.Axis
@fact Plots.discrete_value!(axis, "HI") --> (0.5, 1)
@fact Plots.discrete_value!(axis, :yo) --> (1.5, 2)
@fact Plots.ignorenan_extrema(axis) --> (0.5,1.5)
@fact extrema(axis) --> (0.5,1.5)
@fact axis[:discrete_map] --> Dict{Any,Any}(:yo => 2, "HI" => 1)
Plots.discrete_value!(axis, ["x$i" for i=1:5])
Plots.discrete_value!(axis, ["x$i" for i=0:2])
@fact Plots.ignorenan_extrema(axis) --> (0.5, 7.5)
@fact extrema(axis) --> (0.5, 7.5)
end
+6 -9
View File
@@ -1,8 +1,8 @@
Pkg.add("ImageMagick")
Pkg.build("ImageMagick")
# Pkg.clone("ImageMagick")
# Pkg.build("ImageMagick")
Pkg.clone("GR")
Pkg.build("GR")
# Pkg.clone("GR")
# Pkg.build("GR")
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
@@ -11,12 +11,11 @@ Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
Pkg.clone("StatPlots")
Pkg.checkout("PlotUtils")
# Pkg.clone("Blink")
# Pkg.clone("https://github.com/JunoLab/Blink.jl.git")
# Pkg.build("Blink")
# import Blink
# Blink.AtomShell.install()
# Pkg.add("Rsvg")
# Pkg.add("PlotlyJS")
# Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")
# Pkg.checkout("RecipesBase")
# Pkg.clone("VisualRegressionTests")
@@ -26,6 +25,4 @@ ENV["PYTHON"] = ""
Pkg.add("PyPlot")
Pkg.build("PyPlot")
# Pkg.add("InspectDR")
Pkg.test("Plots"; coverage=false)