Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25bf71aff2 | |||
| 33d6c3426a | |||
| 9d038ea05e |
+1
-1
@@ -4,7 +4,7 @@ os:
|
||||
- linux
|
||||
# - osx
|
||||
julia:
|
||||
- 0.6
|
||||
- 0.5
|
||||
matrix:
|
||||
allow_failures:
|
||||
- julia: nightly
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/JuliaPlots/Plots.jl)
|
||||
[](https://travis-ci.org/tbreloff/Plots.jl)
|
||||
[](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
|
||||
abstract type ColorScheme end
|
||||
abstract ColorScheme
|
||||
|
||||
Base.getindex(scheme::ColorScheme, i::Integer) = getColor(scheme, i)
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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 # Freedman–Diaconis rule
|
||||
_cl(_span(v) / (2 * _iqr(v) / n^(1/3)))
|
||||
else
|
||||
error("Unknown auto-binning mode $mode")
|
||||
end::Int
|
||||
end
|
||||
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) = binning
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::NTuple{N}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...))
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...))
|
||||
|
||||
_hist_norm_mode(mode::Symbol) = mode
|
||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
||||
|
||||
function _make_hist{N}(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing)
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user