Compare commits

..

3 Commits

Author SHA1 Message Date
Thomas Breloff 01b9cc92fd remove mkdir call in tests; closes #397 2016-07-22 11:18:47 -04:00
Thomas Breloff 7a650d918b unicodeplots size fix 2016-07-22 09:45:44 -04:00
Thomas Breloff b6fa4bcda4 plotly ticks fix 2016-07-21 11:01:04 -04:00
50 changed files with 3151 additions and 9354 deletions
+3 -2
View File
@@ -2,9 +2,10 @@
language: julia
os:
- linux
# - osx
- osx
julia:
- 0.6
- release
- nightly
matrix:
allow_failures:
- julia: nightly
+3 -377
View File
@@ -3,385 +3,11 @@
#### notes on release changes, ongoing development, and future planned work
- Minor version 0.17 is the last one to support Julia 0.6!!
- Minor version 0.11 is the last one to support Julia 0.5!!
- Critical bugfixes only
- `backports` branch is for Julia 0.5
---
## (current master)
- All new development should target Julia 0.7!
## 0.17.4
- fix thickness_scaling for pyplot
## 0.17.3
- Log-scale heatmap edge computation
- Fix size and dpi for GR and PyPlot
- Fix fillrange with line segments on PyPlot and Plotly
- fix flip for heatmap and image on GR
- New attributes for PGFPlots
- Widen axes for most series types and log scales
- Plotly: fix log scale with no ticks
- Fix axis flip on Plotly
- Fix hover and zcolor interaction in Plotly
- WebIO integration for PlotlyJS backend
## 0.17.2
- fix single subplot in plotly
- implement `(xyz)lims = :round`
- PyPlot: fix bg_legend = invisible()
- set fallback tick specification for axes with discrete values
- restructure of show methods
## 0.17.1
- Fix contour for PGFPlots
- 32Bit fix: Int64 -> Int
- Make series of shapes and segments toggle together in Plotly(JS)
- Fix marker arguments
- Fix processing order of series recipes
- Fix Plotly(JS) ribbon
- Contour plots with x,y in grid form on PyPlot
## 0.17.0
- Add GR dependency to make it the default backend
- Improve histogram2d bin estimation
- Allow vector arguments for certain series attributes and support line_z and fill_z on GR, PyPlot, Plotly(JS) and PGFPlots
- Automatic scientific notation for tick labels
- Allow to set the theme in PLOTS_DEFAULTS
- Implement plots_heatmap seriestype providing a Plots recipe for heatmaps
## 0.16.0
- fix 3D plotting in PyPlot
- Infinite objects
## 0.15.1
- fix scientific notation for labels in GR
- fix labels with logscale
- fix image cropping with GR
- fix grouping of annotations
- fix annotations in Plotly
- allow saving notebook with plots as pdf from IJulia
- fix fillrange and ribbon for step recipes
- implement native ticks that respond to zoom
- fix bar plot with one bar
- contour labels and colorbar fixes
- interactive linked axis for PyPlot
- add `NamedTuple` syntax to group with named legend
- use bar recipe in Plotly
- implement categorical ticks
## 0.15.0
- improve resolution of png output of GR with savefig()
- add check for ticks=nothing
- allow transparency in heatmaps
- fix line_z for GR
- fix legendcolor for pyplot
- fix pyplot ignoring alpha values of images
- don't let `abline!` change subplot limits
- update showtheme recipe
## 0.14.2
- fix plotly bar lines bug
- allow passing multiple series to `ribbon`
- add a new example for `line_z`
## 0.14.1
- Add linestyle argument to the legend
- Plotly: bar_width and stroke_width support for bar plots
- abline! does not change axis limits
- Fix default log scale ticks in GR backend
- Use the :fontsize keys so the scalefontsizes command works
- Prepare support for new PlotTheme type in PlotThemes
## 0.14.0
- remove use of imagemagick; saving gifs now requires ffmpeg
- improvements to ffmpeg gif quality and speed
- overhaul of fonts, allows setting fonts in recipes and with magic arguments
- added `camera` attribute to control camera position for 3d plots
- added `showaxis` attribute to control which axes to display
- improvements of polar plots axes, and better backend consistency
- changed the 'spy' recipe back to using heatmap
- added `scatterpath` seriestype
- allow plotlyjs to save svg
- add `reset_defaults()` function to reset plot defaults
- update syntax to 0.6
- make `fill = true` fill to 0 rather than to 1
- use new `@df` syntax in StatPlots examples
- allow changing the color of legend box
- implement `title_location` for gr
- add `hline` marker to pgfplots - fixes errorbars
- pyplot legends now show marker types
- pyplot colorbars take font style from y axis
- pyplot tickmarks color the same as axis color
- allow setting linewidth for contour in gr
- allow legend to be outside plot area for pgfplots
- expand axis extrema for heatmap
- extendg grid lines to axis limits
- fix `line_z` for pyplot and gr
- fixed colorbar problem for flipped axes with gr
- fix marker_z for 3d plots in gr
- fix `weights` functionality for histograms
- fix gr annotations with colorbar
- fix aspect ratio in gr
- fix "hidden window" problem after savefig in gr
- fix pgfplots logscale ticks error
- fix pgfplots legends symbols
- fix axis linking for plotlyjs
- fix plotting of grayscale images
## 0.13.1
- fix a bug when passing a vector of functions with no bounds (e.g. `plot([sin, cos])`)
- export `pct` and `px` from Plots.PlotMeasures
## 0.13.0
- support `plotattributes` rather than `d` in recipes
- no longer export `w`, `h` and names from Measures.jl; use `using Plots.PlotMeasures` to get these names back
- `bar_width` now depends on the minimum distance between bars, not the mean
- better automatic x axis limits for plotting Functions
- `tick_direction` attribute now allows ticks to be on the inside of the plot border
- removed a bug where `p1 = plot(randn(10)); plot(p1, p2)` made `display(p1)` impossible
- allow `plot([])` to generate an empty plot
- add `origin` framestyle
- ensure finite bin number on histograms with only one unique value
- better automatic histogram bins for 2d histograms
- more informative error message on passing unsupported seriestype in a recipe
- allow grouping in user recipes
- GR now has `line_z` and `fill_z` attributes for determining the color of shapes and lines
- change GR default view angle for 3D plots to match that of PyPlot
- fix `clims` on GR
- fix `marker_z` for plotly backend
- implement `framestyle` for plotly
- fix logscale bug error for values < 1e-16 on pyplot
- fix an issue on pyplot where >1 colorbar would be shown if there was >1 series
- fix `writemime` for eps
## 0.12.4
- added a new `framestyle` argument with choices: :box, :semi, :axes, :grid and :none
- changed the default bar width to 0.8
- added working ribbon to plotly backend
- ensure that automatic ticks always generate 4 to 8 ticks
- group now groups keyword arguments of the same length as the input
- allow passing DateTime objects as ticks
- allow specifying the number of ticks as an integre
- fix bug on errorbars in gr
- fixed some but not all world age issues
- better margin with room for text
- added a `match` option for linecolor
- better error message un unsupported series types
- add a 'stride' keyword for the pyplot backend
## 0.12.3
- new grid line style defaults
- `grid` is now an axis attribute and a magic argument: it is now possible to modify the grid line style, alpha and line width
- Enforce plot order in user recipes
- import `plot!` from RecipesBase
- GR no longer automatically handles _ and ^ in texts
- fix GR colorbar for scatter plots
#### 0.12.2
- fix an issue with Juno/PlotlyJS compatibility on new installations
- fix markers not showing up in seriesrecipes using :scatter
- don't use pywrap in the pyplot backend
- improve the bottom margin for the gr backend
#### 0.12.1
- fix deprecation warnings
- switch from FixedSizeArrays to StaticArrays.FixedSizeArrays
- drop FactCheck in tests
- remove julia 0.5 compliant uses of transpose operator
- fix GR heatmap bugs
- fix GR guide padding
- improve legend markers in GR
- add surface alpha for Plotly(JS)
- add fillrange to Plotly(JS)
- allow usage of Matplotlib 1.5 with PyPlot
- fix GLVisualize for julia 0.6
- conform to changes in InspectDR
#### 0.12.0
- 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 2.0 compatibility
- add inspectdr backend
- improved histogram functionality:
- added a `:stephist` and `:scatterhist` series type as well as ``:barhist` (the default)
- support for log scale axes with histograms
- support for plotting `StatsBase.Histogram`
- allowing bins to be specified as `:sturges`, `:rice`, `:scott` or :fd
- allow `normalization` to be specified as :density (for unequal bins) or :pdf (sum to 1)
- add a `plotattr` function to access documentation for Plots attribute
- add `fill_z` attribute for pyplot
- add colorbar_title to plotlyjs
- enable standalone window for plotlyjs
- improved support for pgfplots, ticks rotation, clims, series_annotations
- restore colorbars for GR
- better axis labels for heatmap in GR
- better marker sizes in GR
- fix color representation in GR
- update GR legend
- fix image bug on GR
- fix glvisualize dependencies
- set dotted grid lines for pyplot
- several improvements to inspectdr
- improved tick positions for TimeType x axes
- support for improved color gradient capability in PlotUtils
- add a showlibrary recipe to display color libraries
- add a showgradient recipe to display color gradients
- add `vectorfield` as an alias for `quiver`
- use `PlotUtils.adaptedgrid` for functions
#### 0.9.5
- added dependency on PlotThemes
- set_theme --> theme
- remove Compat from REQUIRE
- warning for DataFrames without StatPlots
- closeall exported and implemented for gr/pyplot
- fix DateTime recipe
- reset theme with theme(:none)
- fix link_axes! for nested subplots
- fix plotly lims for log scale
#### 0.9.4
- optimizations surrounding Subplot.series_list
- better Atom support, support plotlyjs
- gr:
- gks_wstype defaults and gr_set_output
- heatmap uses GR.drawimage
- histogram2d puts NaN for zeros
- improved support of NaN in heatmaps
- rebuilt spy recipes to output scatters with marker_z set
- deprecate png support in plotly... point to plotlyjs
- fixes:
- axis widen with lims set
- reset_extrema, setxyz
- bar plot widen
- better tick padding
- consistent tick rotation
- consistent aspect_ratio
- pyplot dpi
- plotly horizontal bars
- handle series attributes when combining subplots
- gr images transposed
- converted Date/DateTime to new type recipe approach for arrays
- issues closed include: #505 #513 #479 #523 #526 #529
#### 0.9.3
- support pdf and eps in plotlyjs backend
- allow curly after grid: `@layout [a b; grid(4,4){0.8h}]`
- add_backend redesign
#### 0.9.2
- glvisualize backend (@SimonDanisch)
- too much to list! ready for alpha testing
- Volume and volume seriestype
- Atom: support for PlotPane and proper gui display
- gr:
- clims
- aspect ratio
- pgfplots:
- fixes for ticks, axes, and more
- pyplot:
- font families
- colorbar guide
- pixel marker
- unicodeplots
- basic support for shapes
- improved add_backend
- refactor of is_supported methods
- element-wise type recipes (see https://github.com/tbreloff/Plots.jl/issues/460#issuecomment-248428908)
- several other fixes/improvements
#### 0.9.1
- Pkg.dir --> dirname (@tkelman)
- `axis = nothing` magic
- fixes:
- clim for line_z
- sticks default range for log scale
- rotate method
- pyplot heatmap
- spurious scale warnings
- gr image/alpha
- plotly.js path
- orientation extrema
- bar, reset orientation
- switch transpose_z to use permutedims
- skinny x/+ markers
- ticks in pgfplots
- eps in savefig (@anriseth)
- add_backend convenience
- type recipes for Date/DateTime (@maximsch2)
- mirror attribute and twinx convenience
- Axis.sp --> Axis.sps
- recipe postprocessing for allowing aliases and magic args in recipe bodies
#### 0.9.0
- fixes to cycle
- add back single function recipe: `plot!(cos)`
- new axis formatter attribute... accepts functions to convert numbers to strings
- fix for inset plots
- GR:
- fillrange fix
- annotations fix
- force double buffering in display
- All new development should target 0.7!
---
## 0.8
#### 0.8.2 (backported bug fixes for julia 0.4)
- plotly ticks fix
- unicodeplots size fix
- remove mkdir call in tests
#### 0.8.1
- manual drawing of axes/ticks/labels
- get_ticks uses optimize_ticks and Showoff
- changed PLOTS_DEFAULTS to be a global variable, not ENV key
- parameterized Segments for pushing tuples
- fix to axis extrema for Bool/nothing
- GR:
- manually draw 2D axes... fixes several issues and missing features
- fontsize fix
- PGFPlots: pass axis syle
## 0.8 (current master/dev)
#### 0.8.0
@@ -578,7 +204,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
+3 -6
View File
@@ -1,16 +1,13 @@
# Plots
[![Build Status](https://travis-ci.org/JuliaPlots/Plots.jl.svg?branch=master)](https://travis-ci.org/JuliaPlots/Plots.jl)
[![Build status](https://ci.appveyor.com/api/projects/status/github/juliaplots/plots.jl?branch=master&svg=true)](https://ci.appveyor.com/project/mkborregaard/plots-jl)
[![Build Status](https://travis-ci.org/tbreloff/Plots.jl.svg?branch=master)](https://travis-ci.org/tbreloff/Plots.jl)
[![Join the chat at https://gitter.im/tbreloff/Plots.jl](https://badges.gitter.im/tbreloff/Plots.jl.svg)](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.3.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
<!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.4.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
<!-- [![Coverage Status](https://coveralls.io/repos/tbreloff/Plots.jl/badge.svg?branch=master)](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
<!-- [![codecov.io](http://codecov.io/github/tbreloff/Plots.jl/coverage.svg?branch=master)](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
#### Created by Tom Breloff (@tbreloff)
#### Maintained by the [JuliaPlot members](https://github.com/orgs/JuliaPlots/people)
#### Author: Thomas Breloff (@tbreloff)
Plots is a plotting API and toolset. My goals with the package are:
@@ -22,4 +19,4 @@ Plots is a plotting API and toolset. My goals with the package are:
- **Lightweight**. Very few dependencies.
- **Smart**. Attempts to figure out what you **want** it to do... not just what you **tell** it.
View the [full documentation](http://docs.juliaplots.org/latest).
View the [full documentation](http://juliaplots.github.io).
+5 -12
View File
@@ -1,16 +1,9 @@
julia 0.6
julia 0.4
RecipesBase 0.2.3
PlotUtils 0.4.1
PlotThemes 0.1.3
RecipesBase
PlotUtils
Reexport
StaticArrays 0.5
FixedPointNumbers 0.3
Compat
FixedSizeArrays
Measures
Showoff
StatsBase 0.14.0
JSON
NaNMath
Requires
Contour
GR 0.31.0
+8 -18
View File
@@ -1,15 +1,11 @@
environment:
matrix:
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-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"
matrix:
allow_failures:
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" #check and address
- 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"
# Releases
- JULIAVERSION: "stable/win32"
- JULIAVERSION: "stable/win64"
# Nightlies
- JULIAVERSION: "download/win32"
- JULIAVERSION: "download/win64"
notifications:
- provider: Email
@@ -18,14 +14,8 @@ 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://status.julialang.org/"+$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
@@ -36,4 +26,4 @@ build_script:
test_script:
# - C:\projects\julia\bin\julia -e "Pkg.test(\"Plots\")"
- C:\projects\julia\bin\julia -e "include(Pkg.dir(\"Plots\", \"test\", \"travis_commands.jl\"))"
- C:\projects\julia\bin\julia -e "include(Pkg.dir(\"Plots\", \"test\", \"travis_commands.jl\"))"
+93 -148
View File
@@ -1,41 +1,46 @@
__precompile__(true)
__precompile__()
module Plots
using Compat
using Reexport
import StaticArrays
using StaticArrays.FixedSizeArrays
# @reexport using Colors
# using Requires
using FixedSizeArrays
@reexport using RecipesBase
import RecipesBase: plot, plot!, animate
using Base.Meta
@reexport using PlotUtils
@reexport using PlotThemes
import Showoff
import StatsBase
import JSON
using Requires
export
AbstractPlot,
Plot,
Subplot,
AbstractLayout,
GridLayout,
grid,
EmptyLayout,
bbox,
plotarea,
@layout,
AVec,
AMat,
KW,
wrap,
theme,
set_theme,
add_theme,
plot,
plot!,
attr!,
current,
default,
with,
twinx,
@userplot,
@shorthands,
pie,
pie!,
@@ -55,46 +60,44 @@ export
yflip!,
xaxis!,
yaxis!,
xgrid!,
ygrid!,
xlims,
ylims,
zlims,
savefig,
png,
gui,
inline,
closeall,
backend,
backends,
backend_name,
backend_object,
add_backend,
aliases,
dataframes,
Shape,
text,
font,
Axis,
stroke,
brush,
Surface,
OHLC,
arrow,
Segments,
Formatted,
debugplots,
supported_args,
supported_types,
supported_styles,
supported_markers,
is_subplot_supported,
Animation,
frame,
gif,
mov,
mp4,
animate,
@animate,
@gif,
spy,
test_examples,
iter_segments,
coords,
@@ -107,55 +110,23 @@ export
P2,
P3,
BezierCurve,
plotattr
curve_points,
directed_curve
# ---------------------------------------------------------
import NaNMath # define functions that ignores NaNs. To overcome the destructive effects of https://github.com/JuliaLang/julia/pull/12563
ignorenan_minimum(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.minimum(x)
ignorenan_minimum(x) = Base.minimum(x)
ignorenan_maximum(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.maximum(x)
ignorenan_maximum(x) = Base.maximum(x)
ignorenan_mean(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.mean(x)
ignorenan_mean(x) = Base.mean(x)
ignorenan_extrema(x::AbstractArray{F}) where {F<:AbstractFloat} = 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
# ---------------------------------------------------------
import Measures
module PlotMeasures
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
# allow pixels and percentages
const px = AbsoluteLength(0.254)
const pct = Length{:pct, Float64}(1.0)
export BBox, BoundingBox, mm, cm, inch, px, pct, pt, w, h
end
using .PlotMeasures
import .PlotMeasures: Length, AbsoluteLength, Measure, width, height
# ---------------------------------------------------------
include("types.jl")
include("utils.jl")
include("components.jl")
include("axes.jl")
include("backends.jl")
include("args.jl")
include("themes.jl")
include("plot.jl")
@@ -165,31 +136,34 @@ include("layouts.jl")
include("subplots.jl")
include("recipes.jl")
include("animation.jl")
include("output.jl")
include("examples.jl")
include("arg_desc.jl")
include("plotattr.jl")
include("backends.jl")
include("output.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
@shorthands plots_heatmap
@shorthands hexbin
@shorthands sticks
@shorthands hline
@shorthands vline
@shorthands hspan
@shorthands vspan
@shorthands ohlc
@shorthands contour
@shorthands contourf
@@ -203,109 +177,80 @@ include("output.jl")
@shorthands quiver
@shorthands curves
"Plot a pie diagram"
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
"Plot with seriestype :path3d"
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
"Add title to an existing plot"
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
"Add xlabel to an existing plot"
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
"Add ylabel to an existing plot"
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
"Set xlims for an existing plot"
xlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; xlims = lims, kw...)
"Set ylims for an existing plot"
ylims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; ylims = lims, kw...)
"Set zlims for an existing plot"
zlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; zlims = lims, kw...)
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; ylims = lims, kw...)
zlims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; zlims = lims, kw...)
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin,zmax), kw...)
"Set xticks for an existing plot"
xticks!(v::AVec{T}; kw...) where {T<:Real} = plot!(; xticks = v, kw...)
"Set yticks for an existing plot"
yticks!(v::AVec{T}; kw...) where {T<:Real} = plot!(; yticks = v, kw...)
xticks!(
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; xticks = (ticks,labels), kw...)
yticks!(
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; yticks = (ticks,labels), kw...)
"Add annotations to an existing plot"
xticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; xticks = v, kw...)
yticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; yticks = v, kw...)
xticks!{T<:Real,S<:AbstractString}(
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:AbstractString}(
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; yticks = (ticks,labels), kw...)
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
annotate!(anns::AVec{T}; kw...) where {T<:Tuple} = plot!(; annotation = anns, kw...)
"Flip the current plots' x axis"
annotate!{T<:Tuple}(anns::AVec{T}; kw...) = plot!(; annotation = anns, kw...)
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
"Flip the current plots' y axis"
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
"Specify x axis attributes for an existing plot"
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
"Specify x axis attributes for an existing plot"
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
xgrid!(args...; kw...) = plot!(; xgrid = args, kw...)
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
let PlotOrSubplot = Union{Plot, Subplot}
title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
xlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; xlims = lims, kw...)
ylims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; ylims = lims, kw...)
zlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; zlims = lims, kw...)
xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
xticks!(plt::PlotOrSubplot, ticks::AVec{T}; kw...) where {T<:Real} = plot!(plt; xticks = ticks, kw...)
yticks!(plt::PlotOrSubplot, ticks::AVec{T}; kw...) where {T<:Real} = plot!(plt; yticks = ticks, kw...)
xticks!(plt::PlotOrSubplot,
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; xticks = (ticks,labels), kw...)
yticks!(plt::PlotOrSubplot,
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; yticks = (ticks,labels), kw...)
xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
annotate!(plt::PlotOrSubplot, anns::AVec{T}; kw...) where {T<:Tuple} = plot!(plt; annotation = anns, kw...)
xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
end
title!(plt::Plot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
zlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
xlims!(plt::Plot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
ylims!(plt::Plot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
zlims!(plt::Plot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
xticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
yticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
xticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
annotate!(plt::Plot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
annotate!{T<:Tuple}(plt::Plot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
xflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
yflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
xaxis!(plt::Plot, args...; kw...) = plot!(plt; xaxis = args, kw...)
yaxis!(plt::Plot, args...; kw...) = plot!(plt; yaxis = args, kw...)
# ---------------------------------------------------------
const CURRENT_BACKEND = CurrentBackend(:none)
# for compatibility with Requires.jl:
@init begin
function __init__()
setup_ijulia()
setup_atom()
if isdefined(Main, :PLOTS_DEFAULTS)
if haskey(Main.PLOTS_DEFAULTS, :theme)
theme(Main.PLOTS_DEFAULTS[:theme])
end
for (k,v) in Main.PLOTS_DEFAULTS
k == :theme || default(k, v)
default(k, v)
end
end
end
# ---------------------------------------------------------
# if VERSION >= v"0.4.0-dev+5512"
# include("precompile.jl")
# _precompile_()
# end
# ---------------------------------------------------------
end # module
+44 -92
View File
@@ -1,108 +1,62 @@
"Represents an animation object"
struct Animation
dir::String
frames::Vector{String}
immutable Animation
dir::Compat.ASCIIString
frames::Vector{Compat.ASCIIString}
end
function Animation()
tmpdir = convert(String, mktempdir())
Animation(tmpdir, String[])
tmpdir = convert(Compat.ASCIIString, mktempdir())
Animation(tmpdir, Compat.ASCIIString[])
end
"""
frame(animation[, plot])
Add a plot (the current plot if not specified) to an existing animation
"""
function frame(anim::Animation, plt::P=current()) where P<:AbstractPlot
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
end
giffn() = (isijulia() ? "tmp.gif" : tempname()*".gif")
movfn() = (isijulia() ? "tmp.mov" : tempname()*".mov")
mp4fn() = (isijulia() ? "tmp.mp4" : tempname()*".mp4")
mutable struct FrameIterator
itr
every::Int
kw
end
FrameIterator(itr; every=1, kw...) = FrameIterator(itr, every, kw)
"""
Animate from an iterator which returns the plot args each iteration.
"""
function animate(fitr::FrameIterator, fn = giffn(); kw...)
anim = Animation()
for (i, plotargs) in enumerate(fitr.itr)
if mod1(i, fitr.every) == 1
plot(wraptuple(plotargs)...; fitr.kw...)
frame(anim)
end
end
gif(anim, fn; kw...)
end
# most things will implement this
function animate(obj, fn = giffn(); every=1, fps=20, loop=0, kw...)
animate(FrameIterator(obj, every, kw), fn; fps=fps, loop=loop)
end
# -----------------------------------------------
"Wraps the location of an animated gif so that it can be displayed"
struct AnimatedGif
filename::String
immutable AnimatedGif
filename::Compat.ASCIIString
end
file_extension(fn) = Base.Filesystem.splitext(fn)[2][2:end]
function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif"); fps::Integer = 20)
fn = abspath(fn)
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim.dir, fn; kw...)
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim.dir, fn, false; kw...)
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim.dir, fn, false; kw...)
try
function buildanimation(animdir::AbstractString, fn::AbstractString,
is_animated_gif::Bool=true;
fps::Integer = 20, loop::Integer = 0,
variable_palette::Bool=false,
show_msg::Bool=true)
fn = abspath(fn)
if is_animated_gif
if variable_palette
# generate a colorpalette for each frame for highest quality, but larger filesize
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -lavfi "$palette" -y $fn`)
else
# generate a colorpalette first so ffmpeg does not have to guess it
run(`ffmpeg -v 0 -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
# then apply the palette to get better results
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
end
else
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
# high quality
speed = round(Int, 100 / fps)
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
include(file)
end
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
run(`convert -delay $speed -loop 0 $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
show_msg && info("Saved animation to ", fn)
AnimatedGif(fn)
catch err
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`
Will try ffmpeg, but it's lower quality...)""")
# low quality
run(`ffmpeg -v 0 -framerate $fps -i $(anim.dir)/%06d.png -y $fn`)
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
end
info("Saved animation to ", fn)
AnimatedGif(fn)
end
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
ext = file_extension(agif.filename)
write(io, if ext == "gif"
"<img src=\"$(relpath(agif.filename))?$(rand())>\" />"
elseif ext in ("mov", "mp4")
"<video controls><source src=\"$(relpath(agif.filename))?$(rand())>\" type=\"video/$ext\"></video>"
else
error("Cannot show animation with extension $ext: $agif")
end)
function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif)
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
end
@@ -116,7 +70,6 @@ function _animate(forloop::Expr, args...; callgif = false)
# add the call to frame to the end of each iteration
animsym = gensym("anim")
countersym = gensym("counter")
freqassert = :()
block = forloop.args[2]
# create filter
@@ -129,7 +82,7 @@ function _animate(forloop::Expr, args...; callgif = false)
# filter every `freq` frames (starting with the first frame)
@assert n == 2
freq = args[2]
freqassert = :(@assert isa($freq, Integer) && $freq > 0)
@assert isa(freq, Integer) && freq > 0
:(mod1($countersym, $freq) == 1)
elseif args[1] == :when
@@ -149,7 +102,6 @@ function _animate(forloop::Expr, args...; callgif = false)
# full expression:
esc(quote
$freqassert # if filtering, check frequency is an Integer > 0
$animsym = Animation() # init animation object
$countersym = 1 # init iteration counter
$forloop # for loop, saving a frame after each iteration
@@ -170,7 +122,7 @@ Example:
```
"""
macro gif(forloop::Expr, args...)
_animate(forloop, args...; callgif = true)
_animate(forloop, args...; callgif = true)
end
"""
@@ -179,13 +131,13 @@ Collect one frame per for-block iteration and return an `Animation` object.
Example:
```
p = plot(1)
anim = @animate for x=0:0.1:5
p = plot(1)
anim = @animate for x=0:0.1:5
push!(p, 1, sin(x))
end
gif(anim)
end
gif(anim)
```
"""
macro animate(forloop::Expr, args...)
_animate(forloop, args...)
_animate(forloop, args...)
end
+11 -46
View File
@@ -19,9 +19,9 @@ const _arg_desc = KW(
:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.",
:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)",
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:seriescolor`.",
: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 (the Freedman-Diaconis rule). 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",
@@ -29,7 +29,6 @@ const _arg_desc = KW(
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
:levels => "Integer, NTuple{2,Integer}. Number of levels (or x-levels/y-levels) for a contour type.",
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
@@ -40,10 +39,9 @@ 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 discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven 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.",
:contour_labels => "Bool. Show labels at the contour lines?",
: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`.",
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
@@ -65,81 +63,48 @@ const _arg_desc = KW(
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
:dpi => "Number. Dots Per Inch of output figures",
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides",
# subplot args
:title => "String. Subplot title.",
:title_location => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`",
:titlefontfamily => "String or Symbol. Font family of subplot title.",
:titlefontsize => "Integer. Font pointsize of subplot title.",
:titlefonthalign => "Symbol. Font horizontal alignment of subplot title: :hcenter, :left, :right or :center",
:titlefontvalign => "Symbol. Font vertical alignment of subplot title: :vcenter, :top, :bottom or :center",
:titlefontrotation => "Real. Font rotation of subplot title",
:titlefontcolor => "Color Type. Font color of subplot title",
:titlefont => "Font. Font of subplot title.",
:background_color_subplot => "Color Type or `:match` (matches `:background_color`). Base background color of the subplot.",
:background_color_legend => "Color Type or `:match` (matches `:background_color_subplot`). Background color of the legend.",
:background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).",
:foreground_color_subplot => "Color Type or `:match` (matches `:foreground_color`). Base foreground color of the subplot.",
:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.",
:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.",
: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)",
:legendfontfamily => "String or Symbol. Font family of legend entries.",
:legendfontsize => "Integer. Font pointsize of legend entries.",
:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center",
:legendfontvalign => "Symbol. Font vertical alignment of legend entries: :vcenter, :top, :bottom or :center",
:legendfontrotation => "Real. Font rotation of legend entries",
:legendfontcolor => "Color Type. Font color of legend entries",
: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.",
:grid => "Bool. Show the grid lines?",
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
:projection => "Symbol or String. '3d' or 'polar'",
:aspect_ratio => "Symbol (:equal) or Number. Plot area is resized so that 1 y-unit is the same size as `apect_ratio` x-units.",
:aspect_ratio => "Symbol (:equal) or Number (width to height ratio of plot area).",
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
:right_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the right of the subplot.",
:bottom_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the bottom of the subplot.",
:subplot_index => "Integer. Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list.",
:colorbar_title => "String. Title of colorbar.",
:framestyle => "Symbol. Style of the axes frame. Choose from $(_allFramestyles)",
:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots",
# axis args
:guide => "String. Axis guide (label).",
:lims => "NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example). `:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]",
:lims => "NTuple{2,Number}. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).",
:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
:rotation => "Number. Degrees rotation of tick labels.",
:flip => "Bool. Should we flip (reverse) the axis?",
:formatter => "Function, :scientific, or :auto. A method which converts a number to a string for tick labeling.",
:tickfontfamily => "String or Symbol. Font family of tick labels.",
:tickfontsize => "Integer. Font pointsize of tick labels.",
:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center",
:tickfontvalign => "Symbol. Font vertical alignment of tick labels: :vcenter, :top, :bottom or :center",
:tickfontrotation => "Real. Font rotation of tick labels",
:tickfontcolor => "Color Type. Font color of tick labels",
:guidefontfamily => "String or Symbol. Font family of axes guides.",
:guidefontsize => "Integer. Font pointsize of axes guides.",
:guidefonthalign => "Symbol. Font horizontal alignment of axes guides: :hcenter, :left, :right or :center",
:guidefontvalign => "Symbol. Font vertical alignment of axes guides: :vcenter, :top, :bottom or :center",
:guidefontrotation => "Real. Font rotation of axes guides",
:guidefontcolor => "Color Type. Font color of axes guides",
:tickfont => "Font. Font of axis tick labels.",
:guidefont => "Font. Font of axis guide (label).",
:foreground_color_axis => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis ticks.",
:foreground_color_border => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of plot area border (spines).",
:foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.",
:foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).",
:mirror => "Bool. Switch the side of the tick labels (right or top).",
:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`",
:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.",
:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.",
:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)",
:gridlinewidth => "Number. Width of the grid lines (in pixels)",
:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`",
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.",
)
+112 -503
View File
File diff suppressed because it is too large Load Diff
+108 -295
View File
@@ -23,13 +23,13 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
lk = Symbol(letter, k)
lv = _axis_defaults_byletter[lk]
d[k] = (lv == :match ? v : lv)
end
end
# merge!(d, _axis_defaults)
d[:discrete_values] = []
# update the defaults
attr!(Axis([sp], d), args...; kw...)
update!(Axis(sp, d), args...; kw...)
end
function get_axis(sp::Subplot, letter::Symbol)
@@ -70,23 +70,17 @@ function process_axis_arg!(d::KW, arg, letter = "")
elseif arg == nothing
d[Symbol(letter,:ticks)] = []
elseif T <: Bool || arg in _allShowaxisArgs
d[Symbol(letter,:showaxis)] = showaxis(arg, letter)
elseif typeof(arg) <: Number
d[Symbol(letter,:rotation)] = arg
elseif typeof(arg) <: Function
d[Symbol(letter,:formatter)] = arg
elseif !handleColors!(d, arg, Symbol(letter, :foreground_color_axis))
else
warn("Skipped $(letter)axis arg $arg")
end
end
# update an Axis object with magic args and keywords
function attr!(axis::Axis, args...; kw...)
function update!(axis::Axis, args...; kw...)
# first process args
d = axis.d
for arg in args
@@ -121,7 +115,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}(
@@ -135,12 +129,6 @@ const _inv_scale_funcs = Dict{Symbol,Function}(
:ln => exp,
)
# const _label_func = Dict{Symbol,Function}(
# :log10 => x -> "10^$x",
# :log2 => x -> "2^$x",
# :ln => x -> "e^$x",
# )
const _label_func = Dict{Symbol,Function}(
:log10 => x -> "10^$x",
:log2 => x -> "2^$x",
@@ -153,125 +141,59 @@ invscalefunc(scale::Symbol) = x -> get(_inv_scale_funcs, scale, identity)(Float6
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
amin,amax = axis_limits(axis)
lims = axis_limits(axis)
# scale the limits
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
scaled_lims = map(scalefunc(scale), lims)
# @show lims scaled_lims
# get a list of well-laid-out ticks
if ticks == nothing
scaled_ticks = optimize_ticks(
sf(amin),
sf(amax);
k_min = 4, # minimum number of ticks
cv = if ticks == nothing
optimize_ticks(scaled_lims...,
k_min = 5, # minimum number of ticks
k_max = 8, # maximum number of ticks
# span_buffer = 0.0 # padding buffer in case nice ticks are closeby
)[1]
elseif typeof(ticks) <: Int
scaled_ticks, viewmin, viewmax = optimize_ticks(
sf(amin),
sf(amax);
k_min = ticks, # minimum number of ticks
k_max = ticks, # maximum number of ticks
k_ideal = ticks,
# `strict_span = false` rewards cases where the span of the
# chosen ticks is not too much bigger than amin - amax:
strict_span = false,
)
axis[:lims] = map(invscalefunc(scale), (viewmin, viewmax))
else
scaled_ticks = map(sf, (filter(t -> amin <= t <= amax, ticks)))
end
unscaled_ticks = map(invscalefunc(scale), scaled_ticks)
labels = if any(isfinite, unscaled_ticks)
formatter = axis[:formatter]
if formatter == :auto
# the default behavior is to make strings of the scaled values and then apply the labelfunc
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :auto))
elseif formatter == :plain
# Leave the numbers in plain format
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :plain))
elseif formatter == :scientific
Showoff.showoff(unscaled_ticks, :scientific)
else
# there was an override for the formatter... use that on the unscaled ticks
map(formatter, unscaled_ticks)
# if the formatter left us with numbers, still apply the default formatter
# However it leave us with the problem of unicode number decoding by the backend
# if eltype(unscaled_ticks) <: Number
# Showoff.showoff(unscaled_ticks, :auto)
# end
end
else
# no finite ticks to show...
String[]
ticks
end
# @show unscaled_ticks labels
# labels = Showoff.showoff(unscaled_ticks, scale == :log10 ? :scientific : :auto)
unscaled_ticks, labels
# # expand to ensure we see all the ticks
# expand_extrema!(axis, cv)
# rescale and return values and labels
# @show cv
ticklabels = if any(isfinite, cv)
map(labelfunc(scale, backend()), Showoff.showoff(cv, :plain))
else
UTF8String[]
end
tickvals = map(invscalefunc(scale), cv)
# @show tickvals ticklabels
# ticklabels = Showoff.showoff(tickvals, scale == :log10 ? :scientific : :auto)
tickvals, ticklabels
# basestr = scale == :log10 ? "10^" : scale == :log2 ? "2^" : scale == :ln ? "e^" : ""
# tickvals, ["$basestr$cvi" for cvi in cv]
end
# return (continuous_values, discrete_values) for the ticks on this axis
function get_ticks(axis::Axis)
ticks = _transform_ticks(axis[:ticks])
ticks = axis[:ticks]
ticks in (nothing, false) && return nothing
# treat :native ticks as :auto
ticks = ticks == :native ? :auto : ticks
dvals = axis[:discrete_values]
cv, dv = if typeof(ticks) <: Symbol
if !isempty(dvals)
# discrete ticks...
n = length(dvals)
rng = if ticks == :auto
Int[round(Int,i) for i in linspace(1, n, 15)]
else # if ticks == :all
1:n
end
axis[:continuous_values][rng], dvals[rng]
elseif ispolar(axis.sps[1]) && axis[:letter] == :x
#force theta axis to be full circle
(collect(0:pi/4:7pi/4), string.(0:45:315))
else
# compute optimal ticks and labels
optimal_ticks_and_labels(axis)
end
elseif typeof(ticks) <: Union{AVec, Int}
if !isempty(dvals) && typeof(ticks) <: Int
rng = Int[round(Int,i) for i in linspace(1, length(dvals), ticks)]
axis[:continuous_values][rng], dvals[rng]
else
# override ticks, but get the labels
optimal_ticks_and_labels(axis, ticks)
end
elseif typeof(ticks) <: NTuple{2, Any}
cv, dv = if !isempty(dvals) && ticks == :auto
# discrete ticks...
axis[:continuous_values], dvals
elseif ticks == :auto
# compute optimal ticks and labels
optimal_ticks_and_labels(axis)
elseif typeof(ticks) <: AVec
# override ticks, but get the labels
optimal_ticks_and_labels(axis, ticks)
elseif typeof(ticks) <: NTuple{2}
# assuming we're passed (ticks, labels)
ticks
else
@@ -279,29 +201,20 @@ function get_ticks(axis::Axis)
end
# @show ticks dvals cv dv
return cv, dv
# TODO: better/smarter cutoff values for sampling ticks
if length(cv) > 30
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
cv[rng], dv[rng]
else
cv, dv
end
end
_transform_ticks(ticks) = ticks
_transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks)
_transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2])
# -------------------------------------------------------------------------
function reset_extrema!(sp::Subplot)
for asym in (:x,:y,:z)
sp[Symbol(asym,:axis)][:extrema] = Extrema()
end
for series in sp.series_list
expand_extrema!(sp, series.d)
end
end
function expand_extrema!(ex::Extrema, v::Number)
ex.emin = isfinite(v) ? min(v, ex.emin) : ex.emin
ex.emax = isfinite(v) ? max(v, ex.emax) : ex.emax
ex.emin = min(v, ex.emin)
ex.emax = max(v, ex.emax)
ex
end
@@ -314,13 +227,13 @@ expand_extrema!(axis::Axis, ::Void) = axis[:extrema]
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
function expand_extrema!(axis::Axis, v::Tuple{MIN,MAX}) where {MIN<:Number,MAX<:Number}
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
ex = axis[:extrema]
ex.emin = isfinite(v[1]) ? min(v[1], ex.emin) : ex.emin
ex.emax = isfinite(v[2]) ? max(v[2], ex.emax) : ex.emax
ex.emin = min(v[1], ex.emin)
ex.emax = max(v[2], ex.emax)
ex
end
function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
function expand_extrema!{N<:Number}(axis::Axis, v::AVec{N})
ex = axis[:extrema]
for vi in v
expand_extrema!(ex, vi)
@@ -330,25 +243,11 @@ end
function expand_extrema!(sp::Subplot, d::KW)
vert = isvertical(d)
# first expand for the data
for letter in (:x, :y, :z)
data = d[if vert
letter
else
letter == :x ? :y : letter == :y ? :x : :z
end]
if letter != :z && d[:seriestype] == :straightline && any(series[:seriestype] != :straightline for series in series_list(sp)) && data[1] != data[2]
data = [NaN]
end
axis = sp[Symbol(letter, "axis")]
if isa(data, Volume)
expand_extrema!(sp[:xaxis], data.x_extents)
expand_extrema!(sp[:yaxis], data.y_extents)
expand_extrema!(sp[:zaxis], data.z_extents)
elseif eltype(data) <: Number || (isa(data, Surface) && all(di -> isa(di, Number), data.surf))
data = d[letter]
axis = sp.attr[Symbol(letter, "axis")]
if eltype(data) <: Number || (isa(data, Surface) && all(di -> isa(di, Number), data.surf))
if !(eltype(data) <: Number)
# huh... must have been a mis-typed surface? lets swap it out
data = d[letter] = Surface(Matrix{Float64}(data.surf))
@@ -370,11 +269,12 @@ function expand_extrema!(sp::Subplot, d::KW)
# end
# expand for fillrange
vert = isvertical(d)
fr = d[:fillrange]
if fr == nothing && d[:seriestype] == :bar
fr = 0.0
end
if fr != nothing && !all3D(d)
if fr != nothing
axis = sp.attr[vert ? :yaxis : :xaxis]
if typeof(fr) <: Tuple
for fri in fr
@@ -392,22 +292,15 @@ function expand_extrema!(sp::Subplot, d::KW)
bw = d[:bar_width]
if bw == nothing
bw = d[:bar_width] = _bar_width * ignorenan_minimum(filter(x->x>0,diff(sort(data))))
bw = d[:bar_width] = mean(diff(data))
end
# @show data bw
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
# expand for heatmaps
if d[:seriestype] == :heatmap
for letter in (:x, :y)
data = d[letter]
axis = sp[Symbol(letter, "axis")]
scale = get(d, Symbol(letter, "scale"), :identity)
expand_extrema!(axis, heatmap_edges(data, scale))
end
end
end
function expand_extrema!(sp::Subplot, xmin, xmax, ymin, ymax)
@@ -418,38 +311,27 @@ end
# -------------------------------------------------------------------------
# push the limits out slightly
function widen(lmin, lmax, scale = :identity)
f, invf = scalefunc(scale), invscalefunc(scale)
span = f(lmax) - f(lmin)
# eps = NaNMath.max(1e-16, min(1e-2span, 1e-10))
eps = NaNMath.max(1e-16, 0.03span)
invf(f(lmin)-eps), invf(f(lmax)+eps)
function widen(lmin, lmax)
span = lmax - lmin
# eps = max(1e-16, min(1e-2span, 1e-10))
eps = max(1e-16, 0.03span)
lmin-eps, lmax+eps
end
# figure out if widening is a good idea.
const _widen_seriestypes = (:line, :path, :steppre, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d)
# figure out if widening is a good idea. if there's a scale set it's too tricky,
# so lazy out and don't widen
function default_should_widen(axis::Axis)
should_widen = false
if !is_2tuple(axis[:lims])
for sp in axis.sps
for series in series_list(sp)
if series.d[:seriestype] in _widen_seriestypes
should_widen = true
end
if axis[:scale] == :identity
for series in series_list(axis.sp)
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
should_widen = true
end
end
end
should_widen
end
function round_limits(amin,amax)
scale = 10^(1-round(log10(amax - amin)))
amin = floor(amin*scale)/scale
amax = ceil(amax*scale)/scale
amin, amax
end
# using the axis extrema and limit overrides, return the min/max value for this axis
function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis))
ex = axis[:extrema]
@@ -466,22 +348,8 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
if amax <= amin && isfinite(amin)
amax = amin + 1.0
end
if !isfinite(amin) && !isfinite(amax)
amin, amax = 0.0, 1.0
end
if ispolar(axis.sps[1])
if axis[:letter] == :x
amin, amax = 0, 2pi
elseif lims == :auto
#widen max radius so ticks dont overlap with theta axis
amin, amax + 0.1 * abs(amax - amin)
else
amin, amax
end
elseif should_widen && axis[:widen]
widen(amin, amax, axis[:scale])
elseif lims == :round
round_limits(amin,amax)
if should_widen
widen(amin, amax)
else
amin, amax
end
@@ -497,7 +365,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)
@@ -562,94 +430,39 @@ function axis_drawing_info(sp::Subplot)
ymin, ymax = axis_limits(yaxis)
xticks = get_ticks(xaxis)
yticks = get_ticks(yaxis)
xaxis_segs = Segments(2)
yaxis_segs = Segments(2)
xtick_segs = Segments(2)
ytick_segs = Segments(2)
xgrid_segs = Segments(2)
ygrid_segs = Segments(2)
xborder_segs = Segments(2)
yborder_segs = Segments(2)
spine_segs = Segments(2)
grid_segs = Segments(2)
if sp[:framestyle] != :none
# xaxis
if xaxis[:showaxis]
if sp[:framestyle] != :grid
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
0.0, 0.0
else
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
end
push!(xaxis_segs, (xmin, y1), (xmax, y1))
# don't show the 0 tick label for the origin framestyle
if sp[:framestyle] == :origin && !(xticks in (nothing,false)) && length(xticks) > 1
showticks = xticks[1] .!= 0
xticks = (xticks[1][showticks], xticks[2][showticks])
end
end
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin, y2), (xmax, y2)) # top spine
end
if !(xaxis[:ticks] in (nothing, false))
f = scalefunc(yaxis[:scale])
invf = invscalefunc(yaxis[:scale])
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
t1 = invf(f(ymin) + 0.015 * (f(ymax) - f(ymin)) * ticks_in)
t2 = invf(f(ymax) - 0.015 * (f(ymax) - f(ymin)) * ticks_in)
t3 = invf(f(0) + 0.015 * (f(ymax) - f(ymin)) * ticks_in)
if !(xaxis[:ticks] in (nothing, false))
f = scalefunc(yaxis[:scale])
invf = invscalefunc(yaxis[:scale])
t1 = invf(f(ymin) + 0.015*(f(ymax)-f(ymin)))
t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin)))
for xtick in xticks[1]
if xaxis[:showaxis]
tick_start, tick_stop = if sp[:framestyle] == :origin
(0, t3)
else
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, t2) : (ymin, t1)
end
push!(xtick_segs, (xtick, tick_start), (xtick, tick_stop)) # bottom tick
end
# sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick
xaxis[:grid] && push!(xgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid
end
end
# yaxis
if yaxis[:showaxis]
if sp[:framestyle] != :grid
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
0.0, 0.0
else
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, xmin) : (xmin, xmax)
end
push!(yaxis_segs, (x1, ymin), (x1, ymax))
# don't show the 0 tick label for the origin framestyle
if sp[:framestyle] == :origin && !(yticks in (nothing,false)) && length(yticks) > 1
showticks = yticks[1] .!= 0
yticks = (yticks[1][showticks], yticks[2][showticks])
end
end
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (x2, ymin), (x2, ymax)) # right spine
end
if !(yaxis[:ticks] in (nothing, false))
f = scalefunc(xaxis[:scale])
invf = invscalefunc(xaxis[:scale])
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
t1 = invf(f(xmin) + 0.015 * (f(xmax) - f(xmin)) * ticks_in)
t2 = invf(f(xmax) - 0.015 * (f(xmax) - f(xmin)) * ticks_in)
t3 = invf(f(0) + 0.015 * (f(xmax) - f(xmin)) * ticks_in)
for ytick in yticks[1]
if yaxis[:showaxis]
tick_start, tick_stop = if sp[:framestyle] == :origin
(0, t3)
else
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, t2) : (xmin, t1)
end
push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick
end
# sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick
yaxis[:grid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
end
push!(spine_segs, (xmin,ymin), (xmax,ymin)) # bottom spine
push!(spine_segs, (xmin,ymax), (xmax,ymax)) # top spine
for xtick in xticks[1]
push!(spine_segs, (xtick, ymin), (xtick, t1)) # bottom tick
push!(grid_segs, (xtick, t1), (xtick, t2)) # vertical grid
push!(spine_segs, (xtick, ymax), (xtick, t2)) # top tick
end
end
xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs
if !(yaxis[:ticks] in (nothing, false))
f = scalefunc(xaxis[:scale])
invf = invscalefunc(xaxis[:scale])
t1 = invf(f(xmin) + 0.015*(f(xmax)-f(xmin)))
t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin)))
push!(spine_segs, (xmin,ymin), (xmin,ymax)) # left spine
push!(spine_segs, (xmax,ymin), (xmax,ymax)) # right spine
for ytick in yticks[1]
push!(spine_segs, (xmin, ytick), (t1, ytick)) # left tick
push!(grid_segs, (t1, ytick), (t2, ytick)) # horizontal grid
push!(spine_segs, (xmax, ytick), (t2, ytick)) # right tick
end
end
xticks, yticks, spine_segs, grid_segs
end
+45 -113
View File
@@ -1,15 +1,12 @@
struct NoBackend <: AbstractBackend end
immutable NoBackend <: AbstractBackend end
const _backendType = Dict{Symbol, DataType}(:none => NoBackend)
const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none)
const _backends = Symbol[]
const _initialized_backends = Set{Symbol}()
"Returns a list of supported backends"
backends() = _backends
"Returns the name of the current backend"
backend_name() = CURRENT_BACKEND.sym
_backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
@@ -18,7 +15,7 @@ macro init_backend(s)
sym = Symbol(str)
T = Symbol(string(s) * "Backend")
esc(quote
struct $T <: AbstractBackend end
immutable $T <: AbstractBackend end
export $sym
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
backend_name(::$T) = Symbol($str)
@@ -29,18 +26,24 @@ macro init_backend(s)
end)
end
@init_backend Immerse
@init_backend Gadfly
@init_backend PyPlot
@init_backend Qwt
@init_backend UnicodePlots
@init_backend Winston
@init_backend Bokeh
@init_backend Plotly
@init_backend PlotlyJS
@init_backend GR
@init_backend GLVisualize
@init_backend PGFPlots
include("backends/web.jl")
# include("backends/supported.jl")
# ---------------------------------------------------------
function add_backend(pkg::Symbol)
info("To do a standard install of $pkg, copy and run this:\n\n")
println(add_backend_string(_backend_instance(pkg)))
println()
end
add_backend_string(b::AbstractBackend) = warn("No custom install defined for $(backend_name(b))")
# don't do anything as a default
_create_backend_figure(plt::Plot) = nothing
_prepare_plot_object(plt::Plot) = nothing
@@ -51,49 +54,18 @@ _series_updated(plt::Plot, series::Series) = nothing
_before_layout_calcs(plt::Plot) = nothing
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefontsize] * pt
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefontsize] * pt
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
"Returns the (width,height) of a text label."
function text_size(lablen::Int, sz::Number, rot::Number = 0)
# we need to compute the size of the ticks generically
# this means computing the bounding box and then getting the width/height
# note:
ptsz = sz * pt
width = 0.8lablen * ptsz
# now compute the generalized "height" after rotation as the "opposite+adjacent" of 2 triangles
height = abs(sind(rot)) * width + abs(cosd(rot)) * ptsz
width = abs(sind(rot+90)) * width + abs(cosd(rot+90)) * ptsz
width, height
end
text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(lab), sz, rot)
# account for the size/length/rotation of tick labels
# TODO: this should account for both tick font and the size/length/rotation of tick labels
function tick_padding(axis::Axis)
ticks = get_ticks(axis)
if ticks == nothing
ptsz = axis[:tickfont].pointsize * pt
if axis[:ticks] in (nothing,false)
0mm
elseif axis[:letter] == :x
2mm + ptsz
else
vals, labs = ticks
isempty(labs) && return 0mm
# ptsz = axis[:tickfont].pointsize * pt
longest_label = maximum(length(lab) for lab in labs)
# generalize by "rotating" y labels
rot = axis[:rotation] + (axis[:letter] == :y ? 90 : 0)
# # we need to compute the size of the ticks generically
# # this means computing the bounding box and then getting the width/height
# labelwidth = 0.8longest_label * ptsz
#
#
# # now compute the generalized "height" after rotation as the "opposite+adjacent" of 2 triangles
# hgt = abs(sind(rot)) * labelwidth + abs(cosd(rot)) * ptsz + 1mm
# hgt
# get the height of the rotated label
text_size(longest_label, axis[:tickfontsize], rot)[2]
8mm
end
end
@@ -105,15 +77,6 @@ function _update_min_padding!(sp::Subplot)
toppad = sp[:top_margin] + title_padding(sp)
rightpad = sp[:right_margin]
bottompad = tick_padding(sp[:xaxis]) + sp[:bottom_margin] + guide_padding(sp[:xaxis])
# switch them?
if sp[:xaxis][:mirror]
bottompad, toppad = toppad, bottompad
end
if sp[:yaxis][:mirror]
leftpad, rightpad = rightpad, leftpad
end
# @show (leftpad, toppad, rightpad, bottompad)
sp.minpad = (leftpad, toppad, rightpad, bottompad)
end
@@ -123,7 +86,7 @@ _update_plot_object(plt::Plot) = nothing
# ---------------------------------------------------------
mutable struct CurrentBackend
type CurrentBackend
sym::Symbol
pkg::AbstractBackend
end
@@ -151,7 +114,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 ("PyPlot", "GR", "PlotlyJS", "Immerse", "Gadfly", "UnicodePlots")
if Pkg.installed(pkgstr) != nothing
return backend(Symbol(lowercase(pkgstr)))
end
@@ -177,15 +140,14 @@ function backend()
sym = CURRENT_BACKEND.sym
if !(sym in _initialized_backends)
# # initialize
# println("[Plots.jl] Initializing backend: ", sym)
# initialize
println("[Plots.jl] Initializing backend: ", sym)
inst = _backend_instance(sym)
try
_initialize_backend(inst)
catch err
warn("Couldn't initialize $sym. (might need to install it?)")
add_backend(sym)
rethrow(err)
end
@@ -202,14 +164,12 @@ function backend(pkg::AbstractBackend)
CURRENT_BACKEND.sym = backend_name(pkg)
warn_on_deprecated_backend(CURRENT_BACKEND.sym)
CURRENT_BACKEND.pkg = pkg
backend()
end
function backend(modname::Symbol)
warn_on_deprecated_backend(modname)
CURRENT_BACKEND.sym = modname
CURRENT_BACKEND.pkg = _backend_instance(modname)
backend()
end
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse]
@@ -220,7 +180,23 @@ function warn_on_deprecated_backend(bsym::Symbol)
end
end
# ---------------------------------------------------------
supported_types(::AbstractBackend) = []
supported_styles(::AbstractBackend) = [:solid]
supported_markers(::AbstractBackend) = [:none]
supported_scales(::AbstractBackend) = [:identity]
is_subplot_supported(::AbstractBackend) = false
is_string_supported(::AbstractBackend) = false
nativeImagesSupported(b::AbstractBackend) = :image in supported_types(b)
supported_types() = supported_types(backend())
supported_styles() = supported_styles(backend())
supported_markers() = supported_markers(backend())
supported_scales() = supported_scales(backend())
is_subplot_supported() = is_subplot_supported(backend())
is_string_supported() = is_string_supported(backend())
nativeImagesSupported() = nativeImagesSupported(backend())
# ---------------------------------------------------------
@@ -250,7 +226,7 @@ const _base_supported_args = [
:subplot_index,
:discrete_values,
:projection,
]
function merge_with_base_supported(v::AVec)
@@ -262,49 +238,5 @@ function merge_with_base_supported(v::AVec)
end
end
end
Set(v)
v
end
# @init_backend Immerse
# @init_backend Gadfly
@init_backend PyPlot
# @init_backend Qwt
@init_backend UnicodePlots
# @init_backend Winston
# @init_backend Bokeh
@init_backend Plotly
@init_backend PlotlyJS
@init_backend GR
@init_backend GLVisualize
@init_backend PGFPlots
@init_backend InspectDR
@init_backend HDF5
# ---------------------------------------------------------
# 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())
@@ -2,7 +2,7 @@
# https://github.com/bokeh/Bokeh.jl
supported_attrs(::BokehBackend) = merge_with_base_supported([
supported_args(::BokehBackend) = merge_with_base_supported([
# :annotations,
# :axis,
# :background_color,
@@ -194,7 +194,7 @@ end
# ----------------------------------------------------------------
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
# TODO: write a png to io
warn("mime png not implemented")
end
@@ -2,7 +2,7 @@
# https://github.com/dcjones/Gadfly.jl
supported_attrs(::GadflyBackend) = merge_with_base_supported([
supported_args(::GadflyBackend) = merge_with_base_supported([
:annotations,
:background_color, :foreground_color, :color_palette,
:group, :label, :seriestype,
@@ -41,7 +41,7 @@ function _initialize_backend(::GadflyBackend; kw...)
@eval begin
import Gadfly, Compose
export Gadfly, Compose
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
end
end
@@ -244,7 +244,7 @@ function addToGadflyLegend(plt::Plot, d::KW)
# add the legend if needed
if all(g -> !isa(g, Gadfly.Guide.ManualColorKey), gplt.guides)
unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", @compat(AbstractString)[], Color[]))
end
# now add the series to the legend
@@ -456,7 +456,7 @@ function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims, xfunc, yfunc)
end
function findGuideAndSet(gplt, t::DataType, args...; kw...)
function findGuideAndSet(gplt, t::DataType, args...; kw...) #s::@compat(AbstractString))
for (i,guide) in enumerate(gplt.guides)
if isa(guide, t)
gplt.guides[i] = t(args...; kw...)
@@ -537,7 +537,7 @@ end
# ----------------------------------------------------------------
function createGadflyAnnotationObject(x, y, val::AbstractString)
function createGadflyAnnotationObject(x, y, val::@compat(AbstractString))
Gadfly.Guide.annotation(Compose.compose(
Compose.context(),
Compose.text(x, y, val)
@@ -558,7 +558,7 @@ function createGadflyAnnotationObject(x, y, txt::PlotText)
))
end
function _add_annotations(plt::Plot{GadflyBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
function _add_annotations{X,Y,V}(plt::Plot{GadflyBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
end
@@ -614,7 +614,7 @@ function getxy(plt::Plot{GadflyBackend}, i::Integer)
mapping[:x], mapping[:y]
end
function setxy!(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
function setxy!{X,Y}(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer)
for mapping in getGadflyMappings(plt, i)
mapping[:x], mapping[:y] = xy
end
@@ -677,7 +677,7 @@ setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.attr[:size]...)
# -------------------------------------------------------------------------
function doshow(io::IO, func, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
function dowritemime{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, func, plt::AbstractPlot{P})
gplt = getGadflyContext(plt)
setGadflyDisplaySize(plt)
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
@@ -692,9 +692,9 @@ getGadflyWriteFunc(::MIME"application/x-tex") = Gadfly.PGF
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript", MIME"application/x-tex")
@eval function Base.show(io::IO, ::$mime, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
@eval function Base.writemime{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, ::$mime, plt::AbstractPlot{P})
func = getGadflyWriteFunc($mime())
doshow(io, func, plt)
dowritemime(io, func, plt)
end
end
@@ -2,7 +2,7 @@
# Geometry which displays arbitrary shapes at given (x, y) positions.
# note: vertices is a list of shapes
struct ShapeGeometry <: Gadfly.GeometryElement
immutable ShapeGeometry <: Gadfly.GeometryElement
vertices::AbstractVector #{Tuple{Float64,Float64}}
tag::Symbol
@@ -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
+266 -1486
View File
File diff suppressed because it is too large Load Diff
+249 -669
View File
File diff suppressed because it is too large Load Diff
-665
View File
@@ -1,665 +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.
==#
@require Revise begin
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "hdf5.jl"))
end
import FixedPointNumbers: N0f8 #In core Julia
#Dispatch types:
struct HDF5PlotNative; end #Indentifies a data element that can natively be handled by HDF5
struct HDF5CTuple; end #Identifies a "complex" tuple structure
mutable struct 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, :straightline,
: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,
"SURFACE" => Surface,
"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
# ----------------------------------------------------------------
# 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(grp, ::Type{Array{T}}) where T<:Any
tstr = HDF5PLOT_MAP_TELEM2STR[Array] #ANY
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype(grp, ::Type{T}) where T<:BoundingBox
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(grp, k::String, v::Array{T}) where T<:Number #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(grp, k::String, v::Array{T}) where 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(grp, k::String, v::Length{T}) where 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
function _hdf5plot_gwrite(grp, k::String, v::Surface)
grp = HDF5.g_create(grp, k)
_hdf5plot_gwrite(grp, "data2d", v.surf)
_hdf5plot_writetype(grp, Surface)
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{Surface}, dtid)
grp = HDF5.g_open(grp, k)
data2d = _hdf5plot_read(grp, "data2d")
return Surface(data2d)
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
@@ -1,7 +1,7 @@
# https://github.com/JuliaGraphics/Immerse.jl
supported_attrs(::ImmerseBackend) = supported_attrs(GadflyBackend())
supported_args(::ImmerseBackend) = supported_args(GadflyBackend())
supported_types(::ImmerseBackend) = supported_types(GadflyBackend())
supported_styles(::ImmerseBackend) = supported_styles(GadflyBackend())
supported_markers(::ImmerseBackend) = supported_markers(GadflyBackend())
@@ -14,7 +14,7 @@ function _initialize_backend(::ImmerseBackend; kw...)
@eval begin
import Immerse, Gadfly, Compose, Gtk
export Immerse, Gadfly, Compose, Gtk
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
end
end
@@ -61,7 +61,7 @@ end
# ----------------------------------------------------------------
function _add_annotations(plt::Plot{ImmerseBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
function _add_annotations{X,Y,V}(plt::Plot{ImmerseBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
end
@@ -76,7 +76,7 @@ function getxy(plt::Plot{ImmerseBackend}, i::Integer)
mapping[:x], mapping[:y]
end
function setxy!(plt::Plot{ImmerseBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
function setxy!{X,Y}(plt::Plot{ImmerseBackend}, xy::Tuple{X,Y}, i::Integer)
for mapping in getGadflyMappings(plt, i)
mapping[:x], mapping[:y] = xy
end
-543
View File
@@ -1,543 +0,0 @@
# https://github.com/ma-laforge/InspectDR.jl
#=TODO:
Tweak scale factor for width & other sizes
Not supported by InspectDR:
:foreground_color_grid
:foreground_color_border
:polar,
Add in functionality to Plots.jl:
:aspect_ratio,
=#
@require Revise begin
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "inspectdr.jl"))
end
# ---------------------------------------------------------------------------
#TODO: remove features
const _inspectdr_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,
:markerstrokestyle, #Causes warning not to have it... what is this?
:fillcolor, :fillalpha, #:fillrange,
# :bins, :bar_width, :bar_edges, :bar_position,
:title, :title_location,
:window_title,
:guide, :lims, :scale, #:ticks, :flip, :rotation,
:titlefontfamily, :titlefontsize, :titlefontcolor,
:legendfontfamily, :legendfontsize, :legendfontcolor,
:tickfontfamily, :tickfontsize, :tickfontcolor,
:guidefontfamily, :guidefontsize, :guidefontcolor,
:grid, :legend, #:colorbar,
# :marker_z,
# :line_z,
# :levels,
# :ribbon, :quiver, :arrow,
# :orientation,
:overwrite_figure,
:polar,
# :normalize, :weights,
# :contours, :aspect_ratio,
:match_dimensions,
# :clims,
# :inset_subplots,
:dpi,
# :colorbar_title,
])
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
const _inspectdr_seriestype = [
:path, :scatter, :shape, :straightline, #, :steppre, :steppost
]
#see: _allMarkers, _shape_keys
const _inspectdr_marker = Symbol[
:none, :auto,
:circle, :rect, :diamond,
:cross, :xcross,
:utriangle, :dtriangle, :rtriangle, :ltriangle,
:pentagon, :hexagon, :heptagon, :octagon,
:star4, :star5, :star6, :star7, :star8,
:vline, :hline, :+, :x,
]
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
return s
end
function _inspectdr_mapglyph(s::Shape)
x, y = coords(s)
return InspectDR.GlyphPolyline(x, y)
end
# py_marker(markers::AVec) = map(py_marker, markers)
function _inspectdr_mapglyph(markers::AVec)
warn("Vectors of markers are currently unsupported in InspectDR.")
_inspectdr_mapglyph(markers[1])
end
_inspectdr_mapglyphsize(v::Real) = v
function _inspectdr_mapglyphsize(v::Vector)
warn("Vectors of marker sizes are currently unsupported in InspectDR.")
_inspectdr_mapglyphsize(v[1])
end
_inspectdr_mapcolor(v::Colorant) = v
function _inspectdr_mapcolor(g::PlotUtils.ColorGradient)
warn("Color gradients are currently unsupported in InspectDR.")
#Pick middle color:
_inspectdr_mapcolor(g.colors[div(1+end,2)])
end
function _inspectdr_mapcolor(v::AVec)
warn("Vectors of colors are currently unsupported in InspectDR.")
#Pick middle color:
_inspectdr_mapcolor(v[div(1+end,2)])
end
#Hack: suggested point size does not seem adequate relative to plot size, for some reason.
_inspectdr_mapptsize(v) = 1.5*v
function _inspectdr_add_annotations(plot, x, y, val)
#What kind of annotation is this?
end
#plot::InspectDR.Plot2D
function _inspectdr_add_annotations(plot, x, y, val::PlotText)
vmap = Dict{Symbol, Symbol}(:top=>:t, :bottom=>:b) #:vcenter
hmap = Dict{Symbol, Symbol}(:left=>:l, :right=>:r) #:hcenter
align = Symbol(get(vmap, val.font.valign, :c), get(hmap, val.font.halign, :c))
fnt = InspectDR.Font(val.font.family, val.font.pointsize,
color =_inspectdr_mapcolor(val.font.color)
)
ann = InspectDR.atext(val.str, x=x, y=y,
font=fnt, angle=val.font.rotation, align=align
)
InspectDR.add(plot, ann)
return
end
# ---------------------------------------------------------------------------
function _inspectdr_getscale(s::Symbol, yaxis::Bool)
#TODO: Support :asinh, :sqrt
kwargs = yaxis ? (:tgtmajor=>8, :tgtminor=>2) : () #More grid lines on y-axis
if :log2 == s
return InspectDR.AxisScale(:log2; kwargs...)
elseif :log10 == s
return InspectDR.AxisScale(:log10; kwargs...)
elseif :ln == s
return InspectDR.AxisScale(:ln; kwargs...)
else #identity
return InspectDR.AxisScale(:lin; kwargs...)
end
end
# ---------------------------------------------------------------------------
function add_backend_string(::InspectDRBackend)
"""
if !Plots.is_installed("InspectDR")
Pkg.add("InspectDR")
end
"""
end
function _initialize_backend(::InspectDRBackend; kw...)
@eval begin
import InspectDR
export InspectDR
#Glyph used when plotting "Shape"s:
const INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
)
mutable struct InspecDRPlotRef
mplot::Union{Void, InspectDR.Multiplot}
gui::Union{Void, InspectDR.GtkPlot}
end
_inspectdr_getmplot(::Any) = nothing
_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot
_inspectdr_getgui(::Any) = nothing
_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed ? nothing : gplot)
_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui)
end
end
# ---------------------------------------------------------------------------
# Create the window/figure for this backend.
function _create_backend_figure(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
gplot = _inspectdr_getgui(plt.o)
#:overwrite_figure: want to reuse current figure
if plt[:overwrite_figure] && mplot != nothing
mplot.subplots = [] #Reset
if gplot != nothing #Ensure still references current plot
gplot.src = mplot
end
else #want new one:
mplot = InspectDR.Multiplot()
gplot = nothing #Will be created later
end
#break link with old subplots
for sp in plt.subplots
sp.o = nothing
end
return InspecDRPlotRef(mplot, gplot)
end
# ---------------------------------------------------------------------------
# # this is called early in the pipeline, use it to make the plot current or something
# function _prepare_plot_object(plt::Plot{InspectDRBackend})
# 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"
return plot
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{InspectDRBackend}, series::Series)
st = series[:seriestype]
sp = series[:subplot]
plot = sp.o
#Don't do anything without a "subplot" object: Will process later.
if nothing == plot; return; end
_vectorize(v) = isa(v, Vector) ? v : collect(v) #InspectDR only supports vectors
x, y = if st == :straightline
straightline_data(series)
else
_vectorize(series[:x]), _vectorize(series[:y])
end
#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
series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
elseif ny > nx
series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
end
#= TODO: Eventually support
series[:fillcolor] #I think this is fill under line
zorder = series[:series_plotindex]
For st in :shape:
zorder = series[:series_plotindex],
=#
if st in (:shape,)
x, y = shape_data(series)
nmax = 0
for (i,rng) in enumerate(iter_segments(x, y))
nmax = i
if length(rng) > 1
linewidth = series[:linewidth]
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)
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))
wfrm = InspectDR.add(plot, Float64[], Float64[], id=series[:label])
wfrm.line = InspectDR.line(
style=:none, width=linewidth, #linewidth affects glyph
)
wfrm.glyph = InspectDR.glyph(
shape = INSPECTDR_GLYPH_SHAPE, size = 8,
color = linecolor, fillcolor = fillcolor
)
end
elseif st in (:path, :scatter, :straightline) #, :steppre, :steppost)
#NOTE: In Plots.jl, :scatter plots have 0-linewidths (I think).
linewidth = series[:linewidth]
#More efficient & allows some support for markerstrokewidth:
_style = (0==linewidth ? :none : series[:linestyle])
wfrm = InspectDR.add(plot, x, y, id=series[:label])
wfrm.line = InspectDR.line(
style = _style,
width = series[:linewidth],
color = series[:linecolor],
)
#InspectDR does not control markerstrokewidth independently.
if :none == _style
#Use this property only if no line is displayed:
wfrm.line.width = series[:markerstrokewidth]
end
wfrm.glyph = InspectDR.glyph(
shape = _inspectdr_mapglyph(series[:markershape]),
size = _inspectdr_mapglyphsize(series[:markersize]),
color = _inspectdr_mapcolor(series[:markerstrokecolor]),
fillcolor = _inspectdr_mapcolor(series[:markercolor]),
)
end
# this is all we need to add the series_annotations text
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, x, y)
_inspectdr_add_annotations(plot, xi, yi, PlotText(str, fnt))
end
return
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{InspectDRBackend}, series::Series)
#Nothing to do
end
# ---------------------------------------------------------------------------
function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
const plot = sp.o
const strip = plot.strips[1] #Only 1 strip supported with Plots.jl
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
xgrid_show = xaxis[:grid]
ygrid_show = yaxis[:grid]
strip.grid = InspectDR.GridRect(
vmajor=xgrid_show, # vminor=xgrid_show,
hmajor=ygrid_show, # hminor=ygrid_show,
)
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
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)
a = plot.annotation
a.title = sp[:title]
a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]]
l = plot.layout
l[:frame_canvas].fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
l[:frame_data].fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
l[:frame_data].line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
l[:font_title] = InspectDR.Font(sp[:titlefontfamily],
_inspectdr_mapptsize(sp[:titlefontsize]),
color = _inspectdr_mapcolor(sp[:titlefontcolor])
)
#Cannot independently control fonts of axes with InspectDR:
l[:font_axislabel] = InspectDR.Font(xaxis[:guidefontfamily],
_inspectdr_mapptsize(xaxis[:guidefontsize]),
color = _inspectdr_mapcolor(xaxis[:guidefontcolor])
)
l[:font_ticklabel] = InspectDR.Font(xaxis[:tickfontfamily],
_inspectdr_mapptsize(xaxis[:tickfontsize]),
color = _inspectdr_mapcolor(xaxis[:tickfontcolor])
)
l[:enable_legend] = (sp[:legend] != :none)
#l[:halloc_legend] = 150 #TODO: compute???
l[:font_legend] = InspectDR.Font(sp[:legendfontfamily],
_inspectdr_mapptsize(sp[:legendfontsize]),
color = _inspectdr_mapcolor(sp[:legendfontcolor])
)
l[:frame_legend].fillcolor = _inspectdr_mapcolor(sp[:background_color_legend])
end
# called just before updating layout bounding boxes... in case you need to prep
# for the calcs
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.layout[: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)
if !isassigned(mplot.subplots, i)
mplot.subplots[i] = InspectDR.Plot2D()
end
sp.o = mplot.subplots[i]
plot = sp.o
_initialize_subplot(plt, sp)
_inspectdr_setupsubplot(sp)
# add the annotations
for ann in sp[:annotations]
_inspectdr_add_annotations(plot, ann...)
end
end
#Do not yet support absolute plot positionning.
#Just try to make things look more-or less ok:
if nsubplots <= 1
mplot.layout[:ncolumns] = 1
elseif nsubplots <= 4
mplot.layout[:ncolumns] = 2
elseif nsubplots <= 6
mplot.layout[:ncolumns] = 3
elseif nsubplots <= 12
mplot.layout[:ncolumns] = 4
else
mplot.layout[:ncolumns] = 5
end
for series in plt.series_list
_series_added(plt, series)
end
return
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.values, InspectDR.BoundingBox(0,0,0,0))
#NOTE: plotbounds always pads for titles, legends, etc. even if not in use.
#TODO: possibly zero-out items not in use??
# add in the user-specified margin to InspectDR padding:
leftpad = abs(bb.xmin)*px + sp[:left_margin]
toppad = abs(bb.ymin)*px + sp[:top_margin]
rightpad = abs(bb.xmax)*px + sp[:right_margin]
bottompad = abs(bb.ymax)*px + sp[:bottom_margin]
sp.minpad = (leftpad, toppad, rightpad, bottompad)
end
# ----------------------------------------------------------------
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
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.values, graphbb)
end
gplot = _inspectdr_getgui(plt.o)
if nothing == gplot; return; end
gplot.src = mplot #Ensure still references current plot
InspectDR.refresh(gplot)
return
end
# ----------------------------------------------------------------
const _inspectdr_mimeformats_dpi = Dict(
"image/png" => "png"
)
const _inspectdr_mimeformats_nodpi = Dict(
"image/svg+xml" => "svg",
"application/eps" => "eps",
"image/eps" => "eps",
# "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface
"application/pdf" => "pdf"
)
_inspectdr_show(io::IO, mime::MIME, ::Void, w, h) =
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
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]...)
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]...)
end
end
# ----------------------------------------------------------------
# Display/show the plot (open a GUI window, or browser page, for example).
function _display(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end
gplot = _inspectdr_getgui(plt.o)
if nothing == gplot
gplot = display(InspectDR.GtkDisplay(), mplot)
else
#redundant... Plots.jl will call _update_plot_object:
#InspectDR.refresh(gplot)
end
plt.o = InspecDRPlotRef(mplot, gplot)
return gplot
end
+79 -387
View File
@@ -2,18 +2,13 @@
# significant contributions by: @pkofod
@require Revise begin
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "pgfplots.jl"))
end
const _pgfplots_attr = merge_with_base_supported([
:annotations,
:background_color_legend,
supported_args(::PGFPlotsBackend) = merge_with_base_supported([
# :annotations,
# :background_color_legend,
:background_color_inside,
# :background_color_outside,
# :foreground_color_legend,
:foreground_color_grid, :foreground_color_axis,
:foreground_color_text, :foreground_color_border,
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
# :foreground_color_text, :foreground_color_border,
:label,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
@@ -27,34 +22,25 @@ const _pgfplots_attr = merge_with_base_supported([
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend,
:colorbar,
:fill_z, :line_z, :marker_z, :levels,
# :colorbar,
# :marker_z, :levels,
# :ribbon, :quiver, :arrow,
# :orientation,
# :overwrite_figure,
:polar,
# :polar,
# :normalize, :weights, :contours,
:aspect_ratio,
# :match_dimensions,
:tick_direction,
:framestyle,
:camera,
:contour_labels,
])
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape, :straightline,]
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline] #vcat(_allMarkers, Shape)
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
supported_types(::PGFPlotsBackend) = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
supported_styles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supported_markers(::PGFPlotsBackend) = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
supported_scales(::PGFPlotsBackend) = [:identity, :ln, :log2, :log10]
is_subplot_supported(::PGFPlotsBackend) = false
# --------------------------------------------------------------------------------------
function add_backend_string(::PGFPlotsBackend)
"""
Pkg.add("PGFPlots")
Pkg.build("PGFPlots")
"""
end
function _initialize_backend(::PGFPlotsBackend; kw...)
@eval begin
@@ -78,8 +64,6 @@ const _pgfplots_markers = KW(
:none => "none",
:cross => "+",
:xcross => "x",
:+ => "+",
:x => "x",
:utriangle => "triangle*",
:dtriangle => "triangle*",
:circle => "*",
@@ -88,7 +72,6 @@ const _pgfplots_markers = KW(
:star6 => "asterisk",
:diamond => "diamond*",
:pentagon => "pentagon*",
:hline => "-"
)
const _pgfplots_legend_pos = KW(
@@ -96,7 +79,6 @@ const _pgfplots_legend_pos = KW(
:bottomright => "south east",
:topright => "north east",
:topleft => "north west",
:outertopright => "outer north east",
)
@@ -109,131 +91,69 @@ 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"
)
const _pgf_framestyles = [:box, :axes, :origin, :zerolines, :grid, :none]
const _pgf_framestyle_defaults = Dict(:semi => :box)
function pgf_framestyle(style::Symbol)
if style in _pgf_framestyles
return style
else
default_style = get(_pgf_framestyle_defaults, style, :axes)
warn("Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead.")
default_style
end
end
# --------------------------------------------------------------------------------------
# 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
pgf_thickness_scaling(plt::Plot) = plt[:thickness_scaling]
pgf_thickness_scaling(sp::Subplot) = pgf_thickness_scaling(sp.plt)
pgf_thickness_scaling(series) = pgf_thickness_scaling(series[:subplot])
function pgf_fillstyle(d, i = 1)
cstr,a = pgf_color(get_fillcolor(d, i))
fa = get_fillalpha(d, i)
if fa != nothing
a = fa
end
function pgf_fillstyle(d::KW)
cstr,a = pgf_color(d[:fillcolor])
"fill = $cstr, fill opacity=$a"
end
function pgf_linestyle(linewidth::Real, color, α = 1, linestyle = "solid")
cstr, a = pgf_color(plot_color(color, α))
function pgf_linestyle(d::KW)
cstr,a = pgf_color(d[:linecolor])
"""
color = $cstr,
draw opacity = $a,
line width = $linewidth,
$(get(_pgfplots_linestyles, linestyle, "solid"))"""
draw opacity=$a,
line width=$(d[:linewidth]),
$(get(_pgfplots_linestyles, d[:linestyle], "solid"))"""
end
function pgf_linestyle(d, i = 1)
lw = pgf_thickness_scaling(d) * get_linewidth(d, i)
lc = get_linecolor(d, i)
la = get_linealpha(d, i)
ls = get_linestyle(d, i)
return pgf_linestyle(lw, lc, la, ls)
end
function pgf_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
fs = fontsize * thickness_scaling
return string("{\\fontsize{", fs, " pt}{", 1.3fs, " pt}", font, "}")
end
function pgf_marker(d, i = 1)
shape = _cycle(d[:markershape], i)
cstr, a = pgf_color(plot_color(get_markercolor(d, i), get_markeralpha(d, i)))
cstr_stroke, a_stroke = pgf_color(plot_color(get_markerstrokecolor(d, i), get_markerstrokealpha(d, i)))
function pgf_marker(d::KW)
shape = d[:markershape]
cstr, a = pgf_color(d[:markercolor])
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor])
"""
mark = $(get(_pgfplots_markers, shape, "*")),
mark size = $(pgf_thickness_scaling(d) * 0.5 * _cycle(d[:markersize], i)),
mark size = $(0.5 * d[:markersize]),
mark options = {
color = $cstr_stroke, draw opacity = $a_stroke,
fill = $cstr, fill opacity = $a,
line width = $(pgf_thickness_scaling(d) * _cycle(d[:markerstrokewidth], i)),
line width = $(d[:markerstrokewidth]),
rotate = $(shape == :dtriangle ? 180 : 0),
$(get(_pgfplots_linestyles, _cycle(d[:markerstrokestyle], i), "solid"))
$(get(_pgfplots_linestyles, d[:markerstrokestyle], "solid"))
}"""
end
function pgf_add_annotation!(o, x, y, val, thickness_scaling = 1)
# 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),
font=$(pgf_font(val.font.pointsize, thickness_scaling))
"""))
end
# --------------------------------------------------------------------------------------
function pgf_series(sp::Subplot, series::Series)
d = series.d
st = d[:seriestype]
series_collection = PGFPlots.Plot[]
style = []
kw = KW()
push!(style, pgf_linestyle(d))
push!(style, pgf_marker(d))
if d[:fillrange] != nothing
push!(style, pgf_fillstyle(d))
end
# add to legend?
if sp[:legend] != :none && should_add_to_legend(series)
kw[:legendentry] = d[:label]
end
# function args
args = if st == :contour
args = if st == :contour
d[:z].surf, d[:x], d[:y]
elseif is3d(st)
d[:x], d[:y], d[:z]
elseif st == :straightline
straightline_data(series)
elseif st == :shape
shape_data(series)
elseif ispolar(sp)
theta, r = filter_radial_data(d[:x], d[:y], axis_limits(sp[:yaxis]))
rad2deg.(theta), r
else
d[:x], d[:y]
end
@@ -244,131 +164,34 @@ function pgf_series(sp::Subplot, series::Series)
else
a
end, args)
# for (i,a) in enumerate(args)
# if typeof(a) <: AbstractVector && typeof(a) != Vector
# args[i] = collect(a)
# end
# end
if st in (:contour, :histogram2d)
style = []
kw = KW()
push!(style, pgf_linestyle(d))
push!(style, pgf_marker(d))
push!(style, "forget plot")
kw[:style] = join(style, ',')
func = if st == :histogram2d
PGFPlots.Histogram2
else
kw[:labels] = series[:contour_labels]
kw[:levels] = series[:levels]
PGFPlots.Contour
end
push!(series_collection, func(args...; kw...))
else
# series segments
segments = iter_segments(series)
for (i, rng) in enumerate(segments)
style = []
kw = KW()
push!(style, pgf_linestyle(d, i))
push!(style, pgf_marker(d, i))
if st == :shape
push!(style, pgf_fillstyle(d, i))
end
# add to legend?
if i == 1 && sp[:legend] != :none && should_add_to_legend(series)
if d[:fillrange] != nothing
push!(style, "forget plot")
push!(series_collection, pgf_fill_legend_hack(d, args))
else
kw[:legendentry] = d[:label]
if st == :shape # || d[:fillrange] != nothing
push!(style, "area legend")
end
end
else
push!(style, "forget plot")
end
seg_args = (arg[rng] for arg in args)
# include additional style, then add to the kw
if haskey(_pgf_series_extrastyle, st)
push!(style, _pgf_series_extrastyle[st])
end
kw[:style] = join(style, ',')
# add fillrange
if series[:fillrange] != nothing && st != :shape
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
end
# build/return the series object
func = if st == :path3d
PGFPlots.Linear3
elseif st == :scatter
PGFPlots.Scatter
else
PGFPlots.Linear
end
push!(series_collection, func(seg_args...; kw...))
end
end
series_collection
end
function pgf_fillrange_series(series, i, fillrange, args...)
st = series[:seriestype]
style = []
kw = KW()
push!(style, "line width = 0")
push!(style, "draw opacity = 0")
push!(style, pgf_fillstyle(series, i))
push!(style, pgf_marker(series, i))
push!(style, "forget plot")
# include additional style, then add to the kw
if haskey(_pgf_series_extrastyle, st)
push!(style, _pgf_series_extrastyle[st])
end
kw[:style] = join(style, ',')
func = is3d(series) ? PGFPlots.Linear3 : PGFPlots.Linear
return func(pgf_fillrange_args(fillrange, args...)...; kw...)
end
function pgf_fillrange_args(fillrange, x, y)
n = length(x)
x_fill = [x; x[n:-1:1]; x[1]]
y_fill = [y; _cycle(fillrange, n:-1:1); y[1]]
return x_fill, y_fill
end
function pgf_fillrange_args(fillrange, x, y, z)
n = length(x)
x_fill = [x; x[n:-1:1]; x[1]]
y_fill = [y; y[n:-1:1]; x[1]]
z_fill = [z; _cycle(fillrange, n:-1:1); z[1]]
return x_fill, y_fill, z_fill
end
function pgf_fill_legend_hack(d, args)
style = []
kw = KW()
push!(style, pgf_linestyle(d, 1))
push!(style, pgf_marker(d, 1))
push!(style, pgf_fillstyle(d, 1))
push!(style, "area legend")
kw[:legendentry] = d[:label]
kw[:style] = join(style, ',')
st = d[:seriestype]
# build/return the series object
func = if st == :path3d
PGFPlots.Linear3
elseif st == :scatter
PGFPlots.Scatter
elseif st == :histogram2d
PGFPlots.Histogram2
elseif st == :contour
PGFPlots.Contour
else
PGFPlots.Linear
end
return func(([arg[1]] for arg in args)...; kw...)
func(args...; kw...)
end
# ----------------------------------------------------------------
function pgf_axis(sp::Subplot, letter)
@@ -376,19 +199,9 @@ function pgf_axis(sp::Subplot, letter)
style = []
kw = KW()
# turn off scaled ticks
push!(style, "scaled $(letter) ticks = false")
# set to supported framestyle
framestyle = pgf_framestyle(sp[:framestyle])
# axis guide
kw[Symbol(letter,:label)] = axis[:guide]
# Add label font
cstr, α = pgf_color(plot_color(axis[:guidefontcolor]))
push!(style, string(letter, "label style = {font = ", pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:guidefontrotation], "}"))
# flip/reverse?
axis[:flip] && push!(style, "$letter dir=reverse")
@@ -400,76 +213,18 @@ function pgf_axis(sp::Subplot, letter)
end
# ticks on or off
if axis[:ticks] in (nothing, false, :none) || framestyle == :none
if axis[:ticks] in (nothing, false)
push!(style, "$(letter)majorticks=false")
end
# grid on or off
if axis[:grid] && framestyle != :none
push!(style, "$(letter)majorgrids = true")
else
push!(style, "$(letter)majorgrids = false")
end
# limits
# TODO: support zlims
if letter != :z
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(axis)) : axis_limits(axis)
lims = axis_limits(axis)
kw[Symbol(letter,:min)] = lims[1]
kw[Symbol(letter,:max)] = lims[2]
end
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
ticks = get_ticks(axis)
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
push!(style, string(letter, "tick = {", join(tick_values,","), "}"))
if axis[:showaxis] && axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
# wrap the power part of label with }
tick_labels = String[begin
base, power = split(label, "^")
power = string("{", power, "}")
string(base, "^", power)
end for label in ticks[2]]
push!(style, string(letter, "ticklabels = {\$", join(tick_labels,"\$,\$"), "\$}"))
elseif axis[:showaxis]
tick_labels = ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
if axis[:formatter] in (:scientific, :auto)
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
tick_labels = replace.(tick_labels, "×", "\\times")
end
push!(style, string(letter, "ticklabels = {", join(tick_labels,","), "}"))
else
push!(style, string(letter, "ticklabels = {}"))
end
push!(style, string(letter, "tick align = ", (axis[:tick_direction] == :out ? "outside" : "inside")))
cstr, α = pgf_color(plot_color(axis[:tickfontcolor]))
push!(style, string(letter, "ticklabel style = {font = ", pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:tickfontrotation], "}"))
push!(style, string(letter, " grid style = {", pgf_linestyle(pgf_thickness_scaling(sp) * axis[:gridlinewidth], axis[:foreground_color_grid], axis[:gridalpha], axis[:gridstyle]), "}"))
end
# framestyle
if framestyle in (:axes, :origin)
axispos = framestyle == :axes ? "left" : "middle"
# the * after lines disables the arrows at the axes
push!(style, string("axis lines* = ", axispos))
end
if framestyle == :zerolines
push!(style, string("extra ", letter, " ticks = 0"))
push!(style, string("extra ", letter, " tick labels = "))
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}}"))
end
if !axis[:showaxis]
push!(style, "separate axis lines")
end
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
else
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}"))
end
# return the style list and KW args
style, kw
end
@@ -477,14 +232,10 @@ end
# ----------------------------------------------------------------
function _update_plot_object(plt::Plot{PGFPlotsBackend})
function _make_pgf_plot!(plt::Plot)
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()
@@ -492,30 +243,29 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
for letter in (:x, :y, :z)
if letter != :z || is3d(sp)
axisstyle, axiskw = pgf_axis(sp, letter)
append!(style, axisstyle)
merge!(kw, axiskw)
for sty in axisstyle
push!(style, sty)
end
end
end
# 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,
width = $(width(bb).value)mm,
height = $(height(bb).value)mm,
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
""")
kw[:width] = "$(width(bb).value)mm"
kw[:height] = "$(height(bb).value)mm"
if sp[:title] != ""
kw[:title] = "$(sp[:title])"
cstr, α = pgf_color(plot_color(sp[:titlefontcolor]))
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
push!(style, "title = $(sp[:title])")
end
sp[:grid] && push!(style, "grid = major")
if sp[:aspect_ratio] in (1, :equal)
kw[:axisEqual] = "true"
end
@@ -524,81 +274,28 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
if haskey(_pgfplots_legend_pos, legpos)
kw[:legendPos] = _pgfplots_legend_pos[legpos]
end
cstr, a = pgf_color(plot_color(sp[:background_color_legend]))
push!(style, string("legend style = {", pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], 1.0, "solid"), ",", "fill = $cstr,", "font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)), "}"))
if any(s[:seriestype] == :contour for s in series_list(sp))
kw[:view] = "{0}{90}"
kw[:colorbar] = !(sp[:colorbar] in (:none, :off, :hide, false))
elseif is3d(sp)
azim, elev = sp[:camera]
kw[:view] = "{$(azim)}{$(elev)}"
end
axisf = PGFPlots.Axis
if sp[:projection] == :polar
axisf = PGFPlots.PolarAxis
#make radial axis vertical
kw[:xmin] = 90
kw[:xmax] = 450
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, :linecolor)
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 = join(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), pgf_thickness_scaling(series))
end
push!(o, pgf_series(sp, series))
end
# add the annotations
for ann in sp[:annotations]
pgf_add_annotation!(o, locate_annotation(sp, ann...)..., pgf_thickness_scaling(sp))
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)
function _writemime(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
writemime(io, mime, plt.o)
end
function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
function _writemime(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
# prepare the object
pgfplt = PGFPlots.plot(plt.o)
@@ -607,20 +304,15 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
# read it into io
write(io, readstring(open(fn)))
write(io, readall(open(fn)))
# cleanup
PGFPlots.cleanup(plt.o)
end
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
fn = tempname()*".tex"
PGFPlots.save(fn, backend_object(plt), include_preamble=false)
write(io, readstring(open(fn)))
end
function _display(plt::Plot{PGFPlotsBackend})
# prepare the object
_make_pgf_plot!(plt)
pgfplt = PGFPlots.plot(plt.o)
# save an svg
+166 -523
View File
@@ -1,15 +1,11 @@
# https://plot.ly/javascript/getting-started
@require Revise begin
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "plotly.jl"))
end
const _plotly_attr = merge_with_base_supported([
supported_args(::PlotlyBackend) = merge_with_base_supported([
:annotations,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_legend, :foreground_color_guide,
:foreground_color_grid, :foreground_color_axis,
# :foreground_color_grid, :foreground_color_axis,
:foreground_color_text, :foreground_color_border,
:foreground_color_title,
:label,
@@ -19,76 +15,45 @@ const _plotly_attr = merge_with_base_supported([
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
:fillrange, :fillcolor, :fillalpha,
:bins,
:title, :title_location,
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
:titlefontcolor,
:legendfontfamily, :legendfontsize, :legendfontcolor,
:tickfontfamily, :tickfontsize, :tickfontcolor,
:guidefontfamily, :guidefontsize, :guidefontcolor,
:title, :title_location, :titlefont,
:window_title,
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :gridalpha, :gridlinewidth,
:legend, :colorbar, :colorbar_title,
:marker_z, :fill_z, :line_z, :levels,
:grid, :legend, :colorbar,
:marker_z, :levels,
:ribbon, :quiver,
:orientation,
# :overwrite_figure,
:polar,
:normalize, :weights,
# :contours,
:aspect_ratio,
# :contours, :aspect_ratio,
:hover,
:inset_subplots,
:bar_width,
:clims,
:framestyle,
:tick_direction,
:camera,
:contour_labels,
])
const _plotly_seriestype = [
:path, :scatter, :pie, :heatmap,
:contour, :surface, :wireframe, :path3d, :scatter3d, :shape, :scattergl,
:straightline
supported_types(::PlotlyBackend) = [
:path, :scatter, :bar, :pie, :heatmap,
:contour, :surface, :path3d, :scatter3d, :shape, :scattergl,
]
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
const _plotly_marker = [
supported_styles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supported_markers(::PlotlyBackend) = [
:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle,
:cross, :xcross, :pentagon, :hexagon, :octagon, :vline, :hline
]
const _plotly_scale = [:identity, :log10]
supported_scales(::PlotlyBackend) = [:identity, :log10]
is_subplot_supported(::PlotlyBackend) = true
# is_string_supported(::PlotlyBackend) = true
const _plotly_framestyles = [:box, :axes, :zerolines, :grid, :none]
const _plotly_framestyle_defaults = Dict(:semi => :box, :origin => :zerolines)
function _plotly_framestyle(style::Symbol)
if style in _plotly_framestyles
return style
else
default_style = get(_plotly_framestyle_defaults, style, :axes)
warn("Framestyle :$style is not supported by Plotly and PlotlyJS. :$default_style was cosen instead.")
default_style
end
end
is_string_supported(::PlotlyBackend) = true
# --------------------------------------------------------------------------------------
function add_backend_string(::PlotlyBackend)
"""
Pkg.build("Plots")
"""
end
const _plotly_js_path = joinpath(dirname(@__FILE__), "..", "..", "deps", "plotly-latest.min.js")
const _plotly_js_path_remote = "https://cdn.plot.ly/plotly-latest.min.js"
function _initialize_backend(::PlotlyBackend; kw...)
@eval begin
_js_code = open(readstring, _plotly_js_path, "r")
import JSON
JSON._print(io::IO, state::JSON.State, dt::Union{Date,DateTime}) = print(io, '"', dt, '"')
_js_path = Pkg.dir("Plots", "deps", "plotly-latest.min.js")
_js_code = open(@compat(readstring), _js_path, "r")
# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2
_js_script = """
@@ -119,20 +84,6 @@ end
# ----------------------------------------------------------------
const _plotly_legend_pos = KW(
:right => [1., 0.5],
:left => [0., 0.5],
:top => [0.5, 1.],
:bottom => [0.5, 0.],
:bottomleft => [0., 0.],
:bottomright => [1., 0.],
:topright => [1., 1.],
:topleft => [0., 1.]
)
plotly_legend_pos(pos::Symbol) = get(_plotly_legend_pos, pos, [1.,1.])
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real, T<:Real} = v
function plotly_font(font::Font, color = font.color)
KW(
:family => font.family,
@@ -141,13 +92,12 @@ function plotly_font(font::Font, color = font.color)
)
end
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
KW(
:text => val,
:xref => xref,
:x => x,
:yref => yref,
:yref => xref,
:y => y,
:showarrow => false,
)
@@ -158,7 +108,7 @@ function plotly_annotation_dict(x, y, ptxt::PlotText; xref="paper", yref="paper"
:font => plotly_font(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
:rotation => -ptxt.font.rotation,
:rotation => ptxt.font.rotation,
))
end
@@ -193,97 +143,55 @@ function plotly_scale(scale::Symbol)
end
end
function shrink_by(lo, sz, ratio)
amt = 0.5 * (1.0 - ratio) * sz
lo + amt, sz - 2amt
end
function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
aspect_ratio = sp[:aspect_ratio]
if aspect_ratio != :none
if aspect_ratio == :equal
aspect_ratio = 1.0
end
xmin,xmax = axis_limits(sp[:xaxis])
ymin,ymax = axis_limits(sp[:yaxis])
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
parea_ratio = width(plotarea) / height(plotarea)
if want_ratio > parea_ratio
# need to shrink y
ratio = parea_ratio / want_ratio
pcts[2], pcts[4] = shrink_by(pcts[2], pcts[4], ratio)
elseif want_ratio < parea_ratio
# need to shrink x
ratio = want_ratio / parea_ratio
pcts[1], pcts[3] = shrink_by(pcts[1], pcts[3], ratio)
end
pcts
end
pcts
end
# this method gets the start/end in percentage of the canvas for this axis direction
function plotly_domain(sp::Subplot, letter)
figw, figh = sp.plt[:size]
pcts = bbox_to_pcts(sp.plotarea, figw*px, figh*px)
pcts = plotly_apply_aspect_ratio(sp, sp.plotarea, pcts)
i1,i2 = (letter == :x ? (1,3) : (2,4))
[pcts[i1], pcts[i1]+pcts[i2]]
end
function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
function plotly_axis(axis::Axis, sp::Subplot)
letter = axis[:letter]
framestyle = sp[:framestyle]
ax = KW(
:visible => framestyle != :none,
:title => axis[:guide],
:showgrid => axis[:grid],
:gridcolor => rgba_string(plot_color(axis[:foreground_color_grid], axis[:gridalpha])),
:gridwidth => axis[:gridlinewidth],
:zeroline => framestyle == :zerolines,
:zerolinecolor => rgba_string(axis[:foreground_color_axis]),
:showline => framestyle in (:box, :axes) && axis[:showaxis],
:linecolor => rgba_string(plot_color(axis[:foreground_color_axis])),
:ticks => axis[:tick_direction] == :out ? "outside" : "inside",
:mirror => framestyle == :box,
:showticklabels => axis[:showaxis],
:showgrid => sp[:grid],
:zeroline => false,
)
if letter in (:x,:y)
ax[:domain] = plotly_domain(sp, letter)
if is3d(sp)
# don't link 3d axes for synchronized interactivity
x_idx = y_idx = sp[:subplot_index]
else
x_idx, y_idx = plotly_link_indicies(plt, sp)
ax[:anchor] = "$(letter==:x ? :y : :x)$(plotly_subplot_index(sp))"
end
rot = axis[:rotation]
if rot != 0
ax[:tickangle] = rot
end
if !(axis[:ticks] in (nothing, :none))
ax[:titlefont] = plotly_font(axis[:guidefont], axis[:foreground_color_guide])
ax[:type] = plotly_scale(axis[:scale])
ax[:tickfont] = plotly_font(axis[:tickfont], axis[:foreground_color_text])
ax[:tickcolor] = rgba_string(axis[:foreground_color_border])
ax[:linecolor] = rgba_string(axis[:foreground_color_border])
# lims
lims = axis[:lims]
if lims != :auto && limsType(lims) == :limits
ax[:range] = lims
end
ax[:anchor] = "$(letter==:x ? "y$(y_idx)" : "x$(x_idx)")"
end
ax[:tickangle] = -axis[:rotation]
ax[:type] = plotly_scale(axis[:scale])
lims = axis_limits(axis)
if axis[:ticks] != :native || axis[:lims] != :auto
ax[:range] = map(scalefunc(axis[:scale]), lims)
end
if !(axis[:ticks] in (nothing, :none, false))
ax[:titlefont] = plotly_font(guidefont(axis))
ax[:tickfont] = plotly_font(tickfont(axis))
ax[:tickcolor] = framestyle in (:zerolines, :grid) || !axis[:showaxis] ? rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
ax[:linecolor] = rgba_string(axis[:foreground_color_axis])
# flip
if axis[:flip]
ax[:range] = reverse(ax[:range])
ax[:autorange] = "reversed"
end
# ticks
if axis[:ticks] != :native
ticks = get_ticks(axis)
ticks = get_ticks(axis)
if ticks != :auto
ttype = ticksType(ticks)
if ttype == :ticks
ax[:tickmode] = "array"
@@ -298,23 +206,6 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
ax[:showgrid] = false
end
ax
end
function plotly_polaraxis(axis::Axis)
ax = KW(
:visible => axis[:showaxis],
:showline => axis[:grid],
)
if axis[:letter] == :x
ax[:range] = rad2deg.(axis_limits(axis))
else
ax[:range] = axis_limits(axis)
ax[:orientation] = -90
end
ax
end
@@ -324,15 +215,13 @@ 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[]
multiple_subplots = length(plt.subplots) > 1
for sp in plt.subplots
spidx = multiple_subplots ? sp[:subplot_index] : ""
x_idx, y_idx = multiple_subplots ? plotly_link_indicies(plt, sp) : ("", "")
spidx = plotly_subplot_index(sp)
# add an annotation for the title... positioned horizontally relative to plotarea,
# but vertically just below the top of the subplot bounding box
if sp[:title] != ""
@@ -346,71 +235,38 @@ function plotly_layout(plt::Plot)
0.5 * (left(bb) + right(bb))
end
titlex, titley = xy_mm_to_pcts(xmm, top(bbox(sp)), w*px, h*px)
title_font = font(titlefont(sp), :top)
push!(d_out[:annotations], plotly_annotation_dict(titlex, titley, text(sp[:title], title_font)))
titlefont = font(sp[:titlefont], :top, sp[:foreground_color_title])
push!(d_out[:annotations], plotly_annotation_dict(titlex, titley, text(sp[:title], titlefont)))
end
d_out[:plot_bgcolor] = rgba_string(sp[:background_color_inside])
# set to supported framestyle
sp[:framestyle] = _plotly_framestyle(sp[:framestyle])
# TODO: x/y axis tick values/labels
# if any(is3d, seriesargs)
if is3d(sp)
azim = sp[:camera][1] - 90 #convert azimuthal to match GR behaviour
theta = 90 - sp[:camera][2] #spherical coordinate angle from z axis
d_out[:scene] = KW(
Symbol("xaxis$(spidx)") => plotly_axis(plt, sp[:xaxis], sp),
Symbol("yaxis$(spidx)") => plotly_axis(plt, sp[:yaxis], sp),
Symbol("zaxis$(spidx)") => plotly_axis(plt, sp[:zaxis], sp),
#2.6 multiplier set camera eye such that whole plot can be seen
:camera => KW(
:eye => KW(
:x => cosd(azim)*sind(theta)*2.6,
:y => sind(azim)*sind(theta)*2.6,
:z => cosd(theta)*2.6,
),
),
Symbol("xaxis$spidx") => plotly_axis(sp[:xaxis], sp),
Symbol("yaxis$spidx") => plotly_axis(sp[:yaxis], sp),
Symbol("zaxis$spidx") => plotly_axis(sp[:zaxis], sp),
)
elseif ispolar(sp)
d_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp[:xaxis])
d_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp[:yaxis])
else
d_out[Symbol("xaxis$(x_idx)")] = plotly_axis(plt, sp[:xaxis], sp)
# don't allow yaxis to be reupdated/reanchored in a linked subplot
spidx == y_idx ? d_out[Symbol("yaxis$(y_idx)")] = plotly_axis(plt, sp[:yaxis], sp) : nothing
d_out[Symbol("xaxis$spidx")] = plotly_axis(sp[:xaxis], sp)
d_out[Symbol("yaxis$spidx")] = plotly_axis(sp[:yaxis], sp)
end
# legend
d_out[:showlegend] = sp[:legend] != :none
xpos,ypos = plotly_legend_pos(sp[:legend])
if sp[:legend] != :none
d_out[:legend] = KW(
:bgcolor => rgba_string(sp[:background_color_legend]),
:bordercolor => rgba_string(sp[:foreground_color_legend]),
:font => plotly_font(legendfont(sp)),
:tracegroupgap => 0,
:x => xpos,
:y => ypos
:font => plotly_font(sp[:legendfont], sp[:foreground_color_legend]),
)
end
# annotations
for ann in sp[:annotations]
append!(d_out[:annotations], KW[plotly_annotation_dict(locate_annotation(sp, ann...)...; xref = "x$(x_idx)", yref = "y$(y_idx)")])
end
# series_annotations
for series in series_list(sp)
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
push!(d_out[:annotations], plotly_annotation_dict(
xi,
yi,
PlotText(str,fnt); xref = "x$(x_idx)", yref = "y$(y_idx)")
)
end
end
append!(d_out[:annotations], KW[plotly_annotation_dict(ann...; xref = "x$spidx", yref = "y$spidx") for ann in sp[:annotations]])
# # arrows
# for sargs in seriesargs
@@ -443,24 +299,14 @@ end
function plotly_colorscale(grad::ColorGradient, α)
[[grad.values[i], rgba_string(plot_color(grad.colors[i], α))] for i in 1:length(grad.colors)]
[[grad.values[i], rgb_string(grad.colors[i])] for i in 1:length(grad.colors)]
end
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
function plotly_colorscale(c::AbstractVector{<:RGBA}, α)
if length(c) == 1
return [[0.0, rgba_string(plot_color(c[1], α))], [1.0, rgba_string(plot_color(c[1], α))]]
else
vals = linspace(0.0, 1.0, length(c))
return [[vals[i], rgba_string(plot_color(c[i], α))] for i in eachindex(c)]
end
end
# plotly_colorscale(c, alpha = nothing) = plotly_colorscale(cgrad(), alpha)
const _plotly_markers = KW(
:rect => "square",
:xcross => "x",
:x => "x",
:utriangle => "triangle-up",
:dtriangle => "triangle-down",
:star5 => "star-triangle-up",
@@ -468,15 +314,9 @@ const _plotly_markers = KW(
:hline => "line-ew",
)
# find indicies of axes to which the supblot links to
function plotly_link_indicies(plt::Plot, sp::Subplot)
if plt[:link] in (:x, :y, :both)
x_idx = sp[:xaxis].sps[1][:subplot_index]
y_idx = sp[:yaxis].sps[1][:subplot_index]
else
x_idx = y_idx = sp[:subplot_index]
end
x_idx, y_idx
function plotly_subplot_index(sp::Subplot)
spidx = sp[:subplot_index]
spidx == 1 ? "" : spidx
end
@@ -486,59 +326,13 @@ function plotly_close_shapes(x, y)
xs, ys = nansplit(x), nansplit(y)
for i=1:length(xs)
shape = Shape(xs[i], ys[i])
xs[i], ys[i] = coords(shape)
xs[i], ys[i] = shape_coords(shape)
end
nanvcat(xs), nanvcat(ys)
end
function plotly_data(series::Series, letter::Symbol, data)
axis = series[:subplot][Symbol(letter, :axis)]
data = if axis[:ticks] == :native && data != nothing
plotly_native_data(axis, data)
else
data
end
if series[:seriestype] in (:heatmap, :contour, :surface, :wireframe)
plotly_surface_data(series, data)
else
plotly_data(data)
end
end
plotly_data(v) = v != nothing ? collect(v) : v
plotly_data(surf::Surface) = surf.surf
plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v)
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)
function plotly_native_data(axis::Axis, data::AbstractArray)
if !isempty(axis[:discrete_values])
construct_categorical_data(data, axis)
elseif axis[:formatter] in (datetimeformatter, dateformatter, timeformatter)
plotly_convert_to_datetime(data, axis[:formatter])
else
data
end
end
plotly_native_data(axis::Axis, a::Surface) = Surface(plotly_native_data(axis, a.surf))
function plotly_convert_to_datetime(x::AbstractArray, formatter::Function)
if formatter == datetimeformatter
map(xi -> replace(formatter(xi), "T", " "), x)
elseif formatter == dateformatter
map(xi -> string(formatter(xi), " 00:00:00"), x)
elseif formatter == timeformatter
map(xi -> string(Dates.Date(Dates.now()), " ", formatter(xi)), x)
else
error("Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter")
end
end
#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 = α)
plotly_data(v) = collect(v)
plotly_data{R<:Rational}(v::AbstractArray{R}) = float(v)
# 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)
@@ -551,78 +345,56 @@ function plotly_series(plt::Plot, series::Series)
d_out = KW()
# these are the axes that the series should be mapped to
x_idx, y_idx = plotly_link_indicies(plt, sp)
d_out[:xaxis] = "x$(x_idx)"
d_out[:yaxis] = "y$(y_idx)"
spidx = plotly_subplot_index(sp)
d_out[:xaxis] = "x$spidx"
d_out[:yaxis] = "y$spidx"
d_out[:showlegend] = should_add_to_legend(series)
if st == :straightline
x, y = straightline_data(series)
z = series[:z]
else
x, y, z = series[:x], series[:y], series[:z]
end
x, y, z = (plotly_data(series, letter, data)
for (letter, data) in zip((:x, :y, :z), (x, y, z))
)
x, y = plotly_data(series[:x]), plotly_data(series[:y])
d_out[:name] = series[:label]
isscatter = st in (:scatter, :scatter3d, :scattergl)
hasmarker = isscatter || series[:markershape] != :none
hasline = st in (:path, :path3d, :straightline)
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
d_out[:colorbar] = KW(:title => sp[:colorbar_title])
clims = sp[:clims]
if is_2tuple(clims)
d_out[:zmin], d_out[:zmax] = clims
end
hasline = st in (:path, :path3d)
# set the "type"
if st in (:path, :scatter, :scattergl, :straightline, :path3d, :scatter3d)
return plotly_series_segments(series, d_out, x, y, z)
if st in (:path, :scatter, :scattergl)
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
if series[:fillrange] == true || series[:fillrange] == 0
d_out[:fill] = "tozeroy"
d_out[:fillcolor] = rgba_string(series[:fillcolor])
elseif !(series[:fillrange] in (false, nothing))
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(series[:fillrange])")
end
d_out[:x], d_out[:y] = x, y
elseif st == :bar
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
d_out[:orientation] = isvertical(series) ? "v" : "h"
elseif st == :heatmap
x = heatmap_edges(x, sp[:xaxis][:scale])
y = heatmap_edges(y, sp[:yaxis][:scale])
d_out[:type] = "heatmap"
d_out[:x], d_out[:y], d_out[:z] = x, y, z
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
d_out[:showscale] = hascolorbar(sp)
elseif st == :contour
d_out[:type] = "contour"
d_out[:x], d_out[:y], d_out[:z] = x, y, z
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
# d_out[:showscale] = series[:colorbar] != :none
d_out[:ncontours] = series[:levels]
d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines")
d_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
d_out[:showscale] = hascolorbar(sp)
elseif st in (:surface, :wireframe)
d_out[:type] = "surface"
d_out[:x], d_out[:y], d_out[:z] = x, y, z
if st == :wireframe
d_out[:hidesurface] = true
wirelines = KW(
:show => true,
:color => rgba_string(plot_color(series[:linecolor], series[:linealpha])),
:highlightwidth => series[:linewidth],
)
d_out[:contours] = KW(:x => wirelines, :y => wirelines, :z => wirelines)
d_out[:showscale] = false
else
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
d_out[:opacity] = series[:fillalpha]
if series[:fill_z] != nothing
d_out[:surfacecolor] = plotly_surface_data(series, series[:fill_z])
end
d_out[:showscale] = hascolorbar(sp)
end
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
elseif st == :pie
d_out[:type] = "pie"
@@ -630,6 +402,16 @@ function plotly_series(plt::Plot, series::Series)
d_out[:values] = y
d_out[:hoverinfo] = "label+percent+name"
elseif st in (:path3d, :scatter3d)
d_out[:type] = "scatter3d"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
d_out[:x], d_out[:y] = x, y
d_out[:z] = plotly_data(series[:z])
else
warn("Plotly: seriestype $st isn't supported.")
return KW()
@@ -637,235 +419,98 @@ function plotly_series(plt::Plot, series::Series)
# add "marker"
if hasmarker
inds = eachindex(x)
d_out[:marker] = KW(
:symbol => get(_plotly_markers, series[:markershape], string(series[:markershape])),
# :opacity => series[:markeralpha],
:size => 2 * _cycle(series[:markersize], inds),
:color => rgba_string.(plot_color.(get_markercolor.(series, inds), get_markeralpha.(series, inds))),
:size => 2 * series[:markersize],
# :color => rgba_string(series[:markercolor]),
:line => KW(
:color => rgba_string.(plot_color.(get_markerstrokecolor.(series, inds), get_markerstrokealpha.(series, inds))),
:width => _cycle(series[:markerstrokewidth], inds),
:color => rgba_string(series[:markerstrokecolor]),
:width => series[:markerstrokewidth],
),
)
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
d_out[:marker][:color] = if series[:marker_z] == nothing
rgba_string(series[:markercolor])
else
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
grad = series[:markercolor]
zmin, zmax = extrema(series[:marker_z])
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]]
end
end
# add "line"
if hasline
d_out[:line] = KW(
:color => rgba_string(series[:linecolor]),
:width => series[:linewidth],
:shape => if st == :steppre
"vh"
elseif st == :steppost
"hv"
else
"linear"
end,
:dash => string(series[:linestyle]),
# :dash => "solid",
)
end
plotly_polar!(d_out, series)
plotly_hover!(d_out, series[:hover])
return [d_out]
[d_out]
end
function plotly_series_shapes(plt::Plot, series::Series)
segments = iter_segments(series)
d_outs = Vector{KW}(length(segments))
d_outs = []
# TODO: create a d_out for each polygon
# x, y = series[:x], series[:y]
# these are the axes that the series should be mapped to
x_idx, y_idx = plotly_link_indicies(plt, series[:subplot])
d_base = KW(
:xaxis => "x$(x_idx)",
:yaxis => "y$(y_idx)",
:name => series[:label],
:legendgroup => series[:label],
)
x, y = (plotly_data(series, letter, data)
for (letter, data) in zip((:x, :y), shape_data(series))
)
for (i,rng) in enumerate(segments)
spidx = plotly_subplot_index(series[:subplot])
base_d = KW()
base_d[:xaxis] = "x$spidx"
base_d[:yaxis] = "y$spidx"
base_d[:name] = series[:label]
# base_d[:legendgroup] = series[:label]
x, y = plotly_data(series[:x]), plotly_data(series[:y])
for (i,rng) in enumerate(iter_segments(x,y))
length(rng) < 2 && continue
# to draw polygons, we actually draw lines with fill
d_out = merge(d_base, KW(
d_out = merge(base_d, KW(
:type => "scatter",
:mode => "lines",
:x => vcat(x[rng], x[rng[1]]),
:y => vcat(y[rng], y[rng[1]]),
:fill => "tozeroy",
:fillcolor => rgba_string(plot_color(get_fillcolor(series, i), get_fillalpha(series, i))),
:fillcolor => rgba_string(cycle(series[:fillcolor], i)),
))
if series[:markerstrokewidth] > 0
d_out[:line] = KW(
:color => rgba_string(plot_color(get_linecolor(series, i), get_linealpha(series, i))),
:width => get_linewidth(series, i),
:dash => string(get_linestyle(series, 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))
d_outs[i] = d_out
end
if series[:fill_z] != nothing
push!(d_outs, plotly_colorbar_hack(series, d_base, :fill))
elseif series[:line_z] != nothing
push!(d_outs, plotly_colorbar_hack(series, d_base, :line))
elseif series[:marker_z] != nothing
push!(d_outs, plotly_colorbar_hack(series, d_base, :marker))
plotly_hover!(d_out, cycle(series[:hover], i))
push!(d_outs, d_out)
end
d_outs
end
function plotly_series_segments(series::Series, d_base::KW, x, y, z)
st = series[:seriestype]
sp = series[:subplot]
isscatter = st in (:scatter, :scatter3d, :scattergl)
hasmarker = isscatter || series[:markershape] != :none
hasline = st in (:path, :path3d, :straightline)
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
segments = iter_segments(series)
d_outs = Vector{KW}((hasfillrange ? 2 : 1 ) * length(segments))
for (i,rng) in enumerate(segments)
!isscatter && length(rng) < 2 && continue
d_out = deepcopy(d_base)
d_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
d_out[:legendgroup] = series[:label]
# set the type
if st in (:path, :scatter, :scattergl, :straightline)
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
if series[:fillrange] == true || series[:fillrange] == 0 || isa(series[:fillrange], Tuple)
d_out[:fill] = "tozeroy"
d_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, i), get_fillalpha(series, i)))
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real}, Real}
d_out[:fill] = "tonexty"
d_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, i), get_fillalpha(series, i)))
elseif !(series[:fillrange] in (false, nothing))
warn("fillrange ignored... plotly only supports filling to zero and to a vector of values. fillrange: $(series[:fillrange])")
end
d_out[:x], d_out[:y] = x[rng], y[rng]
elseif st in (:path3d, :scatter3d)
d_out[:type] = "scatter3d"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
d_out[:x], d_out[:y], d_out[:z] = x[rng], y[rng], z[rng]
end
# add "marker"
if hasmarker
d_out[:marker] = KW(
:symbol => get(_plotly_markers, _cycle(series[:markershape], i), string(_cycle(series[:markershape], i))),
# :opacity => series[:markeralpha],
:size => 2 * _cycle(series[:markersize], i),
:color => rgba_string(plot_color(get_markercolor(series, i), get_markeralpha(series, i))),
:line => KW(
:color => rgba_string(plot_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i))),
:width => _cycle(series[:markerstrokewidth], i),
),
)
end
# add "line"
if hasline
d_out[:line] = KW(
:color => rgba_string(plot_color(get_linecolor(series, i), get_linealpha(series, i))),
:width => get_linewidth(series, i),
:shape => if st == :steppre
"vh"
elseif st == :steppost
"hv"
else
"linear"
end,
:dash => string(get_linestyle(series, i)),
)
end
plotly_polar!(d_out, series)
plotly_hover!(d_out, _cycle(series[:hover], rng))
if hasfillrange
# if hasfillrange is true, return two dictionaries (one for original
# series, one for series being filled to) instead of one
d_out_fillrange = deepcopy(d_out)
d_out_fillrange[:showlegend] = false
# if fillrange is provided as real or tuple of real, expand to array
if typeof(series[:fillrange]) <: Real
series[:fillrange] = fill(series[:fillrange], length(rng))
elseif typeof(series[:fillrange]) <: Tuple
f1 = typeof(series[:fillrange][1]) <: Real ? fill(series[:fillrange][1], length(rng)) : series[:fillrange][1][rng]
f2 = typeof(series[:fillrange][2]) <: Real ? fill(series[:fillrange][2], length(rng)) : series[:fillrange][2][rng]
series[:fillrange] = (f1, f2)
end
if isa(series[:fillrange], AbstractVector)
d_out_fillrange[:y] = series[:fillrange][rng]
delete!(d_out_fillrange, :fill)
delete!(d_out_fillrange, :fillcolor)
else
# if fillrange is a tuple with upper and lower limit, d_out_fillrange
# is the series that will do the filling
fillrng = Tuple(series[:fillrange][i][rng] for i in 1:2)
d_out_fillrange[:x], d_out_fillrange[:y] = concatenate_fillrange(x[rng], fillrng)
d_out_fillrange[:line][:width] = 0
delete!(d_out, :fill)
delete!(d_out, :fillcolor)
end
d_outs[(2 * i - 1):(2 * i)] = [d_out_fillrange, d_out]
else
d_outs[i] = d_out
end
end
if series[:line_z] != nothing
push!(d_outs, plotly_colorbar_hack(series, d_base, :line))
elseif series[:fill_z] != nothing
push!(d_outs, plotly_colorbar_hack(series, d_base, :fill))
elseif series[:marker_z] != nothing
push!(d_outs, plotly_colorbar_hack(series, d_base, :marker))
end
d_outs
end
function plotly_colorbar_hack(series::Series, d_base::KW, sym::Symbol)
d_out = deepcopy(d_base)
cmin, cmax = get_clims(series[:subplot])
d_out[:showlegend] = false
d_out[:type] = is3d(series) ? :scatter3d : :scatter
d_out[:hoverinfo] = :none
d_out[:mode] = :markers
d_out[:x], d_out[:y] = [series[:x][1]], [series[:y][1]]
if is3d(series)
d_out[:z] = [series[:z][1]]
end
# 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)
d_out[:marker] = KW(
:size => 0,
:opacity => 0,
:color => [0.5],
:cmin => cmin,
:cmax => cmax,
:colorscale => plotly_colorscale(series[Symbol("$(sym)color")], 1),
:showscale => hascolorbar(series[:subplot]),
)
return d_out
end
function plotly_polar!(d_out::KW, series::Series)
# convert polar plots x/y to theta/radius
if ispolar(series[:subplot])
theta, r = filter_radial_data(pop!(d_out, :x), pop!(d_out, :y), axis_limits(series[:subplot][:yaxis]))
d_out[:t] = rad2deg.(theta)
d_out[:r] = r
d_out[:t] = rad2deg(pop!(d_out, :x))
d_out[:r] = pop!(d_out, :y)
end
end
@@ -880,25 +525,19 @@ function plotly_hover!(d_out::KW, hover)
end
# get a list of dictionaries, each representing the series params
function plotly_series(plt::Plot)
function plotly_series_json(plt::Plot)
slist = []
for series in plt.series_list
append!(slist, plotly_series(plt, series))
end
slist
JSON.json(slist)
# JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
end
# get json string for a list of dictionaries, each representing the series params
plotly_series_json(plt::Plot) = JSON.json(plotly_series(plt))
# ----------------------------------------------------------------
const _use_remote = Ref(false)
function html_head(plt::Plot{PlotlyBackend})
jsfilename = _use_remote[] ? _plotly_js_path_remote : ("file://" * _plotly_js_path)
# "<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
"<script src=\"$jsfilename\"></script>"
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
end
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
@@ -928,7 +567,11 @@ end
# ----------------------------------------------------------------
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
writemime_png_from_html(io, plt)
end
function _writemime(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
write(io, html_head(plt) * html_body(plt))
end
+14 -70
View File
@@ -1,32 +1,16 @@
@require Revise begin
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "plotlyjs.jl"))
end
# https://github.com/spencerlyon2/PlotlyJS.jl
const _plotlyjs_attr = _plotly_attr
const _plotlyjs_seriestype = _plotly_seriestype
const _plotlyjs_style = _plotly_style
const _plotlyjs_marker = _plotly_marker
const _plotlyjs_scale = _plotly_scale
supported_args(::PlotlyJSBackend) = supported_args(PlotlyBackend())
supported_types(::PlotlyJSBackend) = supported_types(PlotlyBackend())
supported_styles(::PlotlyJSBackend) = supported_styles(PlotlyBackend())
supported_markers(::PlotlyJSBackend) = supported_markers(PlotlyBackend())
supported_scales(::PlotlyJSBackend) = supported_scales(PlotlyBackend())
is_subplot_supported(::PlotlyJSBackend) = true
is_string_supported(::PlotlyJSBackend) = true
# --------------------------------------------------------------------------------------
function add_backend_string(::PlotlyJSBackend)
"""
if !Plots.is_installed("PlotlyJS")
Pkg.add("PlotlyJS")
end
if !Plots.is_installed("Rsvg")
Pkg.add("Rsvg")
end
import Blink
Blink.AtomShell.install()
"""
end
function _initialize_backend(::PlotlyJSBackend; kw...)
@eval begin
import PlotlyJS
@@ -43,11 +27,7 @@ end
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
else
PlotlyJS.plot()
end
PlotlyJS.plot()
end
@@ -63,15 +43,10 @@ end
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
z = series[:z]
if z != nothing
kw[:z] = (isa(z,Surface) ? transpose_z(series, series[:z].surf, false) : z,)
end
PlotlyJS.restyle!(
plt.o,
findfirst(plt.series_list, series),
kw
KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
)
end
@@ -88,47 +63,16 @@ end
# ----------------------------------------------------------------
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
if isijulia() && !_use_remote[]
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
else
show(io, MIME("text/html"), plt.o)
end
function _writemime(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
writemime(io, MIME("text/html"), plt.o)
end
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
tmpfn = tempname() * "." * ext
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
tmpfn = tempname() * ".png"
PlotlyJS.savefig(plt.o, tmpfn)
write(io, read(open(tmpfn)))
end
_show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "svg")
_show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "png")
_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
end
@require WebIO begin
function WebIO.render(plt::Plot{PlotlyJSBackend})
prepare_output(plt)
WebIO.render(plt.o)
end
end
function closeall(::PlotlyJSBackend)
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
close(current().o)
end
display(plt.o)
end
+297 -470
View File
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@
# https://github.com/tbreloff/Qwt.jl
supported_attrs(::QwtBackend) = merge_with_base_supported([
supported_args(::QwtBackend) = merge_with_base_supported([
:annotations,
:linecolor,
:fillrange,
@@ -39,7 +39,7 @@ end
# -------------------------------
const _qwtAliases = KW(
@compat const _qwtAliases = KW(
:bins => :heatmap_n,
:fillrange => :fillto,
:linewidth => :width,
@@ -133,7 +133,7 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
w = plt.o.widget
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
if typeof(lims) <: Union{Tuple,AVec} && length(lims) == 2
if typeof(lims) <: @compat(Union{Tuple,AVec}) && length(lims) == 2
if isx
plt.o.autoscale_x = false
else
@@ -211,14 +211,14 @@ function createQwtAnnotation(plt::Plot, x, y, val::PlotText)
marker[:attach](plt.o.widget)
end
function createQwtAnnotation(plt::Plot, x, y, val::AbstractString)
function createQwtAnnotation(plt::Plot, x, y, val::@compat(AbstractString))
marker = Qwt.QWT.QwtPlotMarker()
marker[:setValue](x, y)
marker[:setLabel](Qwt.QWT.QwtText(val))
marker[:attach](plt.o.widget)
end
function _add_annotations(plt::Plot{QwtBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
function _add_annotations{X,Y,V}(plt::Plot{QwtBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createQwtAnnotation(plt, ann...)
end
@@ -233,7 +233,7 @@ function getxy(plt::Plot{QwtBackend}, i::Int)
series.x, series.y
end
function setxy!(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
function setxy!{X,Y}(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer)
series = plt.o.lines[i]
series.x, series.y = xy
plt
@@ -242,6 +242,8 @@ end
# -------------------------------
# savepng(::QwtBackend, plt::AbstractPlot, fn::@compat(AbstractString), args...) = Qwt.savepng(plt.o, fn)
# -------------------------------
# # create the underlying object (each backend will do this differently)
@@ -280,13 +282,13 @@ end
# ----------------------------------------------------------------
function Base.show(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
Qwt.refresh(plt.o)
Qwt.savepng(plt.o, "/tmp/dfskjdhfkh.png")
write(io, readall("/tmp/dfskjdhfkh.png"))
end
# function Base.show(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
# function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
# for plt in subplt.plts
# Qwt.refresh(plt.o)
# end
+1 -1
View File
@@ -67,7 +67,7 @@ end
# "image/png" => "png",
# "application/postscript" => "ps",
# "image/svg+xml" => "svg"
function _show(io::IO, ::MIME"image/png", plt::Plot{[PkgName]Backend})
function _writemime(io::IO, ::MIME"image/png", plt::Plot{[PkgName]Backend})
end
# Display/show the plot (open a GUI window, or browser page, for example).
+17 -78
View File
@@ -1,11 +1,7 @@
# https://github.com/Evizero/UnicodePlots.jl
@require Revise begin
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "unicodeplots.jl"))
end
const _unicodeplots_attr = merge_with_base_supported([
supported_args(::UnicodePlotsBackend) = merge_with_base_supported([
:label,
:legend,
:seriescolor,
@@ -16,30 +12,21 @@ const _unicodeplots_attr = merge_with_base_supported([
:title,
:guide, :lims,
])
const _unicodeplots_seriestype = [
:path, :scatter, :straightline,
# :bar,
:shape,
:histogram2d,
:spy
supported_types(::UnicodePlotsBackend) = [
:path, :scatter,
:histogram2d
]
const _unicodeplots_style = [:auto, :solid]
const _unicodeplots_marker = [:none, :auto, :circle]
const _unicodeplots_scale = [:identity]
supported_styles(::UnicodePlotsBackend) = [:auto, :solid]
supported_markers(::UnicodePlotsBackend) = [:none, :auto, :circle]
supported_scales(::UnicodePlotsBackend) = [:identity]
is_subplot_supported(::UnicodePlotsBackend) = true
# don't warn on unsupported... there's just too many warnings!!
warnOnUnsupported_args(::UnicodePlotsBackend, d::KW) = nothing
warnOnUnsupported_args(pkg::UnicodePlotsBackend, d::KW) = nothing
# --------------------------------------------------------------------------------------
function add_backend_string(::UnicodePlotsBackend)
"""
Pkg.add("UnicodePlots")
Pkg.build("UnicodePlots")
"""
end
function _initialize_backend(::UnicodePlotsBackend; kw...)
@eval begin
import UnicodePlots
@@ -49,18 +36,6 @@ end
# -------------------------------
const _canvas_type = Ref(:auto)
function _canvas_map()
KW(
:braille => UnicodePlots.BrailleCanvas,
:ascii => UnicodePlots.AsciiCanvas,
:block => UnicodePlots.BlockCanvas,
:dot => UnicodePlots.DotCanvas,
:density => UnicodePlots.DensityCanvas,
)
end
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
function rebuildUnicodePlot!(plt::Plot, width, height)
@@ -82,33 +57,7 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
ct = _canvas_type[]
canvas_type = if ct == :auto
isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
else
_canvas_map()[ct]
end
# special handling for spy
if length(sp.series_list) == 1
series = sp.series_list[1]
if series[:seriestype] == :spy
push!(plt.o, UnicodePlots.spy(
series[:z].surf,
width = width,
height = height,
title = sp[:title],
canvas = canvas_type
))
continue
end
end
# # make it a bar canvas if plotting bar
# if any(series -> series[:seriestype] == :bar, series_list(sp))
# canvas_type = UnicodePlots.BarplotGraphics
# end
canvas_type = isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
o = UnicodePlots.Plot(x, y, canvas_type;
width = width,
height = height,
@@ -142,39 +91,28 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
return
end
if st in (:path, :straightline)
if st == :path
func = UnicodePlots.lineplot!
elseif st == :scatter || d[:markershape] != :none
func = UnicodePlots.scatterplot!
# elseif st == :bar
# func = UnicodePlots.barplot!
elseif st == :shape
func = UnicodePlots.lineplot!
else
error("Linestyle $st not supported by UnicodePlots")
end
# get the series data and label
x, y = if st == :straightline
straightline_data(d)
elseif st == :shape
shape_data(series)
else
[collect(float(d[s])) for s in (:x, :y)]
end
x, y = [collect(float(d[s])) for s in (:x, :y)]
label = addlegend ? d[:label] : ""
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
# add the series
x, y = Plots.unzip(collect(Base.Iterators.filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
func(o, x, y; color = color, name = label)
end
# -------------------------------
# since this is such a hack, it's only callable using `png`... should error during normal `show`
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
fn = addExtension(fn, "png")
@@ -183,7 +121,7 @@ function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
gui(plt)
# @osx_only begin
@static if is_apple()
@compat @static if is_apple()
# BEGIN HACK
# wait while the plot gets drawn
@@ -210,9 +148,9 @@ function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend})
rebuildUnicodePlot!(plt, div(w, 10), div(h, 20))
end
function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
function _writemime(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
unicodeplots_rebuild(plt)
foreach(x -> show(io, x), plt.o)
map(show, plt.o)
nothing
end
@@ -222,3 +160,4 @@ function _display(plt::Plot{UnicodePlotsBackend})
map(show, plt.o)
nothing
end
+6 -9
View File
@@ -2,9 +2,7 @@
# NOTE: backend should implement `html_body` and `html_head`
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
@require Revise begin
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "web.jl"))
end
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr, :window_title, "Plots.jl"))
"""
@@ -12,7 +10,6 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr
<html>
<head>
<title>$title</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
$(html_head(plt))
</head>
<body>
@@ -23,14 +20,14 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr
end
function open_browser_window(filename::AbstractString)
@static if is_apple()
@compat @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
@compat @static if is_linux()
return run(`xdg-open $(filename)`)
end
@static if is_windows()
return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`)
@compat @static if is_windows()
return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
end
warn("Unknown OS... cannot open browser window.")
end
@@ -54,7 +51,7 @@ function html_to_png(html_fn, png_fn, w, h)
run(`wkhtmltoimage -f png -q --width $w --height $h --disable-smart-width $html_fn $png_fn`)
end
function show_png_from_html(io::IO, plt::AbstractPlot)
function writemime_png_from_html(io::IO, plt::AbstractPlot)
# write html to a temporary file
html_fn = write_temp_html(plt)
@@ -3,7 +3,7 @@
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
supported_attrs(::WinstonBackend) = merge_with_base_supported([
supported_args(::WinstonBackend) = merge_with_base_supported([
:annotations,
:linecolor,
:fillrange,
@@ -44,13 +44,13 @@ end
## dictionaries for conversion of Plots.jl names to Winston ones.
const winston_linestyle = KW(:solid=>"solid",
@compat const winston_linestyle = KW(:solid=>"solid",
:dash=>"dash",
:dot=>"dotted",
:dashdot=>"dotdashed"
)
const winston_marker = KW(:none=>".",
@compat const winston_marker = KW(:none=>".",
:rect => "square",
:circle=>"circle",
:diamond=>"diamond",
@@ -185,7 +185,7 @@ end
# ----------------------------------------------------------------
const _winstonNames = KW(
@compat const _winstonNames = KW(
:xlims => :xrange,
:ylims => :yrange,
:xscale => :xlog,
@@ -213,11 +213,11 @@ end
# ----------------------------------------------------------------
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::AbstractString)
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::@compat(AbstractString))
Winston.text(x, y, val)
end
function _add_annotations(plt::Plot{WinstonBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
function _add_annotations{X,Y,V}(plt::Plot{WinstonBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createWinstonAnnotationObject(plt, ann...)
end
@@ -238,10 +238,10 @@ function addWinstonLegend(plt::Plot, wplt)
end
end
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{WinstonBackend})
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{WinstonBackend})
window, canvas, wplt = getWinstonItems(plt)
addWinstonLegend(plt, wplt)
show(io, "image/png", wplt)
writemime(io, "image/png", wplt)
end
+89 -330
View File
@@ -1,7 +1,7 @@
const P2 = FixedSizeArrays.Vec{2,Float64}
const P3 = FixedSizeArrays.Vec{3,Float64}
typealias P2 FixedSizeArrays.Vec{2,Float64}
typealias P3 FixedSizeArrays.Vec{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))
@@ -11,7 +11,7 @@ compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : ang
# -------------------------------------------------------------
struct Shape
immutable Shape
x::Vector{Float64}
y::Vector{Float64}
# function Shape(x::AVec, y::AVec)
@@ -22,33 +22,22 @@ struct Shape
# end
# end
end
"""
Shape(x, y)
Shape(vertices)
Construct a polygon to be plotted
"""
Shape(verts::AVec) = Shape(unzip(verts)...)
Shape(s::Shape) = deepcopy(s)
get_xs(shape::Shape) = shape.x
get_ys(shape::Shape) = shape.y
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
#deprecated
@deprecate shape_coords coords
"return the vertex points from a Shape or Segments object"
function coords(shape::Shape)
function shape_coords(shape::Shape)
shape.x, shape.y
end
function coords(shapes::AVec{Shape})
function shape_coords(shapes::AVec{Shape})
length(shapes) == 0 && return zeros(0), zeros(0)
xs = map(get_xs, shapes)
ys = map(get_ys, shapes)
x, y = map(copy, coords(shapes[1]))
x, y = map(copy, shape_coords(shapes[1]))
for shape in shapes[2:end]
nanappend!(x, shape.x)
nanappend!(y, shape.y)
@@ -58,7 +47,7 @@ end
"get an array of tuples of points on a circle with radius `r`"
function partialcircle(start_θ, end_θ, n = 20, r=1)
Tuple{Float64,Float64}[(r*cos(u),r*sin(u)) for u in linspace(start_θ, end_θ, n)]
@compat(Tuple{Float64,Float64})[(r*cos(u),r*sin(u)) for u in linspace(start_θ, end_θ, n)]
end
"interleave 2 vectors into each other (like a zipper's teeth)"
@@ -83,13 +72,13 @@ function makestar(n; offset = -0.5, radius = 1.0)
z2 = z1 + π / (n)
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
Shape(weave(outercircle, innercircle))
Shape(weave(outercircle, innercircle)[1:end-2])
end
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
function makeshape(n; offset = -0.5, radius = 1.0)
z = offset * π
Shape(partialcircle(z, z + 2π, n+1, radius))
Shape(partialcircle(z, z + 2π, n+1, radius)[1:end-1])
end
@@ -99,7 +88,7 @@ function makecross(; offset = -0.5, radius = 1.0)
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
Shape(weave(outercircle, innercircle,
ordering=Vector[outercircle,innercircle,outercircle]))
ordering=Vector[outercircle,innercircle,outercircle])[1:end-2])
end
@@ -121,8 +110,6 @@ const _shape_keys = Symbol[
:xcross,
:utriangle,
:dtriangle,
:rtriangle,
:ltriangle,
:pentagon,
:heptagon,
:octagon,
@@ -132,18 +119,14 @@ const _shape_keys = Symbol[
:star8,
:vline,
:hline,
:+,
:x,
]
const _shapes = KW(
:circle => makeshape(20),
:rect => makeshape(4, offset=-0.25),
:diamond => makeshape(4),
:utriangle => makeshape(3, offset=0.5),
:dtriangle => makeshape(3, offset=-0.5),
:rtriangle => makeshape(3, offset=0.0),
:ltriangle => makeshape(3, offset=1.0),
:utriangle => makeshape(3),
:dtriangle => makeshape(3, offset=0.5),
:pentagon => makeshape(5),
:hexagon => makeshape(6),
:heptagon => makeshape(7),
@@ -158,15 +141,12 @@ for n in [4,5,6,7,8]
_shapes[Symbol("star$n")] = makestar(n)
end
Shape(k::Symbol) = deepcopy(_shapes[k])
# -----------------------------------------------------------------------
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
"return the centroid of a Shape"
function center(shape::Shape)
x, y = coords(shape)
x, y = shape_coords(shape)
n = length(x)
A, Cx, Cy = 0.0, 0.0, 0.0
for i=1:n
@@ -183,8 +163,8 @@ function center(shape::Shape)
Cx / 6A, Cy / 6A
end
function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
sx, sy = coords(shape)
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
sx, sy = shape_coords(shape)
cx, cy = c
for i=1:length(sx)
sx[i] = (sx[i] - cx) * x + cx
@@ -193,14 +173,13 @@ function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
shape
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))
shapecopy = deepcopy(shape)
scale!(shapecopy, x, y, c)
scale!(shape, x, y, c)
end
"translate a Shape in space"
function translate!(shape::Shape, x::Real, y::Real = x)
sx, sy = coords(shape)
sx, sy = shape_coords(shape)
for i=1:length(sx)
sx[i] += x
sy[i] += y
@@ -210,7 +189,7 @@ end
function translate(shape::Shape, x::Real, y::Real = x)
shapecopy = deepcopy(shape)
translate!(shapecopy, x, y)
translate!(shape, x, y)
end
function rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
@@ -227,17 +206,15 @@ function rotate(x::Real, y::Real, θ::Real, c = center(shape))
end
function rotate!(shape::Shape, Θ::Real, c = center(shape))
x, y = coords(shape)
x, y = shape_coords(shape)
cx, cy = c
for i=1:length(x)
xi = rotate_x(x[i], y[i], Θ, cx, cy)
yi = rotate_y(x[i], y[i], Θ, cx, cy)
x[i], y[i] = xi, yi
x[i] = rotate_x(x[i], y[i], Θ, cx, cy)
y[i] = rotate_y(x[i], y[i], Θ, cx, cy)
end
shape
end
"rotate an object in space"
function rotate(shape::Shape, Θ::Real, c = center(shape))
shapecopy = deepcopy(shape)
rotate!(shapecopy, Θ, c)
@@ -246,7 +223,7 @@ end
# -----------------------------------------------------------------------
mutable struct Font
immutable Font
family::AbstractString
pointsize::Int
halign::Symbol
@@ -259,7 +236,7 @@ end
function font(args...)
# defaults
family = "sans-serif"
family = "Helvetica"
pointsize = 14
halign = :hcenter
valign = :vcenter
@@ -303,79 +280,40 @@ function font(args...)
Font(family, pointsize, halign, valign, rotation, color)
end
function scalefontsize(k::Symbol, factor::Number)
f = default(k)
f = round(Int, factor * f)
default(k, f)
end
"""
scalefontsizes(factor::Number)
Scales all **current** font sizes by `factor`. For example `scalefontsizes(1.1)` increases all current font sizes by 10%. To reset to initial sizes, use `scalefontsizes()`
"""
function scalefontsizes(factor::Number)
for k in (:titlefontsize, :guidefontsize, :tickfontsize, :legendfontsize)
scalefontsize(k, factor)
end
end
"""
scalefontsizes()
Resets font sizes to initial default values.
"""
function scalefontsizes()
for k in (:titlefontsize, :guidefontsize, :tickfontsize, :legendfontsize)
f = default(k)
if k in keys(_initial_fontsizes)
factor = f / _initial_fontsizes[k]
scalefontsize(k, 1.0/factor)
end
end
end
"Wrap a string with font info"
struct PlotText
immutable PlotText
str::AbstractString
font::Font
end
PlotText(str) = PlotText(string(str), font())
"""
text(string, args...)
Create a PlotText object wrapping a string with font info, for plot annotations
"""
text(t::PlotText) = t
text(t::PlotText, font::Font) = PlotText(t.str, font)
text(str::AbstractString, f::Font) = PlotText(str, f)
function text(str, args...)
PlotText(string(str), font(args...))
end
Base.length(t::PlotText) = length(t.str)
annotations(::Void) = []
annotations(anns::AVec) = anns
annotations(anns) = Any[anns]
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------
struct Stroke
immutable Stroke
width
color
alpha
style
end
"""
stroke(args...; alpha = nothing)
Define the properties of the stroke used in plotting lines
"""
function stroke(args...; alpha = nothing)
width = 1
color = :black
style = :solid
width = nothing
color = nothing
style = nothing
for arg in args
T = typeof(arg)
@@ -402,15 +340,15 @@ function stroke(args...; alpha = nothing)
end
struct Brush
immutable Brush
size # fillrange, markersize, or any other sizey attribute
color
alpha
end
function brush(args...; alpha = nothing)
size = 1
color = :black
size = nothing
color = nothing
for arg in args
T = typeof(arg)
@@ -435,173 +373,22 @@ end
# -----------------------------------------------------------------------
mutable struct SeriesAnnotations
strs::AbstractVector # the labels/names
font::Font
baseshape::Nullable
scalefactor::Tuple
end
function series_annotations(strs::AbstractVector, args...)
fnt = font()
shp = Nullable{Any}()
scalefactor = (1,1)
for arg in args
if isa(arg, Shape) || (isa(arg, AbstractVector) && eltype(arg) == Shape)
shp = Nullable(arg)
elseif isa(arg, Font)
fnt = arg
elseif isa(arg, Symbol) && haskey(_shapes, arg)
shp = _shapes[arg]
elseif isa(arg, Number)
scalefactor = (arg,arg)
elseif is_2tuple(arg)
scalefactor = arg
else
warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
end
end
# if scalefactor != 1
# for s in get(shp)
# scale!(s, scalefactor, scalefactor, (0,0))
# end
# end
SeriesAnnotations(strs, fnt, shp, scalefactor)
end
series_annotations(anns::SeriesAnnotations) = anns
series_annotations(::Void) = nothing
function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
anns = series[:series_annotations]
# msw,msh = anns.scalefactor
# ms = series[:markersize]
# msw,msh = if isa(ms, AbstractVector)
# 1,1
# elseif is_2tuple(ms)
# ms
# else
# ms,ms
# end
# @show msw msh
if anns != nothing && !isnull(anns.baseshape)
# we use baseshape to overwrite the markershape attribute
# with a list of custom shapes for each
msw,msh = anns.scalefactor
msize = Float64[]
shapes = Shape[begin
str = _cycle(anns.strs,i)
# get the width and height of the string (in mm)
sw, sh = text_size(str, anns.font.pointsize)
# how much to scale the base shape?
# note: it's a rough assumption that the shape fills the unit box [-1,-1,1,1],
# so we scale the length-2 shape by 1/2 the total length
scalar = (backend() == PyPlotBackend() ? 1.7 : 1.0)
xscale = 0.5to_pixels(sw) * scalar
yscale = 0.5to_pixels(sh) * scalar
# we save the size of the larger direction to the markersize list,
# 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)
shape = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
end for i=1:length(anns.strs)]
series[:markershape] = shapes
series[:markersize] = msize
end
return
end
mutable struct EachAnn
anns
x
y
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)
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)
end
annotations(::Void) = []
annotations(anns::AVec) = anns
annotations(anns) = Any[anns]
annotations(sa::SeriesAnnotations) = sa
# Expand arrays of coordinates, positions and labels into induvidual annotations
# and make sure labels are of type PlotText
function process_annotation(sp::Subplot, xs, ys, labs, font = font())
anns = []
labs = makevec(labs)
for i in 1:max(length(xs), length(ys), length(labs))
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
if lab == :auto
alphabet = "abcdefghijklmnopqrstuvwxyz"
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
else
push!(anns, (x, y, isa(lab, PlotText) ? lab : text(lab, font)))
end
end
anns
end
function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol}, labs, font = font())
anns = []
positions, labs = makevec(positions), makevec(labs)
for i in 1:max(length(positions), length(labs))
pos, lab = _cycle(positions, i), _cycle(labs, i)
pos = get(_positionAliases, pos, pos)
if lab == :auto
alphabet = "abcdefghijklmnopqrstuvwxyz"
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
else
push!(anns, (pos, isa(lab, PlotText) ? lab : text(lab, font)))
end
end
anns
end
# Give each annotation coordinates based on specified position
function locate_annotation(sp::Subplot, pos::Symbol, lab::PlotText)
position_multiplier = Dict{Symbol, Tuple{Float64,Float64}}(
:topleft => (0.1, 0.9),
:topcenter => (0.5, 0.9),
:topright => (0.9, 0.9),
:bottomleft => (0.1, 0.1),
:bottomcenter => (0.5, 0.1),
:bottomright => (0.9, 0.1),
)
xmin, xmax = ignorenan_extrema(sp[:xaxis])
ymin, ymax = ignorenan_extrema(sp[:yaxis])
x, y = (xmin, ymin).+ position_multiplier[pos].* (xmax - xmin, ymax - ymin)
(x, y, lab)
end
locate_annotation(sp::Subplot, x, y, label::PlotText) = (x, y, label)
# -----------------------------------------------------------------------
"type which represents z-values for colors and sizes (and anything else that might come up)"
struct ZValues
immutable ZValues
values::Vector{Float64}
zrange::Tuple{Float64,Float64}
end
function zvalues(values::AVec{T}, zrange::Tuple{T,T} = (ignorenan_minimum(values), ignorenan_maximum(values))) where T<:Real
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"
struct Surface{M<:AMat} <: AbstractSurface
immutable Surface{M<:AMat} <: AbstractSurface
surf::M
end
@@ -612,8 +399,8 @@ 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.eltype(surf::Surface{T}) where {T} = eltype(T)
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)
ex = a[:extrema]
@@ -624,69 +411,28 @@ function expand_extrema!(a::Axis, surf::Surface)
end
"For the case of representing a surface as a function of x/y... can possibly avoid allocations."
struct SurfaceFunction <: AbstractSurface
immutable SurfaceFunction <: AbstractSurface
f::Function
end
# -----------------------------------------------------------------------
# # I don't want to clash with ValidatedNumerics, but this would be nice:
# ..(a::T, b::T) = (a,b)
struct Volume{T}
v::Array{T,3}
x_extents::Tuple{T,T}
y_extents::Tuple{T,T}
z_extents::Tuple{T,T}
end
default_extents(::Type{T}) where {T} = (zero(T), one(T))
function Volume(v::Array{T,3},
x_extents = default_extents(T),
y_extents = default_extents(T),
z_extents = default_extents(T)) where T
Volume(v, x_extents, y_extents, z_extents)
end
Base.Array(vol::Volume) = vol.v
for f in (:length, :size)
@eval Base.$f(vol::Volume, args...) = $f(vol.v, args...)
end
Base.copy(vol::Volume{T}) where {T} = Volume{T}(copy(vol.v), vol.x_extents, vol.y_extents, vol.z_extents)
Base.eltype(vol::Volume{T}) where {T} = T
# -----------------------------------------------------------------------
# style is :open or :closed (for now)
struct Arrow
immutable Arrow
style::Symbol
side::Symbol # :head (default), :tail, or :both
headlength::Float64
headwidth::Float64
end
"""
arrow(args...)
Define arrowheads to apply to lines - args are `style` (`:open` or `:closed`),
`side` (`:head`, `:tail` or `:both`), `headlength` and `headwidth`
"""
function arrow(args...)
style = :simple
side = :head
headlength = 0.3
headwidth = 0.3
setlength = false
for arg in args
T = typeof(arg)
if T == Symbol
if arg in (:head, :tail, :both)
side = arg
else
style = arg
end
style = arg
elseif T <: Number
# first we apply to both, but if there's more, then only change width after the first number
headwidth = Float64(arg)
@@ -700,7 +446,7 @@ function arrow(args...)
warn("Skipped arrow arg $arg")
end
end
Arrow(style, side, headlength, headwidth)
Arrow(style, headlength, headwidth)
end
@@ -719,45 +465,58 @@ function add_arrows(func::Function, x::AVec, y::AVec)
end
end
# -----------------------------------------------------------------------
"Represents data values with formatting that should apply to the tick labels."
struct Formatted{T}
data::T
formatter::Function
end
# -----------------------------------------------------------------------
"create a BezierCurve for plotting"
mutable struct BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
type BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
end
function (bc::BezierCurve)(t::Real)
p = zero(P2)
n = length(bc.control_points)-1
for i in 0:n
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
end
p
@compat function (bc::BezierCurve)(t::Real)
p = zero(P2)
n = length(bc.control_points)-1
for i in 0:n
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
end
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::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
@deprecate curve_points coords
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
# may create a loop if necessary. Assumes the view is [0,1]
function directed_curve(args...; kw...)
error("directed_curve has been moved to PlotRecipes")
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
mn = mean(p, q)
diff = q - p
minx, maxx = minimum(xview), maximum(xview)
miny, maxy = minimum(yview), maximum(yview)
diffpct = P2(diff[1] / (maxx - minx),
diff[2] / (maxy - miny))
# these points give the initial/final "rise"
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
upper_control = p + vertical_offset
lower_control = q - vertical_offset
# try to figure out when to loop around vs just connecting straight
# TODO: choose loop direction based on sign of p[1]??
# x_close_together = abs(diffpct[1]) <= 0.05
p_is_higher = diff[2] <= 0
inside_control_points = if p_is_higher
# add curve points which will create a loop
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
inside_offset = P2(0.3 * (maxx - minx), 0)
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
else
[]
end
function extrema_plus_buffer(v, buffmult = 0.2)
vmin,vmax = ignorenan_extrema(v)
vdiff = vmax-vmin
buffer = vdiff * buffmult
vmin - buffer, vmax + buffer
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
end
+13 -13
View File
@@ -1,5 +1,5 @@
abstract type ColorScheme end
abstract ColorScheme
Base.getindex(scheme::ColorScheme, i::Integer) = getColor(scheme, i)
@@ -34,9 +34,9 @@ getColorVector(scheme::ColorScheme) = [getColor(scheme)]
colorscheme(scheme::ColorScheme) = scheme
colorscheme(s::AbstractString; kw...) = colorscheme(Symbol(s); kw...)
colorscheme(s::Symbol; kw...) = haskey(_gradients, s) ? ColorGradient(s; kw...) : ColorWrapper(convertColor(s); kw...)
colorscheme(s::Symbol, vals::AVec{T}; kw...) where {T<:Real} = ColorGradient(s, vals; kw...)
colorscheme{T<:Real}(s::Symbol, vals::AVec{T}; kw...) = ColorGradient(s, vals; kw...)
colorscheme(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
colorscheme(cs::AVec{T}; kw...) where {T<:Colorant} = ColorGradient(cs; kw...)
colorscheme{T<:Colorant}(cs::AVec{T}; kw...) = ColorGradient(cs; kw...)
colorscheme(f::Function; kw...) = ColorFunction(f; kw...)
colorscheme(v::AVec; kw...) = ColorVector(v; kw...)
colorscheme(m::AMat; kw...) = size(m,1) == 1 ? map(c->colorscheme(c; kw...), m) : [colorscheme(m[:,i]; kw...) for i in 1:size(m,2)]'
@@ -98,7 +98,7 @@ const _gradients = KW(
:lighttest => map(c -> lighten(c, 0.3), _testColors),
)
function register_gradient_colors(name::Symbol, colors::AVec{C}) where C<:Colorant
function register_gradient_colors{C<:Colorant}(name::Symbol, colors::AVec{C})
_gradients[name] = colors
end
@@ -109,11 +109,11 @@ default_gradient() = ColorGradient(:inferno)
# --------------------------------------------------------------
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
struct ColorGradient <: ColorScheme
immutable ColorGradient <: ColorScheme
colors::Vector
values::Vector
function ColorGradient(cs::AVec, vals::AVec{S} = linspace(0, 1, length(cs)); alpha = nothing) where S<:Real
function ColorGradient{S<:Real}(cs::AVec, vals::AVec{S} = linspace(0, 1, length(cs)); alpha = nothing)
if length(cs) == length(vals)
return new(convertColor(cs,alpha), collect(vals))
end
@@ -138,7 +138,7 @@ Base.getindex(cs::ColorGradient, z::Number) = getColorZ(cs, z)
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
function ColorGradient(s::Symbol, vals::AVec{T} = 0:0; kw...) where T<:Real
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:0; kw...)
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
cs = _gradients[s]
if vals == 0:0
@@ -208,7 +208,7 @@ end
# --------------------------------------------------------------
"Wraps a function, taking an index and returning a Colorant"
struct ColorFunction <: ColorScheme
immutable ColorFunction <: ColorScheme
f::Function
end
@@ -217,7 +217,7 @@ getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx)
# --------------------------------------------------------------
"Wraps a function, taking an z-value and returning a Colorant"
struct ColorZFunction <: ColorScheme
immutable ColorZFunction <: ColorScheme
f::Function
end
@@ -226,7 +226,7 @@ getColorZ(scheme::ColorZFunction, z::Real) = scheme.f(z)
# --------------------------------------------------------------
"Wraps a vector of colors... may be vector of Symbol/String/Colorant"
struct ColorVector <: ColorScheme
immutable ColorVector <: ColorScheme
v::Vector{Colorant}
ColorVector(v::AVec; alpha = nothing) = new(convertColor(v,alpha))
end
@@ -238,7 +238,7 @@ getColorVector(scheme::ColorVector) = scheme.v
# --------------------------------------------------------------
"Wraps a single color"
struct ColorWrapper <: ColorScheme
immutable ColorWrapper <: ColorScheme
c::RGBA
ColorWrapper(c::Colorant; alpha = nothing) = new(convertColor(c, alpha))
end
@@ -347,8 +347,8 @@ function get_color_palette(palette, bgcolor::Union{Colorant,ColorWrapper}, numco
RGBA[getColorZ(grad, z) for z in zrng]
end
function get_color_palette(palette::Vector{C},
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer) where C<:Colorant
function get_color_palette{C<:Colorant}(palette::Vector{C},
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
palette
end
+5 -5
View File
@@ -5,21 +5,21 @@
# 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 = 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))
# missing
convertToAnyVector(v::Void, d::KW) = Any[nothing], nothing
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
# fixed number of blank series
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
# numeric vector
convertToAnyVector(v::AVec{T}, d::KW) where {T<:Number} = Any[v], nothing
convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
# string vector
convertToAnyVector(v::AVec{T}, d::KW) where {T<:AbstractString} = Any[v], nothing
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::KW) = Any[v], nothing
function convertToAnyVector(v::AMat, d::KW)
if all3D(d)
@@ -39,7 +39,7 @@ convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# dates
convertToAnyVector(dts::AVec{D}, d::KW) where {D<:Union{Date,DateTime}} = Any[dts], nothing
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
# list of things (maybe other vectors, functions, or something else)
function convertToAnyVector(v::AVec, d::KW)
+64 -201
View File
@@ -1,7 +1,7 @@
"""
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
"""
mutable struct PlotExample
type PlotExample
header::AbstractString
desc::AbstractString
exprs::Vector{Expr}
@@ -18,14 +18,7 @@ PlotExample("Lines",
),
PlotExample("Functions, adding data, and animations",
"""
Plot multiple functions. You can also put the function first, or use the form `plot(f,
xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data:
`x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with
`push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available
to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the
animation.
""",
"Plot multiple functions. You can also put the function first, or use the form `plot(f, xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data: `x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with `push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the animation.",
[:(begin
p = plot([sin,cos], zeros(0), leg=false)
anim = Animation()
@@ -44,35 +37,23 @@ PlotExample("Parametric plots",
),
PlotExample("Colors",
"""
Access predefined palettes (or build your own with the `colorscheme` method).
Line/marker colors are auto-generated from the plot's palette, unless overridden. Set
the `z` argument to turn on series gradients.
""",
"Access predefined palettes (or build your own with the `colorscheme` method). Line/marker colors are auto-generated from the plot's palette, unless overridden. Set the `z` argument to turn on series gradients.",
[:(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")
y = rand(100)
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=(0,:auto), α=0.6)
scatter!(y, zcolor=abs(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs(y-0.5)+4, lab="grad")
end)]
),
PlotExample("Global",
"""
Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow
you to pass a tuple or value which will be mapped to the relevant args automatically.
The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during
the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`,
`yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`
""",
"Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow you to pass a tuple or value which will be mapped to the relevant args automatically. The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`, `yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`",
[:(begin
y = rand(20,3)
plot(y, xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2),
leg=false)
hline!(mean(y,1)+rand(1,3), line=(4,:dash,0.6,[:lightgreen :green :darkgreen]))
vline!([5,10])
title!("TITLE")
yaxis!("YLABEL", :log10)
y = rand(20,3)
plot(y, xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2), leg=false)
hline!(mean(y,1)+rand(1,3), line=(4,:dash,0.6,[:lightgreen :green :darkgreen]))
vline!([5,10])
title!("TITLE")
yaxis!("YLABEL", :log10)
end)]
),
@@ -80,26 +61,19 @@ yaxis!("YLABEL", :log10)
# "Use the `axis` arguments.",
# [
# :(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT", xlabel="X", title="TITLE"))
# ]),
# ]),
PlotExample("Images",
"Plot an image. y-axis is set to flipped",
[:(begin
import FileIO
img = FileIO.load(Pkg.dir("PlotReferenceImages","Plots","pyplot","0.7.0","ref1.png"))
import Images
img = Images.load(Pkg.dir("PlotReferenceImages","Plots","pyplot","0.7.0","ref1.png"))
plot(img)
end)]
),
PlotExample("Arguments",
"""
Plot multiple series with different numbers of points. Mix arguments that apply to all
series (marker/markersize) with arguments unique to each series (colors). Special
arguments `line`, `marker`, and `fill` will automatically figure out what arguments to
set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with
`line`.) Note that we pass a matrix of colors, and this applies the colors to each
series.
""",
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (marker/markersize) with arguments unique to each series (colors). Special arguments `line`, `marker`, and `fill` will automatically figure out what arguments to set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with `line`.) Note that we pass a matrix of colors, and this applies the colors to each series.",
[:(begin
ys = Vector[rand(10), rand(20)]
plot(ys, color=[:black :orange], line=(:dot,4), marker=([:hex :d],12,0.8,stroke(3,:gray)))
@@ -141,23 +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
n = length(styles)
y = cumsum(randn(20,n),1)
plot(y, line = (5, styles), label = map(string,styles), legendtitle = "linestyle")
end)]
styles = filter(s -> s in 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))
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 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)]
),
@@ -172,30 +143,24 @@ PlotExample("Bar",
PlotExample("Histogram",
"",
[:(begin
histogram(randn(1000), bins = :scott, weights = repeat(1:5, outer = 200))
histogram(randn(1000), nbins=20)
end)]
),
PlotExample("Subplots",
"""
Use the `layout` keyword, and optionally the convenient `@layout` macro to generate
arbitrarily complex subplot layouts.
""",
"""
Use the `layout` keyword, and optionally the convenient `@layout` macro to generate arbitrarily complex subplot layouts.
""",
[:(begin
l = @layout([a{0.1h}; b [c;d e]])
plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false,
ticks=nothing, border=:none)
l = @layout([a{0.1h}; b [c;d e]])
plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false, ticks=nothing, border=false)
end)]
),
PlotExample("Adding to subplots",
"""
Note here the automatic grid layout, as well as the order in which new series are added
to the plots.
""",
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
[:(begin
plot(Plots.fakedata(100,10), layout=4, palette=[:grays :blues :heat :lightrainbow],
bg_inside=[:orange :pink :darkblue :black])
plot(Plots.fakedata(100,10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
end)]
),
@@ -208,68 +173,48 @@ PlotExample("",
),
PlotExample("Open/High/Low/Close",
"""
Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y`
argument. This uses recipes to first convert the tuples to OHLC objects, and
subsequently create a :path series with the appropriate line segments.
""",
"Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y` argument. This uses recipes to first convert the tuples to OHLC objects, and subsequently create a :path series with the appropriate line segments.",
[:(begin
n=20
hgt=rand(n)+1
bot=randn(n)
openpct=rand(n)
closepct=rand(n)
y = OHLC[(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i],
closepct[i]*hgt[i]+bot[i]) for i in 1:n]
ohlc(y)
n=20
hgt=rand(n)+1
bot=randn(n)
openpct=rand(n)
closepct=rand(n)
y = OHLC[(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i], closepct[i]*hgt[i]+bot[i]) for i in 1:n]
ohlc(y)
end)]
),
PlotExample("Annotations",
"""
The `annotations` keyword is used for text annotations in data-coordinates. Pass in a
tuple (x,y,text) or a vector of annotations. `annotate!(ann)` is shorthand for `plot!(;
annotation=ann)`. Series annotations are used for annotating individual data points.
They require only the annotation... x/y values are computed. A `PlotText` object can be
build with the method `text(string, attr...)`, which wraps font and color attributes.
""",
"The `annotations` keyword is used for text annotations in data-coordinates. Pass in a tuple (x,y,text) or a vector of annotations. `annotate!(ann)` is shorthand for `plot!(; annotation=ann)`. Series annotations are used for annotating individual data points. They require only the annotation... x/y values are computed. A `PlotText` object can be build with the method `text(string, attr...)`, which wraps font and color attributes.",
[:(begin
y = rand(10)
plot(y, annotations = (3,y[3],text("this is #3",:left)), leg=false)
annotate!([(5, y[5], text("this is #5",16,:red,:center)),
(10, y[10], text("this is #10",:right,20,"courier"))])
scatter!(linspace(2,8,6), rand(6), marker=(50,0.2,:orange),
series_annotations = ["series","annotations","map","to","series",
text("data",:green)])
y = rand(10)
plot(y, annotations = (3,y[3],text("this is #3",:left)), leg=false)
annotate!([(5, y[5], text("this is #5",16,:red,:center)), (10, y[10], text("this is #10",:right,20,"courier"))])
scatter!(linspace(2,8,6), rand(6), marker=(50,0.2,:orange), series_annotations = ["series","annotations","map","to","series",text("data",:green)])
end)]
),
PlotExample("Custom Markers",
"""A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported
backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and
the size is expected to be rougly the area of the unit circle.
""",
"A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and the size is expected to be rougly the area of the unit circle.",
[:(begin
verts = [(-1.0,1.0),(-1.28,0.6),(-0.2,-1.4),(0.2,-1.4),(1.28,0.6),(1.0,1.0),
(-1.0,1.0),(-0.2,-0.6),(0.0,-0.2),(-0.4,0.6),(1.28,0.6),(0.2,-1.4),
(-0.2,-1.4),(0.6,0.2),(-0.2,0.2),(0.0,-0.2),(0.2,0.2),(-0.2,-0.6)]
x = 0.1:0.2:0.9
y = 0.7rand(5)+0.15
plot(x, y, line = (3,:dash,:lightblue), marker = (Shape(verts),30,RGBA(0,0,0,0.2)),
bg=:pink, fg=:darkblue, xlim = (0,1), ylim=(0,1), leg=false)
verts = [(-1.0,1.0),(-1.28,0.6),(-0.2,-1.4),(0.2,-1.4),(1.28,0.6),(1.0,1.0),
(-1.0,1.0),(-0.2,-0.6),(0.0,-0.2),(-0.4,0.6),(1.28,0.6),(0.2,-1.4),
(-0.2,-1.4),(0.6,0.2),(-0.2,0.2),(0.0,-0.2),(0.2,0.2),(-0.2,-0.6)]
x = 0.1:0.2:0.9
y = 0.7rand(5)+0.15
plot(x, y, line = (3,:dash,:lightblue), marker = (Shape(verts),30,RGBA(0,0,0,0.2)),
bg=:pink, fg=:darkblue, xlim = (0,1), ylim=(0,1), leg=false)
end)]
),
PlotExample("Contours",
"""
Any value for fill works here. We first build a filled contour from a function, then an
unfilled contour from a matrix.
""",
"Any value for fill works here. We first build a filled contour from a function, then an unfilled contour from a matrix.",
[:(begin
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)
@@ -305,9 +250,9 @@ PlotExample("DataFrames",
[:(begin
import RDatasets
iris = RDatasets.dataset("datasets", "iris")
@df iris scatter(:SepalLength, :SepalWidth, group=:Species,
scatter(iris, :SepalLength, :SepalWidth, group=:Species,
title = "My awesome plot", xlabel = "Length", ylabel = "Width",
marker = (0.5, [:cross :hex :star7], 12), bg=RGB(.2,.2,.2))
marker = (0.5, [:+ :h :star7], 12), bg=RGB(.2,.2,.2))
end)]
),
@@ -315,8 +260,7 @@ PlotExample("Groups and Subplots",
"",
[:(begin
group = rand(map(i->"group $i",1:4),100)
plot(rand(100), layout=@layout([a b;c]), group=group,
linetype=[:bar :scatter :steppre], linecolor = :match)
plot(rand(100), layout=@layout([a b;c]), group=group, linetype=[:bar :scatter :steppre])
end)]
),
@@ -324,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)]
),
@@ -334,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)]
),
@@ -342,10 +286,9 @@ PlotExample("Heatmap, categorical axes, and aspect_ratio",
PlotExample("Layouts, margins, label rotation, title location",
"",
[:(begin
using Plots.PlotMeasures # for Measures, e.g. mm and px
plot(rand(100,6),layout=@layout([a b; c]),title=["A" "B" "C"],
title_location=:left, left_margin=[20mm 0mm],
bottom_margin=10px, xrotation=60)
bottom_margin=50px, xrotation=60)
end)]
),
@@ -354,83 +297,10 @@ PlotExample("Boxplot and Violin series recipes",
[:(begin
import RDatasets
singers = RDatasets.dataset("lattice", "singer")
@df singers violin(:VoicePart, :Height, line = 0, fill = (0.2, :blue))
@df singers boxplot!(:VoicePart, :Height, line = (2,:black), fill = (0.3, :orange))
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`.
""",
[:(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), spy(b), title = ["Unique nonzeros" "Different nonzeros"])
end)]
),
PlotExample("Magic grid argument",
"""
The grid lines can be modified individually for each axis with the magic `grid` argument.
""",
[:(begin
x = rand(10)
p1 = plot(x, title = "Default looks")
p2 = plot(x, grid = (:y, :olivedrab, :dot, 1, 0.9), title = "Modified y grid")
p3 = plot(deepcopy(p2), title = "Add x grid")
xgrid!(p3, :on, :cadetblue, 2, :dashdot, 0.4)
plot(p1, p2, p3, layout = (1, 3), label = "", fillrange = 0, fillalpha = 0.3)
end)]
),
PlotExample("Framestyle",
"""
The style of the frame/axes of a (sub)plot can be changed with the `framestyle`
attribute. The default framestyle is `:axes`.
""",
[:(begin
scatter(fill(randn(10), 6), fill(randn(10), 6),
framestyle = [:box :semi :origin :zerolines :grid :none],
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
color = RowVector(1:6), layout = 6, label = "", markerstrokewidth = 0,
ticks = -2:2)
end)]
),
PlotExample("Lines and markers with varying colors",
"""
You can use the `line_z` and `marker_z` properties to associate a color with
each line segment or marker in the plot.
""",
[:(begin
t = linspace(0, 1, 100)
θ = 6π .* t
x = t .* cos.(θ)
y = t .* sin.(θ)
p1 = plot(x, y, line_z=t, linewidth=3, legend=false)
p2 = scatter(x, y, marker_z=t, color=:bluesreds, legend=false)
plot(p1, p2)
end)]
),
)
]
@@ -451,13 +321,6 @@ function test_examples(pkgname::Symbol, idx::Int; debug = false, disp = true)
end
# generate all plots and create a dict mapping idx --> plt
"""
test_examples(pkgname[, idx]; debug = false, disp = true, sleep = nothing,
skip = [], only = nothing
Run the `idx` test example for a given backend, or all examples if `idx`
is not specified.
"""
function test_examples(pkgname::Symbol; debug = false, disp = true, sleep = nothing,
skip = [], only = nothing)
Plots._debugMode.on = debug
+44 -133
View File
@@ -1,30 +1,34 @@
# NOTE: (0,0) is the top-left !!!
# allow pixels and percentages
const px = AbsoluteLength(0.254)
const pct = Length{:pct, Float64}(1.0)
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, a::AbstractArray) = map(ai -> m - ai, a)
Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
@compat Base.:.*(m::Measure, n::Number) = m * n
@compat Base.:.*(n::Number, m::Measure) = m * n
@compat Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
@compat Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
Base.zero(::Type{typeof(mm)}) = 0mm
Base.one(::Type{typeof(mm)}) = 1mm
Base.typemin(::typeof(mm)) = -Inf*mm
Base.typemax(::typeof(mm)) = Inf*mm
Base.convert(::Type{F}, l::AbsoluteLength) where {F<:AbstractFloat} = convert(F, l.value)
Base.convert{F<:AbstractFloat}(::Type{F}, l::AbsoluteLength) = convert(F, l.value)
# TODO: these are unintuitive and may cause tricky bugs
# Base.:+(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
# Base.:+(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (1 + m1.value))
# Base.:-(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 - m2.value))
# Base.:-(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (m1.value - 1))
# @compat Base.:+(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
# @compat Base.:+(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (1 + m1.value))
# @compat Base.:-(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 - m2.value))
# @compat Base.:-(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (m1.value - 1))
Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
@compat Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
@compat Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
@compat Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
@compat Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
Base.zero(::Type{typeof(pct)}) = 0pct
@@ -40,11 +44,11 @@ right(bbox::BoundingBox) = left(bbox) + width(bbox)
bottom(bbox::BoundingBox) = top(bbox) + height(bbox)
Base.size(bbox::BoundingBox) = (width(bbox), height(bbox))
# Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value)
# @compat Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value)
ispositive(m::Measure) = m.value > 0
# union together bounding boxes
function Base.:+(bb1::BoundingBox, bb2::BoundingBox)
@compat function Base.:+(bb1::BoundingBox, bb2::BoundingBox)
# empty boxes don't change the union
ispositive(width(bb1)) || return bb2
ispositive(height(bb1)) || return bb2
@@ -92,32 +96,6 @@ function Base.show(io::IO, bbox::BoundingBox)
print(io, "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}")
end
# -----------------------------------------------------------
# points combined by x/y, pct, and length
mutable struct MixedMeasures
xy::Float64
pct::Float64
len::AbsoluteLength
end
function resolve_mixed(mix::MixedMeasures, sp::Subplot, letter::Symbol)
xy = mix.xy
pct = mix.pct
if mix.len != 0mm
f = (letter == :x ? width : height)
totlen = f(plotarea(sp))
@show totlen
pct += mix.len / totlen
end
if pct != 0
amin, amax = axis_limits(sp[Symbol(letter,:axis)])
xy += pct * (amax-amin)
end
xy
end
# -----------------------------------------------------------
# AbstractLayout
@@ -129,22 +107,15 @@ make_measure_hor(m::Measure) = m
make_measure_vert(n::Number) = n * h
make_measure_vert(m::Measure) = m
"""
bbox(x, y, w, h [,originargs...])
bbox(layout)
Create a bounding box for plotting
"""
function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...)
oargs = vcat(oarg1, originargs...)
orighor = :left
origver = :top
for oarg in oargs
if oarg == :center
orighor = origver = oarg
elseif oarg in (:left, :right, :hcenter)
if oarg in (:left, :right)
orighor = oarg
elseif oarg in (:top, :bottom, :vcenter)
elseif oarg in (:top, :bottom)
origver = oarg
else
warn("Unused origin arg in bbox construction: $oarg")
@@ -161,17 +132,13 @@ function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top)
height = make_measure_vert(height)
left = if h_anchor == :left
x
elseif h_anchor in (:center, :hcenter)
0.5w - 0.5width + x
else
1w - x - width
1w - x - width
end
top = if v_anchor == :top
y
elseif v_anchor in (:center, :vcenter)
0.5h - 0.5height + y
else
1h - y - height
1h - y - height
end
BoundingBox(left, top, width, height)
end
@@ -216,7 +183,7 @@ bottompad(layout::AbstractLayout) = 0mm
# RootLayout
# this is the parent of the top-level layout
struct RootLayout <: AbstractLayout end
immutable RootLayout <: AbstractLayout end
Base.parent(::RootLayout) = nothing
parent_bbox(::RootLayout) = defaultbox
@@ -226,7 +193,7 @@ bbox(::RootLayout) = defaultbox
# EmptyLayout
# contains blank space
mutable struct EmptyLayout <: AbstractLayout
type EmptyLayout <: AbstractLayout
parent::AbstractLayout
bbox::BoundingBox
attr::KW # store label, width, and height for initialization
@@ -244,7 +211,7 @@ _update_min_padding!(layout::EmptyLayout) = nothing
# GridLayout
# nested, gridded layout with optional size percentages
mutable struct GridLayout <: AbstractLayout
type GridLayout <: AbstractLayout
parent::AbstractLayout
minpad::Tuple # leftpad, toppad, rightpad, bottompad
bbox::BoundingBox
@@ -254,13 +221,6 @@ mutable struct GridLayout <: AbstractLayout
attr::KW
end
"""
grid(args...; kw...)
Create a grid layout for subplots. `args` specify the dimensions, e.g.
`grid(3,2, widths = (0.6,04))` creates a grid with three rows and two
columns of different width.
"""
grid(args...; kw...) = GridLayout(args...; kw...)
function GridLayout(dims...;
@@ -279,9 +239,7 @@ function GridLayout(dims...;
# convert(Vector{Float64}, widths),
# convert(Vector{Float64}, heights),
KW(kw))
for i in eachindex(grid)
grid[i] = EmptyLayout(layout)
end
fill!(grid, EmptyLayout(layout))
layout
end
@@ -298,7 +256,7 @@ rightpad(layout::GridLayout) = layout.minpad[3]
bottompad(layout::GridLayout) = layout.minpad[4]
# here's how this works... first we recursively "update the minimum padding" (which
# here's how this works... first we recursively "update the minimum padding" (which
# means to calculate the minimum size needed from the edge of the subplot to plot area)
# for the whole layout tree. then we can compute the "padding borders" of this
# layout as the biggest padding of the children on the perimeter. then we need to
@@ -353,6 +311,7 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
minpad_top = map(toppad, layout.grid)
minpad_right = map(rightpad, layout.grid)
minpad_bottom = map(bottompad, layout.grid)
# @show minpad_left minpad_top minpad_right minpad_bottom
# get the max horizontal (left and right) padding over columns,
# and max vertical (bottom and top) padding over rows
@@ -361,6 +320,7 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
pad_top = maximum(minpad_top, 2)
pad_right = maximum(minpad_right, 1)
pad_bottom = maximum(minpad_bottom, 2)
# @show pad_left pad_top pad_right pad_bottom
# make sure the perimeter match the parent
pad_left[1] = max(pad_left[1], minimum_perimeter[1])
@@ -371,18 +331,22 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
# scale this up to the total padding in each direction
total_pad_horizontal = sum(pad_left + pad_right)
total_pad_vertical = sum(pad_top + pad_bottom)
# @show total_pad_horizontal total_pad_vertical
# now we can compute the total plot area in each direction
total_plotarea_horizontal = width(layout) - total_pad_horizontal
total_plotarea_vertical = height(layout) - total_pad_vertical
# @show total_plotarea_horizontal total_plotarea_vertical
# recompute widths/heights
layout.widths = recompute_lengths(layout.widths)
layout.heights = recompute_lengths(layout.heights)
# @show layout.widths layout.heights
# normalize widths/heights so they sum to 1
# denom_w = sum(layout.widths)
# denom_h = sum(layout.heights)
# @show layout.widths layout.heights denom_w, denom_h
# we have all the data we need... lets compute the plot areas and set the bounding boxes
for r=1:nr, c=1:nc
@@ -423,6 +387,7 @@ end
function update_inset_bboxes!(plt::Plot)
for sp in plt.inset_subplots
p_area = Measures.resolve(plotarea(sp.parent), sp[:relative_bbox])
# @show bbox(sp.parent) sp[:relative_bbox] p_area
plotarea!(sp, p_area)
bbox!(sp, bbox(
@@ -471,7 +436,7 @@ end
function layout_args(d::KW, n_override::Integer)
layout, n = layout_args(get(d, :layout, n_override))
if n != n_override
error("When doing layout, n ($n) != n_override ($(n_override)). You're probably trying to force existing plots into a layout that doesn't fit them.")
error("When doing layout, n != n_override. You're probably trying to force existing plots into a layout that doesn't fit them.")
end
layout, n
end
@@ -481,12 +446,12 @@ function layout_args(n::Integer)
GridLayout(nr, nc), n
end
function layout_args(sztup::NTuple{2,I}) where I<:Integer
function layout_args{I<:Integer}(sztup::NTuple{2,I})
nr, nc = sztup
GridLayout(nr, nc), nr*nc
end
function layout_args(sztup::NTuple{3,I}) where I<:Integer
function layout_args{I<:Integer}(sztup::NTuple{3,I})
n, nr, nc = sztup
nr, nc = compute_gridsize(n, nr, nc)
GridLayout(nr, nc), n
@@ -537,12 +502,6 @@ function build_layout(layout::GridLayout, n::Integer)
i += 1
elseif isa(l, GridLayout)
# sub-grid
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
end
l, sps, m = build_layout(l, n-i)
append!(subplots, sps)
merge!(spmap, m)
@@ -579,12 +538,6 @@ function build_layout(layout::GridLayout, numsp::Integer, plts::AVec{Plot})
i += length(plt.subplots)
elseif isa(l, GridLayout)
# sub-grid
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
end
l, sps, m = build_layout(l, numsp-i, plts)
append!(subplots, sps)
merge!(spmap, m)
@@ -644,7 +597,7 @@ function create_grid(expr::Expr)
create_grid_curly(expr)
else
# if it's something else, just return that (might be an existing layout?)
esc(expr)
expr
end
end
@@ -681,18 +634,13 @@ function create_grid_vcat(expr::Expr)
end
function create_grid_curly(expr::Expr)
s = expr.args[1]
kw = KW()
for (i,arg) in enumerate(expr.args[2:end])
add_layout_pct!(kw, arg, i, length(expr.args)-1)
end
s = expr.args[1]
if isa(s, Expr) && s.head == :call && s.args[1] == :grid
create_grid(:(grid($(s.args[2:end]...), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto))))))
elseif isa(s, Symbol)
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
else
error("Unknown use of curly brackets: $expr")
end
# @show kw
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
end
function create_grid(s::Symbol)
@@ -706,42 +654,20 @@ end
# -------------------------------------------------------------------------
# make all reference the same axis extrema/values.
# merge subplot lists.
# make all reference the same axis extrema/values
function link_axes!(axes::Axis...)
a1 = axes[1]
for i=2:length(axes)
a2 = axes[i]
expand_extrema!(a1, ignorenan_extrema(a2))
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
a2[k] = a1[k]
end
# make a2's subplot list refer to a1's and add any missing values
sps2 = a2.sps
for sp in sps2
sp in a1.sps || push!(a1.sps, sp)
end
a2.sps = a1.sps
end
end
# figure out which subplots to link
function link_subplots(a::AbstractArray{AbstractLayout}, axissym::Symbol)
subplots = []
for l in a
if isa(l, Subplot)
push!(subplots, l)
elseif isa(l, GridLayout) && size(l) == (1,1)
push!(subplots, l[1,1])
end
end
subplots
end
# for some vector or matrix of layouts, filter only the Subplots and link those axes
function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
subplots = link_subplots(a, axissym)
subplots = filter(l -> isa(l, Subplot), a)
axes = [sp.attr[axissym] for sp in subplots]
if length(axes) > 0
link_axes!(axes...)
@@ -783,18 +709,3 @@ function link_axes!(layout::GridLayout, link::Symbol)
link_axes!(l, link)
end
end
# -------------------------------------------------------------------------
"Adds a new, empty subplot overlayed on top of `sp`, with a mirrored y-axis and linked x-axis."
function twinx(sp::Subplot)
sp[:right_margin] = max(sp[:right_margin], 30px)
plot!(sp.plt, inset = (sp[:subplot_index], bbox(0,0,1,1)))
twinsp = sp.plt.subplots[end]
twinsp[:yaxis][:mirror] = true
twinsp[:background_color_inside] = RGBA{Float64}(0,0,0,0)
link_axes!(sp[:xaxis], twinsp[:xaxis])
twinsp
end
twinx(plt::Plot = current()) = twinx(plt[1])
+83 -168
View File
@@ -5,7 +5,7 @@ defaultOutputFormat(plt::Plot) = "png"
function png(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "png")
io = open(fn, "w")
show(io, MIME("image/png"), plt)
writemime(io, MIME("image/png"), plt)
close(io)
end
png(fn::AbstractString) = png(current(), fn)
@@ -13,7 +13,7 @@ png(fn::AbstractString) = png(current(), fn)
function svg(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "svg")
io = open(fn, "w")
show(io, MIME("image/svg+xml"), plt)
writemime(io, MIME("image/svg+xml"), plt)
close(io)
end
svg(fn::AbstractString) = svg(current(), fn)
@@ -22,7 +22,7 @@ svg(fn::AbstractString) = svg(current(), fn)
function pdf(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "pdf")
io = open(fn, "w")
show(io, MIME("application/pdf"), plt)
writemime(io, MIME("application/pdf"), plt)
close(io)
end
pdf(fn::AbstractString) = pdf(current(), fn)
@@ -31,49 +31,30 @@ pdf(fn::AbstractString) = pdf(current(), fn)
function ps(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "ps")
io = open(fn, "w")
show(io, MIME("application/postscript"), plt)
writemime(io, MIME("application/postscript"), plt)
close(io)
end
ps(fn::AbstractString) = ps(current(), fn)
function eps(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "eps")
io = open(fn, "w")
show(io, MIME("image/eps"), plt)
close(io)
end
eps(fn::AbstractString) = eps(current(), fn)
function tex(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "tex")
io = open(fn, "w")
show(io, MIME("application/x-tex"), plt)
writemime(io, MIME("application/x-tex"), plt)
close(io)
end
tex(fn::AbstractString) = tex(current(), fn)
function html(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "html")
io = open(fn, "w")
_use_remote[] = true
show(io, MIME("text/html"), plt)
_use_remote[] = false
close(io)
end
html(fn::AbstractString) = html(current(), fn)
# ----------------------------------------------------------------
const _savemap = Dict(
@compat const _savemap = Dict(
"png" => png,
"svg" => svg,
"pdf" => pdf,
"ps" => ps,
"eps" => eps,
"tex" => tex,
"html" => html,
)
function getExtension(fn::AbstractString)
@@ -97,13 +78,6 @@ function addExtension(fn::AbstractString, ext::AbstractString)
end
end
"""
savefig([plot,] filename)
Save a Plot (the current plot if `plot` is not passed) to file. The file
type is inferred from the file extension. All backends support png and pdf
file types, some also support svg, ps, eps, html and tex.
"""
function savefig(plt::Plot, fn::AbstractString)
# get the extension
@@ -126,20 +100,9 @@ savefig(fn::AbstractString) = savefig(current(), fn)
# ---------------------------------------------------------
"""
gui([plot])
Display a plot using the backends' gui window
"""
gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
# IJulia only... inline display
function inline(plt::Plot = current())
isijulia() || error("inline() is IJulia-only")
Main.IJulia.clear_output(true)
display(Main.IJulia.InlineDisplay(), plt)
end
function Base.display(::PlotsDisplay, plt::Plot)
prepare_output(plt)
_display(plt)
@@ -148,75 +111,64 @@ end
# override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
_do_plot_show(plt, showval::Bool) = showval && gui(plt)
function _do_plot_show(plt, showval::Symbol)
showval == :gui && gui(plt)
showval in (:inline,:ijulia) && inline(plt)
end
# ---------------------------------------------------------
const _mimeformats = Dict(
"application/eps" => "eps",
"image/eps" => "eps",
"application/pdf" => "pdf",
"image/png" => "png",
"application/postscript" => "ps",
"image/svg+xml" => "svg",
"text/plain" => "txt",
)
const _best_html_output_type = KW(
:pyplot => :png,
:unicodeplots => :txt,
:glvisualize => :png,
:plotlyjs => :html,
:plotly => :html
)
# a backup for html... passes to svg or png depending on the html_output_format arg
function _show(io::IO, ::MIME"text/html", plt::Plot)
function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
output_type = Symbol(plt.attr[:html_output_format])
if output_type == :auto
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
end
if output_type == :png
# info("writing png to html output")
print(io, "<img src=\"data:image/png;base64,", base64encode(show, MIME("image/png"), plt), "\" />")
print(io, "<img src=\"data:image/png;base64,", base64encode(writemime, MIME("image/png"), plt), "\" />")
elseif output_type == :svg
# info("writing svg to html output")
show(io, MIME("image/svg+xml"), plt)
writemime(io, MIME("image/svg+xml"), plt)
elseif output_type == :txt
show(io, MIME("text/plain"), plt)
writemime(io, MIME("text/plain"), plt)
else
error("only png or svg allowed. got: $(repr(output_type))")
error("only png or svg allowed. got: $output_type")
end
end
# delegate mimewritable (showable on julia 0.7) to _show instead
function Base.mimewritable(m::M, plt::P) where {M<:MIME, P<:Plot}
return method_exists(_show, Tuple{IO, M, P})
function _writemime(io::IO, m, plt::Plot)
warn("_writemime is not defined for this backend. m=", string(m))
end
function _display(plt::Plot)
warn("_display is not defined for this backend.")
end
# for writing to io streams... first prepare, then callback
for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml",
"application/eps", "application/pdf", "application/postscript",
"application/x-tex")
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
for mime in keys(_mimeformats)
@eval function Base.writemime(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
prepare_output(plt)
_show(io, m, plt)
_writemime(io, m, plt)
end
end
# default text/plain for all backends
_show(io::IO, ::MIME{Symbol("text/plain")}, plt::Plot) = show(io, plt)
"Close all open gui windows of the current backend"
closeall() = closeall(backend())
# ---------------------------------------------------------
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
if is_installed("FileIO")
@eval import FileIO
function _show(io::IO, ::MIME"image/png", plt::Plot{<:PDFBackends})
function _writemime(io::IO, ::MIME"image/png", plt::Plot)
fn = tempname()
# first save a pdf file
@@ -230,18 +182,22 @@ if is_installed("FileIO")
FileIO.save(pngfn, s)
# now write from the file
write(io, readstring(open(pngfn)))
write(io, readall(open(pngfn)))
end
end
# function html_output_format(fmt)
# if fmt == "png"
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
# print(io, "<img src=\"data:image/png;base64,", base64(show, MIME("image/png"), plt), "\" />")
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
# print(io, "<img src=\"data:image/png;base64,", base64(writemime, MIME("image/png"), plt), "\" />")
# end
# elseif fmt == "svg"
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
# show(io, MIME("image/svg+xml"), plt)
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
# writemime(io, MIME("image/svg+xml"), plt)
# end
# else
# error("only png or svg allowed. got: $fmt")
@@ -254,108 +210,67 @@ end
# IJulia
# ---------------------------------------------------------
@require IJulia begin
if IJulia.inited
const _ijulia_output = Compat.ASCIIString["text/html"]
"""
Add extra jupyter mimetypes to display_dict based on the plot backed.
The default is nothing, except for plotly based backends, where it
adds data for `application/vnd.plotly.v1+json` that is used in
frontends like jupyterlab and nteract.
"""
_extra_mime_info!(plt::Plot, out::Dict) = out
function _extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
out["application/vnd.plotly.v1+json"] = JSON.lower(plt.o)
out
end
function _extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
out["application/vnd.plotly.v1+json"] = Dict(
:data => plotly_series(plt),
:layout => plotly_layout(plt)
)
out
end
function IJulia.display_dict(plt::Plot)
output_type = Symbol(plt.attr[:html_output_format])
if output_type == :auto
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
function setup_ijulia()
# override IJulia inline display
if isijulia()
@eval begin
import IJulia
export set_ijulia_output
function set_ijulia_output(mimestr::AbstractString)
# info("Setting IJulia output format to $mimestr")
global _ijulia_output
_ijulia_output[1] = mimestr
end
out = Dict()
if output_type == :txt
mime = "text/plain"
out[mime] = sprint(show, MIME(mime), plt)
elseif output_type == :png
mime = "image/png"
out[mime] = base64encode(show, MIME(mime), plt)
elseif output_type == :svg
mime = "image/svg+xml"
out[mime] = sprint(show, MIME(mime), plt)
elseif output_type == :html
mime = "text/html"
out[mime] = sprint(show, MIME(mime), plt)
else
error("Unsupported output type $output_type")
function IJulia.display_dict(plt::Plot)
global _ijulia_output
Dict{Compat.ASCIIString, ByteString}(_ijulia_output[1] => sprint(writemime, _ijulia_output[1], plt))
end
_extra_mime_info!(plt, out)
out
end
ENV["MPLBACKEND"] = "Agg"
# default text/plain passes to html... handles Interact issues
function Base.writemime(io::IO, m::MIME"text/plain", plt::Plot)
writemime(io, MIME("text/html"), plt)
end
end
set_ijulia_output("text/html")
end
end
# ---------------------------------------------------------
# Atom PlotPane
# ---------------------------------------------------------
@require Juno begin
import Hiccup, Media
if Juno.isactive()
Media.media(Plot, Media.Plot)
function setup_atom()
# @require Atom begin
if isatom() && get(ENV, "PLOTS_USE_ATOM_PLOTPANE", false) in (true, 1, "1", "true", "yes")
# @eval import Atom, Media
@eval import Atom
function Juno.render(e::Juno.Editor, plt::Plot)
Juno.render(e, nothing)
# # connects the render function
# for T in (GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend)
# Atom.Media.media(Plot{T}, Atom.Media.Plot)
# end
Atom.Media.media(Plot, Atom.Media.Graphical)
# Atom.Media.media{T <: Union{GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend}}(Plot{T}, Atom.Media.Plot)
# Atom.displaysize(::Plot) = (535, 379)
# Atom.displaytitle(plt::Plot) = "Plots.jl (backend: $(backend(plt)))"
# this is like "display"... sends an html div with the plot to the PlotPane
function Atom.Media.render(pane::Atom.PlotPane, plt::Plot)
@show "here"
Atom.Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
end
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
function Juno.render(pane::Juno.PlotPane, plt::Plot)
# temporarily overwrite size to be Atom.plotsize
sz = plt[:size]
dpi = plt[:dpi]
thickness_scaling = plt[:thickness_scaling]
jsize = Juno.plotsize()
jsize[1] == 0 && (jsize[1] = 400)
jsize[2] == 0 && (jsize[2] = 500)
# # force text/plain to output to the PlotPane
# function Base.writemime(io::IO, ::MIME"text/plain", plt::Plot)
# # writemime(io::IO, MIME("text/html"), plt)
# Atom.Media.render(pane)
# end
scale = minimum(jsize[i] / sz[i] for i in 1:2)
plt[:size] = (s * scale for s in sz)
plt[:dpi] = Plots.DPI
plt[:thickness_scaling] *= scale
Juno.render(pane, HTML(stringmime(MIME("text/html"), plt)))
plt[:size] = sz
plt[:dpi] = dpi
plt[:thickness_scaling] = thickness_scaling
end
# special handling for PlotlyJS
function Juno.render(pane::Juno.PlotPane, plt::Plot{PlotlyJSBackend})
display(Plots.PlotsDisplay(), plt)
end
else
function Juno.render(pane::Juno.PlotPane, plt::Plot)
display(Plots.PlotsDisplay(), plt)
s = "PlotPane turned off. Unset ENV[\"PLOTS_USE_ATOM_PLOTPANE\"] and restart Julia to enable it."
Juno.render(pane, HTML(s))
end
end
# special handling for plotly... use PlotsDisplay
function Juno.render(pane::Juno.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."
Juno.render(pane, HTML(s))
end
# function Atom.Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend})
# html = Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
# end
end
end
+44 -68
View File
@@ -13,7 +13,7 @@ function _expand_seriestype_array(d::KW, args)
delete!(d, :seriestype)
RecipeData[begin
dc = copy(d)
dc[:seriestype] = sts[r:r,:]
dc[:seriestype] = sts[r,:]
RecipeData(dc, args)
end for r=1:size(sts,1)]
else
@@ -60,26 +60,29 @@ function _process_userrecipes(plt::Plot, d::KW, args)
args = _preprocess_args(d, args, still_to_process)
# for plotting recipes, swap out the args and update the parameter dictionary
# we are keeping a stack of series that still need to be processed.
# we are keeping a queue of series that still need to be processed.
# each pass through the loop, we pop one off and apply the recipe.
# the recipe will return a list a Series objects... the ones that are
# finished (no more args) get added to the kw_list, the ones that are not
# are placed on top of the stack and are then processed further.
# finished (no more args) get added to the kw_list, and the rest go into the queue
# for processing.
kw_list = KW[]
while !isempty(still_to_process)
# grab the first in line to be processed and either add it to the kw_list or
# pass it through apply_recipe to generate a list of RecipeData objects (data + attributes)
# for further processing.
# grab the first in line to be processed and pass it through apply_recipe
# to generate a list of RecipeData objects (data + attributes)
next_series = shift!(still_to_process)
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
if !(typeof(next_series) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $next_series")
end
if isempty(next_series.args)
_process_userrecipe(plt, kw_list, next_series)
else
rd_list = RecipesBase.apply_recipe(next_series.d, next_series.args...)
prepend!(still_to_process,rd_list)
rd_list = RecipesBase.apply_recipe(next_series.d, next_series.args...)
for recipedata in rd_list
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
if !(typeof(recipedata) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $recipedata")
end
if isempty(recipedata.args)
_process_userrecipe(plt, kw_list, recipedata)
else
# args are non-empty, so there's still processing to do... add it back to the queue
push!(still_to_process, recipedata)
end
end
end
@@ -92,7 +95,6 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD
# when the arg tuple is empty, that means there's nothing left to recursively
# process... finish up and add to the kw_list
kw = recipedata.d
preprocessArgs!(kw)
_preprocess_userrecipe(kw)
warnOnUnsupported_scales(plt.backend, kw)
@@ -150,7 +152,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,
@@ -181,7 +183,6 @@ function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_pr
st = kw[:seriestype] = get(_typeAliases, st, st)
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
for data in datalist
preprocessArgs!(data.d)
if data.d[:seriestype] == st
error("Plot recipe $st returned the same seriestype: $(data.d)")
end
@@ -210,7 +211,7 @@ function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
# TODO: init subplots here
_update_plot_args(plt, d)
if !plt.init
plt.o = Base.invokelatest(_create_backend_figure, plt)
plt.o = _create_backend_figure(plt)
# create the layout and subplots from the inputs
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
@@ -241,13 +242,12 @@ function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
end
sp = Subplot(backend(), parent=parent)
sp.plt = plt
push!(plt.subplots, sp)
push!(plt.inset_subplots, sp)
sp.attr[:relative_bbox] = bb
sp.attr[:subplot_index] = length(plt.subplots)
push!(plt.subplots, sp)
push!(plt.inset_subplots, sp)
end
end
plt[:inset_subplots] = nothing
end
function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
@@ -259,12 +259,12 @@ 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
attr = KW()
for (k,v) in collect(kw)
for (k,v) in kw
if haskey(_subplot_defaults, k) || haskey(_axis_defaults_byletter, k)
attr[k] = pop!(kw, k)
end
@@ -274,24 +274,13 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
attr[Symbol(letter,k)] = v
end
end
for k in (:scale,), letter in (:x,:y,:z)
# Series recipes may need access to this information
lk = Symbol(letter,k)
if haskey(attr, lk)
kw[lk] = attr[lk]
end
end
end
sp_attrs[sp] = attr
end
# override subplot/axis args. `sp_attrs` take precendence
for (idx,sp) in enumerate(plt.subplots)
attr = if !haskey(d, :subplot) || d[:subplot] == idx
merge(d, get(sp_attrs, sp, KW()))
else
get(sp_attrs, sp, KW())
end
attr = merge(d, get(sp_attrs, sp, KW()))
_update_subplot_args(plt, sp, attr, idx, false)
end
@@ -301,7 +290,7 @@ end
# getting ready to add the series... last update to subplot from anything
# that might have been added during series recipes
function _prepare_subplot(plt::Plot{T}, d::KW) where T
function _prepare_subplot{T}(plt::Plot{T}, d::KW)
st::Symbol = d[:seriestype]
sp::Subplot{T} = d[:subplot]
sp_idx = get_subplot_index(plt, sp)
@@ -327,7 +316,7 @@ end
function _override_seriestype_check(d::KW, st::Symbol)
# do we want to override the series type?
if !is3d(st) && !(st in (:contour,:contour3d))
if !is3d(st)
z = d[:z]
if !isa(z, Void) && (size(d[:x]) == size(d[:y]) == size(z))
st = (st == :scatter ? :scatter3d : :path3d)
@@ -341,41 +330,33 @@ function _prepare_annotations(sp::Subplot, d::KW)
# strip out series annotations (those which are based on series x/y coords)
# and add them to the subplot attr
sp_anns = annotations(sp[:annotations])
# series_anns = annotations(pop!(d, :series_annotations, []))
# if isa(series_anns, SeriesAnnotations)
# series_anns.x = d[:x]
# series_anns.y = d[:y]
# elseif length(series_anns) > 0
# x, y = d[:x], d[:y]
# nx, ny, na = map(length, (x,y,series_anns))
# n = max(nx, ny, na)
# series_anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(series_anns[mod1(i,na)])) for i=1:n]
# end
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
anns = annotations(pop!(d, :series_annotations, []))
if length(anns) > 0
x, y = d[:x], d[:y]
nx, ny, na = map(length, (x,y,anns))
n = max(nx, ny, na)
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
end
sp.attr[:annotations] = vcat(sp_anns, anns)
end
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
# adjust extrema and discrete info
if st == :image
xmin, xmax = ignorenan_extrema(d[:x]); ymin, ymax = ignorenan_extrema(d[:y])
expand_extrema!(sp[:xaxis], (xmin, xmax))
expand_extrema!(sp[:yaxis], (ymin, ymax))
elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
w, h = size(d[:z])
expand_extrema!(sp[:xaxis], (0,w))
expand_extrema!(sp[:yaxis], (0,h))
sp[:yaxis].d[:flip] = true
elseif !(st in (:pie, :histogram, :histogram2d))
expand_extrema!(sp, d)
end
# expand for zerolines (axes through origin)
if sp[:framestyle] in (:origin, :zerolines)
expand_extrema!(sp[:xaxis], 0.0)
expand_extrema!(sp[:yaxis], 0.0)
end
end
function _add_the_series(plt, sp, d)
function _add_the_series(plt, d)
warnOnUnsupported_args(plt.backend, d)
warnOnUnsupported(plt.backend, d)
series = Series(d)
push!(plt.series_list, series)
push!(sp.series_list, series)
_series_added(plt, series)
end
@@ -394,12 +375,11 @@ function _process_seriesrecipe(plt::Plot, d::KW)
end
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
if is_seriestype_supported(st)
if st in supported_types()
sp = _prepare_subplot(plt, d)
_prepare_annotations(sp, d)
_expand_subplot_extrema(sp, d, st)
_update_series_attributes!(d, plt, sp)
_add_the_series(plt, sp, d)
_add_the_series(plt, d)
else
# get a sub list of series for this seriestype
@@ -408,10 +388,6 @@ function _process_seriesrecipe(plt::Plot, d::KW)
# assuming there was no error, recursively apply the series recipes
for data in datalist
if isa(data, RecipeData)
preprocessArgs!(data.d)
if data.d[:seriestype] == st
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
end
_process_seriesrecipe(plt, data.d)
else
warn("Unhandled recipe: $(data)")
+18 -50
View File
@@ -1,15 +1,11 @@
mutable struct CurrentPlot
type CurrentPlot
nullableplot::Nullable{AbstractPlot}
end
const CURRENT_PLOT = CurrentPlot(Nullable{AbstractPlot}())
isplotnull() = isnull(CURRENT_PLOT.nullableplot)
"""
current()
Returns the Plot object for the current plot
"""
function current()
if isplotnull()
error("No current plot/subplot")
@@ -33,7 +29,7 @@ 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 add to an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the current
@@ -42,9 +38,7 @@ The main plot command. Use `plot` to create a new plot object, and `plot!` to ad
```
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
When you pass in matrices, it splits by columns. To see the list of available attributes, use the `plotattr([attr])`
function, where `attr` is the symbol `:Series:`, `:Subplot:`, `:Plot` or `:Axis`. Pass any attribute to `plotattr`
as a String to look up its docstring; e.g. `plotattr("seriestype")`.
When you pass in matrices, it splits by columns. See the documentation for more info.
"""
# this creates a new plot with args/kw and sets it to be the current plot
@@ -66,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
@@ -86,7 +80,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
# TODO: replace this with proper processing from a merged user_attr KW
# update plot args, first with existing plots, then override with d
for p in plts
_update_plot_args(plt, copy(p.attr))
_update_plot_args(plt, p.attr)
plt.n += p.n
end
_update_plot_args(plt, d)
@@ -95,21 +89,8 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
plt.o = _create_backend_figure(plt)
plt.init = true
series_attr = KW()
for (k,v) in d
if haskey(_series_defaults, k)
series_attr[k] = pop!(d,k)
end
end
# create the layout
# create the layout and initialize the subplots
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
# do we need to link any axes together?
link_axes!(plt.layout, plt[:link])
# initialize the subplots
cmdidx = 1
for (idx, sp) in enumerate(plt.subplots)
_initialize_subplot(plt, sp)
serieslist = series_list(sp)
@@ -119,11 +100,8 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
sp.plt = plt
sp.attr[:subplot_index] = idx
for series in serieslist
merge!(series.d, series_attr)
_add_defaults!(series.d, plt, sp, cmdidx)
push!(plt.series_list, series)
_series_added(plt, series)
cmdidx += 1
end
end
@@ -132,9 +110,14 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
_update_subplot_args(plt, sp, d, idx, false)
end
# do we need to link any axes together?
link_axes!(plt.layout, plt[:link])
# finish up
current(plt)
_do_plot_show(plt, get(d, :show, default(:show)))
if get(d, :show, default(:show))
gui()
end
plt
end
@@ -165,12 +148,7 @@ end
# a list of series KW dicts.
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
function _plot!(plt::Plot, d::KW, args::Tuple)
d[:plot_object] = plt
if !isempty(args) && !isdefined(Main, :StatPlots) &&
first(split(string(typeof(args[1])), ".")) == "DataFrames"
warn("You're trying to plot a DataFrame, but this functionality is provided by StatPlots")
end
# d[:plot_object] = plt
# --------------------------------
# "USER RECIPES"
@@ -178,9 +156,6 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
kw_list = _process_userrecipes(plt, d, args)
# info(1)
# map(DD, kw_list)
# --------------------------------
# "PLOT RECIPES"
@@ -197,9 +172,6 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
end
# info(2)
# map(DD, kw_list)
# --------------------------------
# Plot/Subplot/Layout setup
# --------------------------------
@@ -212,10 +184,7 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
# --------------------------------
# "SERIES RECIPES"
# --------------------------------
# info(3)
# map(DD, kw_list)
for kw in kw_list
sp::Subplot = kw[:subplot]
# idx = get_subplot_index(plt, sp)
@@ -240,10 +209,9 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
current(plt)
# do we want to force display?
# if plt[:show]
# gui(plt)
# end
_do_plot_show(plt, plt[:show])
if plt[:show]
gui(plt)
end
plt
end
@@ -274,7 +242,7 @@ function prepare_output(plt::Plot)
_update_plot_object(plt)
end
function backend_object(plt::Plot)
function prepared_object(plt::Plot)
prepare_output(plt)
plt.o
end
-62
View File
@@ -1,62 +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
"""
plotattr([attr])
Look up the properties of a Plots attribute, or specify an attribute type. Call `plotattr()` for options.
The information is the same as that given on https://juliaplots.github.io/attributes/.
"""
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
+465
View File
@@ -1,3 +1,468 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
precompile(Plots.py_add_series, (Plots.Plot{Plots.PyPlotBackend}, Plots.Series,))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{Float64, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Base.StepRange{Int64, Int64},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Function,))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{Plots.OHLC, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{Float64, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Base.LinSpace{Float64},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, DataFrames.DataFrame,))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{Int64, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{Union{UTF8String, ASCIIString}, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{Function, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{ASCIIString, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.UnicodePlotsBackend}, Base.Dict{Symbol, Any},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Base.FloatRange{Float64},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any},))
precompile(Plots._plot!, (Plots.Plot{Plots.UnicodePlotsBackend}, Base.Dict{Symbol, Any}, Array{Float64, 1},))
precompile(Plots._plot!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Array{Float64, 2},))
precompile(Plots._add_defaults!, (Base.Dict{Symbol, Any}, Plots.Plot{Plots.UnicodePlotsBackend}, Plots.Subplot{Plots.UnicodePlotsBackend}, Int64,))
precompile(Plots._before_layout_calcs, (Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots._apply_series_recipe, (Plots.Plot{Plots.UnicodePlotsBackend}, Base.Dict{Symbol, Any},))
precompile(Plots._add_defaults!, (Base.Dict{Symbol, Any}, Plots.Plot{Plots.PyPlotBackend}, Plots.Subplot{Plots.PyPlotBackend}, Int64,))
precompile(Plots._apply_series_recipe, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any},))
precompile(Plots.setup_ijulia, ())
precompile(Plots.call, (Type{Plots.Plot{Plots.UnicodePlotsBackend}}, Plots.UnicodePlotsBackend, Int64, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Array{Plots.Series, 1}, Void, Array{Plots.Subplot, 1}, Base.Dict{Any, Plots.Subplot}, Plots.EmptyLayout, Array{Plots.Subplot, 1}, Bool,))
precompile(Plots.expand_extrema!, (Plots.Subplot{Plots.UnicodePlotsBackend}, Base.Dict{Symbol, Any},))
precompile(Plots.create_grid_vcat, (Expr,))
precompile(Plots.expand_extrema!, (Plots.Subplot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any},))
precompile(Plots.update_child_bboxes!, (Plots.GridLayout, Array{Measures.Length{:mm, Float64}, 1},))
precompile(Plots.preprocessArgs!, (Base.Dict{Symbol, Any},))
precompile(Plots.call, (Type{Plots.Plot{Plots.PyPlotBackend}}, Plots.PyPlotBackend, Int64, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Array{Plots.Series, 1}, Void, Array{Plots.Subplot, 1}, Base.Dict{Any, Plots.Subplot}, Plots.EmptyLayout, Array{Plots.Subplot, 1}, Bool,))
precompile(Plots.fix_xy_lengths!, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any},))
precompile(Plots._update_min_padding!, (Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.warnOnUnsupported_args, (Plots.PyPlotBackend, Base.Dict{Symbol, Any},))
precompile(Plots.build_layout, (Plots.GridLayout, Int64,))
precompile(Plots.build_layout, (Plots.GridLayout, Int64, Array{Plots.Plot, 1},))
precompile(Plots.warnOnUnsupported, (Plots.UnicodePlotsBackend, Base.Dict{Symbol, Any},))
precompile(Plots.link_axes!, (Plots.GridLayout, Symbol,))
precompile(Plots.warnOnUnsupported, (Plots.PyPlotBackend, Base.Dict{Symbol, Any},))
precompile(Plots._update_plot_args, (Plots.Plot{Plots.UnicodePlotsBackend}, Base.Dict{Symbol, Any},))
precompile(Plots.font, (Int64,))
precompile(Plots.recompute_lengths, (Array{Measures.Measure, 1},))
precompile(Plots._update_plot_object, (Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots.font, (Symbol,))
precompile(Plots.create_grid, (Expr,))
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, Void, Int64,))
precompile(Plots.pickDefaultBackend, ())
precompile(Plots.default_should_widen, (Plots.Axis,))
precompile(Plots.setup_atom, ())
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, Symbol, Int64,))
precompile(Plots.my_hist_2d, (Array{Any, 1}, Array{Float64, 1}, Array{Float64, 1}, Int64,))
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, ASCIIString, Int64,))
precompile(Plots.create_grid_curly, (Expr,))
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, Bool, Int64,))
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, Int64, Int64,))
precompile(Plots.my_hist, (Array{Any, 1}, Array{Float64, 1}, Int64,))
precompile(Plots.getpctrange, (Int64,))
precompile(Plots.call, (Type{Plots.ColorGradient}, Array{Symbol, 1},))
precompile(Plots.default, (Symbol,))
precompile(Plots.process_axis_arg!, (Base.Dict{Symbol, Any}, Symbol, Symbol,))
precompile(Plots.pie_labels, (Plots.Subplot{Plots.PyPlotBackend}, Plots.Series,))
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, Measures.Length{:mm, Float64}, Int64,))
precompile(Plots.py_path, (Array{Float64, 1}, Array{Float64, 1},))
precompile(Plots._update_min_padding!, (Plots.GridLayout,))
precompile(Plots.warnOnUnsupported_scales, (Plots.UnicodePlotsBackend, Base.Dict{Symbol, Any},))
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, Plots.Font, Int64,))
precompile(Plots.process_axis_arg!, (Base.Dict{Symbol, Any}, Tuple{Int64, Int64}, Symbol,))
precompile(Plots.slice_arg!, (Array{Any, 1}, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Symbol, Array{Any, 1}, Int64,))
precompile(Plots.default, (Symbol, Tuple{Int64, Int64},))
precompile(Plots.warnOnUnsupported_scales, (Plots.PyPlotBackend, Base.Dict{Symbol, Any},))
precompile(Plots.axis_limits, (Plots.Axis, Bool,))
precompile(Plots.default, (Symbol, Bool,))
precompile(Plots.getColorZ, (Plots.ColorGradient, Float64,))
precompile(Plots._update_plot_args, (Plots.Plot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any},))
precompile(Plots.call, (Type{Plots.Surface}, Function, Base.FloatRange{Float64}, Base.FloatRange{Float64},))
precompile(Plots.font, (Symbol,))
precompile(Plots.process_axis_arg!, (Base.Dict{Symbol, Any}, Base.StepRange{Int64, Int64}, Symbol,))
precompile(Plots.call, (Array{Any, 1}, Type{Plots.Subplot}, Plots.UnicodePlotsBackend,))
precompile(Plots.extractGroupArgs, (Array{ASCIIString, 1}, Array{Float64, 1},))
precompile(Plots._update_subplot_args, (Array{Any, 1}, Plots.Plot{Plots.UnicodePlotsBackend}, Plots.Subplot{Plots.UnicodePlotsBackend}, Base.Dict{Symbol, Any}, Int64,))
precompile(Plots.call, (Array{Any, 1}, Type{Plots.Subplot}, Plots.PyPlotBackend,))
precompile(Plots._update_subplot_args, (Array{Any, 1}, Plots.Plot{Plots.PyPlotBackend}, Plots.Subplot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Int64,))
precompile(Plots.extractGroupArgs, (Array{Union{UTF8String, ASCIIString}, 1},))
precompile(Plots.bbox_to_pcts, (Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}}, Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}, Bool,))
precompile(Plots.plot, (Array{Any, 1}, Array{Float64, 1},))
precompile(Plots.py_marker, (Plots.Shape,))
precompile(Plots.getindex, (Plots.Subplot{Plots.UnicodePlotsBackend}, Symbol,))
precompile(Plots.getindex, (Plots.Subplot{Plots.PyPlotBackend}, Symbol,))
precompile(Plots.discrete_value!, (Plots.Axis, Array{Union{UTF8String, ASCIIString}, 1},))
precompile(Plots.prepare_output, (Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots.update_inset_bboxes!, (Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots.add_layout_pct!, (Base.Dict{Symbol, Any}, Expr, Int64, Int64,))
precompile(Plots.process_axis_arg!, (Base.Dict{Symbol, Any}, ASCIIString, Symbol,))
precompile(Plots.call, (Type{Plots.Shape}, Array{Float64, 1}, Array{Float64, 1},))
precompile(Plots.plot, (Array{Any, 1}, Base.LinSpace{Float64}, Array{Float64, 2},))
precompile(Plots.call, (Array{Any, 1}, Type{Plots.EmptyLayout}, Plots.RootLayout,))
precompile(Plots.plot, (Array{Any, 1}, Function, Function,))
precompile(Plots.should_add_to_legend, (Plots.Series,))
precompile(Plots.plot, (Array{Any, 1}, DataFrames.DataFrame, Symbol,))
precompile(Plots.plot, (Array{Any, 1}, Array{Plots.OHLC, 1},))
precompile(Plots.convertToAnyVector, (Array{Float64, 2}, Base.Dict{Symbol, Any},))
precompile(Plots.plot, (Array{Any, 1}, Array{Float64, 1}, Array{Float64, 1},))
precompile(Plots.plot, (Array{Any, 1}, Array{ASCIIString, 1}, Array{Float64, 1},))
precompile(Plots.plot!, (Array{Any, 1}, Plots.Plot{Plots.PyPlotBackend}, Array{Float64, 1},))
precompile(Plots.processLineArg, (Base.Dict{Symbol, Any}, Symbol,))
precompile(Plots.aliasesAndAutopick, (Base.Dict{Symbol, Any}, Symbol, Base.Dict{Symbol, Any}, Array{Symbol, 1}, Int64,))
precompile(Plots.plot!, (Array{Any, 1}, Plots.Plot{Plots.PyPlotBackend}, Array{Int64, 1},))
precompile(Plots.pie, (Array{Any, 1}, Array{ASCIIString, 1},))
precompile(Plots.aliasesAndAutopick, (Base.Dict{Symbol, Any}, Symbol, Base.Dict{Symbol, Any}, Array{Any, 1}, Int64,))
precompile(Plots.merge_with_base_supported, (Array{Symbol, 1},))
precompile(Plots.histogram2d, (Array{Any, 1}, Array{Float64, 1},))
precompile(Plots.plot, (Array{Any, 1}, Base.StepRange{Int64, Int64}, Array{Float64, 2},))
precompile(Plots.plot, (Array{Any, 1}, Base.LinSpace{Float64}, Array{Float64, 1},))
precompile(Plots.plot, (Array{Any, 1}, Array{Float64, 1}, Array{Float64, 1},))
precompile(Plots.font, ())
precompile(Plots.plot, (Array{Any, 1}, Base.FloatRange{Float64}, Array{Float64, 1},))
precompile(Plots.plot, (Array{Any, 1}, Base.FloatRange{Float64}, Base.FloatRange{Float64},))
precompile(Plots.plot, (Array{Any, 1}, Array{Function, 1}, Array{Float64, 1},))
precompile(Plots.scatter, (Array{Any, 1}, Base.LinSpace{Float64},))
precompile(Plots.plot!, (Array{Any, 1}, Plots.Plot{Plots.PyPlotBackend}, Base.LinSpace{Float64},))
precompile(Plots.plot!, (Array{Any, 1}, Base.LinSpace{Float64}, Array{Float64, 1},))
precompile(Plots.get_zvalues, (Int64,))
precompile(Plots.plot, (Array{Any, 1}, Array{Union{UTF8String, ASCIIString}, 1}, Array{Union{UTF8String, ASCIIString}, 1},))
precompile(Plots.histogram, (Array{Any, 1}, Array{Float64, 1},))
precompile(Plots.hline!, (Array{Any, 1}, Array{Float64, 2},))
precompile(Plots.layout_args, (Base.Dict{Symbol, Any}, Int64,))
precompile(Plots.heatmap, (Array{Any, 1}, Array{Union{UTF8String, ASCIIString}, 1},))
precompile(Plots._replace_linewidth, (Base.Dict{Symbol, Any},))
precompile(Plots.plot!, (Array{Any, 1}, Array{Float64, 1}, Array{Float64, 1},))
precompile(Plots.plot!, (Array{Any, 1}, Plots.Plot{Plots.PyPlotBackend}, Array{Float64, 2},))
precompile(Plots.py_markercolor, (Base.Dict{Symbol, Any},))
precompile(Plots.unzip, (Array{Tuple{Float64, Float64}, 1},))
precompile(Plots.link_axes!, (Array{Plots.AbstractLayout, 2}, Symbol,))
precompile(Plots.plot!, (Array{Any, 1}, Plots.Plot{Plots.PyPlotBackend}, Array{Float64, 1},))
precompile(Plots.contour, (Array{Any, 1}, Base.FloatRange{Float64},))
precompile(Plots.scatter, (Array{Any, 1}, DataFrames.DataFrame,))
precompile(Plots.scatter!, (Array{Any, 1}, Base.LinSpace{Float64},))
precompile(Plots.scatter!, (Array{Any, 1}, Array{Float64, 1},))
precompile(Plots.getxy, (Plots.Plot{Plots.PyPlotBackend}, Int64,))
precompile(Plots.plot, (Array{Any, 1}, Array{Float64, 2},))
precompile(Plots.get_xy, (Array{Plots.OHLC, 1}, Base.UnitRange{Int64},))
precompile(Plots.convertToAnyVector, (Array{Function, 1}, Base.Dict{Symbol, Any},))
precompile(Plots.py_add_annotations, (Plots.Subplot{Plots.PyPlotBackend}, Int64, Float64, Plots.PlotText,))
precompile(Plots.plot!, (Array{Any, 1}, Array{Float64, 1},))
precompile(Plots.get_color_palette, (Symbol, ColorTypes.RGB{Float64}, Int64,))
precompile(Plots.py_add_annotations, (Plots.Subplot{Plots.PyPlotBackend}, Float64, Float64, Plots.PlotText,))
precompile(Plots.py_colormap, (Plots.ColorGradient, Float64,))
precompile(Plots.processMarkerArg, (Base.Dict{Symbol, Any}, Plots.Stroke,))
precompile(Plots.call, (Type{Plots.ColorVector}, Array{Symbol, 1},))
precompile(Plots.py_colormap, (Plots.ColorGradient, Void,))
precompile(Plots.py_fillcolor, (Base.Dict{Symbol, Any},))
precompile(Plots.processLineArg, (Base.Dict{Symbol, Any}, Float64,))
precompile(Plots.plot!, (Array{Any, 1}, Array{Float64, 2},))
precompile(Plots.processMarkerArg, (Base.Dict{Symbol, Any}, Symbol,))
precompile(Plots.call, (Type{Plots.Plot},))
precompile(Plots.call, (Type{Plots.ColorGradient}, Array{ColorTypes.RGBA{Float64}, 1},))
precompile(Plots.plot!, (Array{Any, 1}, Array{Int64, 1},))
precompile(Plots.getExtension, (UTF8String,))
precompile(Plots.call, (Array{Any, 1}, Type{Plots.EmptyLayout},))
precompile(Plots.update!, (Array{Any, 1}, Plots.Axis,))
precompile(Plots.frame, (Plots.Animation, Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots.processMarkerArg, (Base.Dict{Symbol, Any}, Int64,))
precompile(Plots.link_axes!, (Array{Plots.AbstractLayout, 1}, Symbol,))
precompile(Plots.call, (Type{Plots.ColorGradient}, Array{ColorTypes.RGB{Float64}, 1},))
precompile(Plots.fakedata, (Int64,))
precompile(Plots.plot!, (Array{Any, 1}, Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots.py_compute_axis_minval, (Plots.Axis,))
precompile(Plots.processLineArg, (Base.Dict{Symbol, Any}, Int64,))
precompile(Plots.command_idx, (Array{Base.Dict{Symbol, Any}, 1}, Base.Dict{Symbol, Any},))
precompile(Plots.getindex, (Plots.Axis, Symbol,))
precompile(Plots.__init__, ())
precompile(Plots.isvertical, (Base.Dict{Symbol, Any},))
precompile(Plots.getExtension, (ASCIIString,))
precompile(Plots.py_marker, (Symbol,))
precompile(Plots.py_init_subplot, (Plots.Plot{Plots.PyPlotBackend}, Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.processMarkerArg, (Base.Dict{Symbol, Any}, ColorTypes.RGBA{Float64},))
precompile(Plots.bucket_index, (Float64, Base.LinSpace{Float64},))
precompile(Plots.default, (Array{Any, 1},))
precompile(Plots.filter_data!, (Base.Dict{Symbol, Any}, Array{Int64, 1},))
precompile(Plots.call, (Type{Plots.ColorGradient}, Array{Symbol, 1}, Base.LinSpace{Float64},))
precompile(Plots.slice_arg, (Array{Symbol, 2}, Int64,))
precompile(Plots.getindex, (Plots.Plot{Plots.UnicodePlotsBackend}, Symbol,))
precompile(Plots.plot!, (Array{Any, 1},))
precompile(Plots.push!, (Plots.Plot{Plots.PyPlotBackend}, Array{Float64, 1}, Array{Float64, 1},))
precompile(Plots.processMarkerArg, (Base.Dict{Symbol, Any}, Float64,))
precompile(Plots.expand_extrema!, (Plots.Axis, Base.FloatRange{Float64},))
precompile(Plots.getindex, (Plots.Plot{Plots.PyPlotBackend}, Symbol,))
precompile(Plots.handle_dfs, (DataFrames.DataFrame, Base.Dict{Symbol, Any}, ASCIIString, Symbol,))
precompile(Plots.filter_data, (Base.UnitRange{Int64}, Array{Int64, 1},))
precompile(Plots.call, (Type{Plots.ColorWrapper}, ColorTypes.RGBA{Float64},))
precompile(Plots.processMarkerArg, (Base.Dict{Symbol, Any}, Array{Symbol, 2},))
precompile(Plots.processLineArg, (Base.Dict{Symbol, Any}, Array{Symbol, 2},))
precompile(Plots.color_or_nothing!, (Base.Dict{Symbol, Any}, Symbol,))
precompile(Plots.processMarkerArg, (Base.Dict{Symbol, Any}, Plots.Shape,))
precompile(Plots.py_fillcolormap, (Base.Dict{Symbol, Any},))
precompile(Plots.transpose_z, (Base.Dict{Symbol, Any}, Array{Float64, 2}, Bool,))
precompile(Plots.py_linecolor, (Base.Dict{Symbol, Any},))
precompile(Plots.call, (Type{Plots.OHLC}, Float64, Float64, Float64, Float64,))
precompile(Plots.setxy!, (Plots.Plot{Plots.PyPlotBackend}, Tuple{Array{Float64, 1}, Array{Float64, 1}}, Int64,))
precompile(Plots.push!, (Plots.Segments, Float64, Float64, Float64, Float64,))
precompile(Plots.handle_group, (DataFrames.DataFrame, Base.Dict{Symbol, Any},))
precompile(Plots.lightness_from_background, (ColorTypes.RGB{Float64},))
precompile(Plots.expand_extrema!, (Plots.Axis, Base.LinSpace{Float64},))
precompile(Plots.py_bbox, (Array{Any, 1},))
precompile(Plots.py_markerstrokecolor, (Base.Dict{Symbol, Any},))
precompile(Plots.arrow, ())
precompile(Plots.convert, (Type{Array{Float64, 1}}, Base.StepRange{Int64, Int64},))
precompile(Plots.expand_extrema!, (Plots.Axis, Array{Float64, 1},))
precompile(Plots.convertLegendValue, (Symbol,))
precompile(Plots.slice_arg, (Array{Measures.Length{:mm, Float64}, 2}, Int64,))
precompile(Plots.calc_num_subplots, (Plots.GridLayout,))
precompile(Plots.processFillArg, (Base.Dict{Symbol, Any}, Int64,))
precompile(Plots.slice_arg, (Array{Plots.ColorWrapper, 2}, Int64,))
precompile(Plots.slice_arg, (Array{ASCIIString, 2}, Int64,))
precompile(Plots.filter_data, (Array{Float64, 1}, Array{Int64, 1},))
precompile(Plots.py_linecolormap, (Base.Dict{Symbol, Any},))
precompile(Plots.discrete_value!, (Plots.Axis, ASCIIString,))
precompile(Plots.allShapes, (ColorTypes.RGBA{Float64},))
precompile(Plots.expand_extrema!, (Plots.Axis, Array{Int64, 1},))
precompile(Plots.py_color_fix, (Tuple{Float64, Float64, Float64, Float64}, Base.LinSpace{Float64},))
precompile(Plots.discrete_value!, (Plots.Axis, Symbol,))
precompile(Plots.push!, (Plots.Segments, Float64, Int64, Int64, Float64,))
precompile(Plots.expand_extrema!, (Plots.Axis, Plots.Surface{Array{Float64, 2}},))
precompile(Plots.heatmap_edges, (Array{Float64, 1},))
precompile(Plots.expand_extrema!, (Plots.Axis, Base.StepRange{Int64, Int64},))
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, Plots.Shape, Symbol,))
precompile(Plots.py_color, (Plots.ColorWrapper, Float64,))
precompile(Plots.call, (Type{Plots.GridLayout}, Int64, Int64,))
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, ColorTypes.RGBA{Float64}, Symbol,))
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, Bool, Symbol,))
precompile(Plots.hvline_limits, (Plots.Axis,))
precompile(Plots.current, ())
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, Float64, Symbol,))
precompile(Plots.compute_gridsize, (Int64, Int64, Int64,))
precompile(Plots.py_color, (ColorTypes.RGB{Float64}, Void,))
precompile(Plots.interpolate_rgb, (ColorTypes.RGBA{Float64}, ColorTypes.RGBA{Float64}, Float64,))
precompile(Plots.expand_extrema!, (Plots.Axis, Base.UnitRange{Int64},))
precompile(Plots.allShapes, (Float64,))
precompile(Plots.warn_on_deprecated_backend, (Symbol,))
precompile(Plots.py_color_fix, (Tuple{Float64, Float64, Float64, Float64}, Array{Int64, 1},))
precompile(Plots.setindex!, (Plots.GridLayout, Plots.EmptyLayout, Int64, Int64,))
precompile(Plots.expand_extrema!, (Plots.Axis, Tuple{Int64, Int64},))
precompile(Plots.allShapes, (Int64,))
precompile(Plots.py_markercolormap, (Base.Dict{Symbol, Any},))
precompile(Plots.interpolate_rgb, (ColorTypes.RGB{Float64}, ColorTypes.RGB{Float64}, Float64,))
precompile(Plots.get_ticks, (Plots.Axis,))
precompile(Plots.backend, ())
precompile(Plots.get_xy, (Plots.OHLC{Float64}, Int64, Float64,))
precompile(Plots.allShapes, (Plots.Stroke,))
precompile(Plots.replaceAliases!, (Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any},))
precompile(Plots._create_backend_figure, (Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots.png, (Plots.Plot{Plots.PyPlotBackend}, UTF8String,))
precompile(Plots.processFillArg, (Base.Dict{Symbol, Any}, Symbol,))
precompile(Plots.allShapes, (Plots.Shape,))
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, Plots.Stroke, Symbol,))
precompile(Plots.py_color_fix, (Tuple{Float64, Float64, Float64, Float64}, Array{Float64, 1},))
precompile(Plots.plot, (Array{Float64, 1},))
precompile(Plots.rowsize, (Expr,))
precompile(Plots.get_axis, (Plots.Subplot{Plots.PyPlotBackend}, Symbol,))
precompile(Plots.png, (Plots.Plot{Plots.PyPlotBackend}, ASCIIString,))
precompile(Plots.expand_extrema!, (Plots.Axis, Tuple{Float64, Float64},))
precompile(Plots._markershape_supported, (Plots.PyPlotBackend, Plots.Shape,))
precompile(Plots.processFillArg, (Base.Dict{Symbol, Any}, Bool,))
precompile(Plots.supported_types, (Plots.PyPlotBackend,))
precompile(Plots.get_axis, (Plots.Subplot{Plots.UnicodePlotsBackend}, Symbol,))
precompile(Plots.setindex!, (Plots.GridLayout, Plots.GridLayout, Int64, Int64,))
precompile(Plots.plot, ())
precompile(Plots.py_color_fix, (Tuple{Float64, Float64, Float64, Float64}, Base.FloatRange{Float64},))
precompile(Plots._markershape_supported, (Plots.PyPlotBackend, Symbol,))
precompile(Plots.ok, (Float64, Float64, Int64,))
precompile(Plots.allShapes, (Symbol,))
precompile(Plots._initialize_backend, (Plots.PyPlotBackend,))
precompile(Plots.plot, (Plots.Plot{Plots.PyPlotBackend}, Plots.Plot{Plots.PyPlotBackend},))
precompile(Plots.bar, (Array{Float64, 1},))
precompile(Plots.call, (Type{Plots.ColorGradient}, Array{ColorTypes.RGBA{Float64}, 1}, Base.LinSpace{Float64},))
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, Int64, Symbol,))
precompile(Plots.nanappend!, (Array{Float64, 1}, Array{Float64, 1},))
precompile(Plots.autopick, (Array{ColorTypes.RGBA, 1}, Int64,))
precompile(Plots.compute_xyz, (Void, Array{Int64, 1}, Void,))
precompile(Plots._add_markershape, (Base.Dict{Symbol, Any},))
precompile(Plots._backend_instance, (Symbol,))
precompile(Plots.py_color, (ColorTypes.RGBA{Float64}, Float64,))
precompile(Plots.update_child_bboxes!, (Plots.GridLayout,))
precompile(Plots._replace_markershape, (Symbol,))
precompile(Plots.has_black_border_for_default, (Symbol,))
precompile(Plots._filter_input_data!, (Base.Dict{Symbol, Any},))
precompile(Plots.compute_xyz, (Array{Float64, 1}, Array{Float64, 1}, Base.UnitRange{Int64},))
precompile(Plots.text, (ASCIIString, Int64, Symbol,))
precompile(Plots.allShapes, (Array{Symbol, 2},))
precompile(Plots.py_color_fix, (Tuple{Float64, Float64, Float64, Float64}, Base.UnitRange{Int64},))
precompile(Plots.replaceAlias!, (Base.Dict{Symbol, Any}, Symbol, Base.Dict{Symbol, Any},))
precompile(Plots.compute_xyz, (Array{ASCIIString, 1}, Array{Float64, 1}, Void,))
precompile(Plots.layout_args, (Int64,))
precompile(Plots.text, (ASCIIString, Symbol, Int64,))
precompile(Plots.compute_xyz, (Base.FloatRange{Float64}, Base.FloatRange{Float64}, Plots.Surface{Array{Float64, 2}},))
precompile(Plots.all3D, (Base.Dict{Symbol, Any},))
precompile(Plots.cycle, (Array{Plots.Subplot, 1}, Int64,))
precompile(Plots.compute_xyz, (Base.StepRange{Int64, Int64}, Array{Float64, 1}, Void,))
precompile(Plots.plotarea!, (Plots.Subplot{Plots.PyPlotBackend}, Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.like_surface, (Symbol,))
precompile(Plots.build_layout, (Base.Dict{Symbol, Any},))
precompile(Plots.compute_xyz, (Array{Union{UTF8String, ASCIIString}, 1}, Array{Union{UTF8String, ASCIIString}, 1}, Plots.Surface{Array{Float64, 2}},))
precompile(Plots.py_linestyle, (Symbol, Symbol,))
precompile(Plots.plot!, (Array{Float64, 2},))
precompile(Plots.plot!, (Plots.Plot{Plots.PyPlotBackend}, Array{Float64, 2},))
precompile(Plots.call, (Type{Plots.ColorGradient}, Array{ColorTypes.RGB{Float64}, 1}, Base.LinSpace{Float64},))
precompile(Plots.py_color, (ColorTypes.RGBA{Float64}, Void,))
precompile(Plots.yaxis!, (ASCIIString, Symbol,))
precompile(Plots.compute_xyz, (Base.LinSpace{Float64}, Array{Float64, 1}, Void,))
precompile(Plots.isijulia, ())
precompile(Plots.addExtension, (UTF8String, ASCIIString,))
precompile(Plots.call, (Type{Plots.ColorGradient}, Array{ColorTypes.RGBA{Float64}, 1}, Array{Float64, 1},))
precompile(Plots.expand_extrema!, (Plots.Subplot{Plots.PyPlotBackend}, Float64, Float64, Float64, Float64,))
precompile(Plots.plotarea!, (Plots.GridLayout, Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.layout_args, (Base.Dict{Symbol, Any},))
precompile(Plots.compute_xyz, (Base.FloatRange{Float64}, Array{Float64, 1}, Void,))
precompile(Plots.addExtension, (ASCIIString, ASCIIString,))
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, Symbol, Symbol,))
precompile(Plots._initialize_backend, (Plots.PlotlyBackend,))
precompile(Plots.push!, (Plots.Plot{Plots.PyPlotBackend}, Int64, Float64, Float64,))
precompile(Plots.bbox!, (Plots.Subplot{Plots.PyPlotBackend}, Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.vline!, (Array{Int64, 1},))
precompile(Plots.supported_markers, (Plots.PyPlotBackend,))
precompile(Plots.bbox!, (Plots.GridLayout, Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.stroke, (Int64,))
precompile(Plots.compute_xyz, (Array{Float64, 1}, Array{Float64, 1}, Void,))
precompile(Plots.right, (Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.extractGroupArgs, (Symbol, DataFrames.DataFrame, Symbol,))
precompile(Plots.generate_colorgradient, (ColorTypes.RGB{Float64},))
precompile(Plots.expand_extrema!, (Plots.Extrema, Bool,))
precompile(Plots.py_colormap, (Plots.ColorWrapper, Void,))
precompile(Plots.expand_extrema!, (Plots.Extrema, Float64,))
precompile(Plots.contour, (Base.FloatRange{Float64},))
precompile(Plots.bottom, (Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.text, (ASCIIString, Symbol,))
precompile(Plots.create_grid, (Symbol,))
precompile(Plots.expand_extrema!, (Plots.Axis, Bool,))
precompile(Plots.handleColors!, (Base.Dict{Symbol, Any}, Array{Symbol, 2}, Symbol,))
precompile(Plots.convertToAnyVector, (Void, Base.Dict{Symbol, Any},))
precompile(Plots.py_dpi_scale, (Plots.Plot{Plots.PyPlotBackend}, Int64,))
precompile(Plots.png, (ASCIIString,))
precompile(Plots.supported_scales, (Plots.PyPlotBackend,))
precompile(Plots.convertColor, (ColorTypes.RGBA{Float64}, Float64,))
precompile(Plots.extendSeriesData, (Array{Float64, 1}, Float64,))
precompile(Plots.compute_xyz, (Void, Array{Float64, 1}, Void,))
precompile(Plots.py_color, (Symbol,))
precompile(Plots.expand_extrema!, (Plots.Extrema, Int64,))
precompile(Plots.supported_styles, (Plots.PyPlotBackend,))
precompile(Plots._initialize_backend, (Plots.UnicodePlotsBackend,))
precompile(Plots._initialize_backend, (Plots.GRBackend,))
precompile(Plots.is3d, (Symbol,))
precompile(Plots.supported_types, (Plots.UnicodePlotsBackend,))
precompile(Plots.compute_xyz, (Array{Float64, 1}, Function, Void,))
precompile(Plots.py_color, (Symbol, Void,))
precompile(Plots.trueOrAllTrue, (Function, Array{Symbol, 2},))
precompile(Plots.typemin, (Measures.Length{:mm, Float64},))
precompile(Plots.expand_extrema!, (Plots.Axis, Float64,))
precompile(Plots.get_subplot_index, (Plots.Plot{Plots.UnicodePlotsBackend}, Plots.Subplot{Plots.UnicodePlotsBackend},))
precompile(Plots.leftpad, (Plots.GridLayout,))
precompile(Plots._update_subplot_args, (Plots.Plot{Plots.UnicodePlotsBackend}, Plots.Subplot{Plots.UnicodePlotsBackend}, Base.Dict{Symbol, Any}, Int64,))
precompile(Plots.centers, (Base.LinSpace{Float64},))
precompile(Plots.expand_extrema!, (Plots.Axis, Int64,))
precompile(Plots.text, (ASCIIString,))
precompile(Plots.convertColor, (ColorTypes.RGB{Float64},))
precompile(Plots.py_color, (Plots.ColorWrapper, Void,))
precompile(Plots.py_color, (Plots.ColorWrapper,))
precompile(Plots.trueOrAllTrue, (Function, Int64,))
precompile(Plots.convertColor, (ColorTypes.RGBA{Float64},))
precompile(Plots.extrema, (Plots.Axis,))
precompile(Plots.top, (Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.convertColor, (Symbol,))
precompile(Plots.size, (Plots.Surface{Array{Float64, 2}},))
precompile(Plots.isdark, (ColorTypes.RGB{Float64},))
precompile(Plots.rowsize, (Symbol,))
precompile(Plots.series_list, (Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.convertToAnyVector, (Array{Float64, 1}, Base.Dict{Symbol, Any},))
precompile(Plots.update!, (Plots.Axis,))
precompile(Plots.push!, (Plots.Plot{Plots.PyPlotBackend}, Float64, Array{Float64, 1},))
precompile(Plots.wraptuple, (Bool,))
precompile(Plots.call, (Type{Plots.Shape}, Array{Tuple{Float64, Float64}, 1},))
precompile(Plots.slice_arg, (Base.StepRange{Int64, Int64}, Int64,))
precompile(Plots.sticks_fillfrom, (Void, Int64,))
precompile(Plots.left, (Measures.BoundingBox{Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}, Tuple{Measures.Length{:mm, Float64}, Measures.Length{:mm, Float64}}},))
precompile(Plots.gr, ())
precompile(Plots.leftpad, (Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.supported_markers, ())
precompile(Plots.colorscheme, (Symbol,))
precompile(Plots.convertColor, (ColorTypes.RGB{Float64}, Void,))
precompile(Plots.isscalar, (Int64,))
precompile(Plots.wraptuple, (Int64,))
precompile(Plots.call, (Type{Plots.Plot}, Plots.UnicodePlotsBackend, Int64, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Array{Plots.Series, 1}, Void, Array{Plots.Subplot, 1}, Base.Dict{Any, Plots.Subplot}, Plots.EmptyLayout, Array{Plots.Subplot, 1}, Bool,))
precompile(Plots.supported_styles, ())
precompile(Plots.frame, (Plots.Animation,))
precompile(Plots.toppad, (Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.link_axes!, (Plots.Subplot{Plots.UnicodePlotsBackend}, Symbol,))
precompile(Plots.convertLegendValue, (Bool,))
precompile(Plots.convertColor, (ColorTypes.RGBA{Float64}, Void,))
precompile(Plots.plotly, ())
precompile(Plots.ispositive, (Measures.Length{:mm, Float64},))
precompile(Plots.rightpad, (Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.call, (Type{Plots.GroupBy}, Array{ASCIIString, 1}, Array{Array{Int64, 1}, 1},))
precompile(Plots.px2inch, (Int64,))
precompile(Plots.slice_arg, (Tuple{Int64, Int64}, Int64,))
precompile(Plots.call, (Type{Plots.UnicodePlotsBackend},))
precompile(Plots.trueOrAllTrue, (Function, Symbol,))
precompile(Plots.bottompad, (Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.annotate!, (Array{Tuple{Int64, Float64, Plots.PlotText}, 1},))
precompile(Plots.get_subplot_index, (Plots.Plot{Plots.PyPlotBackend}, Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.call, (Type{Plots.Plot}, Plots.PyPlotBackend, Int64, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Array{Plots.Series, 1}, Void, Array{Plots.Subplot, 1}, Base.Dict{Any, Plots.Subplot}, Plots.EmptyLayout, Array{Plots.Subplot, 1}, Bool,))
precompile(Plots.title!, (ASCIIString,))
precompile(Plots.slice_arg, (Bool, Int64,))
precompile(Plots.wraptuple, (Float64,))
precompile(Plots.bottompad, (Plots.GridLayout,))
precompile(Plots.py_stepstyle, (Symbol,))
precompile(Plots.link_axes!, (Plots.Subplot{Plots.PyPlotBackend}, Symbol,))
precompile(Plots.call, (Type{Plots.GRBackend},))
precompile(Plots.unicodeplots, ())
precompile(Plots.rightpad, (Plots.GridLayout,))
precompile(Plots.call, (Type{Plots.PlotlyBackend},))
precompile(Plots.py_color, (ColorTypes.RGBA{Float64},))
precompile(Plots.toppad, (Plots.GridLayout,))
precompile(Plots.calc_edges, (Array{Float64, 1}, Int64,))
precompile(Plots.colorscheme, (ColorTypes.RGBA{Float64},))
precompile(Plots.slice_arg, (Int64, Int64,))
precompile(Plots.pyplot, ())
precompile(Plots._update_subplot_args, (Plots.Plot{Plots.PyPlotBackend}, Plots.Subplot{Plots.PyPlotBackend}, Base.Dict{Symbol, Any}, Int64,))
precompile(Plots._series_added, (Plots.Plot{Plots.PyPlotBackend}, Plots.Series,))
precompile(Plots.get_color_palette, (Array{ColorTypes.RGBA, 1}, ColorTypes.RGB{Float64}, Int64,))
precompile(Plots._initialize_subplot, (Plots.Plot{Plots.UnicodePlotsBackend}, Plots.Subplot{Plots.UnicodePlotsBackend},))
precompile(Plots.slice_arg, (Void, Int64,))
precompile(Plots.cycle, (Int64, Int64,))
precompile(Plots._initialize_subplot, (Plots.Plot{Plots.PyPlotBackend}, Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.call, (Type{Plots.PyPlotBackend},))
precompile(Plots.update_child_bboxes!, (Plots.Subplot{Plots.PyPlotBackend}, Array{Measures.Length{:mm, Float64}, 1},))
precompile(Plots.filter_data, (Void, Array{Int64, 1},))
precompile(Plots.slice_arg, (Symbol, Int64,))
precompile(Plots.slice_arg, (ASCIIString, Int64,))
precompile(Plots.layout_args, (Plots.GridLayout,))
precompile(Plots.wraptuple, (Tuple{},))
precompile(Plots.calc_num_subplots, (Plots.EmptyLayout,))
precompile(Plots.wraptuple, (Tuple{Symbol, Float64, Plots.Stroke},))
precompile(Plots.wraptuple, (Tuple{ASCIIString, Tuple{Int64, Int64}, Base.StepRange{Int64, Int64}, Symbol},))
precompile(Plots.wraptuple, (Tuple{Int64, Symbol, Float64, Array{Symbol, 2}},))
precompile(Plots.wraptuple, (Tuple{Int64, Array{Symbol, 2}},))
precompile(Plots.wraptuple, (Tuple{ASCIIString, Symbol},))
precompile(Plots._replace_markershape, (Array{Symbol, 2},))
precompile(Plots.wraptuple, (Tuple{Symbol, Int64},))
precompile(Plots.wraptuple, (Tuple{Array{Symbol, 2}, Int64},))
precompile(Plots.wraptuple, (Tuple{Int64, Symbol, Symbol},))
precompile(Plots.wraptuple, (Tuple{Int64, Float64, Symbol, Plots.Stroke},))
precompile(Plots.wraptuple, (Tuple{Float64, Array{Symbol, 2}, Int64},))
precompile(Plots.py_color, (ColorTypes.RGB{Float64},))
precompile(Plots.wraptuple, (Tuple{Int64, Float64, Symbol},))
precompile(Plots.tovec, (Array{Float64, 1},))
precompile(Plots.get_subplot, (Plots.Plot{Plots.PyPlotBackend}, Plots.Subplot{Plots.PyPlotBackend},))
precompile(Plots.eltype, (Plots.Surface{Array{Float64, 2}},))
precompile(Plots.nobigs, (Array{Float64, 1},))
precompile(Plots.get_subplot, (Plots.Plot{Plots.UnicodePlotsBackend}, Plots.Subplot{Plots.UnicodePlotsBackend},))
precompile(Plots.annotations, (Array{Any, 1},))
precompile(Plots.wraptuple, (Tuple{Int64, Symbol},))
precompile(Plots.colorscheme, (Plots.ColorWrapper,))
precompile(Plots.colorscheme, (Plots.ColorGradient,))
precompile(Plots.text, (Plots.PlotText,))
precompile(Plots._replace_markershape, (Plots.Shape,))
precompile(Plots.wraptuple, (Tuple{Array{Symbol, 2}, Int64, Float64, Plots.Stroke},))
precompile(Plots.wraptuple, (Tuple{Plots.Shape, Int64, ColorTypes.RGBA{Float64}},))
end
+417 -593
View File
File diff suppressed because it is too large Load Diff
+82 -302
View File
@@ -6,24 +6,21 @@
# 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 @compat(Union{Function, AVec{Function}})
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(d, :seriestype, :none))
# unknown
convertToAnyVector(x, d::KW) = error("No user recipe defined for $(typeof(x))")
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
# missing
convertToAnyVector(v::Void, d::KW) = Any[nothing], nothing
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
# fixed number of blank series
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
# numeric vector
convertToAnyVector(v::AVec{T}, d::KW) where {T<:Number} = Any[v], nothing
convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
# string vector
convertToAnyVector(v::AVec{T}, d::KW) where {T<:AbstractString} = Any[v], nothing
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::KW) = Any[v], nothing
function convertToAnyVector(v::AMat, d::KW)
if all3D(d)
@@ -39,13 +36,10 @@ convertToAnyVector(f::Function, d::KW) = Any[f], nothing
# surface
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
# volume
convertToAnyVector(v::Volume, d::KW) = Any[v], nothing
# # vector of OHLC
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# # dates
# dates
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
# list of things (maybe other vectors, functions, or something else)
@@ -99,8 +93,8 @@ nobigs(v) = v
end
# not allowed
compute_xyz(x::Void, y::FuncOrFuncs{F}, z) where {F<:Function} = error("If you want to plot the function `$y`, you need to define the x values!")
compute_xyz(x::Void, y::Void, z::FuncOrFuncs{F}) where {F<:Function} = error("If you want to plot the function `$z`, you need to define x and y values!")
compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
# --------------------------------------------------------------------
@@ -109,120 +103,63 @@ compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
# we are going to build recipes to do the processing and splitting of the args
# ensure we dispatch to the slicer
struct SliceIt end
immutable SliceIt end
# the catch-all recipes
@recipe function f(::Type{SliceIt}, x, y, z)
# @show "HERE", typeof((x,y,z))
xs, _ = convertToAnyVector(x, d)
ys, _ = convertToAnyVector(y, d)
zs, _ = convertToAnyVector(z, d)
# handle data with formatting attached
if typeof(x) <: Formatted
xformatter := x.formatter
x = x.data
end
if typeof(y) <: Formatted
yformatter := y.formatter
y = y.data
end
if typeof(z) <: Formatted
zformatter := z.formatter
z = z.data
end
xs, _ = convertToAnyVector(x, plotattributes)
ys, _ = convertToAnyVector(y, plotattributes)
zs, _ = convertToAnyVector(z, plotattributes)
fr = pop!(plotattributes, :fillrange, nothing)
fr = pop!(d, :fillrange, nothing)
fillranges, _ = if typeof(fr) <: Number
([fr],nothing)
else
convertToAnyVector(fr, plotattributes)
convertToAnyVector(fr, d)
end
mf = length(fillranges)
rib = pop!(plotattributes, :ribbon, nothing)
ribbons, _ = if typeof(rib) <: Number
([fr],nothing)
else
convertToAnyVector(rib, plotattributes)
end
mr = length(ribbons)
# @show zs
mx = length(xs)
my = length(ys)
mz = length(zs)
if mx > 0 && my > 0 && mz > 0
for i in 1:max(mx, my, mz)
# add a new series
di = copy(plotattributes)
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
# ret = Any[]
for i in 1:max(mx, my, mz)
# add a new series
di = copy(d)
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
# @show i, typeof((xi, yi, zi))
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
# @show i, typeof((di[:x], di[:y], di[:z]))
# handle fillrange
fr = fillranges[mod1(i,mf)]
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
# handle fillrange
fr = fillranges[mod1(i,mf)]
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
# handle ribbons
rib = ribbons[mod1(i,mr)]
di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib
push!(series_list, RecipeData(di, ()))
end
# @show i, di[:x], di[:y], di[:z]
push!(series_list, RecipeData(di, ()))
end
nothing # don't add a series for the main block
end
# this is the default "type recipe"... just pass the object through
@recipe f(::Type{T}, v::T) where {T<:Any} = v
@recipe f{T<:Any}(::Type{T}, v::T) = v
# this should catch unhandled "series recipes" and error with a nice message
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
@recipe f{V<:Val}(::Type{V}, x, y, z) = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
_apply_type_recipe(d, v) = RecipesBase.apply_recipe(d, typeof(v), v)[1].args[1]
# Handle type recipes when the recipe is defined on the elements.
# This sort of recipe should return a pair of functions... one to convert to number,
# and one to format tick values.
function _apply_type_recipe(d, v::AbstractArray)
isempty(v) && return Float64[]
args = RecipesBase.apply_recipe(d, typeof(v[1]), v[1])[1].args
if length(args) == 2 && typeof(args[1]) <: Function && typeof(args[2]) <: Function
numfunc, formatter = args
Formatted(map(numfunc, v), formatter)
else
v
end
end
# # special handling for Surface... need to properly unwrap and re-wrap
# function _apply_type_recipe(d, v::Surface)
# T = eltype(v.surf)
# @show T
# if T <: Integer || T <: AbstractFloat
# v
# else
# ret = _apply_type_recipe(d, v.surf)
# if typeof(ret) <: Formatted
# Formatted(Surface(ret.data), ret.formatter)
# else
# v
# end
# end
# end
# don't do anything for ints or floats
_apply_type_recipe(d, v::AbstractArray{T}) where {T<:Union{Integer,AbstractFloat}} = v
# handle "type recipes" by converting inputs, and then either re-calling or slicing
@recipe function f(x, y, z)
did_replace = false
newx = _apply_type_recipe(plotattributes, x)
newx = _apply_type_recipe(d, x)
x === newx || (did_replace = true)
newy = _apply_type_recipe(plotattributes, y)
newy = _apply_type_recipe(d, y)
y === newy || (did_replace = true)
newz = _apply_type_recipe(plotattributes, z)
newz = _apply_type_recipe(d, z)
z === newz || (did_replace = true)
if did_replace
newx, newy, newz
@@ -232,9 +169,9 @@ _apply_type_recipe(d, v::AbstractArray{T}) where {T<:Union{Integer,AbstractFloat
end
@recipe function f(x, y)
did_replace = false
newx = _apply_type_recipe(plotattributes, x)
newx = _apply_type_recipe(d, x)
x === newx || (did_replace = true)
newy = _apply_type_recipe(plotattributes, y)
newy = _apply_type_recipe(d, y)
y === newy || (did_replace = true)
if did_replace
newx, newy
@@ -243,7 +180,7 @@ end
end
end
@recipe function f(y)
newy = _apply_type_recipe(plotattributes, y)
newy = _apply_type_recipe(d, y)
if y !== newy
newy
else
@@ -256,7 +193,7 @@ end
@recipe function f(v1, v2, v3, v4, vrest...)
did_replace = false
newargs = map(v -> begin
newv = _apply_type_recipe(plotattributes, v)
newv = _apply_type_recipe(d, v)
if newv !== v
did_replace = true
end
@@ -273,60 +210,29 @@ end
# # 1 argument
# # --------------------------------------------------------------------
# helper function to ensure relevant attributes are wrapped by Surface
function wrap_surfaces(d::KW)
if haskey(d, :fill_z)
v = d[:fill_z]
if !isa(v, Surface)
d[:fill_z] = Surface(v)
end
end
end
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
@recipe f(n::Integer) = n, n, n
# return a surface if this is a 3d plot, otherwise let it be sliced up
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat}
if all3D(plotattributes)
@recipe function f{T<:Number}(mat::AMat{T})
if all3D(d)
n,m = size(mat)
wrap_surfaces(plotattributes)
SliceIt, 1:m, 1:n, Surface(mat)
else
SliceIt, nothing, mat, nothing
end
end
# if a matrix is wrapped by Formatted, do similar logic, but wrap data with Surface
@recipe function f(fmt::Formatted{T}) where T<:AbstractMatrix
if all3D(plotattributes)
mat = fmt.data
n,m = size(mat)
wrap_surfaces(plotattributes)
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
else
SliceIt, nothing, fmt, nothing
end
end
# assume this is a Volume, so construct one
@recipe function f(vol::AbstractArray{T,3}, args...) where T<:Number
seriestype := :volume
SliceIt, nothing, Volume(vol, args...), nothing
end
# # images - grays
@recipe function f(mat::AMat{T}) where T<:Gray
n, m = size(mat)
if is_seriestype_supported(:image)
@recipe function f{T<:Gray}(mat::AMat{T})
if nativeImagesSupported()
seriestype := :image
yflip --> true
n, m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
else
seriestype := :heatmap
yflip --> true
cbar --> false
fillcolor --> ColorGradient([:black, :white])
SliceIt, 1:m, 1:n, Surface(convert(Matrix{Float64}, mat))
end
@@ -334,18 +240,15 @@ end
# # images - colors
@recipe function f(mat::AMat{T}) where T<:Colorant
n, m = size(mat)
if is_seriestype_supported(:image)
@recipe function f{T<:Colorant}(mat::AMat{T})
if nativeImagesSupported()
seriestype := :image
yflip --> true
n, m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
else
seriestype := :heatmap
yflip --> true
cbar --> false
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
z, d[:fillcolor] = replace_image_with_heatmap(mat)
SliceIt, 1:m, 1:n, Surface(z)
end
end
@@ -355,55 +258,30 @@ end
@recipe function f(shape::Shape)
seriestype --> :shape
coords(shape)
shape_coords(shape)
end
@recipe function f(shapes::AVec{Shape})
seriestype --> :shape
coords(shapes)
shape_coords(shapes)
end
@recipe function f(shapes::AMat{Shape})
seriestype --> :shape
for j in 1:size(shapes,2)
@series coords(vec(shapes[:,j]))
@series shape_coords(vec(shapes[:,j]))
end
end
#
#
# # function without range... use the current range of the x-axis
# @recipe function f(f::FuncOrFuncs)
# plt = d[:plot_object]
# f, xmin(plt), xmax(plt)
# end
# function without range... use the current range of the x-axis
@recipe function f(f::FuncOrFuncs{F}) where F<:Function
plt = plotattributes[:plot_object]
xmin, xmax = try
axis_limits(plt[1][:xaxis])
catch
xm = tryrange(f, [-5,-1,0,0.01])
xm, tryrange(f, filter(x->x>xm, [5,1,0.99, 0, -0.01]))
end
f, xmin, xmax
end
# try some intervals over which the function may be defined
function tryrange(F::AbstractArray, vec)
rets = [tryrange(f, vec) for f in F] # get the preferred for each
maxind = maximum(indexin(rets, vec)) # get the last attempt that succeeded (most likely to fit all)
rets .= [tryrange(f, vec[maxind:maxind]) for f in F] # ensure that all functions compute there
rets[1]
end
function tryrange(F, vec)
for v in vec
try
tmp = F(v)
return v
catch
end
end
error("$F is not a Function, or is not defined at any of the values $vec")
end
#
# # --------------------------------------------------------------------
# # 2 arguments
@@ -413,9 +291,8 @@ end
# # if functions come first, just swap the order (not to be confused with parametric functions...
# # as there would be more than one function passed in)
@recipe function f(f::FuncOrFuncs{F}, x) where F<:Function
F2 = typeof(x)
@assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here
@recipe function f(f::FuncOrFuncs, x)
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
x, f
end
@@ -444,7 +321,6 @@ end
# seriestype := :path3d
# end
# end
wrap_surfaces(plotattributes)
SliceIt, x, y, z
end
@@ -454,7 +330,6 @@ end
@recipe function f(x::AVec, y::AVec, zf::Function)
# x = X <: Number ? sort(x) : x
# y = Y <: Number ? sort(y) : y
wrap_surfaces(plotattributes)
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
end
@@ -462,45 +337,12 @@ end
# # surface-like... matrix grid
@recipe function f(x::AVec, y::AVec, z::AMat)
if !like_surface(get(plotattributes, :seriestype, :none))
plotattributes[:seriestype] = :contour
if !like_surface(get(d, :seriestype, :none))
d[:seriestype] = :contour
end
wrap_surfaces(plotattributes)
SliceIt, x, y, Surface(z)
end
# # images - grays
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray
if is_seriestype_supported(:image)
seriestype := :image
yflip --> true
SliceIt, x, y, Surface(mat)
else
seriestype := :heatmap
yflip --> true
cbar --> false
fillcolor --> ColorGradient([:black, :white])
SliceIt, x, y, Surface(convert(Matrix{Float64}, mat))
end
end
# # images - colors
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant
if is_seriestype_supported(:image)
seriestype := :image
yflip --> true
SliceIt, x, y, Surface(mat)
else
seriestype := :heatmap
yflip --> true
cbar --> false
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
SliceIt, x, y, Surface(z)
end
end
#
#
# # --------------------------------------------------------------------
@@ -509,23 +351,16 @@ end
#
# # special handling... xmin/xmax with parametric function(s)
@recipe function f(f::Function, xmin::Number, xmax::Number)
xs = adapted_grid(f, (xmin, xmax))
xs, f
end
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
xs = Any[adapted_grid(f, (xmin, xmax)) for f in fs]
xs, fs
end
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, linspace(umin, umax, n)
@recipe f(f::FuncOrFuncs, xmin::Number, xmax::Number) = linspace(xmin, xmax, 100), f
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n)
#
# # special handling... 3D parametric function(s)
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function}
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec)
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
end
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200) where {F<:Function,G<:Function,H<:Function}
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints = 200)
fx, fy, fz, linspace(umin, umax, numPoints)
end
@@ -540,28 +375,28 @@ end
#
# # (x,y) tuples
@recipe f(xy::AVec{Tuple{R1,R2}}) where {R1<:Number,R2<:Number} = unzip(xy)
@recipe f(xy::Tuple{R1,R2}) where {R1<:Number,R2<:Number} = [xy[1]], [xy[2]]
@recipe f{R1<:Number,R2<:Number}(xy::AVec{Tuple{R1,R2}}) = unzip(xy)
@recipe f{R1<:Number,R2<:Number}(xy::Tuple{R1,R2}) = [xy[1]], [xy[2]]
#
# # (x,y,z) tuples
@recipe f(xyz::AVec{Tuple{R1,R2,R3}}) where {R1<:Number,R2<:Number,R3<:Number} = unzip(xyz)
@recipe f(xyz::Tuple{R1,R2,R3}) where {R1<:Number,R2<:Number,R3<:Number} = [xyz[1]], [xyz[2]], [xyz[3]]
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::AVec{Tuple{R1,R2,R3}}) = unzip(xyz)
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::Tuple{R1,R2,R3}) = [xyz[1]], [xyz[2]], [xyz[3]]
# these might be points+velocity, or OHLC or something else
@recipe f(xyuv::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
@recipe f(xyuv::Tuple{R1,R2,R3,R4}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::AVec{Tuple{R1,R2,R3,R4}}) = get(d,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::Tuple{R1,R2,R3,R4}) = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
#
# # 2D FixedSizeArrays
@recipe f(xy::AVec{FixedSizeArrays.Vec{2,T}}) where {T<:Number} = unzip(xy)
@recipe f(xy::FixedSizeArrays.Vec{2,T}) where {T<:Number} = [xy[1]], [xy[2]]
@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]]
#
# # 3D FixedSizeArrays
@recipe f(xyz::AVec{FixedSizeArrays.Vec{3,T}}) where {T<:Number} = unzip(xyz)
@recipe f(xyz::FixedSizeArrays.Vec{3,T}) where {T<:Number} = [xyz[1]], [xyz[2]], [xyz[3]]
@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]]
#
# # --------------------------------------------------------------------
@@ -581,69 +416,14 @@ end
# nothing
# end
splittable_kw(key, val, lengthGroup) = false
splittable_kw(key, val::AbstractArray, lengthGroup) = (key != :group) && size(val,1) == lengthGroup
splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, lengthGroup))
splittable_kw(key, val::SeriesAnnotations, lengthGroup) = splittable_kw(key, val.strs, lengthGroup)
split_kw(key, val::AbstractArray, indices) = val[indices, fill(Colon(), ndims(val)-1)...]
split_kw(key, val::Tuple, indices) = Tuple(split_kw(key, v, indices) for v in val)
function split_kw(key, val::SeriesAnnotations, indices)
split_strs = split_kw(key, val.strs, indices)
return SeriesAnnotations(split_strs, val.font, val.baseshape, val.scalefactor)
end
function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1])
y_mat = Array{promote_type(eltype(y), typeof(def_val))}(length(keys(x_ind)), length(groupby.groupLabels))
fill!(y_mat, def_val)
for i in 1:length(groupby.groupLabels)
xi = x[groupby.groupIds[i]]
yi = y[groupby.groupIds[i]]
y_mat[getindex.(x_ind, xi), i] = yi
end
return y_mat
end
groupedvec2mat(x_ind, x, y::Tuple, groupby) = Tuple(groupedvec2mat(x_ind, x, v, groupby) for v in y)
group_as_matrix(t) = false
# split the group into 1 series per group, and set the label and idxfilter for each
@recipe function f(groupby::GroupBy, args...)
lengthGroup = maximum(union(groupby.groupIds...))
if !(group_as_matrix(args[1]))
for (i,glab) in enumerate(groupby.groupLabels)
@series begin
label --> string(glab)
idxfilter --> groupby.groupIds[i]
for (key,val) in plotattributes
if splittable_kw(key, val, lengthGroup)
:($key) := split_kw(key, val, groupby.groupIds[i])
end
end
args
end
for (i,glab) in enumerate(groupby.groupLabels)
@series begin
label --> string(glab)
idxfilter --> groupby.groupIds[i]
args
end
else
g = args[1]
if length(g.args) == 1
x = zeros(Int, lengthGroup)
for indexes in groupby.groupIds
x[indexes] = 1:length(indexes)
end
last_args = g.args
else
x = g.args[1]
last_args = g.args[2:end]
end
x_u = unique(x)
x_ind = Dict(zip(x_u, 1:length(x_u)))
for (key,val) in plotattributes
if splittable_kw(key, val, lengthGroup)
:($key) := groupedvec2mat(x_ind, x, val, groupby)
end
end
label --> reshape(groupby.groupLabels, 1, :)
typeof(g)((x_u, (groupedvec2mat(x_ind, x, arg, groupby, NaN) for arg in last_args)...))
end
end
+4 -10
View File
@@ -1,9 +1,8 @@
function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
function Subplot{T<:AbstractBackend}(::T; parent = RootLayout())
Subplot{T}(
parent,
Series[],
(20mm, 5mm, 2mm, 10mm),
defaultbox,
defaultbox,
@@ -13,11 +12,6 @@ function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
)
end
"""
plotarea(subplot)
Return the bounding box of a subplot
"""
plotarea(sp::Subplot) = sp.plotarea
plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox)
@@ -37,14 +31,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)
series_list(sp::Subplot) = 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
))
+60 -163
View File
@@ -1,168 +1,65 @@
"""
theme(s::Symbol)
Specify the colour theme for plots.
"""
function theme(s::Symbol; kw...)
defaults = _get_defaults(s)
_theme(s, defaults; kw...)
end
const _invisible = RGBA(0,0,0,0)
function _get_defaults(s::Symbol)
thm = PlotThemes._themes[s]
if :defaults in fieldnames(thm)
return thm.defaults
else # old PlotTheme type
defaults = KW(
:bg => thm.bg_secondary,
:bginside => thm.bg_primary,
:fg => thm.lines,
:fgtext => thm.text,
:fgguide => thm.text,
:fglegend => thm.text,
:palette => thm.palette,
)
if thm.gradient != nothing
push!(defaults, :gradient => thm.gradient)
end
return defaults
end
end
function _theme(s::Symbol, defaults::KW; kw...)
# Reset to defaults to overwrite active theme
reset_defaults()
# Set the theme's gradient as default
if haskey(defaults, :gradient)
PlotUtils.clibrary(:misc)
PlotUtils.default_cgrad(default = :sequential, sequential = PlotThemes.gradient_name(s))
else
PlotUtils.clibrary(:Plots)
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
end
# maybe overwrite the theme's gradient
kw = KW(kw)
if haskey(kw, :gradient)
kwgrad = pop!(kw, :gradient)
for clib in clibraries()
if kwgrad in cgradients(clib)
PlotUtils.clibrary(clib)
PlotUtils.default_cgrad(default = :sequential, sequential = kwgrad)
break
end
end
end
# Set the theme's defaults
default(; defaults..., kw...)
return
end
@deprecate set_theme(s) theme(s)
@userplot ShowTheme
_color_functions = KW(
:protanopic => protanopic,
:deuteranopic => deuteranopic,
:tritanopic => tritanopic,
const _themes = KW(
:default => KW(
:bg => :white,
:bglegend => :match,
:bginside => :match,
:bgoutside => :match,
:fg => :auto,
:fglegend => :match,
:fggrid => :match,
:fgaxis => :match,
:fgtext => :match,
:fgborder => :match,
:fgguide => :match,
)
)
_get_showtheme_args(thm::Symbol) = thm, identity
_get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func, identity)
@recipe function showtheme(st::ShowTheme)
thm, cfunc = _get_showtheme_args(st.args...)
defaults = _get_defaults(thm)
# get the gradient
gradient_colors = get(defaults, :gradient, cgrad(:inferno).colors)
gradient = cgrad(cfunc.(RGB.(gradient_colors)))
# get the palette
palette = get(defaults, :palette, get_color_palette(:auto, plot_color(:white), 17))
palette = cfunc.(RGB.(palette))
# apply the theme
for k in keys(defaults)
k in (:gradient, :palette) && continue
def = defaults[k]
arg = get(_keyAliases, k, k)
plotattributes[arg] = if typeof(def) <: Colorant
cfunc(RGB(def))
elseif eltype(def) <: Colorant
cfunc.(RGB.(def))
elseif contains(string(arg), "color")
cfunc.(RGB.(plot_color.(def)))
else
def
end
end
srand(1)
label := ""
colorbar := false
layout := (2, 3)
for j in 1:4
@series begin
subplot := 1
palette := palette
seriestype := :path
cumsum(randn(50))
end
@series begin
subplot := 2
seriestype := :scatter
palette := palette
marker := (:circle, :diamond, :star5, :square)[j]
randn(10), randn(10)
end
end
@series begin
subplot := 3
seriestype := :histogram
palette := palette
randn(1000) .+ (0:2:4)'
end
f(r) = sin(r) / r
_norm(x, y) = norm([x, y])
x = y = linspace(-3π, 3π, 30)
z = f.(_norm.(x, y'))
wi = 2:3:30
@series begin
subplot := 4
seriestype := :heatmap
seriescolor := gradient
ticks := -5:5:5
x, y, z
end
@series begin
subplot := 5
seriestype := :surface
seriescolor := gradient
x, y, z
end
n = 100
ts = linspace(0, 10π, n)
x = ts .* cos.(ts)
y = (0.1ts) .* sin.(ts)
z = 1:n
@series begin
subplot := 6
seriescolor := gradient
linewidth := 3
line_z := z
x, y, z
end
function add_theme(sym::Symbol, theme::KW)
_themes[sym] = theme
end
# add a new theme, using an existing theme as the base
function add_theme(sym::Symbol;
base = :default, # start with this theme
bg = _themes[base][:bg],
bglegend = _themes[base][:bglegend],
bginside = _themes[base][:bginside],
bgoutside = _themes[base][:bgoutside],
fg = _themes[base][:fg],
fglegend = _themes[base][:fglegend],
fggrid = _themes[base][:fggrid],
fgaxis = _themes[base][:fgaxis],
fgtext = _themes[base][:fgtext],
fgborder = _themes[base][:fgborder],
fgguide = _themes[base][:fgguide],
kw...)
_themes[sym] = merge(KW(
:bg => bg,
:bglegend => bglegend,
:bginside => bginside,
:bgoutside => bgoutside,
:fg => fg,
:fglegend => fglegend,
:fggrid => fggrid,
:fgaxis => fgaxis,
:fgtext => fgtext,
:fgborder => fgborder,
:fgguide => fgguide,
), KW(kw))
end
add_theme(:ggplot2,
bglegend = _invisible,
bginside = :lightgray,
fg = :white,
fglegend = _invisible,
fgtext = :gray,
fgguide = :black
)
function set_theme(sym::Symbol)
default(; _themes[sym]...)
end
+24 -32
View File
@@ -2,36 +2,30 @@
# 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}
struct PlotsDisplay <: Display end
immutable PlotsDisplay <: Display end
abstract AbstractBackend
abstract AbstractPlot{T<:AbstractBackend}
abstract AbstractLayout
# -----------------------------------------------------------
struct InputWrapper{T}
immutable InputWrapper{T}
obj::T
end
wrap(obj::T) where {T} = InputWrapper{T}(obj)
wrap{T}(obj::T) = InputWrapper{T}(obj)
Base.isempty(wrapper::InputWrapper) = false
# -----------------------------------------------------------
mutable struct Series
d::KW
end
attr(series::Series, k::Symbol) = series.d[k]
attr!(series::Series, v, k::Symbol) = (series.d[k] = v)
# -----------------------------------------------------------
# a single subplot
mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout
type Subplot{T<:AbstractBackend} <: AbstractLayout
parent::AbstractLayout
series_list::Vector{Series} # arguments for each series
minpad::Tuple # leftpad, toppad, rightpad, bottompad
bbox::BoundingBox # the canvas area which is available to this subplot
plotarea::BoundingBox # the part where the data goes
@@ -40,17 +34,15 @@ mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout
plt # the enclosing Plot object (can't give it a type because of no forward declarations)
end
Base.show(io::IO, sp::Subplot) = print(io, "Subplot{$(sp[:subplot_index])}")
# -----------------------------------------------------------
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
mutable struct Axis
sps::Vector{Subplot}
type Axis
sp::Subplot
d::KW
end
mutable struct Extrema
type Extrema
emin::Float64
emax::Float64
end
@@ -58,12 +50,20 @@ Extrema() = Extrema(Inf, -Inf)
# -----------------------------------------------------------
const SubplotMap = Dict{Any, Subplot}
typealias SubplotMap Dict{Any, Subplot}
# -----------------------------------------------------------
type Series
d::KW
end
mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
attr(series::Series, k::Symbol) = series.d[k]
attr!(series::Series, v, k::Symbol) = (series.d[k] = v)
# -----------------------------------------------------------
type Plot{T<:AbstractBackend} <: AbstractPlot{T}
backend::T # the backend type
n::Int # number of series
attr::KW # arguments for the whole plot
@@ -86,18 +86,10 @@ end
# -----------------------------------------------------------------------
Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
Base.length(plt::Plot) = length(plt.subplots)
Base.endof(plt::Plot) = length(plt)
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
Base.size(plt::Plot) = size(plt.layout)
Base.size(plt::Plot, i::Integer) = size(plt.layout)[i]
Base.ndims(plt::Plot) = 2
# attr(plt::Plot, k::Symbol) = plt.attr[k]
# attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
Base.getindex(sp::Subplot, i::Integer) = series_list(sp)[i]
Base.endof(sp::Subplot) = length(series_list(sp))
# -----------------------------------------------------------------------
+90 -518
View File
@@ -3,7 +3,7 @@ calcMidpoints(edges::AbstractVector) = Float64[0.5 * (edges[i] + edges[i+1]) for
"Make histogram-like bins of data"
function binData(data, nbins)
lo, hi = ignorenan_extrema(data)
lo, hi = extrema(data)
edges = collect(linspace(lo, hi, nbins+1))
midpoints = calcMidpoints(edges)
buckets = Int[max(2, min(searchsortedfirst(edges, x), length(edges)))-1 for x in data]
@@ -109,12 +109,12 @@ 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
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
@show T, size(z)
n, m = size(z)
# idx = 0
@@ -131,21 +131,21 @@ function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
end
function imageHack(d::KW)
is_seriestype_supported(:heatmap) || error("Neither :image or :heatmap are supported!")
:heatmap in supported_types() || error("Neither :image or :heatmap are supported!")
d[:seriestype] = :heatmap
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
end
# ---------------------------------------------------------------
"Build line segments for plotting"
mutable struct Segments{T}
type Segments{T}
pts::Vector{T}
end
# Segments() = Segments{Float64}(zeros(0))
Segments() = Segments(Float64)
Segments(::Type{T}) where {T} = Segments(T[])
Segments{T}(::Type{T}) = Segments(T[])
Segments(p::Int) = Segments(NTuple{2,Float64}[])
@@ -157,7 +157,7 @@ to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN)
coords(segs::Segments{Float64}) = segs.pts
coords(segs::Segments{NTuple{2,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
function Base.push!(segments::Segments{T}, vs...) where T
function Base.push!{T}(segments::Segments{T}, vs...)
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
@@ -167,7 +167,7 @@ function Base.push!(segments::Segments{T}, vs...) where T
segments
end
function Base.push!(segments::Segments{T}, vs::AVec) where T
function Base.push!{T}(segments::Segments{T}, vs::AVec)
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
@@ -181,43 +181,24 @@ end
# -----------------------------------------------------
# helper to manage NaN-separated segments
mutable struct SegmentsIterator
type SegmentsIterator
args::Tuple
n::Int
end
function iter_segments(args...)
tup = Plots.wraptuple(args)
n = maximum(map(length, tup))
SegmentsIterator(tup, n)
end
function iter_segments(series::Series)
x, y, z = series[:x], series[:y], series[:z]
if has_attribute_segments(series)
if series[:seriestype] in (:scatter, :scatter3d)
return [[i] for i in 1:length(y)]
else
return [i:(i + 1) for i in 1:(length(y) - 1)]
end
else
segs = UnitRange{Int}[]
args = is3d(series) ? (x, y, z) : (x, y)
for seg in iter_segments(args...)
push!(segs, seg)
end
return segs
end
end
# helpers to figure out if there are NaN values in a list of array types
anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, 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)
function Base.start(itr::SegmentsIterator)
nextidx = 1
if !any(isempty,itr.args) && anynan(1, itr.args)
if anynan(1, itr.args)
_, nextidx = next(itr, 1)
end
nextidx
@@ -247,71 +228,61 @@ function Base.next(itr::SegmentsIterator, nextidx::Int)
istart:iend, i
end
# Find minimal type that can contain NaN and x
# To allow use of NaN separated segments with categorical x axis
float_extended_type(x::AbstractArray{T}) where {T} = Union{T,Float64}
float_extended_type(x::AbstractArray{T}) where {T<:Real} = Float64
# ------------------------------------------------------------------------------------
nop() = nothing
notimpl() = error("This has not been implemented yet")
isnothing(x::Void) = true
isnothing(x) = false
Base.cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
Base.cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
_cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
_cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
Base.cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
Base.cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
Base.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
Base.cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
Base.cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
Base.cycle(v, idx::AVec{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(grad::ColorGradient, idx::Int) = _cycle(grad.colors, idx)
_cycle(grad::ColorGradient, indices::AVec{Int}) = _cycle(grad.colors, indices)
Base.cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
makevec(v::AVec) = v
makevec(v::T) where {T} = T[v]
makevec{T}(v::T) = T[v]
"duplicate a single value, or pass the 2-tuple through"
maketuple(x::Real) = (x,x)
maketuple(x::Tuple{T,S}) where {T,S} = x
maketuple{T,S}(x::@compat(Tuple{T,S})) = x
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{F}, u::AVec) where {F<:Function} = [map(f, u) for f in fs]
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
unzip(xy::AVec{Tuple{X,Y}}) where {X,Y} = [t[1] for t in xy], [t[2] for t in xy]
unzip(xyz::AVec{Tuple{X,Y,Z}}) where {X,Y,Z} = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
unzip(xyuv::AVec{Tuple{X,Y,U,V}}) where {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{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t in xy]
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(xy::AVec{FixedSizeArrays.Vec{2,T}}) where {T} = T[t[1] for t in xy], T[t[2] for t in xy]
unzip(xy::FixedSizeArrays.Vec{2,T}) where {T} = T[xy[1]], T[xy[2]]
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(xyz::AVec{FixedSizeArrays.Vec{3,T}}) where {T} = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
unzip(xyz::FixedSizeArrays.Vec{3,T}) where {T} = T[xyz[1]], T[xyz[2]], T[xyz[3]]
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(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) where {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(xyuv::FixedSizeArrays.Vec{4,T}) where {T} = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
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]]
# 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...)
@@ -343,7 +314,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
@@ -351,55 +322,32 @@ Base.first(x::Symbol) = x
sortedkeys(d::Dict) = sort(collect(keys(d)))
const _scale_base = Dict{Symbol, Real}(
:log10 => 10,
:log2 => 2,
:ln => e,
)
function _heatmap_edges(v::AVec)
vmin, vmax = ignorenan_extrema(v)
extra_min = (v[2] - v[1]) / 2
extra_max = (v[end] - v[end - 1]) / 2
vcat(vmin-extra_min, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra_max)
end
"create an (n+1) list of the outsides of heatmap rectangles"
function heatmap_edges(v::AVec, scale::Symbol = :identity)
f, invf = scalefunc(scale), invscalefunc(scale)
map(invf, _heatmap_edges(map(f,v)))
function heatmap_edges(v::AVec)
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
theta, r = filter_radial_data(x, y, r_extrema)
r = (r - rmin) / (rmax - rmin)
x = r.*cos.(theta)
y = r.*sin.(theta)
x, y
end
# Filters radial data for points within the axis limits
function filter_radial_data(theta, r, r_extrema::Tuple{Real, Real})
n = max(length(theta), length(r))
rmin, rmax = r_extrema
x, y = zeros(n), zeros(n)
phi, r = x, y
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(theta, i)
y[i] = _cycle(r, i)
x[i] = cycle(r,i) * cos(cycle(phi,i))
y[i] = cycle(r,i) * sin(cycle(phi,i))
end
points = map((a, b) -> (a, b), x, y)
filter!(a -> a[2] >= rmin && a[2] <= rmax, points)
x = map(a -> a[1], points)
y = map(a -> a[2], points)
x, y
end
@@ -416,7 +364,7 @@ isatom() = isdefined(Main, :Atom) && Main.Atom.isconnected()
function is_installed(pkgstr::AbstractString)
try
Pkg.installed(pkgstr) === nothing ? false : true
Pkg.installed(pkgstr) === nothing ? false: true
catch
false
end
@@ -438,20 +386,21 @@ isvertical(d::KW) = get(d, :orientation, :vertical) in (:vertical, :v, :vert)
isvertical(series::Series) = isvertical(series.d)
ticksType(ticks::AVec{T}) where {T<:Real} = :ticks
ticksType(ticks::AVec{T}) where {T<:AbstractString} = :labels
ticksType(ticks::Tuple{T,S}) where {T<:AVec,S<:AVec} = :ticks_and_labels
# ticksType{T<:Real,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
ticksType{T<:Real}(ticks::AVec{T}) = :ticks
ticksType{T<:AbstractString}(ticks::AVec{T}) = :labels
ticksType{T<:AVec,S<:AVec}(ticks::@compat(Tuple{T,S})) = :ticks_and_labels
ticksType(ticks) = :invalid
limsType(lims::Tuple{T,S}) where {T<:Real,S<:Real} = :limits
limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :invalid
# axis_Symbol(letter, postfix) = Symbol(letter * postfix)
# axis_symbols(letter, postfix...) = map(s -> axis_Symbol(letter, s), postfix)
Base.convert(::Type{Vector{T}}, rng::Range{T}) where {T<:Real} = T[x for x in rng]
Base.convert(::Type{Vector{T}}, rng::Range{S}) where {T<:Real,S<:Real} = T[x for x in rng]
Base.convert{T<:Real}(::Type{Vector{T}}, rng::Range{T}) = T[x for x in rng]
Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]
Base.merge(a::AbstractVector, b::AbstractVector) = sort(unique(vcat(a,b)))
@@ -496,8 +445,7 @@ end
# for example: PyPlot sends rows to y, so transpose_on_match should be true
function transpose_z(d, z, transpose_on_match::Bool = true)
if d[:match_dimensions] == transpose_on_match
# z'
permutedims(z, [2,1])
z'
else
z
end
@@ -511,7 +459,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, cycle(rib)))
frs[i] = yi + ri
end
frs
@@ -522,186 +470,13 @@ function make_fillrange_from_ribbon(kw::KW)
y, rib = kw[:y], kw[:ribbon]
rib = wraptuple(rib)
rib1, rib2 = -first(rib), last(rib)
# kw[:ribbon] = nothing
kw[:ribbon] = nothing
kw[:fillrange] = make_fillrange_side(y, rib1), make_fillrange_side(y, rib2)
(get(kw, :fillalpha, nothing) == nothing) && (kw[:fillalpha] = 0.5)
end
#turn tuple of fillranges to one path
function concatenate_fillrange(x,y::Tuple)
rib1, rib2 = first(y), last(y)
yline = vcat(rib1,(rib2)[end:-1:1])
xline = vcat(x,x[end:-1:1])
return xline, yline
end
function get_sp_lims(sp::Subplot, letter::Symbol)
axis_limits(sp[Symbol(letter, :axis)])
end
"""
xlims([plt])
Returns the x axis limits of the current plot or subplot
"""
xlims(sp::Subplot) = get_sp_lims(sp, :x)
"""
ylims([plt])
Returns the y axis limits of the current plot or subplot
"""
ylims(sp::Subplot) = get_sp_lims(sp, :y)
"""
zlims([plt])
Returns the z axis limits of the current plot or subplot
"""
zlims(sp::Subplot) = get_sp_lims(sp, :z)
xlims(plt::Plot, sp_idx::Int = 1) = xlims(plt[sp_idx])
ylims(plt::Plot, sp_idx::Int = 1) = ylims(plt[sp_idx])
zlims(plt::Plot, sp_idx::Int = 1) = zlims(plt[sp_idx])
xlims(sp_idx::Int = 1) = xlims(current(), sp_idx)
ylims(sp_idx::Int = 1) = ylims(current(), sp_idx)
zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
function get_clims(sp::Subplot)
zmin, zmax = Inf, -Inf
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface)
for series in series_list(sp)
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Real)
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals.surf)...)
elseif (vals != nothing) && (eltype(vals) <: Real)
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals)...)
end
end
end
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (zmin = clims[1])
isfinite(clims[2]) && (zmax = clims[2])
end
return zmin < zmax ? (zmin, zmax) : (-0.1, 0.1)
end
_update_clims(zmin, zmax, emin, emax) = min(zmin, emin), max(zmax, emax)
function hascolorbar(series::Series)
st = series[:seriestype]
hascbar = st == :heatmap
if st == :contour
hascbar = (isscalar(series[:levels]) ? (series[:levels] > 1) : (length(series[:levels]) > 1)) && (length(unique(Array(series[:z]))) > 1)
end
if series[:marker_z] != nothing || series[:line_z] != nothing || series[:fill_z] != nothing
hascbar = true
end
# no colorbar if we are creating a surface LightSource
if xor(st == :surface, series[:fill_z] != nothing)
hascbar = true
end
return hascbar
end
function hascolorbar(sp::Subplot)
cbar = sp[:colorbar]
hascbar = false
if cbar != :none
for series in series_list(sp)
if hascolorbar(series)
hascbar = true
end
end
end
hascbar
end
function get_linecolor(series, i::Int = 1)
lc = series[:linecolor]
lz = series[:line_z]
if lz == nothing
isa(lc, ColorGradient) ? lc : plot_color(_cycle(lc, i))
else
cmin, cmax = get_clims(series[:subplot])
grad = isa(lc, ColorGradient) ? lc : cgrad()
grad[clamp((_cycle(lz, i) - cmin) / (cmax - cmin), 0, 1)]
end
end
function get_linealpha(series, i::Int = 1)
_cycle(series[:linealpha], i)
end
function get_linewidth(series, i::Int = 1)
_cycle(series[:linewidth], i)
end
function get_linestyle(series, i::Int = 1)
_cycle(series[:linestyle], i)
end
function get_fillcolor(series, i::Int = 1)
fc = series[:fillcolor]
fz = series[:fill_z]
if fz == nothing
isa(fc, ColorGradient) ? fc : plot_color(_cycle(fc, i))
else
cmin, cmax = get_clims(series[:subplot])
grad = isa(fc, ColorGradient) ? fc : cgrad()
grad[clamp((_cycle(fz, i) - cmin) / (cmax - cmin), 0, 1)]
end
end
function get_fillalpha(series, i::Int = 1)
_cycle(series[:fillalpha], i)
end
function get_markercolor(series, i::Int = 1)
mc = series[:markercolor]
mz = series[:marker_z]
if mz == nothing
isa(mc, ColorGradient) ? mc : plot_color(_cycle(mc, i))
else
cmin, cmax = get_clims(series[:subplot])
grad = isa(mc, ColorGradient) ? mc : cgrad()
grad[clamp((_cycle(mz, i) - cmin) / (cmax - cmin), 0, 1)]
end
end
function get_markeralpha(series, i::Int = 1)
_cycle(series[:markeralpha], i)
end
function get_markerstrokecolor(series, i::Int = 1)
msc = series[:markerstrokecolor]
isa(msc, ColorGradient) ? msc : _cycle(msc, i)
end
function get_markerstrokealpha(series, i::Int = 1)
_cycle(series[:markerstrokealpha], i)
end
function has_attribute_segments(series::Series)
# we want to check if a series needs to be split into segments just because
# of its attributes
for letter in (:x, :y, :z)
# If we have NaNs in the data they define the segments and
# SegmentsIterator is used
series[letter] != nothing && NaN in collect(series[letter]) && return false
end
series[:seriestype] == :shape && return false
# ... else we check relevant attributes if they have multiple inputs
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray{<:Real} for attr in (:line_z, :fill_z, :marker_z))
end
# ---------------------------------------------------------------
makekw(; kw...) = KW(kw)
wraptuple(x::Tuple) = x
wraptuple(x::@compat(Tuple)) = x
wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
@@ -709,7 +484,7 @@ trueOrAllTrue(f::Function, x) = f(x)
allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
allShapes(arg) = trueOrAllTrue(a -> is_marker_supported(get(_markerAliases, a, a)), arg) ||
allShapes(arg) = trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg) ||
trueOrAllTrue(a -> isa(a, Shape), arg)
allAlphas(arg) = trueOrAllTrue(a -> (typeof(a) <: Real && a > 0 && a < 1) ||
(typeof(a) <: AbstractFloat && (a == zero(typeof(a)) || a == one(typeof(a)))), arg)
@@ -723,7 +498,7 @@ allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
"""
Allows temporary setting of backend and defaults for Plots. Settings apply only for the `do` block. Example:
```
with(:gr, size=(400,400), type=:histogram) do
with(:gadfly, size=(400,400), type=:histogram) do
plot(rand(10))
plot(rand(10))
end
@@ -808,7 +583,7 @@ end
# ---------------------------------------------------------------
# ---------------------------------------------------------------
mutable struct DebugMode
type DebugMode
on::Bool
end
const _debugMode = DebugMode(false)
@@ -845,11 +620,11 @@ 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))
extendSeriesData(v::Range{T}, z::Real) where {T} = extendSeriesData(float(collect(v)), z)
extendSeriesData(v::Range{T}, z::AVec) where {T} = extendSeriesData(float(collect(v)), z)
extendSeriesData(v::AVec{T}, z::Real) where {T} = (push!(v, convert(T, z)); v)
extendSeriesData(v::AVec{T}, z::AVec) where {T} = (append!(v, convert(Vector{T}, z)); 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)
extendSeriesData{T}(v::AVec{T}, z::AVec) = (append!(v, convert(Vector{T}, z)); v)
# -------------------------------------------------------
@@ -867,90 +642,30 @@ function getxyz(plt::Plot, i::Integer)
tovec(d[:x]), tovec(d[:y]), tovec(d[:z])
end
function setxy!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y}
function setxy!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer)
series = plt.series_list[i]
series.d[:x], series.d[:y] = xy
sp = series.d[:subplot]
reset_extrema!(sp)
expand_extrema!(sp.attr[:xaxis], xy[1])
expand_extrema!(sp.attr[:yaxis], xy[2])
_series_updated(plt, series)
end
function setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z}
function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
series = plt.series_list[i]
series.d[:x], series.d[:y], series.d[:z] = xyz
sp = series.d[:subplot]
reset_extrema!(sp)
expand_extrema!(sp.attr[:xaxis], xyz[1])
expand_extrema!(sp.attr[:yaxis], xyz[2])
expand_extrema!(sp.attr[:zaxis], xyz[3])
_series_updated(plt, series)
end
function setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z<:AbstractMatrix}
setxyz!(plt, (xyz[1], xyz[2], Surface(xyz[3])), i)
end
# -------------------------------------------------------
# indexing notation
# Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
Base.setindex!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y} = (setxy!(plt, xy, i); plt)
Base.setindex!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z} = (setxyz!(plt, xyz, i); plt)
# -------------------------------------------------------
# operate on individual series
function push_x!(series::Series, xi)
push!(series[:x], xi)
expand_extrema!(series[:subplot][:xaxis], xi)
return
end
function push_y!(series::Series, yi)
push!(series[:y], yi)
expand_extrema!(series[:subplot][:yaxis], yi)
return
end
function push_z!(series::Series, zi)
push!(series[:z], zi)
expand_extrema!(series[:subplot][:zaxis], zi)
return
end
function Base.push!(series::Series, yi)
x = extendSeriesByOne(series[:x])
expand_extrema!(series[:subplot][:xaxis], x[end])
series[:x] = x
push_y!(series, yi)
end
Base.push!(series::Series, xi, yi) = (push_x!(series,xi); push_y!(series,yi))
Base.push!(series::Series, xi, yi, zi) = (push_x!(series,xi); push_y!(series,yi); push_z!(series,zi))
# -------------------------------------------------------
function attr!(series::Series; kw...)
d = KW(kw)
preprocessArgs!(d)
for (k,v) in d
if haskey(_series_defaults, k)
series[k] = v
else
warn("unused key $k in series attr")
end
end
_series_updated(series[:subplot].plt, series)
series
end
function attr!(sp::Subplot; kw...)
d = KW(kw)
preprocessArgs!(d)
for (k,v) in d
if haskey(_subplot_defaults, k)
sp[k] = v
else
warn("unused key $k in subplot attr")
end
end
sp
end
Base.setindex!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer) = setxy!(plt, xy, i)
Base.setindex!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) = setxyz!(plt, xyz, i)
# -------------------------------------------------------
# push/append for one series
@@ -1004,10 +719,10 @@ function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec, z::AVec)
end
# tuples
Base.push!(plt::Plot, xy::Tuple{X,Y}) where {X,Y} = push!(plt, 1, xy...)
Base.push!(plt::Plot, xyz::Tuple{X,Y,Z}) where {X,Y,Z} = push!(plt, 1, xyz...)
Base.push!(plt::Plot, i::Integer, xy::Tuple{X,Y}) where {X,Y} = push!(plt, i, xy...)
Base.push!(plt::Plot, i::Integer, xyz::Tuple{X,Y,Z}) where {X,Y,Z} = push!(plt, i, xyz...)
Base.push!{X,Y}(plt::Plot, xy::Tuple{X,Y}) = push!(plt, 1, xy...)
Base.push!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}) = push!(plt, 1, xyz...)
Base.push!{X,Y}(plt::Plot, i::Integer, xy::Tuple{X,Y}) = push!(plt, i, xy...)
Base.push!{X,Y,Z}(plt::Plot, i::Integer, xyz::Tuple{X,Y,Z}) = push!(plt, i, xyz...)
# -------------------------------------------------------
# push/append for all series
@@ -1071,152 +786,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))
# ---------------------------------------------------------------
# get fonts from objects:
titlefont(sp::Subplot) = font(
sp[:titlefontfamily],
sp[:titlefontsize],
sp[:titlefontvalign],
sp[:titlefonthalign],
sp[:titlefontrotation],
sp[:titlefontcolor],
)
legendfont(sp::Subplot) = font(
sp[:legendfontfamily],
sp[:legendfontsize],
sp[:legendfontvalign],
sp[:legendfonthalign],
sp[:legendfontrotation],
sp[:legendfontcolor],
)
tickfont(ax::Axis) = font(
ax[:tickfontfamily],
ax[:tickfontsize],
ax[:tickfontvalign],
ax[:tickfonthalign],
ax[:tickfontrotation],
ax[:tickfontcolor],
)
guidefont(ax::Axis) = font(
ax[:guidefontfamily],
ax[:guidefontsize],
ax[:guidefontvalign],
ax[:guidefonthalign],
ax[:guidefontrotation],
ax[:guidefontcolor],
)
# ---------------------------------------------------------------
# converts unicode scientific notation unsupported by pgfplots and gr
# into a format that works
function convert_sci_unicode(label::AbstractString)
unicode_dict = Dict(
'⁰' => "0",
'¹' => "1",
'²' => "2",
'³' => "3",
'⁴' => "4",
'⁵' => "5",
'⁶' => "6",
'⁷' => "7",
'⁸' => "8",
'⁹' => "9",
'⁻' => "-",
"×10" => "×10^{",
)
for key in keys(unicode_dict)
label = replace(label, key, unicode_dict[key])
end
if contains(label, "10^{")
label = string(label, "}")
end
label
end
function straightline_data(series)
sp = series[:subplot]
xl, yl = isvertical(series) ? (xlims(sp), ylims(sp)) : (ylims(sp), xlims(sp))
x, y = series[:x], series[:y]
n = length(x)
if n == 2
return straightline_data(xl, yl, x, y)
else
k, r = divrem(n, 3)
if r == 0
xdata, ydata = fill(NaN, n), fill(NaN, n)
for i in 1:k
inds = (3 * i - 2):(3 * i - 1)
xdata[inds], ydata[inds] = straightline_data(xl, yl, x[inds], y[inds])
end
return xdata, ydata
else
error("Misformed data. `straightline_data` either accepts vectors of length 2 or 3k. The provided series has length $n")
end
end
end
function straightline_data(xl, yl, x, y)
x_vals, y_vals = if y[1] == y[2]
if x[1] == x[2]
error("Two identical points cannot be used to describe a straight line.")
else
[xl[1], xl[2]], [y[1], y[2]]
end
elseif x[1] == x[2]
[x[1], x[2]], [yl[1], yl[2]]
else
# get a and b from the line y = a * x + b through the points given by
# the coordinates x and x
b = y[1] - (y[1] - y[2]) * x[1] / (x[1] - x[2])
a = (y[1] - y[2]) / (x[1] - x[2])
# get the data values
xdata = [clamp(x[1] + (x[1] - x[2]) * (ylim - y[1]) / (y[1] - y[2]), xl...) for ylim in yl]
xdata, a .* xdata .+ b
end
# expand the data outside the axis limits, by a certain factor too improve
# plotly(js) and interactive behaviour
factor = 100
x_vals = x_vals .+ (x_vals[2] - x_vals[1]) .* factor .* [-1, 1]
y_vals = y_vals .+ (y_vals[2] - y_vals[1]) .* factor .* [-1, 1]
return x_vals, y_vals
end
function shape_data(series)
sp = series[:subplot]
xl, yl = isvertical(series) ? (xlims(sp), ylims(sp)) : (ylims(sp), xlims(sp))
x, y = series[:x], series[:y]
factor = 100
for i in eachindex(x)
if x[i] == -Inf
x[i] = xl[1] - factor * (xl[2] - xl[1])
elseif x[i] == Inf
x[i] = xl[2] + factor * (xl[2] - xl[1])
end
end
for i in eachindex(y)
if y[i] == -Inf
y[i] = yl[1] - factor * (yl[2] - yl[1])
elseif y[i] == Inf
y[i] = yl[2] + factor * (yl[2] - yl[1])
end
end
return x, y
end
function construct_categorical_data(x::AbstractArray, axis::Axis)
map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x)
end
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))
+11 -1
View File
@@ -1,8 +1,18 @@
julia 0.4
RecipesBase
PlotUtils
StatPlots
Reexport
Measures
Showoff
FactCheck
Images
ImageMagick
@osx QuartzImageIO
GR 0.31.0
GR
DataFrames
RDatasets
VisualRegressionTests
UnicodePlots
Glob
+9 -5
View File
@@ -15,7 +15,8 @@ end
using Plots
using StatPlots
using Base.Test
using FactCheck
using Glob
default(size=(500,300))
@@ -23,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.17.4"
const _current_plots_version = v"0.8.1"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
@@ -42,8 +43,11 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
fn = "ref$idx.png"
# firgure out version info
vns = filter(x->x[1] != '.', readdir(refdir))
versions = sort(VersionNumber.(vns), rev = true)
G = glob(joinpath(relpath(refdir), "*"))
# @show refdir fn G
slash = (@windows ? "\\" : "/")
versions = map(fn -> VersionNumber(split(fn, slash)[end]), G)
versions = reverse(sort(versions))
versions = filter(v -> v <= _current_plots_version, versions)
# @show refdir fn versions
@@ -95,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
@test image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success == true
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
end
end
end
+3 -5
View File
@@ -5,11 +5,9 @@ set -ex
sudo apt-get -qq update
# sudo apt-get install -y wkhtmltopdf
sudo apt-get install -y xfonts-75dpi xfonts-base
wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.2.1/wkhtmltox-0.12.2.1_linux-precise-amd64.deb
sudo dpkg -i wkhtmltox-0.12.2.1_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
+53 -138
View File
@@ -7,170 +7,85 @@ srand(1234)
default(show=false, reuse=true)
img_eps = isinteractive() ? 1e-2 : 10e-2
@testset "GR" begin
ENV["PLOTS_TEST"] = "true"
ENV["GKSwstype"] = "100"
@test gr() == Plots.GRBackend()
@test backend() == Plots.GRBackend()
# 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
image_comparison_facts(:gr, eps=img_eps)
facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend()
image_comparison_facts(:pyplot, skip=[], eps=img_eps)
end
facts("GR") do
@fact gr() --> Plots.GRBackend()
@fact backend() --> Plots.GRBackend()
# @linux_only image_comparison_facts(:gr, skip=[], 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
# @linux_only image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
end
#@testset "PyPlot" begin
# @test pyplot() == Plots.PyPlotBackend()
# @test backend() == Plots.PyPlotBackend()
#
# image_comparison_facts(:pyplot, eps=img_eps)
#end
@testset "UnicodePlots" begin
@test unicodeplots() == Plots.UnicodePlotsBackend()
@test backend() == Plots.UnicodePlotsBackend()
# lets just make sure it runs without error
@test isa(plot(rand(10)), Plots.Plot) == true
end
# The plotlyjs testimages return a connection error on travis:
# connect: connection refused (ECONNREFUSED)
# @testset "PlotlyJS" begin
# @test plotlyjs() == Plots.PlotlyJSBackend()
# @test 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
# @testset "InspectDR" begin
# @test inspectdr() == Plots.InspectDRBackend()
# @test 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
# @testset "Plotly" begin
# @test plotly() == Plots.PlotlyBackend()
# @test 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
# @testset "Immerse" begin
# @test immerse() == Plots.ImmerseBackend()
# @test backend() == Plots.ImmerseBackend()
# facts("Immerse") do
# @fact immerse() --> Plots.ImmerseBackend()
# @fact backend() --> Plots.ImmerseBackend()
#
# # as long as we can plot anything without error, it should be the same as Gadfly
# image_comparison_facts(:immerse, only=[1], eps=img_eps)
# end
# @testset "PlotlyJS" begin
# @test plotlyjs() == Plots.PlotlyJSBackend()
# @test backend() == Plots.PlotlyJSBackend()
# facts("PlotlyJS") do
# @fact plotlyjs() --> Plots.PlotlyJSBackend()
# @fact backend() --> Plots.PlotlyJSBackend()
#
# # as long as we can plot anything without error, it should be the same as Plotly
# image_comparison_facts(:plotlyjs, only=[1], eps=img_eps)
# end
# @testset "Gadfly" begin
# @test gadfly() == Plots.GadflyBackend()
# @test backend() == Plots.GadflyBackend()
#
# @test typeof(plot(1:10)) == Plots.Plot{Plots.GadflyBackend}
# @test plot(Int[1,2,3], rand(3)) == not(nothing)
# @test plot(sort(rand(10)), rand(Int, 10, 3)) == not(nothing)
# @test 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)), Plot) --> true
end
@testset "Axes" begin
facts("Axes") do
p = plot()
axis = p.subplots[1][:xaxis]
@test typeof(axis) == Plots.Axis
@test Plots.discrete_value!(axis, "HI") == (0.5, 1)
@test Plots.discrete_value!(axis, :yo) == (1.5, 2)
@test Plots.ignorenan_extrema(axis) == (0.5,1.5)
@test axis[:discrete_map] == Dict{Any,Any}(:yo => 2, "HI" => 1)
@fact typeof(axis) --> Axis
@fact Plots.discrete_value!(axis, "HI") --> (0.5, 1)
@fact Plots.discrete_value!(axis, :yo) --> (1.5, 2)
@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])
@test Plots.ignorenan_extrema(axis) == (0.5, 7.5)
@fact extrema(axis) --> (0.5, 7.5)
end
@testset "NoFail" begin
histogram([1, 0, 0, 0, 0, 0])
end
# tests for preprocessing recipes
# @testset "recipes" begin
# user recipe
# type T end
# @recipe function f(::T)
# line := (3,0.3,:red)
# marker := (20,0.5,:blue,:o)
# bg := :yellow
# rand(10)
# end
# plot(T())
# plot recipe
# @recipe function f(::Type{Val{:hiplt}},plt::Plot)
# line := (3,0.3,:red)
# marker := (20,0.5,:blue,:o)
# t := :path
# bg:=:green
# ()
# end
# plot(rand(10),t=:hiplt)
# series recipe
# @recipe function f(::Type{Val{:hi}},x,y,z)
# line := (3,0.3,:red)
# marker := (20,0.5,:blue,:o)
# t := :path
# ()
# end
# plot(rand(10),t=:hiplt)
# end
FactCheck.exitstatus()
end # module
+2 -2
View File
@@ -4,7 +4,7 @@ import SnoopCompile
# This only needs to be run once (to generate "/tmp/plots_compiles.csv")
# SnoopCompile.@snoop "/tmp/plots_compiles.csv" begin
# include(joinpath(dirname(@__FILE__), "runtests.jl"))
# include(Pkg.dir("Plots", "test","runtests.jl"))
# end
# ----------------------------------------------------------
@@ -32,5 +32,5 @@ blacklist = ["MIME"]
pc, discards = SnoopCompile.parcel(data[end:-1:1,2], subst=subst, blacklist=blacklist)
SnoopCompile.write("/tmp/precompile", pc)
pdir = joinpath(dirname(@__FILE__), "..")
pdir = Pkg.dir("Plots")
run(`cp /tmp/precompile/precompile_Plots.jl $pdir/src/precompile.jl`)
+5 -8
View File
@@ -1,5 +1,5 @@
Pkg.add("ImageMagick")
Pkg.build("ImageMagick")
# Pkg.clone("ImageMagick")
# Pkg.build("ImageMagick")
# Pkg.clone("GR")
# Pkg.build("GR")
@@ -9,14 +9,13 @@ Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
# Pkg.clone("https://github.com/JuliaStats/KernelDensity.jl.git")
Pkg.clone("StatPlots")
# Pkg.checkout("PlotUtils")
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)