Compare commits
377 Commits
v0.9.6
...
backports-0.5
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d17aa0093 | |||
| df729cd0f6 | |||
| 230ab34fe9 | |||
| 51fabab7b7 | |||
| f855386bf1 | |||
| b8d1d9d7f1 | |||
| a22412d39d | |||
| f8bc9ba476 | |||
| fe83937368 | |||
| 3def6265c4 | |||
| d9f7c19344 | |||
| db1dd618ea | |||
| 132306fef1 | |||
| 18d29a0c7c | |||
| 4125fa75dd | |||
| bad7772b15 | |||
| 6a23202e89 | |||
| 21c384fcdd | |||
| b140fddebd | |||
| 24ea1c4847 | |||
| 9d115fc02e | |||
| 5e7756810d | |||
| c65abd19ae | |||
| 1a07c39fad | |||
| ee23cb2185 | |||
| ee5617c6b8 | |||
| dc757cbf6c | |||
| 3d3ec2a194 | |||
| d2e79f33a3 | |||
| 6156228c89 | |||
| 90f7e370f3 | |||
| 92688f03a8 | |||
| 80d0d6ecc8 | |||
| d29df4289e | |||
| 84406f0823 | |||
| 380907af8d | |||
| 0e308d9f50 | |||
| 7805f3f85b | |||
| e04d13323d | |||
| 6e8bfe1ea6 | |||
| 3baceb9dc4 | |||
| abf8ca2dc7 | |||
| 2a0a5f2d08 | |||
| d45b228461 | |||
| 231e11715f | |||
| d6f1120dfc | |||
| 7471bc791c | |||
| effcffff8a | |||
| 76186cab97 | |||
| 4756e04e25 | |||
| 5b73f1e56c | |||
| af7d332453 | |||
| 5439c3a3ce | |||
| 6c4b6bbfbe | |||
| 53719e5a8e | |||
| aed34c3b9b | |||
| a5482453ce | |||
| 712d2011c3 | |||
| 0e6942a182 | |||
| ce169edd08 | |||
| e283c6557b | |||
| e8da23c3e7 | |||
| 08d7bee224 | |||
| 16c95c6deb | |||
| 1d03e97721 | |||
| 9abcba3753 | |||
| b501c9bf4b | |||
| 72c506b62b | |||
| f09dfb7035 | |||
| 05eb687ca4 | |||
| 99e4408da4 | |||
| 64dd69f550 | |||
| 8b8de6bcd3 | |||
| b920ee8550 | |||
| 89cd67792b | |||
| 1f7ecb969c | |||
| d4ef1761e4 | |||
| dc01cc5c8e | |||
| db341f1814 | |||
| 06e469f059 | |||
| 9ec1e674d0 | |||
| 5c54722626 | |||
| 0a92b13263 | |||
| 2fbc3c954a | |||
| 746f96cdcd | |||
| ee9d78b190 | |||
| 0e6a02b810 | |||
| 864dddb1ff | |||
| 321495f253 | |||
| eced31e3d7 | |||
| 4b8dc06ddb | |||
| ff3ba18b15 | |||
| d24f327342 | |||
| d801a2789e | |||
| b8c8c49176 | |||
| 601f24a006 | |||
| 160b2bf03a | |||
| 944163e889 | |||
| 8656fe002a | |||
| d5e50983d8 | |||
| f3d31a62db | |||
| ee15b2c166 | |||
| 4d84ab155a | |||
| accc063db8 | |||
| b88d26ce77 | |||
| 886386508c | |||
| d893034f47 | |||
| f4b798765d | |||
| 5e6a9da3ed | |||
| 10f214fbb4 | |||
| 698d583d1d | |||
| 598211c349 | |||
| 7843821109 | |||
| 96f3baca41 | |||
| 67b4157e25 | |||
| a523903c15 | |||
| 92e9fa2084 | |||
| c75e1df190 | |||
| 79bf45b823 | |||
| 58989465f4 | |||
| 1e6b442c6c | |||
| 33addf4449 | |||
| c5fed9524a | |||
| cbfadde43c | |||
| cebe1eedeb | |||
| c22a9e51a1 | |||
| e41022c7be | |||
| 56a9389023 | |||
| 1188230641 | |||
| e760b42560 | |||
| 627c03193a | |||
| 0f837f3438 | |||
| af072da0b0 | |||
| c4f3b93da5 | |||
| 906f11ed76 | |||
| 2e7329931c | |||
| 5e5e1850ac | |||
| 6b250d1ab6 | |||
| 1ee6affcb3 | |||
| 7a1dd0db29 | |||
| e41918569b | |||
| ec6f75b59e | |||
| ec595c23e5 | |||
| 2c5a37d38e | |||
| 3ec61cd012 | |||
| a3b4b9c8b2 | |||
| 5166d16406 | |||
| 3e7dd9bb77 | |||
| 0ab57b3d76 | |||
| 0be0824207 | |||
| 7def4e8e73 | |||
| 2397c78e9f | |||
| 9a59df793d | |||
| 1852ba99b3 | |||
| 0d665c661f | |||
| f11828075b | |||
| 9a1efefbd4 | |||
| 066ff96b58 | |||
| 7d8b00d1b9 | |||
| 5d4f3182fc | |||
| b8d80f5285 | |||
| ccb9df2a08 | |||
| c3ae9e1ee2 | |||
| abcc4b6739 | |||
| dd3f65496a | |||
| 2d69fc39de | |||
| 731cac4262 | |||
| b4846bd5d5 | |||
| fac39344f4 | |||
| ffb9878893 | |||
| 034a1af649 | |||
| 06fd894f79 | |||
| 5731648479 | |||
| 1ebc2cb9c1 | |||
| 0bd76ef7a4 | |||
| ea15f7746d | |||
| ed102269e7 | |||
| 76e1f4720b | |||
| 2317b77314 | |||
| 1a7bb10a22 | |||
| 0732006d5a | |||
| fb2e12089f | |||
| 7a0e03b374 | |||
| f2235abf08 | |||
| 5435ce20a9 | |||
| 124f1fe701 | |||
| 37e2c18c3c | |||
| 032f5a5dab | |||
| 7e41a2f40d | |||
| 8b90eb37c5 | |||
| ff797b11d1 | |||
| 31c665a837 | |||
| 68c39697a6 | |||
| 6f8881b610 | |||
| 1b468ba5d8 | |||
| 7aca2b1584 | |||
| 41e56b1f2e | |||
| cad96e652f | |||
| c7bde0ff19 | |||
| 6b94bbac2d | |||
| f7de9fbea4 | |||
| 5465479e57 | |||
| cd3ce86dbb | |||
| b109025fac | |||
| 6e74411a8e | |||
| 6c51982793 | |||
| 8f841cdea4 | |||
| eab4cfdf2d | |||
| 34c0361d1a | |||
| 24c27737c1 | |||
| b0dabaafe8 | |||
| 9472c0c5e5 | |||
| 89fe3c9150 | |||
| 9e1d551173 | |||
| 2d61b413d6 | |||
| aec08c74e0 | |||
| 48e8cb5eb1 | |||
| fd947ab887 | |||
| 5395b41852 | |||
| b7536f129b | |||
| 28b026c9ad | |||
| d4b7bfbe1d | |||
| 4b2ce036e8 | |||
| 2a350df5eb | |||
| bdc815066e | |||
| 44f4f22fdd | |||
| 5ae2044d02 | |||
| 5e80a05f32 | |||
| eb7534d319 | |||
| 471b09bd6c | |||
| a4549cbfa2 | |||
| d663267cde | |||
| 35a56962fc | |||
| 82161e60bc | |||
| 51a47cb2b1 | |||
| 9804debe86 | |||
| c93e2b80ca | |||
| 1555876280 | |||
| 1d08e6619b | |||
| 0880487be2 | |||
| 2ac4ffc576 | |||
| 7b2a0a92c4 | |||
| d883274586 | |||
| 803de95367 | |||
| 3ca39728d8 | |||
| 1111000217 | |||
| dd505fedcc | |||
| 48ff2fb8b8 | |||
| d5eafad48e | |||
| 2e1c86c0c7 | |||
| c728bf52b8 | |||
| 95a26f5080 | |||
| 85eaef6aa9 | |||
| dee160f2b8 | |||
| 6d38e52321 | |||
| caed94328f | |||
| ebf92d3033 | |||
| fdadfa3a80 | |||
| f8b84185ac | |||
| caf86686dd | |||
| bcd5d9ef90 | |||
| 45b5922d01 | |||
| 1a91eacdcb | |||
| 0421fe9a82 | |||
| 43a213643b | |||
| cd8939fe0b | |||
| c250d7c073 | |||
| 3b570edea1 | |||
| 199a3306db | |||
| 3236e06fb9 | |||
| 492618ae67 | |||
| c5737db4f5 | |||
| 40b49b9873 | |||
| 1fa34574e8 | |||
| 4c2db68037 | |||
| 1c522159df | |||
| 967dca7912 | |||
| 7d66c502f4 | |||
| 9b9e2fe6cd | |||
| 47ee0a4c97 | |||
| f10bda463c | |||
| 72d336265b | |||
| a087a6694b | |||
| 5eaf6b2adc | |||
| 57840a37d0 | |||
| aedcb372f6 | |||
| 7f554d3c84 | |||
| edf1daa7d7 | |||
| f554bf925e | |||
| 9676309ba2 | |||
| c78444fc24 | |||
| 4ab51d4bfa | |||
| e381c13dc6 | |||
| ec31898f25 | |||
| a88d78a2e9 | |||
| 92430afba9 | |||
| 8f60a4df8d | |||
| 08c9e627ab | |||
| c092d3cab4 | |||
| 46ad076751 | |||
| 07731614a9 | |||
| d8ffd729f4 | |||
| b23d94eeb8 | |||
| 4ed7144ca4 | |||
| ad924e8cc9 | |||
| aa929ec24f | |||
| 07f7fd3c7a | |||
| 83beca098b | |||
| 2c3d35d249 | |||
| 90e655d979 | |||
| 8204b51213 | |||
| c3f40d0555 | |||
| cbd134987b | |||
| 167fd68774 | |||
| 2b8cc7cfde | |||
| 62280fb24b | |||
| 2a7604ef5f | |||
| 0b47aafd14 | |||
| 24eefbed2b | |||
| ab7a73c879 | |||
| 81e0868725 | |||
| eba1e366bb | |||
| e9ccdef447 | |||
| bdce27dd7b | |||
| 436ab89d2d | |||
| 74c2db4909 | |||
| e12c3f332e | |||
| 34a64db91d | |||
| e2a4a1e2ea | |||
| 22f12316ef | |||
| e24acece49 | |||
| dd7462bd3f | |||
| da7e0cba3e | |||
| 50219aa872 | |||
| 000660b43a | |||
| fbeaa59f18 | |||
| 578e601368 | |||
| 247fe27f03 | |||
| ee2e807cc2 | |||
| c4a880c870 | |||
| 4eb6fac881 | |||
| 0ab37f49fa | |||
| 376ee02041 | |||
| c779dc6577 | |||
| ad71e81338 | |||
| 9f2ba09fc4 | |||
| 1e6cee2c5d | |||
| 4dc742489d | |||
| d751b985ac | |||
| 8a58190cce | |||
| 612ca15fc0 | |||
| 2200b76054 | |||
| 08771395b8 | |||
| e3c06cad57 | |||
| 4dfadeaf15 | |||
| af1896dc36 | |||
| 350ffdee25 | |||
| 281b92c262 | |||
| 75f1cf8cbb | |||
| bf0f44773f | |||
| dfb89ef7f5 | |||
| 5ef7126a3a | |||
| c9835a53e3 | |||
| 5ea48e6fcd | |||
| 1d1a703e3c | |||
| 8110fec4ef | |||
| 67ecf4e350 | |||
| e8ed0a3034 | |||
| add962c8ea | |||
| 9bed393210 | |||
| 3aac2b2895 | |||
| aa6814a6d3 | |||
| d291582ad9 | |||
| ebc591747a | |||
| 456602b79f | |||
| 8c6c81e8d0 | |||
| 874e94218c |
+1
-2
@@ -2,10 +2,9 @@
|
||||
language: julia
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
# - osx
|
||||
julia:
|
||||
- 0.5
|
||||
- nightly
|
||||
matrix:
|
||||
allow_failures:
|
||||
- julia: nightly
|
||||
|
||||
@@ -3,14 +3,59 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- All new development should target 0.9!
|
||||
- Minor version 0.8 is the last one to support Julia 0.4!!
|
||||
- All new development should target 0.12!
|
||||
- Minor version 0.11 is the last one to support Julia 0.5!!
|
||||
- Critical bugfixes only
|
||||
- `backports` branch is for Julia 0.4
|
||||
|
||||
---
|
||||
|
||||
## 0.9 (current master/dev)
|
||||
## 0.11 (current master/dev)
|
||||
|
||||
#### 0.11.3
|
||||
|
||||
- add HDF5 backend
|
||||
- GR replaces PyPlot as first-choice backend
|
||||
- support for legend position in GR
|
||||
- smaller markers in GR
|
||||
- better viewport size in GR
|
||||
- fix glvisualize support
|
||||
- remove bug with three-argument method of `text`
|
||||
- `legendtitle` attribute added
|
||||
- add test for `spy`
|
||||
|
||||
#### 0.11.0
|
||||
|
||||
- julia 0.6 compatibility
|
||||
- matplotlib 0.2.0 compatibility
|
||||
- add inspectdr backend
|
||||
- improved histogram functionality:
|
||||
- added a `:stephist` and `:scatterhist` series type as well as ``:barhist` (the default)
|
||||
- support for log scale axes with histograms
|
||||
- support for plotting `StatsBase.Histogram`
|
||||
- allowing bins to be specified as `:sturges`, `:rice`, `:scott` or :fd
|
||||
- allow `normalization` to be specified as :density (for unequal bins) or :pdf (sum to 1)
|
||||
- add a `plotattr` function to access documentation for Plots attribute
|
||||
- add `fill_z` attribute for pyplot
|
||||
- add colorbar_title to plotlyjs
|
||||
- enable standalone window for plotlyjs
|
||||
- improved support for pgfplots, ticks rotation, clims, series_annotations
|
||||
- restore colorbars for GR
|
||||
- better axis labels for heatmap in GR
|
||||
- better marker sizes in GR
|
||||
- fix color representation in GR
|
||||
- update GR legend
|
||||
- fix image bug on GR
|
||||
- fix glvisualize dependencies
|
||||
- set dotted grid lines for pyplot
|
||||
- several improvements to inspectdr
|
||||
- improved tick positions for TimeType x axes
|
||||
- support for improved color gradient capability in PlotUtils
|
||||
- add a showlibrary recipe to display color libraries
|
||||
- add a showgradient recipe to display color gradients
|
||||
- add `vectorfield` as an alias for `quiver`
|
||||
- use `PlotUtils.adaptedgrid` for functions
|
||||
|
||||
|
||||
#### 0.9.5
|
||||
|
||||
@@ -331,7 +376,7 @@
|
||||
- z-axis keywords
|
||||
- 3D indexing overhaul: `push!`, `append!` support
|
||||
- matplotlib colormap constants (`:inferno` is the new default colormap for Plots)
|
||||
- `typealias KW Dict{Symbol,Any}` used in place of splatting in many places
|
||||
- `const KW = Dict{Symbol,Any}` used in place of splatting in many places
|
||||
- png generation for plotly backend using wkhtmltoimage
|
||||
- `normalize` and `weights` keywords
|
||||
- background/foreground subcategories for fine-tuning of looks
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/tbreloff/Plots.jl)
|
||||
[](https://travis-ci.org/JuliaPlots/Plots.jl)
|
||||
[](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
julia 0.5
|
||||
|
||||
RecipesBase
|
||||
PlotUtils
|
||||
PlotThemes
|
||||
PlotUtils 0.4.1
|
||||
PlotThemes 0.1.3
|
||||
Reexport
|
||||
FixedSizeArrays
|
||||
FixedPointNumbers 0.3
|
||||
Measures
|
||||
Showoff
|
||||
StatsBase 0.14.0
|
||||
JSON
|
||||
NaNMath
|
||||
|
||||
+6
-5
@@ -1,9 +1,9 @@
|
||||
environment:
|
||||
matrix:
|
||||
- JULIAVERSION: "julialang/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
|
||||
- JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
|
||||
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
|
||||
- JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe"
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
@@ -12,13 +12,14 @@ 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($("http://s3.amazonaws.com/"+$env:JULIAVERSION), "C:\projects\julia-binary.exe")
|
||||
- ps: (new-object net.webclient).DownloadFile($env:JULIA_URL, "C:\projects\julia-binary.exe")
|
||||
# Run installer silently, output to C:\projects\julia
|
||||
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
|
||||
|
||||
|
||||
+50
-38
@@ -1,5 +1,4 @@
|
||||
|
||||
__precompile__()
|
||||
__precompile__(false)
|
||||
|
||||
module Plots
|
||||
|
||||
@@ -10,20 +9,13 @@ using Base.Meta
|
||||
@reexport using PlotUtils
|
||||
@reexport using PlotThemes
|
||||
import Showoff
|
||||
import StatsBase
|
||||
|
||||
export
|
||||
AbstractPlot,
|
||||
Plot,
|
||||
Subplot,
|
||||
AbstractLayout,
|
||||
GridLayout,
|
||||
grid,
|
||||
EmptyLayout,
|
||||
bbox,
|
||||
plotarea,
|
||||
@layout,
|
||||
AVec,
|
||||
AMat,
|
||||
KW,
|
||||
|
||||
wrap,
|
||||
@@ -31,7 +23,7 @@ export
|
||||
|
||||
plot,
|
||||
plot!,
|
||||
update!,
|
||||
attr!,
|
||||
|
||||
current,
|
||||
default,
|
||||
@@ -67,6 +59,7 @@ export
|
||||
savefig,
|
||||
png,
|
||||
gui,
|
||||
inline,
|
||||
closeall,
|
||||
|
||||
backend,
|
||||
@@ -79,7 +72,6 @@ export
|
||||
Shape,
|
||||
text,
|
||||
font,
|
||||
Axis,
|
||||
stroke,
|
||||
brush,
|
||||
Surface,
|
||||
@@ -91,6 +83,9 @@ export
|
||||
Animation,
|
||||
frame,
|
||||
gif,
|
||||
mov,
|
||||
mp4,
|
||||
animate,
|
||||
@animate,
|
||||
@gif,
|
||||
|
||||
@@ -106,14 +101,26 @@ export
|
||||
P2,
|
||||
P3,
|
||||
BezierCurve,
|
||||
curve_points,
|
||||
directed_curve
|
||||
|
||||
plotattr
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
import NaNMath # define functions that ignores NaNs. To overcome the destructive effects of https://github.com/JuliaLang/julia/pull/12563
|
||||
ignoreNaN_minimum{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.minimum(x)
|
||||
ignoreNaN_minimum(x) = Base.minimum(x)
|
||||
ignoreNaN_maximum{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.maximum(x)
|
||||
ignoreNaN_maximum(x) = Base.maximum(x)
|
||||
ignoreNaN_mean{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.mean(x)
|
||||
ignoreNaN_mean(x) = Base.mean(x)
|
||||
ignoreNaN_extrema{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.extrema(x)
|
||||
ignoreNaN_extrema(x) = Base.extrema(x)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
import Measures
|
||||
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
|
||||
typealias BBox Measures.Absolute2DBox
|
||||
const BBox = Measures.Absolute2DBox
|
||||
export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -123,7 +130,6 @@ include("utils.jl")
|
||||
include("components.jl")
|
||||
include("axes.jl")
|
||||
include("args.jl")
|
||||
include("backends.jl")
|
||||
include("themes.jl")
|
||||
include("plot.jl")
|
||||
include("pipeline.jl")
|
||||
@@ -135,6 +141,8 @@ include("animation.jl")
|
||||
include("output.jl")
|
||||
include("examples.jl")
|
||||
include("arg_desc.jl")
|
||||
include("plotattr.jl")
|
||||
include("backends.jl")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -153,6 +161,9 @@ end
|
||||
@shorthands bar
|
||||
@shorthands barh
|
||||
@shorthands histogram
|
||||
@shorthands barhist
|
||||
@shorthands stephist
|
||||
@shorthands scatterhist
|
||||
@shorthands histogram2d
|
||||
@shorthands density
|
||||
@shorthands heatmap
|
||||
@@ -201,28 +212,29 @@ yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip
|
||||
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
||||
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
||||
|
||||
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...)
|
||||
|
||||
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!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
|
||||
ylims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
|
||||
zlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = 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!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
|
||||
yticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
|
||||
xticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
|
||||
yticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
|
||||
annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
annotate!{T<:Tuple}(plt::PlotOrSubplot, anns::AVec{T}; kw...) = 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
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
+86
-38
@@ -1,62 +1,110 @@
|
||||
|
||||
immutable Animation
|
||||
dir::String
|
||||
frames::Vector{String}
|
||||
dir::String
|
||||
frames::Vector{String}
|
||||
end
|
||||
|
||||
function Animation()
|
||||
tmpdir = convert(String, mktempdir())
|
||||
Animation(tmpdir, String[])
|
||||
tmpdir = convert(String, mktempdir())
|
||||
Animation(tmpdir, String[])
|
||||
end
|
||||
|
||||
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)
|
||||
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")
|
||||
|
||||
type 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"
|
||||
immutable AnimatedGif
|
||||
filename::String
|
||||
filename::String
|
||||
end
|
||||
|
||||
function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif"); fps::Integer = 20, loop::Integer = 0)
|
||||
fn = abspath(fn)
|
||||
file_extension(fn) = Base.Filesystem.splitext(fn)[2][2:end]
|
||||
|
||||
try
|
||||
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||
|
||||
# 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)
|
||||
const _imagemagick_initialized = Ref(false)
|
||||
|
||||
function buildanimation(animdir::AbstractString, fn::AbstractString;
|
||||
fps::Integer = 20, loop::Integer = 0)
|
||||
fn = abspath(fn)
|
||||
try
|
||||
if !_imagemagick_initialized[]
|
||||
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
|
||||
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
|
||||
include(file)
|
||||
end
|
||||
_imagemagick_initialized[] = true
|
||||
end
|
||||
|
||||
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
||||
# high quality
|
||||
speed = round(Int, 100 / fps)
|
||||
run(`convert -delay $speed -loop $loop $(joinpath(animdir, "*.png")) -alpha off $fn`)
|
||||
|
||||
catch err
|
||||
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
|
||||
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`.
|
||||
You may also need to install the imagemagick c++ library through your operating system.
|
||||
Will try ffmpeg, but it's lower quality...)""")
|
||||
|
||||
# low quality
|
||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -y $fn`)
|
||||
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
|
||||
end
|
||||
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
||||
run(`convert -delay $speed -loop $loop $(joinpath(anim.dir, "*.png")) -alpha off $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 -loop $loop -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)
|
||||
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)
|
||||
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
@@ -122,7 +170,7 @@ Example:
|
||||
```
|
||||
"""
|
||||
macro gif(forloop::Expr, args...)
|
||||
_animate(forloop, args...; callgif = true)
|
||||
_animate(forloop, args...; callgif = true)
|
||||
end
|
||||
|
||||
"""
|
||||
@@ -131,13 +179,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
|
||||
|
||||
+5
-3
@@ -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 `:seriescolor`.",
|
||||
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
|
||||
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector. For histogram-types, defines the number of bins, or the edges, of the histogram.",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto. For histogram-types, defines the 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)`",
|
||||
: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,6 +29,7 @@ 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)",
|
||||
@@ -39,7 +40,7 @@ const _arg_desc = KW(
|
||||
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
|
||||
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
|
||||
:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.",
|
||||
:normalize => "Bool. Should normalize histogram types? Trying for area == 1.",
|
||||
:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a PDF with integral of 1) and :density (only normalize in respect to bin sizes).",
|
||||
:weights => "AbstractVector. Used in histogram types for weighted counts.",
|
||||
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
|
||||
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
|
||||
@@ -79,6 +80,7 @@ const _arg_desc = KW(
|
||||
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
|
||||
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
|
||||
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)",
|
||||
:legendtitle => "String or nothing (default). Sets the legend title.",
|
||||
:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)",
|
||||
:clims => "`:auto` or NTuple{2,Number}. Fixes the limits of the colorbar.",
|
||||
:legendfont => "Font. Font of legend items.",
|
||||
|
||||
+42
-11
@@ -35,7 +35,9 @@ const _3dTypes = [
|
||||
]
|
||||
const _allTypes = vcat([
|
||||
:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
|
||||
:heatmap, :hexbin, :histogram, :histogram2d, :histogram3d, :density, :bar, :hline, :vline,
|
||||
:heatmap, :hexbin, :barbins, :barhist, :histogram, :scatterbins,
|
||||
:scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :histogram3d,
|
||||
:density, :bar, :hline, :vline,
|
||||
:contour, :pie, :shape, :image
|
||||
], _3dTypes)
|
||||
|
||||
@@ -65,6 +67,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
|
||||
:polygon => :shape,
|
||||
:box => :boxplot,
|
||||
:velocity => :quiver,
|
||||
:vectorfield => :quiver,
|
||||
:gradient => :quiver,
|
||||
:img => :image,
|
||||
:imshow => :image,
|
||||
@@ -77,7 +80,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
|
||||
|
||||
add_non_underscore_aliases!(_typeAliases)
|
||||
|
||||
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
|
||||
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :barhist, :barbins)
|
||||
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
|
||||
like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image)
|
||||
|
||||
@@ -123,6 +126,16 @@ const _markerAliases = Dict{Symbol,Symbol}(
|
||||
:dtri => :dtriangle,
|
||||
:downtri => :dtriangle,
|
||||
:downtriangle => :dtriangle,
|
||||
:> => :rtriangle,
|
||||
:rt => :rtriangle,
|
||||
:rtri => :rtriangle,
|
||||
:righttri => :rtriangle,
|
||||
:righttriangle => :rtriangle,
|
||||
:< => :ltriangle,
|
||||
:lt => :ltriangle,
|
||||
:ltri => :ltriangle,
|
||||
:lighttri => :ltriangle,
|
||||
:lighttriangle => :ltriangle,
|
||||
# :+ => :cross,
|
||||
:plus => :cross,
|
||||
# :x => :xcross,
|
||||
@@ -143,6 +156,8 @@ const _markerAliases = Dict{Symbol,Symbol}(
|
||||
)
|
||||
|
||||
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
const _logScales = [:ln, :log2, :log10]
|
||||
const _logScaleBases = Dict(:ln => e, :log2 => 2.0, :log10 => 10.0)
|
||||
const _scaleAliases = Dict{Symbol,Symbol}(
|
||||
:none => :identity,
|
||||
:log => :log10,
|
||||
@@ -170,7 +185,7 @@ const _series_defaults = KW(
|
||||
:markerstrokewidth => 1,
|
||||
:markerstrokecolor => :match,
|
||||
:markerstrokealpha => nothing,
|
||||
:bins => 30, # number of bins for hists
|
||||
:bins => :auto, # number of bins for hists
|
||||
:smooth => false, # regression line?
|
||||
:group => nothing, # groupby vector
|
||||
:x => nothing,
|
||||
@@ -178,6 +193,7 @@ const _series_defaults = KW(
|
||||
:z => nothing, # depth for contour, surface, etc
|
||||
:marker_z => nothing, # value for color scale
|
||||
:line_z => nothing,
|
||||
:fill_z => nothing,
|
||||
:levels => 15,
|
||||
:orientation => :vertical,
|
||||
:bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side)
|
||||
@@ -194,7 +210,7 @@ const _series_defaults = KW(
|
||||
:match_dimensions => false, # do rows match x (true) or y (false) for heatmap/image/spy? see issue 196
|
||||
# this ONLY effects whether or not the z-matrix is transposed for a heatmap display!
|
||||
:subplot => :auto, # which subplot(s) does this series belong to?
|
||||
:series_annotations => [], # a list of annotations which apply to the coordinates of this series
|
||||
:series_annotations => nothing, # a list of annotations which apply to the coordinates of this series
|
||||
:primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow
|
||||
# one logical series to be broken up (path and markers, for example)
|
||||
:hover => nothing, # text to display when hovering over the data points
|
||||
@@ -235,6 +251,7 @@ const _subplot_defaults = KW(
|
||||
:foreground_color_title => :match, # title color
|
||||
:color_palette => :auto,
|
||||
:legend => :best,
|
||||
:legendtitle => nothing,
|
||||
:colorbar => :legend,
|
||||
:clims => :auto,
|
||||
:legendfont => font(8),
|
||||
@@ -388,7 +405,7 @@ add_aliases(:foreground_color_guide, :fg_guide, :fgguide, :fgcolor_guide, :fg_co
|
||||
# alphas
|
||||
add_aliases(:seriesalpha, :alpha, :α, :opacity)
|
||||
add_aliases(:linealpha, :la, :lalpha, :lα, :lineopacity, :lopacity)
|
||||
add_aliases(:makeralpha, :ma, :malpha, :mα, :makeropacity, :mopacity)
|
||||
add_aliases(:markeralpha, :ma, :malpha, :mα, :markeropacity, :mopacity)
|
||||
add_aliases(:markerstrokealpha, :msa, :msalpha, :msα, :markerstrokeopacity, :msopacity)
|
||||
add_aliases(:fillalpha, :fa, :falpha, :fα, :fillopacity, :fopacity)
|
||||
|
||||
@@ -421,7 +438,9 @@ add_aliases(:zguide, :zlabel, :zlab, :zl)
|
||||
add_aliases(:zlims, :zlim, :zlimit, :zlimits)
|
||||
add_aliases(:zticks, :ztick)
|
||||
add_aliases(:zrotation, :zrot, :zr)
|
||||
add_aliases(:fill_z, :fillz, :fz, :surfacecolor, :surfacecolour, :sc, :surfcolor, :surfcolour)
|
||||
add_aliases(:legend, :leg, :key)
|
||||
add_aliases(:legendtitle, :legend_title, :labeltitle, :label_title, :leg_title, :key_title)
|
||||
add_aliases(:colorbar, :cb, :cbar, :colorkey)
|
||||
add_aliases(:clims, :clim, :cbarlims, :cbar_lims, :climits, :color_limits)
|
||||
add_aliases(:smooth, :regression, :reg)
|
||||
@@ -433,14 +452,14 @@ add_aliases(:color_palette, :palette)
|
||||
add_aliases(:overwrite_figure, :clf, :clearfig, :overwrite, :reuse)
|
||||
add_aliases(:xerror, :xerr, :xerrorbar)
|
||||
add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar)
|
||||
add_aliases(:quiver, :velocity, :quiver2d, :gradient)
|
||||
add_aliases(:quiver, :velocity, :quiver2d, :gradient, :vectorfield)
|
||||
add_aliases(:normalize, :norm, :normed, :normalized)
|
||||
add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio)
|
||||
add_aliases(:match_dimensions, :transpose, :transpose_z)
|
||||
add_aliases(:subplot, :sp, :subplt, :splt)
|
||||
add_aliases(:projection, :proj)
|
||||
add_aliases(:title_location, :title_loc, :titleloc, :title_position, :title_pos, :titlepos, :titleposition, :title_align, :title_alignment)
|
||||
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation)
|
||||
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation, :text, :txt, :texts, :txts)
|
||||
add_aliases(:html_output_format, :format, :fmt, :html_format)
|
||||
add_aliases(:orientation, :direction, :dir)
|
||||
add_aliases(:inset_subplots, :inset, :floating)
|
||||
@@ -692,6 +711,11 @@ function preprocessArgs!(d::KW)
|
||||
end
|
||||
delete!(d, :fill)
|
||||
|
||||
# handle series annotations
|
||||
if haskey(d, :series_annotations)
|
||||
d[:series_annotations] = series_annotations(wraptuple(d[:series_annotations])...)
|
||||
end
|
||||
|
||||
# convert into strokes and brushes
|
||||
|
||||
if haskey(d, :arrow)
|
||||
@@ -842,6 +866,7 @@ function convertLegendValue(val::Symbol)
|
||||
end
|
||||
convertLegendValue(val::Bool) = val ? :best : :none
|
||||
convertLegendValue(val::Void) = :none
|
||||
convertLegendValue{S<:Real, T<:Real}(v::Tuple{S,T}) = v
|
||||
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -1029,8 +1054,14 @@ end
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function _update_subplot_periphery(sp::Subplot, anns::AVec)
|
||||
# extend annotations
|
||||
sp.attr[:annotations] = vcat(anns, sp[:annotations])
|
||||
# extend annotations, and ensure we always have a (x,y,PlotText) tuple
|
||||
newanns = vcat(anns, sp[:annotations])
|
||||
for (i,ann) in enumerate(newanns)
|
||||
x,y,tmp = ann
|
||||
ptxt = isa(tmp, PlotText) ? tmp : text(tmp)
|
||||
newanns[i] = (x,y,ptxt)
|
||||
end
|
||||
sp.attr[:annotations] = newanns
|
||||
|
||||
# handle legend/colorbar
|
||||
sp.attr[:legend] = convertLegendValue(sp.attr[:legend])
|
||||
@@ -1094,7 +1125,7 @@ function _update_axis(axis::Axis, d_in::KW, letter::Symbol, subplot_index::Int)
|
||||
end
|
||||
|
||||
# update the axis
|
||||
update!(axis, args...; kw...)
|
||||
attr!(axis, args...; kw...)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1236,7 +1267,7 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
end
|
||||
|
||||
# scatter plots don't have a line, but must have a shape
|
||||
if d[:seriestype] in (:scatter, :scatter3d)
|
||||
if d[:seriestype] in (:scatter, :scatterbins, :scatterhist, :scatter3d)
|
||||
d[:linewidth] = 0
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
|
||||
+42
-15
@@ -29,7 +29,7 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
d[:discrete_values] = []
|
||||
|
||||
# update the defaults
|
||||
update!(Axis([sp], d), args...; kw...)
|
||||
attr!(Axis([sp], d), args...; kw...)
|
||||
end
|
||||
|
||||
function get_axis(sp::Subplot, letter::Symbol)
|
||||
@@ -83,7 +83,7 @@ function process_axis_arg!(d::KW, arg, letter = "")
|
||||
end
|
||||
|
||||
# update an Axis object with magic args and keywords
|
||||
function update!(axis::Axis, args...; kw...)
|
||||
function attr!(axis::Axis, args...; kw...)
|
||||
# first process args
|
||||
d = axis.d
|
||||
for arg in args
|
||||
@@ -118,7 +118,7 @@ Base.show(io::IO, axis::Axis) = dumpdict(axis.d, "Axis", true)
|
||||
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.d, k)
|
||||
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
|
||||
Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
|
||||
ignoreNaN_extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
|
||||
|
||||
|
||||
const _scale_funcs = Dict{Symbol,Function}(
|
||||
@@ -156,6 +156,30 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
scale = axis[:scale]
|
||||
sf = scalefunc(scale)
|
||||
|
||||
# If the axis input was a Date or DateTime use a special logic to find
|
||||
# "round" Date(Time)s as ticks
|
||||
# This bypasses the rest of optimal_ticks_and_labels, because
|
||||
# optimize_datetime_ticks returns ticks AND labels: the label format (Date
|
||||
# or DateTime) is chosen based on the time span between amin and amax
|
||||
# rather than on the input format
|
||||
# TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime
|
||||
if ticks == nothing && scale == :identity
|
||||
if axis[:formatter] == dateformatter
|
||||
# optimize_datetime_ticks returns ticks and labels(!) based on
|
||||
# integers/floats corresponding to the DateTime type. Thus, the axes
|
||||
# limits, which resulted from converting the Date type to integers,
|
||||
# are converted to 'DateTime integers' (actually floats) before
|
||||
# being passed to optimize_datetime_ticks.
|
||||
# (convert(Int, convert(DateTime, convert(Date, i))) == 87600000*i)
|
||||
ticks, labels = optimize_datetime_ticks(864e5 * amin, 864e5 * amax;
|
||||
k_min = 2, k_max = 4)
|
||||
# Now the ticks are converted back to floats corresponding to Dates.
|
||||
return ticks / 864e5, labels
|
||||
elseif axis[:formatter] == datetimeformatter
|
||||
return optimize_datetime_ticks(amin, amax; k_min = 2, k_max = 4)
|
||||
end
|
||||
end
|
||||
|
||||
# get a list of well-laid-out ticks
|
||||
scaled_ticks = if ticks == nothing
|
||||
optimize_ticks(
|
||||
@@ -205,7 +229,7 @@ function get_ticks(axis::Axis)
|
||||
elseif typeof(ticks) <: AVec
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
elseif typeof(ticks) <: NTuple{2}
|
||||
elseif typeof(ticks) <: NTuple{2, Any}
|
||||
# assuming we're passed (ticks, labels)
|
||||
ticks
|
||||
else
|
||||
@@ -214,7 +238,7 @@ function get_ticks(axis::Axis)
|
||||
# @show ticks dvals cv dv
|
||||
|
||||
# TODO: better/smarter cutoff values for sampling ticks
|
||||
if length(cv) > 30
|
||||
if length(cv) > 30 && ticks == :auto
|
||||
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
|
||||
cv[rng], dv[rng]
|
||||
else
|
||||
@@ -236,8 +260,8 @@ end
|
||||
|
||||
|
||||
function expand_extrema!(ex::Extrema, v::Number)
|
||||
ex.emin = min(v, ex.emin)
|
||||
ex.emax = max(v, ex.emax)
|
||||
ex.emin = NaNMath.min(v, ex.emin)
|
||||
ex.emax = NaNMath.max(v, ex.emax)
|
||||
ex
|
||||
end
|
||||
|
||||
@@ -252,8 +276,8 @@ expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
|
||||
|
||||
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
|
||||
ex = axis[:extrema]
|
||||
ex.emin = min(v[1], ex.emin)
|
||||
ex.emax = max(v[2], ex.emax)
|
||||
ex.emin = NaNMath.min(v[1], ex.emin)
|
||||
ex.emax = NaNMath.max(v[2], ex.emax)
|
||||
ex
|
||||
end
|
||||
function expand_extrema!{N<:Number}(axis::Axis, v::AVec{N})
|
||||
@@ -325,11 +349,11 @@ function expand_extrema!(sp::Subplot, d::KW)
|
||||
|
||||
bw = d[:bar_width]
|
||||
if bw == nothing
|
||||
bw = d[:bar_width] = mean(diff(data))
|
||||
bw = d[:bar_width] = ignoreNaN_mean(diff(data))
|
||||
end
|
||||
axis = sp.attr[Symbol(dsym, :axis)]
|
||||
expand_extrema!(axis, maximum(data) + 0.5maximum(bw))
|
||||
expand_extrema!(axis, minimum(data) - 0.5minimum(bw))
|
||||
expand_extrema!(axis, ignoreNaN_maximum(data) + 0.5maximum(bw))
|
||||
expand_extrema!(axis, ignoreNaN_minimum(data) - 0.5minimum(bw))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -344,8 +368,8 @@ end
|
||||
# push the limits out slightly
|
||||
function widen(lmin, lmax)
|
||||
span = lmax - lmin
|
||||
# eps = max(1e-16, min(1e-2span, 1e-10))
|
||||
eps = max(1e-16, 0.03span)
|
||||
# eps = NaNMath.max(1e-16, min(1e-2span, 1e-10))
|
||||
eps = NaNMath.max(1e-16, 0.03span)
|
||||
lmin-eps, lmax+eps
|
||||
end
|
||||
|
||||
@@ -381,6 +405,9 @@ 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 should_widen
|
||||
widen(amin, amax)
|
||||
else
|
||||
@@ -398,7 +425,7 @@ function discrete_value!(axis::Axis, dv)
|
||||
# @show axis[:discrete_map], axis[:discrete_values], dv
|
||||
if cv_idx == -1
|
||||
ex = axis[:extrema]
|
||||
cv = max(0.5, ex.emax + 1.0)
|
||||
cv = NaNMath.max(0.5, ex.emax + 1.0)
|
||||
expand_extrema!(axis, cv)
|
||||
push!(axis[:discrete_values], dv)
|
||||
push!(axis[:continuous_values], cv)
|
||||
|
||||
+31
-9
@@ -51,6 +51,21 @@ _before_layout_calcs(plt::Plot) = nothing
|
||||
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
|
||||
function tick_padding(axis::Axis)
|
||||
ticks = get_ticks(axis)
|
||||
@@ -58,19 +73,24 @@ function tick_padding(axis::Axis)
|
||||
0mm
|
||||
else
|
||||
vals, labs = ticks
|
||||
ptsz = axis[:tickfont].pointsize * pt
|
||||
|
||||
# we need to compute the size of the ticks generically
|
||||
# this means computing the bounding box and then getting the width/height
|
||||
isempty(labs) && return 0mm
|
||||
# ptsz = axis[:tickfont].pointsize * pt
|
||||
longest_label = maximum(length(lab) for lab in labs)
|
||||
labelwidth = 0.8longest_label * ptsz
|
||||
|
||||
# generalize by "rotating" y labels
|
||||
rot = axis[:rotation] + (axis[:letter] == :y ? 90 : 0)
|
||||
|
||||
# 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
|
||||
# # 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[:tickfont].pointsize, rot)[2]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -128,7 +148,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 ("PyPlot", "GR", "PlotlyJS", "Immerse", "Gadfly", "UnicodePlots")
|
||||
for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize")
|
||||
if Pkg.installed(pkgstr) != nothing
|
||||
return backend(Symbol(lowercase(pkgstr)))
|
||||
end
|
||||
@@ -256,6 +276,8 @@ end
|
||||
@init_backend GR
|
||||
@init_backend GLVisualize
|
||||
@init_backend PGFPlots
|
||||
@init_backend InspectDR
|
||||
@init_backend HDF5
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
+441
-221
File diff suppressed because it is too large
Load Diff
+143
-67
@@ -20,7 +20,7 @@ const _gr_attr = merge_with_base_supported([
|
||||
:title, :window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:grid, :legend, :legendtitle, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
@@ -30,6 +30,7 @@ const _gr_attr = merge_with_base_supported([
|
||||
:normalize, :weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:arrow,
|
||||
])
|
||||
const _gr_seriestype = [
|
||||
:path, :scatter,
|
||||
@@ -75,6 +76,8 @@ const gr_markertype = KW(
|
||||
:diamond => -13,
|
||||
:utriangle => -3,
|
||||
:dtriangle => -5,
|
||||
:ltriangle => -18,
|
||||
:rtriangle => -17,
|
||||
:pentagon => -21,
|
||||
:hexagon => -22,
|
||||
:heptagon => -23,
|
||||
@@ -131,7 +134,7 @@ gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(cycle(c,1)))
|
||||
|
||||
# draw line segments, splitting x/y into contiguous/finite segments
|
||||
# note: this can be used for shapes by passing func `GR.fillarea`
|
||||
function gr_polyline(x, y, func = GR.polyline)
|
||||
function gr_polyline(x, y, func = GR.polyline; arrowside=:none)
|
||||
iend = 0
|
||||
n = length(x)
|
||||
while iend < n-1
|
||||
@@ -159,12 +162,20 @@ function gr_polyline(x, y, func = GR.polyline)
|
||||
# if we found a start and end, draw the line segment, otherwise we're done
|
||||
if istart > 0 && iend > 0
|
||||
func(x[istart:iend], y[istart:iend])
|
||||
if arrowside in (:head,:both)
|
||||
GR.drawarrow(x[iend-1], y[iend-1], x[iend], y[iend])
|
||||
end
|
||||
if arrowside in (:tail,:both)
|
||||
GR.drawarrow(x[istart+1], y[istart+1], x[istart], y[istart])
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
gr_inqtext(x, y, s::Symbol) = gr_inqtext(x, y, string(s))
|
||||
|
||||
function gr_inqtext(x, y, s)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.inqtextext(x, y, s[2:end-1])
|
||||
@@ -175,6 +186,8 @@ function gr_inqtext(x, y, s)
|
||||
end
|
||||
end
|
||||
|
||||
gr_text(x, y, s::Symbol) = gr_text(x, y, string(s))
|
||||
|
||||
function gr_text(x, y, s)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.mathtex(x, y, s[2:end-1])
|
||||
@@ -251,7 +264,7 @@ end
|
||||
|
||||
normalize_zvals(zv::Void) = zv
|
||||
function normalize_zvals(zv::AVec)
|
||||
vmin, vmax = extrema(zv)
|
||||
vmin, vmax = ignoreNaN_extrema(zv)
|
||||
if vmin == vmax
|
||||
zeros(length(zv))
|
||||
else
|
||||
@@ -263,28 +276,33 @@ end
|
||||
|
||||
# draw ONE Shape
|
||||
function gr_draw_marker(xi, yi, msize, shape::Shape)
|
||||
sx, sy = shape_coords(shape)
|
||||
sx, sy = coords(shape)
|
||||
# convert to ndc coords (percentages of window)
|
||||
GR.selntran(0)
|
||||
w, h = gr_plot_size
|
||||
f = msize / (w + h)
|
||||
xi, yi = GR.wctondc(xi, yi)
|
||||
GR.fillarea(xi + sx * 0.0015msize,
|
||||
yi + sy * 0.0015msize)
|
||||
GR.fillarea(xi .+ sx .* f,
|
||||
yi .+ sy .* f)
|
||||
GR.selntran(1)
|
||||
end
|
||||
|
||||
# draw ONE symbol marker
|
||||
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
GR.setmarkersize(0.3msize)
|
||||
w, h = gr_plot_size
|
||||
GR.setmarkersize(0.3msize / ((w + h) * 0.001))
|
||||
GR.polymarker([xi], [yi])
|
||||
end
|
||||
|
||||
|
||||
# draw the markers, one at a time
|
||||
function gr_draw_markers(series::Series, x, y, msize, mz)
|
||||
shape = series[:markershape]
|
||||
if shape != :none
|
||||
shapes = series[:markershape]
|
||||
if shapes != :none
|
||||
for i=1:length(x)
|
||||
msi = cycle(msize, i)
|
||||
shape = cycle(shapes, i)
|
||||
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
|
||||
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
|
||||
|
||||
@@ -320,9 +338,10 @@ end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function gr_set_line(w, style, c) #, a)
|
||||
function gr_set_line(lw, style, c) #, a)
|
||||
GR.setlinetype(gr_linetype[style])
|
||||
GR.setlinewidth(w)
|
||||
w, h = gr_plot_size
|
||||
GR.setlinewidth(max(0, lw / ((w + h) * 0.001)))
|
||||
gr_set_linecolor(c) #, a)
|
||||
end
|
||||
|
||||
@@ -364,20 +383,31 @@ end
|
||||
# values are [xmin, xmax, ymin, ymax]. they range [0,1].
|
||||
const viewport_plotarea = zeros(4)
|
||||
|
||||
function gr_viewport_from_bbox(bb::BoundingBox, w, h, viewport_canvas)
|
||||
# the size of the current plot in pixels
|
||||
const gr_plot_size = zeros(2)
|
||||
|
||||
function gr_viewport_from_bbox(sp::Subplot{GRBackend}, bb::BoundingBox, w, h, viewport_canvas)
|
||||
viewport = zeros(4)
|
||||
viewport[1] = viewport_canvas[2] * (left(bb) / w)
|
||||
viewport[2] = viewport_canvas[2] * (right(bb) / w)
|
||||
viewport[3] = viewport_canvas[4] * (1.0 - bottom(bb) / h)
|
||||
viewport[4] = viewport_canvas[4] * (1.0 - top(bb) / h)
|
||||
if is3d(sp)
|
||||
vp = viewport[:]
|
||||
extent = min(vp[2] - vp[1], vp[4] - vp[3])
|
||||
viewport[1] = 0.5 * (vp[1] + vp[2] - extent)
|
||||
viewport[2] = 0.5 * (vp[1] + vp[2] + extent)
|
||||
viewport[3] = 0.5 * (vp[3] + vp[4] - extent)
|
||||
viewport[4] = 0.5 * (vp[3] + vp[4] + extent)
|
||||
end
|
||||
viewport
|
||||
end
|
||||
|
||||
# change so we're focused on the viewport area
|
||||
function gr_set_viewport_cmap(sp::Subplot)
|
||||
GR.setviewport(
|
||||
viewport_plotarea[2] + (is3d(sp) ? 0.04 : 0.02),
|
||||
viewport_plotarea[2] + (is3d(sp) ? 0.07 : 0.05),
|
||||
viewport_plotarea[2] + (is3d(sp) ? 0.07 : 0.02),
|
||||
viewport_plotarea[2] + (is3d(sp) ? 0.10 : 0.05),
|
||||
viewport_plotarea[3],
|
||||
viewport_plotarea[4]
|
||||
)
|
||||
@@ -398,7 +428,7 @@ function gr_set_viewport_polar()
|
||||
ymax -= 0.05 * (xmax - xmin)
|
||||
xcenter = 0.5 * (xmin + xmax)
|
||||
ycenter = 0.5 * (ymin + ymax)
|
||||
r = 0.5 * min(xmax - xmin, ymax - ymin)
|
||||
r = 0.5 * NaNMath.min(xmax - xmin, ymax - ymin)
|
||||
GR.setviewport(xcenter -r, xcenter + r, ycenter - r, ycenter + r)
|
||||
GR.setwindow(-1, 1, -1, 1)
|
||||
r
|
||||
@@ -408,16 +438,41 @@ end
|
||||
function gr_colorbar(sp::Subplot)
|
||||
if sp[:colorbar] != :none
|
||||
gr_set_viewport_cmap(sp)
|
||||
GR.colormap()
|
||||
GR.colorbar()
|
||||
gr_set_viewport_plotarea()
|
||||
end
|
||||
end
|
||||
|
||||
gr_view_xcenter() = 0.5 * (viewport_plotarea[1] + viewport_plotarea[2])
|
||||
gr_view_ycenter() = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
|
||||
gr_view_xdiff() = viewport_plotarea[2] - viewport_plotarea[1]
|
||||
gr_view_ydiff() = viewport_plotarea[4] - viewport_plotarea[3]
|
||||
|
||||
function gr_legend_pos(s::Symbol,w,h)
|
||||
str = string(s)
|
||||
if str == "best"
|
||||
str = "topright"
|
||||
end
|
||||
if contains(str,"right")
|
||||
xpos = viewport_plotarea[2] - 0.05 - w
|
||||
elseif contains(str,"left")
|
||||
xpos = viewport_plotarea[1] + 0.11
|
||||
else
|
||||
xpos = (viewport_plotarea[2]-viewport_plotarea[1])/2 - w/2 +.04
|
||||
end
|
||||
if contains(str,"top")
|
||||
ypos = viewport_plotarea[4] - 0.06
|
||||
elseif contains(str,"bottom")
|
||||
ypos = viewport_plotarea[3] + h + 0.06
|
||||
else
|
||||
ypos = (viewport_plotarea[4]-viewport_plotarea[3])/2 + h/2
|
||||
end
|
||||
(xpos,ypos)
|
||||
end
|
||||
|
||||
function gr_legend_pos{S<:Real, T<:Real}(v::Tuple{S,T},w,h)
|
||||
xpos = v[1] * (viewport_plotarea[2] - viewport_plotarea[1]) + viewport_plotarea[1]
|
||||
ypos = v[2] * (viewport_plotarea[4] - viewport_plotarea[3]) + viewport_plotarea[3]
|
||||
(xpos,ypos)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
@@ -445,6 +500,7 @@ function gr_display(plt::Plot)
|
||||
# compute the viewport_canvas, normalized to the larger dimension
|
||||
viewport_canvas = Float64[0,1,0,1]
|
||||
w, h = plt[:size]
|
||||
gr_plot_size[:] = [w, h]
|
||||
if w > h
|
||||
ratio = float(h) / w
|
||||
msize = display_width_ratio * w
|
||||
@@ -477,10 +533,15 @@ function gr_display(plt::Plot)
|
||||
end
|
||||
|
||||
|
||||
function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
sp.minpad = (10mm,2mm,2mm,8mm)
|
||||
end
|
||||
|
||||
|
||||
function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# the viewports for this subplot
|
||||
viewport_subplot = gr_viewport_from_bbox(bbox(sp), w, h, viewport_canvas)
|
||||
viewport_plotarea[:] = gr_viewport_from_bbox(plotarea(sp), w, h, viewport_canvas)
|
||||
viewport_subplot = gr_viewport_from_bbox(sp, bbox(sp), w, h, viewport_canvas)
|
||||
viewport_plotarea[:] = gr_viewport_from_bbox(sp, plotarea(sp), w, h, viewport_canvas)
|
||||
# get data limits
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
|
||||
@@ -524,6 +585,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
if st == :heatmap
|
||||
outside_ticks = true
|
||||
x, y = heatmap_edges(series[:x]), heatmap_edges(series[:y])
|
||||
expand_extrema!(sp[:xaxis], x)
|
||||
expand_extrema!(sp[:yaxis], y)
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -542,16 +607,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# set the scale flags and window
|
||||
xmin, xmax, ymin, ymax = data_lims
|
||||
scale = 0
|
||||
scaleop = 0
|
||||
xtick, ytick = 1, 1
|
||||
if xmax > xmin && ymax > ymin
|
||||
# NOTE: for log axes, the major_x and major_y - if non-zero (omit labels) - control the minor grid lines (1 = draw 9 minor grid lines, 2 = no minor grid lines)
|
||||
# NOTE: for log axes, the x_tick and y_tick - if non-zero (omit axes) - only affect the output appearance (1 = nomal, 2 = scientiic notation)
|
||||
xaxis[:scale] == :log10 && (scale |= GR.OPTION_X_LOG)
|
||||
yaxis[:scale] == :log10 && (scale |= GR.OPTION_Y_LOG)
|
||||
xaxis[:flip] && (scale |= GR.OPTION_FLIP_X)
|
||||
yaxis[:flip] && (scale |= GR.OPTION_FLIP_Y)
|
||||
if scale & GR.OPTION_X_LOG == 0
|
||||
xaxis[:scale] == :log10 && (scaleop |= GR.OPTION_X_LOG)
|
||||
yaxis[:scale] == :log10 && (scaleop |= GR.OPTION_Y_LOG)
|
||||
xaxis[:flip] && (scaleop |= GR.OPTION_FLIP_X)
|
||||
yaxis[:flip] && (scaleop |= GR.OPTION_FLIP_Y)
|
||||
if scaleop & GR.OPTION_X_LOG == 0
|
||||
majorx = 1 #5
|
||||
xtick = GR.tick(xmin, xmax) / majorx
|
||||
else
|
||||
@@ -559,7 +624,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
xtick = 2 # scientific notation
|
||||
majorx = 2 # no minor grid lines
|
||||
end
|
||||
if scale & GR.OPTION_Y_LOG == 0
|
||||
if scaleop & GR.OPTION_Y_LOG == 0
|
||||
majory = 1 #5
|
||||
ytick = GR.tick(ymin, ymax) / majory
|
||||
else
|
||||
@@ -570,7 +635,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# NOTE: setwindow sets the "data coordinate" limits of the current "viewport"
|
||||
GR.setwindow(xmin, xmax, ymin, ymax)
|
||||
GR.setscale(scale)
|
||||
GR.setscale(scaleop)
|
||||
end
|
||||
|
||||
# draw the axes
|
||||
@@ -593,15 +658,15 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# GR.setlinetype(GR.LINETYPE_DOTTED)
|
||||
if sp[:grid]
|
||||
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
|
||||
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
|
||||
GR.grid3d(xtick, 0, ztick, xmin, ymax, zmin, 2, 0, 2)
|
||||
GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0)
|
||||
end
|
||||
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
|
||||
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
|
||||
|
||||
elseif ispolar(sp)
|
||||
r = gr_set_viewport_polar()
|
||||
rmin, rmax = GR.adjustrange(minimum(r), maximum(r))
|
||||
rmin, rmax = GR.adjustrange(ignoreNaN_minimum(r), ignoreNaN_maximum(r))
|
||||
# rmin, rmax = axis_limits(sp[:yaxis])
|
||||
gr_polaraxes(rmin, rmax)
|
||||
|
||||
@@ -625,13 +690,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# spine (border) and tick marks
|
||||
gr_set_line(1, :solid, sp[:xaxis][:foreground_color_axis])
|
||||
GR.setclip(0)
|
||||
gr_polyline(coords(spine_segs)...)
|
||||
GR.setclip(1)
|
||||
|
||||
if !(xticks in (nothing, false))
|
||||
# x labels
|
||||
flip = sp[:yaxis][:flip]
|
||||
mirror = sp[:xaxis][:mirror]
|
||||
gr_set_font(sp[:xaxis][:tickfont],
|
||||
halign = (:left, :hcenter, :right)[sign(sp[:xaxis][:rotation]) + 2],
|
||||
valign = (mirror ? :bottom : :top),
|
||||
color = sp[:xaxis][:foreground_color_axis],
|
||||
rotation = sp[:xaxis][:rotation])
|
||||
@@ -639,7 +707,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# use xor ($) to get the right y coords
|
||||
xi, yi = GR.wctondc(cv, (flip $ mirror) ? ymax : ymin)
|
||||
# @show cv dv ymin xi yi flip mirror (flip $ mirror)
|
||||
gr_text(xi, yi + (mirror ? 1 : -1) * 2e-3, string(dv))
|
||||
gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3, string(dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -649,28 +717,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
mirror = sp[:yaxis][:mirror]
|
||||
gr_set_font(sp[:yaxis][:tickfont],
|
||||
halign = (mirror ? :left : :right),
|
||||
valign = (:top, :vcenter, :bottom)[sign(sp[:yaxis][:rotation]) + 2],
|
||||
color = sp[:yaxis][:foreground_color_axis],
|
||||
rotation = sp[:yaxis][:rotation])
|
||||
for (cv, dv) in zip(yticks...)
|
||||
# use xor ($) to get the right y coords
|
||||
xi, yi = GR.wctondc((flip $ mirror) ? xmax : xmin, cv)
|
||||
# @show cv dv xmin xi yi
|
||||
gr_text(xi + (mirror ? 1 : -1) * 2e-3, yi, string(dv))
|
||||
gr_text(xi + (mirror ? 1 : -1) * 1e-2, yi, string(dv))
|
||||
end
|
||||
end
|
||||
|
||||
# window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
|
||||
# ticksize = 0.0075 * window_diag
|
||||
# if outside_ticks
|
||||
# ticksize = -ticksize
|
||||
# end
|
||||
# # TODO: this should be done for each axis separately
|
||||
# gr_set_linecolor(xaxis[:foreground_color_axis])
|
||||
|
||||
# x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
|
||||
# y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
|
||||
# GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
|
||||
# GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
|
||||
end
|
||||
# end
|
||||
|
||||
@@ -730,12 +786,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
frng = series[:fillrange]
|
||||
|
||||
# add custom frame shapes to markershape?
|
||||
series_annotations_shapes!(series)
|
||||
# -------------------------------------------------------
|
||||
|
||||
# recompute data
|
||||
if typeof(z) <: Surface
|
||||
# if st == :heatmap
|
||||
# expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
|
||||
# expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
|
||||
# end
|
||||
z = vec(transpose_z(series, z.surf, false))
|
||||
elseif ispolar(sp)
|
||||
if frng != nothing
|
||||
@@ -764,7 +820,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# draw the line(s)
|
||||
if st == :path
|
||||
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
|
||||
gr_polyline(x, y)
|
||||
arrowside = isa(series[:arrow], Arrow) ? series[:arrow].side : :none
|
||||
gr_polyline(x, y; arrowside = arrowside)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -779,12 +836,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
if typeof(series[:levels]) <: Array
|
||||
h = series[:levels]
|
||||
else
|
||||
h = linspace(zmin, zmax, series[:levels])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
if series[:fillrange] != nothing
|
||||
GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
|
||||
else
|
||||
@@ -794,7 +851,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# create the colorbar of contour levels
|
||||
if sp[:colorbar] != :none
|
||||
gr_set_viewport_cmap(sp)
|
||||
l = round(Int32, 1000 + (h - minimum(h)) / (maximum(h) - minimum(h)) * 255)
|
||||
l = round(Int32, 1000 + (h - ignoreNaN_minimum(h)) / (ignoreNaN_maximum(h) - ignoreNaN_minimum(h)) * 255)
|
||||
GR.setwindow(xmin, xmax, zmin, zmax)
|
||||
GR.cellarray(xmin, xmax, zmax, zmin, 1, length(l), l)
|
||||
ztick = 0.5 * GR.tick(zmin, zmax)
|
||||
@@ -822,13 +879,14 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
|
||||
colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z]
|
||||
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
|
||||
round(Int, blue(c) * 255) << 16 +
|
||||
round(Int, green(c) * 255) << 8 +
|
||||
round(Int, red(c) * 255) ), colors)
|
||||
GR.drawimage(xmin, xmax, ymin, ymax, length(x), length(y), rgba)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, length(x), length(y), rgba)
|
||||
cmap && gr_colorbar(sp)
|
||||
|
||||
elseif st in (:path3d, :scatter3d)
|
||||
@@ -851,7 +909,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.selntran(0)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
xmin, xmax, ymin, ymax = viewport_plotarea
|
||||
ymax -= 0.05 * (xmax - xmin)
|
||||
ymax -= 0.1 * (xmax - xmin)
|
||||
xcenter = 0.5 * (xmin + xmax)
|
||||
ycenter = 0.5 * (ymin + ymax)
|
||||
if xmax - xmin > ymax - ymin
|
||||
@@ -915,8 +973,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
|
||||
elseif st == :image
|
||||
z = transpose_z(series, series[:z].surf, true)
|
||||
h, w = size(z)
|
||||
z = transpose_z(series, series[:z].surf, true)'
|
||||
w, h = size(z)
|
||||
if eltype(z) <: Colors.AbstractGray
|
||||
grey = round(UInt8, float(z) * 255)
|
||||
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
|
||||
@@ -929,6 +987,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
||||
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)
|
||||
gr_set_font(fnt)
|
||||
gr_text(GR.wctondc(xi, yi)..., str)
|
||||
end
|
||||
|
||||
GR.restorestate()
|
||||
end
|
||||
|
||||
@@ -942,6 +1007,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
w = 0
|
||||
i = 0
|
||||
n = 0
|
||||
if sp[:legendtitle] != nothing
|
||||
tbx, tby = gr_inqtext(0, 0, string(sp[:legendtitle]))
|
||||
w = tbx[3] - tbx[1]
|
||||
n += 1
|
||||
end
|
||||
for series in series_list(sp)
|
||||
should_add_to_legend(series) || continue
|
||||
n += 1
|
||||
@@ -955,9 +1025,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
w = max(w, tbx[3] - tbx[1])
|
||||
end
|
||||
if w > 0
|
||||
xpos = viewport_plotarea[2] - 0.05 - w
|
||||
ypos = viewport_plotarea[4] - 0.06
|
||||
dy = _gr_point_mult[1] * sp[:legendfont].pointsize * 1.75
|
||||
h = dy*n
|
||||
(xpos,ypos) = gr_legend_pos(sp[:legend],w,h)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
gr_set_fillcolor(sp[:background_color_legend])
|
||||
GR.fillrect(xpos - 0.08, xpos + w + 0.02, ypos + dy, ypos - dy * n)
|
||||
@@ -965,13 +1035,19 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.setlinewidth(1)
|
||||
GR.drawrect(xpos - 0.08, xpos + w + 0.02, ypos + dy, ypos - dy * n)
|
||||
i = 0
|
||||
if sp[:legendtitle] != nothing
|
||||
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_HALF)
|
||||
gr_set_textcolor(sp[:foreground_color_legend])
|
||||
gr_text(xpos - 0.03 + 0.5*w, ypos, string(sp[:legendtitle]))
|
||||
ypos -= dy
|
||||
end
|
||||
for series in series_list(sp)
|
||||
should_add_to_legend(series) || continue
|
||||
st = series[:seriestype]
|
||||
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
|
||||
if st == :path
|
||||
if st == :path && series[:fillrange] == nothing
|
||||
GR.polyline([xpos - 0.07, xpos - 0.01], [ypos, ypos])
|
||||
elseif st == :shape
|
||||
elseif st == :shape || series[:fillrange] != nothing
|
||||
gr_set_fill(series[:fillcolor]) #, series[:fillalpha])
|
||||
l, r = xpos-0.07, xpos-0.01
|
||||
b, t = ypos-0.4dy, ypos+0.4dy
|
||||
@@ -982,7 +1058,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
|
||||
if series[:markershape] != :none
|
||||
gr_draw_markers(series, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
|
||||
gr_draw_markers(series, xpos - .035, ypos, 6, nothing)
|
||||
end
|
||||
|
||||
if typeof(series[:label]) <: Array
|
||||
@@ -1044,14 +1120,14 @@ else
|
||||
"use_default"
|
||||
end
|
||||
|
||||
const _gr_wstype = Ref(get(ENV, "GKS_WSTYPE", _gr_wstype_default))
|
||||
const _gr_wstype = Ref(get(ENV, "GKSwstype", _gr_wstype_default))
|
||||
gr_set_output(wstype::String) = (_gr_wstype[] = wstype)
|
||||
|
||||
for (mime, fmt) in _gr_mimeformats
|
||||
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
|
||||
GR.emergencyclosegks()
|
||||
filepath = tempname() * "." * $fmt
|
||||
ENV["GKS_WSTYPE"] = $fmt
|
||||
ENV["GKSwstype"] = $fmt
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
GR.emergencyclosegks()
|
||||
@@ -1064,17 +1140,17 @@ function _display(plt::Plot{GRBackend})
|
||||
if plt[:display_type] == :inline
|
||||
GR.emergencyclosegks()
|
||||
filepath = tempname() * ".pdf"
|
||||
ENV["GKS_WSTYPE"] = "pdf"
|
||||
ENV["GKSwstype"] = "pdf"
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
GR.emergencyclosegks()
|
||||
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(readbytes, filepath)), "\a")
|
||||
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(read, filepath)), "\a")
|
||||
println(content)
|
||||
rm(filepath)
|
||||
else
|
||||
ENV["GKS_DOUBLE_BUF"] = true
|
||||
if _gr_wstype[] != "use_default"
|
||||
ENV["GKS_WSTYPE"] = _gr_wstype[]
|
||||
ENV["GKSwstype"] = _gr_wstype[]
|
||||
end
|
||||
gr_display(plt)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,655 @@
|
||||
#HDF5 Plots: Save/replay plots to/from HDF5
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
#==Usage
|
||||
===============================================================================
|
||||
Write to .hdf5 file using:
|
||||
p = plot(...)
|
||||
Plots.hdf5plot_write(p, "plotsave.hdf5")
|
||||
|
||||
Read from .hdf5 file using:
|
||||
pyplot() #Must first select backend
|
||||
pread = Plots.hdf5plot_read("plotsave.hdf5")
|
||||
display(pread)
|
||||
==#
|
||||
|
||||
|
||||
#==TODO
|
||||
===============================================================================
|
||||
1. Support more features
|
||||
- SeriesAnnotations & GridLayout known to be missing.
|
||||
3. Improve error handling.
|
||||
- Will likely crash if file format is off.
|
||||
2. Save data in a folder parallel to "plot".
|
||||
- Will make it easier for users to locate data.
|
||||
- Use HDF5 reference to link data?
|
||||
3. Develop an actual versioned file format.
|
||||
- Should have some form of backward compatibility.
|
||||
- Should be reliable for archival purposes.
|
||||
==#
|
||||
|
||||
|
||||
import FixedPointNumbers: N0f8 #In core Julia
|
||||
|
||||
#Dispatch types:
|
||||
immutable HDF5PlotNative; end #Indentifies a data element that can natively be handled by HDF5
|
||||
immutable HDF5CTuple; end #Identifies a "complex" tuple structure
|
||||
|
||||
type HDF5Plot_PlotRef
|
||||
ref::Union{Plot, Void}
|
||||
end
|
||||
|
||||
|
||||
#==Useful constants
|
||||
===============================================================================#
|
||||
const _hdf5_plotroot = "plot"
|
||||
const _hdf5_dataroot = "data" #TODO: Eventually move data to different root (easier to locate)?
|
||||
const _hdf5plot_datatypeid = "TYPE" #Attribute identifying type
|
||||
const _hdf5plot_countid = "COUNT" #Attribute for storing count
|
||||
|
||||
#Dict has problems using "Types" as keys. Initialize in "_initialize_backend":
|
||||
const HDF5PLOT_MAP_STR2TELEM = Dict{String, Type}()
|
||||
const HDF5PLOT_MAP_TELEM2STR = Dict{Type, String}()
|
||||
|
||||
#Don't really like this global variable... Very hacky
|
||||
const HDF5PLOT_PLOTREF = HDF5Plot_PlotRef(nothing)
|
||||
|
||||
#Simple sub-structures that can just be written out using _hdf5plot_gwritefields:
|
||||
const HDF5PLOT_SIMPLESUBSTRUCT = Union{Font, BoundingBox,
|
||||
GridLayout, RootLayout, ColorGradient, SeriesAnnotations, PlotText
|
||||
}
|
||||
|
||||
|
||||
#==
|
||||
===============================================================================#
|
||||
|
||||
const _hdf5_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:label,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location, :titlefont,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
:contours, :aspect_ratio,
|
||||
:match_dimensions,
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
:colorbar_title,
|
||||
])
|
||||
const _hdf5_seriestype = [
|
||||
:path, :steppre, :steppost, :shape,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||
]
|
||||
const _hdf5_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _hdf5_marker = vcat(_allMarkers, :pixel)
|
||||
const _hdf5_scale = [:identity, :ln, :log2, :log10]
|
||||
is_marker_supported(::HDF5Backend, shape::Shape) = true
|
||||
|
||||
function add_backend_string(::HDF5Backend)
|
||||
"""
|
||||
if !Plots.is_installed("HDF5")
|
||||
Pkg.add("HDF5")
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
#==Helper functions
|
||||
===============================================================================#
|
||||
|
||||
_hdf5_plotelempath(subpath::String) = "$_hdf5_plotroot/$subpath"
|
||||
_hdf5_datapath(subpath::String) = "$_hdf5_dataroot/$subpath"
|
||||
_hdf5_map_str2telem(k::String) = HDF5PLOT_MAP_STR2TELEM[k]
|
||||
_hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
|
||||
|
||||
function _hdf5_merge!(dest::Dict, src::Dict)
|
||||
for (k, v) in src
|
||||
if isa(v, Axis)
|
||||
_hdf5_merge!(dest[k].d, v.d)
|
||||
else
|
||||
dest[k] = v
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
#==
|
||||
===============================================================================#
|
||||
|
||||
function _initialize_backend(::HDF5Backend)
|
||||
@eval begin
|
||||
import HDF5
|
||||
export HDF5
|
||||
if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
#Possible element types of high-level data types:
|
||||
const telem2str = Dict{String, Type}(
|
||||
"NATIVE" => HDF5PlotNative,
|
||||
"VOID" => Void,
|
||||
"BOOL" => Bool,
|
||||
"SYMBOL" => Symbol,
|
||||
"TUPLE" => Tuple,
|
||||
"CTUPLE" => HDF5CTuple, #Tuple of complex structures
|
||||
"RGBA" => ARGB{N0f8},
|
||||
"EXTREMA" => Extrema,
|
||||
"LENGTH" => Length,
|
||||
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
|
||||
|
||||
#Sub-structure types:
|
||||
"FONT" => Font,
|
||||
"BOUNDINGBOX" => BoundingBox,
|
||||
"GRIDLAYOUT" => GridLayout,
|
||||
"ROOTLAYOUT" => RootLayout,
|
||||
"SERIESANNOTATIONS" => SeriesAnnotations,
|
||||
# "PLOTTEXT" => PlotText,
|
||||
"COLORGRADIENT" => ColorGradient,
|
||||
"AXIS" => Axis,
|
||||
"SUBPLOT" => Subplot,
|
||||
"NULLABLE" => Nullable,
|
||||
)
|
||||
merge!(HDF5PLOT_MAP_STR2TELEM, telem2str)
|
||||
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Create the window/figure for this backend.
|
||||
function _create_backend_figure(plt::Plot{HDF5Backend})
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# # this is called early in the pipeline, use it to make the plot current or something
|
||||
# function _prepare_plot_object(plt::Plot{HDF5Backend})
|
||||
# end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Set up the subplot within the backend object.
|
||||
function _initialize_subplot(plt::Plot{HDF5Backend}, sp::Subplot{HDF5Backend})
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Add one series to the underlying backend object.
|
||||
# Called once per series
|
||||
# NOTE: Seems to be called when user calls plot()... even if backend
|
||||
# plot, sp.o has not yet been constructed...
|
||||
function _series_added(plt::Plot{HDF5Backend}, series::Series)
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# When series data is added/changed, this callback can do dynamic updates to the backend object.
|
||||
# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
|
||||
function _series_updated(plt::Plot{HDF5Backend}, series::Series)
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# called just before updating layout bounding boxes... in case you need to prep
|
||||
# for the calcs
|
||||
function _before_layout_calcs(plt::Plot{HDF5Backend})
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Set the (left, top, right, bottom) minimum padding around the plot area
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot{HDF5Backend})
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
function _update_plot_object(plt::Plot{HDF5Backend})
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{HDF5Backend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Display/show the plot (open a GUI window, or browser page, for example).
|
||||
function _display(plt::Plot{HDF5Backend})
|
||||
msg = "HDF5 interface does not support `display()` function."
|
||||
msg *= "\nUse `Plots.hdf5plot_write(::String)` method to write to .HDF5 \"plot\" file instead."
|
||||
warn(msg)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
#==HDF5 write functions
|
||||
===============================================================================#
|
||||
|
||||
function _hdf5plot_writetype(grp, k::String, tstr::Array{String})
|
||||
d = HDF5.d_open(grp, k)
|
||||
HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_writetype(grp, k::String, T::Type)
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[T]
|
||||
d = HDF5.d_open(grp, k)
|
||||
HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_overwritetype(grp, k::String, T::Type)
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[T]
|
||||
d = HDF5.d_open(grp, k)
|
||||
HDF5.a_delete(d, _hdf5plot_datatypeid)
|
||||
HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_writetype(grp, T::Type) #Write directly to group
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[T]
|
||||
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_overwritetype(grp, T::Type) #Write directly to group
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[T]
|
||||
HDF5.a_delete(grp, _hdf5plot_datatypeid)
|
||||
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_writetype{T<:Any}(grp, ::Type{Array{T}})
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[Array] #ANY
|
||||
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_writetype{T<:BoundingBox}(grp, ::Type{T})
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[BoundingBox]
|
||||
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_writecount(grp, n::Int) #Write directly to group
|
||||
HDF5.a_write(grp, _hdf5plot_countid, n)
|
||||
end
|
||||
function _hdf5plot_gwritefields(grp, k::String, v)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
for _k in fieldnames(v)
|
||||
_v = getfield(v, _k)
|
||||
kstr = string(_k)
|
||||
_hdf5plot_gwrite(grp, kstr, _v)
|
||||
end
|
||||
_hdf5plot_writetype(grp, typeof(v))
|
||||
return
|
||||
end
|
||||
|
||||
# Write data
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _hdf5plot_gwrite(grp, k::String, v) #Default
|
||||
grp[k] = v
|
||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||
end
|
||||
function _hdf5plot_gwrite{T<:Number}(grp, k::String, v::Array{T}) #Default for arrays
|
||||
grp[k] = v
|
||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||
end
|
||||
#=
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Array{Any})
|
||||
# @show grp, k
|
||||
warn("Cannot write Array: $k=$v")
|
||||
end
|
||||
=#
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Void)
|
||||
grp[k] = 0
|
||||
_hdf5plot_writetype(grp, k, Void)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Bool)
|
||||
grp[k] = Int(v)
|
||||
_hdf5plot_writetype(grp, k, Bool)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Symbol)
|
||||
grp[k] = string(v)
|
||||
_hdf5plot_writetype(grp, k, Symbol)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Tuple)
|
||||
varr = [v...]
|
||||
elt = eltype(varr)
|
||||
# if isleaftype(elt)
|
||||
|
||||
_hdf5plot_gwrite(grp, k, varr)
|
||||
if elt <: Number
|
||||
#We just wrote a simple dataset
|
||||
_hdf5plot_overwritetype(grp, k, Tuple)
|
||||
else #Used a more complex scheme (using subgroups):
|
||||
_hdf5plot_overwritetype(grp[k], HDF5CTuple)
|
||||
end
|
||||
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, d::Dict)
|
||||
# warn("Cannot write dict: $k=$d")
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Range)
|
||||
_hdf5plot_gwrite(grp, k, collect(v)) #For now
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::ARGB{N0f8})
|
||||
grp[k] = [v.r.i, v.g.i, v.b.i, v.alpha.i]
|
||||
_hdf5plot_writetype(grp, k, ARGB{N0f8})
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Colorant)
|
||||
_hdf5plot_gwrite(grp, k, ARGB{N0f8}(v))
|
||||
end
|
||||
#Custom vector (when not using simple numeric type):
|
||||
function _hdf5plot_gwritearray{T}(grp, k::String, v::Array{T})
|
||||
if "annotations" == k;
|
||||
return #Hack. Does not yet support annotations.
|
||||
end
|
||||
|
||||
vgrp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_writetype(vgrp, Array) #ANY
|
||||
sz = size(v)
|
||||
|
||||
for iter in eachindex(v)
|
||||
coord = ind2sub(sz, iter)
|
||||
elem = v[iter]
|
||||
idxstr = join(coord, "_")
|
||||
_hdf5plot_gwrite(vgrp, "v$idxstr", v[iter])
|
||||
end
|
||||
|
||||
_hdf5plot_gwrite(vgrp, "dim", [sz...])
|
||||
return
|
||||
end
|
||||
_hdf5plot_gwrite(grp, k::String, v::Array) =
|
||||
_hdf5plot_gwritearray(grp, k, v)
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Extrema)
|
||||
grp[k] = [v.emin, v.emax]
|
||||
_hdf5plot_writetype(grp, k, Extrema)
|
||||
end
|
||||
function _hdf5plot_gwrite{T}(grp, k::String, v::Length{T})
|
||||
grp[k] = v.value
|
||||
_hdf5plot_writetype(grp, k, [HDF5PLOT_MAP_TELEM2STR[Length], string(T)])
|
||||
end
|
||||
|
||||
# Write more complex structures:
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Plot)
|
||||
#Don't write plot references
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::HDF5PLOT_SIMPLESUBSTRUCT)
|
||||
_hdf5plot_gwritefields(grp, k, v)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Axis)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
for (_k, _v) in v.d
|
||||
kstr = string(_k)
|
||||
_hdf5plot_gwrite(grp, kstr, _v)
|
||||
end
|
||||
_hdf5plot_writetype(grp, Axis)
|
||||
return
|
||||
end
|
||||
#TODO: "Properly" support Nullable using _hdf5plot_writetype?
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Nullable)
|
||||
if isnull(v)
|
||||
_hdf5plot_gwrite(grp, k, nothing)
|
||||
else
|
||||
_hdf5plot_gwrite(grp, k, v.value)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function _hdf5plot_gwrite(grp, k::String, v::SeriesAnnotations)
|
||||
#Currently no support for SeriesAnnotations
|
||||
return
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_gwrite(grp, "index", v[:subplot_index])
|
||||
_hdf5plot_writetype(grp, Subplot)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_write(grp, d::Dict)
|
||||
for (k, v) in d
|
||||
kstr = string(k)
|
||||
_hdf5plot_gwrite(grp, kstr, v)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# Write main plot structures:
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _hdf5plot_write(sp::Subplot{HDF5Backend}, subpath::String, f)
|
||||
f = f::HDF5.HDF5File #Assert
|
||||
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/attr"))
|
||||
_hdf5plot_write(grp, sp.attr)
|
||||
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/series_list"))
|
||||
_hdf5plot_writecount(grp, length(sp.series_list))
|
||||
for (i, series) in enumerate(sp.series_list)
|
||||
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
|
||||
_hdf5plot_write(grp, series.d)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function _hdf5plot_write(plt::Plot{HDF5Backend}, f)
|
||||
f = f::HDF5.HDF5File #Assert
|
||||
|
||||
grp = HDF5.g_create(f, _hdf5_plotelempath("attr"))
|
||||
_hdf5plot_write(grp, plt.attr)
|
||||
|
||||
grp = HDF5.g_create(f, _hdf5_plotelempath("subplots"))
|
||||
_hdf5plot_writecount(grp, length(plt.subplots))
|
||||
|
||||
for (i, sp) in enumerate(plt.subplots)
|
||||
_hdf5plot_write(sp, "subplots/subplot$i", f)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
function hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString)
|
||||
HDF5.h5open(path, "w") do file
|
||||
_hdf5plot_write(plt, file)
|
||||
end
|
||||
end
|
||||
hdf5plot_write(path::AbstractString) = hdf5plot_write(current(), path)
|
||||
|
||||
|
||||
#==HDF5 playback (read) functions
|
||||
===============================================================================#
|
||||
|
||||
function _hdf5plot_readcount(grp) #Read directly from group
|
||||
return HDF5.a_read(grp, _hdf5plot_countid)
|
||||
end
|
||||
|
||||
_hdf5plot_convert(T::Type{HDF5PlotNative}, v) = v
|
||||
_hdf5plot_convert(T::Type{Void}, v) = nothing
|
||||
_hdf5plot_convert(T::Type{Bool}, v) = (v!=0)
|
||||
_hdf5plot_convert(T::Type{Symbol}, v) = Symbol(v)
|
||||
_hdf5plot_convert(T::Type{Tuple}, v) = tuple(v...) #With Vector{T<:Number}
|
||||
function _hdf5plot_convert(T::Type{ARGB{N0f8}}, v)
|
||||
r, g, b, a = reinterpret(N0f8, v)
|
||||
return Colors.ARGB{N0f8}(r, g, b, a)
|
||||
end
|
||||
_hdf5plot_convert(T::Type{Extrema}, v) = Extrema(v[1], v[2])
|
||||
|
||||
# Read data structures:
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _hdf5plot_read(grp, k::String, T::Type, dtid)
|
||||
v = HDF5.d_read(grp, k)
|
||||
return _hdf5plot_convert(T, v)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Length}, dtid::Vector)
|
||||
v = HDF5.d_read(grp, k)
|
||||
TU = Symbol(dtid[2])
|
||||
T = typeof(v)
|
||||
return Length{TU,T}(v)
|
||||
end
|
||||
|
||||
# Read more complex data structures:
|
||||
# ----------------------------------------------------------------
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Font}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
family = _hdf5plot_read(grp, "family")
|
||||
pointsize = _hdf5plot_read(grp, "pointsize")
|
||||
halign = _hdf5plot_read(grp, "halign")
|
||||
valign = _hdf5plot_read(grp, "valign")
|
||||
rotation = _hdf5plot_read(grp, "rotation")
|
||||
color = _hdf5plot_read(grp, "color")
|
||||
return Font(family, pointsize, halign, valign, rotation, color)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
|
||||
grp = HDF5.g_open(grp, k)
|
||||
sz = _hdf5plot_read(grp, "dim")
|
||||
if [0] == sz; return []; end
|
||||
sz = tuple(sz...)
|
||||
result = Array{Any}(sz)
|
||||
|
||||
for iter in eachindex(result)
|
||||
coord = ind2sub(sz, iter)
|
||||
idxstr = join(coord, "_")
|
||||
result[iter] = _hdf5plot_read(grp, "v$idxstr")
|
||||
end
|
||||
|
||||
#Hack: Implicitly make Julia detect element type.
|
||||
# (Should probably write it explicitly to file)
|
||||
result = [result[iter] for iter in eachindex(result)] #Potentially make more specific
|
||||
return reshape(result, sz)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple}, dtid)
|
||||
v = _hdf5plot_read(grp, k, Array, dtid)
|
||||
return tuple(v...)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
colors = _hdf5plot_read(grp, "colors")
|
||||
values = _hdf5plot_read(grp, "values")
|
||||
return ColorGradient(colors, values)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{BoundingBox}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
x0 = _hdf5plot_read(grp, "x0")
|
||||
a = _hdf5plot_read(grp, "a")
|
||||
return BoundingBox(x0, a)
|
||||
end
|
||||
_hdf5plot_read(grp, k::String, T::Type{RootLayout}, dtid) = RootLayout()
|
||||
function _hdf5plot_read(grp, k::String, T::Type{GridLayout}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
# parent = _hdf5plot_read(grp, "parent")
|
||||
parent = RootLayout()
|
||||
minpad = _hdf5plot_read(grp, "minpad")
|
||||
bbox = _hdf5plot_read(grp, "bbox")
|
||||
grid = _hdf5plot_read(grp, "grid")
|
||||
widths = _hdf5plot_read(grp, "widths")
|
||||
heights = _hdf5plot_read(grp, "heights")
|
||||
attr = KW() #TODO support attr: _hdf5plot_read(grp, "attr")
|
||||
|
||||
return GridLayout(parent, minpad, bbox, grid, widths, heights, attr)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
return Axis([], kwlist)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Subplot}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
idx = _hdf5plot_read(grp, "index")
|
||||
return HDF5PLOT_PLOTREF.ref.subplots[idx]
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String)
|
||||
dtid = HDF5.a_read(grp[k], _hdf5plot_datatypeid)
|
||||
T = _hdf5_map_str2telem(dtid) #expect exception
|
||||
return _hdf5plot_read(grp, k, T, dtid)
|
||||
end
|
||||
|
||||
#Read in values in group to populate d:
|
||||
function _hdf5plot_read(grp, d::Dict)
|
||||
gnames = names(grp)
|
||||
for k in gnames
|
||||
try
|
||||
v = _hdf5plot_read(grp, k)
|
||||
d[Symbol(k)] = v
|
||||
catch e
|
||||
@show e
|
||||
@show grp
|
||||
warn("Could not read field $k")
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# Read main plot structures:
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
||||
f = f::HDF5.HDF5File #Assert
|
||||
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
_hdf5_merge!(sp.attr, kwlist)
|
||||
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list"))
|
||||
nseries = _hdf5plot_readcount(grp)
|
||||
|
||||
for i in 1:nseries
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
plot!(sp, kwlist[:x], kwlist[:y]) #Add data & create data structures
|
||||
_hdf5_merge!(sp.series_list[end].d, kwlist)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function _hdf5plot_read(plt::Plot, f)
|
||||
f = f::HDF5.HDF5File #Assert
|
||||
#Assumpltion: subplots are already allocated (plt.subplots)
|
||||
|
||||
HDF5PLOT_PLOTREF.ref = plt #Used when reading "layout"
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("attr"))
|
||||
_hdf5plot_read(grp, plt.attr)
|
||||
|
||||
for (i, sp) in enumerate(plt.subplots)
|
||||
_hdf5plot_read(sp, "subplots/subplot$i", f)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function hdf5plot_read(path::AbstractString)
|
||||
plt = nothing
|
||||
HDF5.h5open(path, "r") do file
|
||||
grp = HDF5.g_open(file, _hdf5_plotelempath("subplots"))
|
||||
n = _hdf5plot_readcount(grp)
|
||||
plt = plot(layout=n) #Get reference to a new plot
|
||||
_hdf5plot_read(plt, file)
|
||||
end
|
||||
return plt
|
||||
end
|
||||
|
||||
#Last line
|
||||
@@ -0,0 +1,529 @@
|
||||
|
||||
# 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,
|
||||
=#
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
#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, :titlefont,
|
||||
:window_title,
|
||||
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
: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 #, :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
|
||||
)
|
||||
|
||||
type 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 = _vectorize(series[:x]); y = _vectorize(series[:y])
|
||||
|
||||
#No support for polar grid... but can still perform polar transformation:
|
||||
if ispolar(sp)
|
||||
Θ = x; r = y
|
||||
x = r.*cos(Θ); y = r.*sin(Θ)
|
||||
end
|
||||
|
||||
# doesn't handle mismatched x/y - wrap data (pyplot behaviour):
|
||||
nx = length(x); ny = length(y)
|
||||
if nx < ny
|
||||
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,)
|
||||
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) #, :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 gridon = InspectDR.GridRect(vmajor=true, hmajor=true)
|
||||
const gridoff = InspectDR.GridRect()
|
||||
const plot = sp.o
|
||||
const strip = plot.strips[1] #Only 1 strip supported with Plots.jl
|
||||
|
||||
#No independent control of grid???
|
||||
strip.grid = sp[:grid]? gridon: gridoff
|
||||
|
||||
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
|
||||
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.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
|
||||
l.framedata.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
|
||||
l.framedata.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
|
||||
l.fnttitle = InspectDR.Font(sp[:titlefont].family,
|
||||
_inspectdr_mapptsize(sp[:titlefont].pointsize),
|
||||
color = _inspectdr_mapcolor(sp[:foreground_color_title])
|
||||
)
|
||||
#Cannot independently control fonts of axes with InspectDR:
|
||||
l.fntaxlabel = InspectDR.Font(xaxis[:guidefont].family,
|
||||
_inspectdr_mapptsize(xaxis[:guidefont].pointsize),
|
||||
color = _inspectdr_mapcolor(xaxis[:foreground_color_guide])
|
||||
)
|
||||
l.fntticklabel = InspectDR.Font(xaxis[:tickfont].family,
|
||||
_inspectdr_mapptsize(xaxis[:tickfont].pointsize),
|
||||
color = _inspectdr_mapcolor(xaxis[:foreground_color_text])
|
||||
)
|
||||
leg = l.legend
|
||||
leg.enabled = (sp[:legend] != :none)
|
||||
#leg.width = 150 #TODO: compute???
|
||||
leg.font = InspectDR.Font(sp[:legendfont].family,
|
||||
_inspectdr_mapptsize(sp[:legendfont].pointsize),
|
||||
color = _inspectdr_mapcolor(sp[:foreground_color_legend])
|
||||
)
|
||||
leg.frame.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.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.ncolumns = 1
|
||||
elseif nsubplots <= 4
|
||||
mplot.ncolumns = 2
|
||||
elseif nsubplots <= 6
|
||||
mplot.ncolumns = 3
|
||||
elseif nsubplots <= 12
|
||||
mplot.ncolumns = 4
|
||||
else
|
||||
mplot.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, 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, 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
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# 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
|
||||
+108
-13
@@ -3,7 +3,7 @@
|
||||
# significant contributions by: @pkofod
|
||||
|
||||
const _pgfplots_attr = merge_with_base_supported([
|
||||
# :annotations,
|
||||
:annotations,
|
||||
# :background_color_legend,
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
@@ -22,17 +22,17 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
# :colorbar,
|
||||
# :marker_z, :levels,
|
||||
:colorbar,
|
||||
:marker_z, #:levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
# :polar,
|
||||
:polar,
|
||||
# :normalize, :weights, :contours,
|
||||
:aspect_ratio,
|
||||
# :match_dimensions,
|
||||
])
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape]
|
||||
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||
@@ -98,14 +98,35 @@ const _pgf_series_extrastyle = KW(
|
||||
:xsticks => "xcomb",
|
||||
)
|
||||
|
||||
# PGFPlots uses the anchors to define orientations for example to align left
|
||||
# one needs to use the right edge as anchor
|
||||
const _pgf_annotation_halign = KW(
|
||||
:center => "",
|
||||
:left => "right",
|
||||
:right => "left"
|
||||
)
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
# takes in color,alpha, and returns color and alpha appropriate for pgf style
|
||||
function pgf_color(c)
|
||||
function pgf_color(c::Colorant)
|
||||
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c))
|
||||
cstr, alpha(c)
|
||||
end
|
||||
|
||||
function pgf_color(grad::ColorGradient)
|
||||
# Can't handle ColorGradient here, fallback to defaults.
|
||||
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", 0.0, 0.60560316,0.97868012)
|
||||
cstr, 1
|
||||
end
|
||||
|
||||
# Generates a colormap for pgfplots based on a ColorGradient
|
||||
function pgf_colormap(grad::ColorGradient)
|
||||
join(map(grad.colors) do c
|
||||
@sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c),blue(c))
|
||||
end,", ")
|
||||
end
|
||||
|
||||
function pgf_fillstyle(d::KW)
|
||||
cstr,a = pgf_color(d[:fillcolor])
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
@@ -136,6 +157,19 @@ function pgf_marker(d::KW)
|
||||
}"""
|
||||
end
|
||||
|
||||
function pgf_add_annotation!(o,x,y,val)
|
||||
# Construct the style string.
|
||||
# Currently supports color and orientation
|
||||
cstr,a = pgf_color(val.font.color)
|
||||
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation)
|
||||
"""))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function pgf_series(sp::Subplot, series::Series)
|
||||
@@ -143,11 +177,10 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
st = d[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
|
||||
push!(style, pgf_linestyle(d))
|
||||
push!(style, pgf_marker(d))
|
||||
|
||||
if d[:fillrange] != nothing
|
||||
if d[:fillrange] != nothing || st in (:shape,)
|
||||
push!(style, pgf_fillstyle(d))
|
||||
end
|
||||
|
||||
@@ -163,6 +196,10 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
d[:z].surf, d[:x], d[:y]
|
||||
elseif is3d(st)
|
||||
d[:x], d[:y], d[:z]
|
||||
elseif d[:marker_z] != nothing
|
||||
# If a marker_z is used pass it as third coordinate to a 2D plot.
|
||||
# See "Scatter Plots" in PGFPlots documentation
|
||||
d[:x], d[:y], d[:marker_z]
|
||||
else
|
||||
d[:x], d[:y]
|
||||
end
|
||||
@@ -211,6 +248,9 @@ function pgf_axis(sp::Subplot, letter)
|
||||
# axis guide
|
||||
kw[Symbol(letter,:label)] = axis[:guide]
|
||||
|
||||
# Add ticklabel rotations
|
||||
push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}")
|
||||
|
||||
# flip/reverse?
|
||||
axis[:flip] && push!(style, "$letter dir=reverse")
|
||||
|
||||
@@ -249,8 +289,12 @@ end
|
||||
|
||||
function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
plt.o = PGFPlots.Axis[]
|
||||
# Obtain the total height of the plot by extracting the maximal bottom
|
||||
# coordinate from the bounding box.
|
||||
total_height = bottom(bbox(plt.layout))
|
||||
|
||||
for sp in plt.subplots
|
||||
# first build the PGFPlots.Axis object
|
||||
# first build the PGFPlots.Axis object
|
||||
style = ["unbounded coords=jump"]
|
||||
kw = KW()
|
||||
|
||||
@@ -265,10 +309,12 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
|
||||
# bounding box values are in mm
|
||||
# note: bb origin is top-left, pgf is bottom-left
|
||||
# A round on 2 decimal places should be enough precision for 300 dpi
|
||||
# plots.
|
||||
bb = bbox(sp)
|
||||
push!(style, """
|
||||
xshift = $(left(bb).value)mm,
|
||||
yshift = $((height(bb) - (bottom(bb))).value)mm,
|
||||
yshift = $(round((total_height - (bottom(bb))).value,2))mm,
|
||||
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
|
||||
""")
|
||||
kw[:width] = "$(width(bb).value)mm"
|
||||
@@ -288,19 +334,62 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
kw[:legendPos] = _pgfplots_legend_pos[legpos]
|
||||
end
|
||||
|
||||
o = PGFPlots.Axis(; style = style, kw...)
|
||||
axisf = PGFPlots.Axis
|
||||
if sp[:projection] == :polar
|
||||
axisf = PGFPlots.PolarAxis
|
||||
end
|
||||
|
||||
# Search series for any gradient. In case one series uses a gradient set
|
||||
# the colorbar and colomap.
|
||||
# The reasoning behind doing this on the axis level is that pgfplots
|
||||
# colorbar seems to only works on axis level and needs the proper colormap for
|
||||
# correctly displaying it.
|
||||
# It's also possible to assign the colormap to the series itself but
|
||||
# then the colormap needs to be added twice, once for the axis and once for the
|
||||
# series.
|
||||
# As it is likely that all series within the same axis use the same
|
||||
# colormap this should not cause any problem.
|
||||
for series in series_list(sp)
|
||||
for col in (:markercolor, :fillcolor)
|
||||
if typeof(series.d[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.d[col]))}")
|
||||
|
||||
if sp[:colorbar] == :none
|
||||
kw[:colorbar] = "false"
|
||||
else
|
||||
kw[:colorbar] = "true"
|
||||
end
|
||||
# goto is needed to break out of col and series for
|
||||
@goto colorbar_end
|
||||
end
|
||||
end
|
||||
end
|
||||
@label colorbar_end
|
||||
|
||||
o = axisf(; style = style, kw...)
|
||||
|
||||
# add the series object to the PGFPlots.Axis
|
||||
for series in series_list(sp)
|
||||
push!(o, pgf_series(sp, series))
|
||||
|
||||
# add series annotations
|
||||
anns = series[:series_annotations]
|
||||
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt))
|
||||
end
|
||||
end
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
pgf_add_annotation!(o,ann...)
|
||||
end
|
||||
|
||||
|
||||
# add the PGFPlots.Axis to the list
|
||||
push!(plt.o, o)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _show(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
|
||||
show(io, mime, plt.o)
|
||||
end
|
||||
@@ -314,12 +403,18 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
|
||||
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
|
||||
|
||||
# read it into io
|
||||
write(io, readall(open(fn)))
|
||||
write(io, readstring(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
|
||||
pgfplt = PGFPlots.plot(plt.o)
|
||||
|
||||
+60
-8
@@ -19,8 +19,8 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:grid, :legend, :colorbar, :colorbar_title,
|
||||
:marker_z, :fill_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
# :overwrite_figure,
|
||||
@@ -31,6 +31,7 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:hover,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:clims,
|
||||
])
|
||||
|
||||
const _plotly_seriestype = [
|
||||
@@ -57,6 +58,7 @@ 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
|
||||
@@ -93,6 +95,20 @@ 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{S<:Real, T<:Real}(v::Tuple{S,T}) = v
|
||||
|
||||
function plotly_font(font::Font, color = font.color)
|
||||
KW(
|
||||
:family => font.family,
|
||||
@@ -101,6 +117,7 @@ function plotly_font(font::Font, color = font.color)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
|
||||
KW(
|
||||
:text => val,
|
||||
@@ -252,7 +269,7 @@ function plotly_layout(plt::Plot)
|
||||
w, h = plt[:size]
|
||||
d_out[:width], d_out[:height] = w, h
|
||||
d_out[:paper_bgcolor] = rgba_string(plt[:background_color_outside])
|
||||
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>20)
|
||||
d_out[:margin] = KW(:l=>0, :b=>20, :r=>0, :t=>20)
|
||||
|
||||
d_out[:annotations] = KW[]
|
||||
|
||||
@@ -293,17 +310,32 @@ function plotly_layout(plt::Plot)
|
||||
|
||||
# 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(sp[:legendfont], sp[:foreground_color_legend]),
|
||||
:x => xpos,
|
||||
:y => ypos
|
||||
)
|
||||
end
|
||||
|
||||
# annotations
|
||||
append!(d_out[:annotations], KW[plotly_annotation_dict(ann...; xref = "x$spidx", yref = "y$spidx") for ann in sp[:annotations]])
|
||||
|
||||
# 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$spidx", yref = "y$spidx")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# # arrows
|
||||
# for sargs in seriesargs
|
||||
# a = sargs[:arrow]
|
||||
@@ -340,6 +372,7 @@ end
|
||||
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
|
||||
# plotly_colorscale(c, alpha = nothing) = plotly_colorscale(cgrad(), alpha)
|
||||
|
||||
|
||||
const _plotly_markers = KW(
|
||||
:rect => "square",
|
||||
:xcross => "x",
|
||||
@@ -363,7 +396,7 @@ 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] = shape_coords(shape)
|
||||
xs[i], ys[i] = coords(shape)
|
||||
end
|
||||
nanvcat(xs), nanvcat(ys)
|
||||
end
|
||||
@@ -376,6 +409,10 @@ plotly_surface_data(series::Series, a::AbstractVector) = a
|
||||
plotly_surface_data(series::Series, a::AbstractMatrix) = transpose_z(series, a, false)
|
||||
plotly_surface_data(series::Series, a::Surface) = plotly_surface_data(series, a.surf)
|
||||
|
||||
#ensures that a gradient is called if a single color is supplied where a gradient is needed (e.g. if a series recipe defines marker_z)
|
||||
as_gradient(grad::ColorGradient, α) = grad
|
||||
as_gradient(grad, α) = cgrad(alpha = α)
|
||||
|
||||
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
|
||||
function plotly_series(plt::Plot, series::Series)
|
||||
st = series[:seriestype]
|
||||
@@ -406,6 +443,13 @@ function plotly_series(plt::Plot, series::Series)
|
||||
end
|
||||
end
|
||||
|
||||
d_out[:colorbar] = KW(:title => sp[:colorbar_title])
|
||||
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
d_out[:zmin], d_out[:zmax] = clims
|
||||
end
|
||||
|
||||
# set the "type"
|
||||
if st in (:path, :scatter, :scattergl)
|
||||
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
|
||||
@@ -457,6 +501,9 @@ function plotly_series(plt::Plot, series::Series)
|
||||
d_out[:contours] = KW(:x => wirelines, :y => wirelines, :z => wirelines)
|
||||
else
|
||||
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
if series[:fill_z] != nothing
|
||||
d_out[:surfacecolor] = plotly_surface_data(series, series[:fill_z])
|
||||
end
|
||||
end
|
||||
|
||||
elseif st == :pie
|
||||
@@ -498,9 +545,10 @@ function plotly_series(plt::Plot, series::Series)
|
||||
rgba_string(series[:markercolor])
|
||||
else
|
||||
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
|
||||
grad = series[:markercolor]
|
||||
zmin, zmax = extrema(series[:marker_z])
|
||||
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]]
|
||||
grad = as_gradient(series[:markercolor], series[:markeralpha])
|
||||
zmin, zmax = ignoreNaN_extrema(series[:marker_z])
|
||||
zrange = zmax == zmin ? 1 : zmax - zmin # if all marker_z values are the same, plot all markers same color (avoids division by zero in next line)
|
||||
[rgba_string(grad[(zi - zmin) / zrange]) for zi in series[:marker_z]]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -599,8 +647,12 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const _use_remote = Ref(false)
|
||||
|
||||
function html_head(plt::Plot{PlotlyBackend})
|
||||
"<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
|
||||
jsfilename = _use_remote[] ? _plotly_js_path_remote : _plotly_js_path
|
||||
# "<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
|
||||
"<script src=\"$jsfilename\"></script>"
|
||||
end
|
||||
|
||||
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||
|
||||
@@ -40,7 +40,11 @@ end
|
||||
|
||||
|
||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||
PlotlyJS.plot()
|
||||
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
|
||||
else
|
||||
PlotlyJS.plot()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -59,7 +63,7 @@ function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
|
||||
z = series[:z]
|
||||
if z != nothing
|
||||
kw[:z] = (transpose_z(series, series[:z].surf, false),)
|
||||
kw[:z] = (isa(z,Surface) ? transpose_z(series, series[:z].surf, false) : z,)
|
||||
end
|
||||
PlotlyJS.restyle!(
|
||||
plt.o,
|
||||
@@ -82,7 +86,11 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
|
||||
show(io, MIME("text/html"), plt.o)
|
||||
if isijulia() && !_use_remote[]
|
||||
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
|
||||
else
|
||||
show(io, MIME("text/html"), plt.o)
|
||||
end
|
||||
end
|
||||
|
||||
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
|
||||
@@ -94,6 +102,23 @@ _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hac
|
||||
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "pdf")
|
||||
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "eps")
|
||||
|
||||
function _display(plt::Plot{PlotlyJSBackend})
|
||||
display(plt.o)
|
||||
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
|
||||
|
||||
|
||||
function closeall(::PlotlyJSBackend)
|
||||
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
|
||||
close(current().o)
|
||||
end
|
||||
end
|
||||
|
||||
+85
-48
@@ -17,9 +17,8 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z,
|
||||
:line_z,
|
||||
:grid, :legend, :legendtitle, :colorbar,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:orientation,
|
||||
@@ -56,6 +55,8 @@ function add_backend_string(::PyPlotBackend)
|
||||
withenv("PYTHON" => "") do
|
||||
Pkg.build("PyPlot")
|
||||
end
|
||||
|
||||
# now restart julia!
|
||||
"""
|
||||
end
|
||||
|
||||
@@ -81,10 +82,10 @@ function _initialize_backend(::PyPlotBackend)
|
||||
const pytransforms = PyPlot.pywrap(PyPlot.pyimport("matplotlib.transforms"))
|
||||
const pycollections = PyPlot.pywrap(PyPlot.pyimport("matplotlib.collections"))
|
||||
const pyart3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d.art3d"))
|
||||
end
|
||||
|
||||
# we don't want every command to update the figure
|
||||
PyPlot.ioff()
|
||||
# we don't want every command to update the figure
|
||||
PyPlot.ioff()
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
@@ -124,8 +125,8 @@ end
|
||||
py_colormap(c) = py_colormap(cgrad())
|
||||
|
||||
|
||||
function py_shading(c, z, α=nothing)
|
||||
cmap = py_colormap(c, α)
|
||||
function py_shading(c, z)
|
||||
cmap = py_colormap(c)
|
||||
ls = pycolors.pymember("LightSource")(270,45)
|
||||
ls[:shade](z, cmap, vert_exag=0.1, blend_mode="soft")
|
||||
end
|
||||
@@ -142,7 +143,7 @@ function py_linestyle(seriestype::Symbol, linestyle::Symbol)
|
||||
end
|
||||
|
||||
function py_marker(marker::Shape)
|
||||
x, y = shape_coords(marker)
|
||||
x, y = coords(marker)
|
||||
n = length(x)
|
||||
mat = zeros(n+1,2)
|
||||
for i=1:n
|
||||
@@ -218,6 +219,12 @@ function py_stepstyle(seriestype::Symbol)
|
||||
return "default"
|
||||
end
|
||||
|
||||
function py_fillstepstyle(seriestype::Symbol)
|
||||
seriestype == :steppost && return "post"
|
||||
seriestype == :steppre && return "pre"
|
||||
return nothing
|
||||
end
|
||||
|
||||
# # untested... return a FontProperties object from a Plots.Font
|
||||
# function py_font(font::Font)
|
||||
# pyfont.pymember("FontProperties")(
|
||||
@@ -445,6 +452,9 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
error("Only numbers and vectors are supported with levels keyword")
|
||||
end
|
||||
|
||||
# add custom frame shapes to markershape?
|
||||
series_annotations_shapes!(series, :xy)
|
||||
|
||||
# for each plotting command, optionally build and add a series handle to the list
|
||||
|
||||
# line plot
|
||||
@@ -560,16 +570,46 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
else
|
||||
xyargs
|
||||
end
|
||||
handle = ax[:scatter](xyargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(series[:markershape]),
|
||||
s = py_dpi_scale(plt, series[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(series),
|
||||
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
|
||||
if isa(series[:markershape], AbstractVector{Shape})
|
||||
# this section will create one scatter per data point to accomodate the
|
||||
# vector of shapes
|
||||
handle = []
|
||||
x,y = xyargs
|
||||
shapes = series[:markershape]
|
||||
msc = py_markerstrokecolor(series)
|
||||
lw = py_dpi_scale(plt, series[:markerstrokewidth])
|
||||
for i=1:length(y)
|
||||
extrakw[:c] = if series[:marker_z] == nothing
|
||||
py_color_fix(py_color(cycle(series[:markercolor],i)), x)
|
||||
else
|
||||
extrakw[:c]
|
||||
end
|
||||
|
||||
push!(handle, ax[:scatter](cycle(x,i), cycle(y,i);
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(cycle(shapes,i)),
|
||||
s = py_dpi_scale(plt, cycle(series[:markersize],i) .^ 2),
|
||||
edgecolors = msc,
|
||||
linewidths = lw,
|
||||
extrakw...
|
||||
))
|
||||
end
|
||||
push!(handles, handle)
|
||||
else
|
||||
# do a normal scatter plot
|
||||
handle = ax[:scatter](xyargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(series[:markershape]),
|
||||
s = py_dpi_scale(plt, series[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(series),
|
||||
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
end
|
||||
|
||||
if st == :hexbin
|
||||
@@ -635,21 +675,21 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
x = repmat(x', length(y), 1)
|
||||
y = repmat(y, 1, length(series[:x]))
|
||||
end
|
||||
# z = z'
|
||||
z = transpose_z(series, z)
|
||||
if st == :surface
|
||||
if series[:marker_z] != nothing
|
||||
extrakw[:facecolors] = py_shading(series[:fillcolor], series[:marker_z], series[:fillalpha])
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
if series[:fill_z] != nothing
|
||||
# the surface colors are different than z-value
|
||||
extrakw[:facecolors] = py_shading(series[:fillcolor], transpose_z(series, series[:fill_z].surf))
|
||||
extrakw[:shade] = false
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
else
|
||||
extrakw[:cmap] = py_fillcolormap(series)
|
||||
needs_colorbar = true
|
||||
end
|
||||
needs_colorbar = true
|
||||
end
|
||||
handle = ax[st == :surface ? :plot_surface : :plot_wireframe](x, y, z;
|
||||
label = series[:label],
|
||||
@@ -665,11 +705,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# contours on the axis planes
|
||||
if series[:contours]
|
||||
for (zdir,mat) in (("x",x), ("y",y), ("z",z))
|
||||
offset = (zdir == "y" ? maximum : minimum)(mat)
|
||||
offset = (zdir == "y" ? ignoreNaN_maximum : ignoreNaN_minimum)(mat)
|
||||
handle = ax[:contourf](x, y, z, levelargs...;
|
||||
zdir = zdir,
|
||||
cmap = py_fillcolormap(series),
|
||||
offset = (zdir == "y" ? maximum : minimum)(mat) # where to draw the contour plane
|
||||
offset = (zdir == "y" ? ignoreNaN_maximum : ignoreNaN_minimum)(mat) # where to draw the contour plane
|
||||
)
|
||||
push!(handles, handle)
|
||||
needs_colorbar = true
|
||||
@@ -738,7 +778,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
|
||||
clims = sp[:clims]
|
||||
zmin, zmax = extrema(z)
|
||||
zmin, zmax = ignoreNaN_extrema(z)
|
||||
extrakw[:vmin] = (is_2tuple(clims) && isfinite(clims[1])) ? clims[1] : zmin
|
||||
extrakw[:vmax] = (is_2tuple(clims) && isfinite(clims[2])) ? clims[2] : zmax
|
||||
|
||||
@@ -770,16 +810,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
push!(handle, ax[:add_patch](patches))
|
||||
end
|
||||
end
|
||||
# path = py_path(x, y)
|
||||
# patches = pypatches.pymember("PathPatch")(path;
|
||||
# label = series[:label],
|
||||
# zorder = series[:series_plotindex],
|
||||
# edgecolor = py_linecolor(series),
|
||||
# facecolor = py_fillcolor(series),
|
||||
# linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
# fill = true
|
||||
# )
|
||||
# handle = ax[:add_patch](patches)
|
||||
push!(handles, handle)
|
||||
end
|
||||
|
||||
@@ -842,13 +872,19 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
dim1, expand_data(fillrange[1], n), expand_data(fillrange[2], n)
|
||||
end
|
||||
|
||||
handle = ax[f](args...;
|
||||
handle = ax[f](args..., trues(n), false, py_fillstepstyle(st);
|
||||
zorder = series[:series_plotindex],
|
||||
facecolor = py_fillcolor(series),
|
||||
linewidths = 0
|
||||
)
|
||||
push!(handles, handle)
|
||||
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)
|
||||
py_add_annotations(sp, xi, yi, PlotText(str, fnt))
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -890,14 +926,14 @@ function py_compute_axis_minval(axis::Axis)
|
||||
for series in series_list(sp)
|
||||
v = series.d[axis[:letter]]
|
||||
if !isempty(v)
|
||||
minval = min(minval, minimum(abs(v)))
|
||||
minval = NaNMath.min(minval, ignoreNaN_minimum(abs(v)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# now if the axis limits go to a smaller abs value, use that instead
|
||||
vmin, vmax = axis_limits(axis)
|
||||
minval = min(minval, abs(vmin), abs(vmax))
|
||||
minval = NaNMath.min(minval, abs(vmin), abs(vmax))
|
||||
|
||||
minval
|
||||
end
|
||||
@@ -918,7 +954,7 @@ function py_set_scale(ax, axis::Axis)
|
||||
elseif scale == :log10
|
||||
10
|
||||
end
|
||||
kw[Symbol(:linthresh,letter)] = max(1e-16, py_compute_axis_minval(axis))
|
||||
kw[Symbol(:linthresh,letter)] = NaNMath.max(1e-16, py_compute_axis_minval(axis))
|
||||
"symlog"
|
||||
end
|
||||
func(arg; kw...)
|
||||
@@ -1022,7 +1058,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
end
|
||||
if sp[:grid]
|
||||
fgcolor = py_color(sp[:foreground_color_grid])
|
||||
pyaxis[:grid](true, color = fgcolor)
|
||||
pyaxis[:grid](true, color = fgcolor, linestyle = ":")
|
||||
ax[:set_axisbelow](true)
|
||||
end
|
||||
py_set_axis_colors(ax, axis)
|
||||
@@ -1038,7 +1074,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
py_add_legend(plt, sp, ax)
|
||||
|
||||
# this sets the bg color inside the grid
|
||||
ax[:set_axis_bgcolor](py_color(sp[:background_color_inside]))
|
||||
ax[:set_facecolor](py_color(sp[:background_color_inside]))
|
||||
end
|
||||
py_drawfig(fig)
|
||||
end
|
||||
@@ -1070,7 +1106,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
# optionally add the width of colorbar labels and colorbar to rightpad
|
||||
if haskey(sp.attr, :cbar_ax)
|
||||
bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]())
|
||||
sp.attr[:cbar_width] = _cbar_width + width(bb) + 1mm + (sp[:colorbar_title] == "" ? 0px : 30px)
|
||||
sp.attr[:cbar_width] = _cbar_width + width(bb) + 2.3mm + (sp[:colorbar_title] == "" ? 0px : 30px)
|
||||
rightpad = rightpad + sp.attr[:cbar_width]
|
||||
end
|
||||
|
||||
@@ -1151,6 +1187,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
# framealpha = 0.6
|
||||
)
|
||||
leg[:set_zorder](1000)
|
||||
sp[:legendtitle] != nothing && leg[:set_title](sp[:legendtitle])
|
||||
|
||||
fgcolor = py_color(sp[:foreground_color_legend])
|
||||
for txt in leg[:get_texts]()
|
||||
|
||||
@@ -45,6 +45,18 @@ 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)
|
||||
@@ -66,7 +78,12 @@ 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
|
||||
canvas_type = isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
|
||||
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
|
||||
|
||||
+2
-2
@@ -23,11 +23,11 @@ function open_browser_window(filename::AbstractString)
|
||||
@static if is_apple()
|
||||
return run(`open $(filename)`)
|
||||
end
|
||||
@static if is_linux()
|
||||
@static if is_linux() || is_bsd() # is_bsd() addition is as yet untested, but based on suggestion in https://github.com/JuliaPlots/Plots.jl/issues/681
|
||||
return run(`xdg-open $(filename)`)
|
||||
end
|
||||
@static if is_windows()
|
||||
return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
|
||||
return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`)
|
||||
end
|
||||
warn("Unknown OS... cannot open browser window.")
|
||||
end
|
||||
|
||||
+178
-73
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
typealias P2 FixedSizeArrays.Vec{2,Float64}
|
||||
typealias P3 FixedSizeArrays.Vec{3,Float64}
|
||||
const P2 = FixedSizeArrays.Vec{2,Float64}
|
||||
const 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))
|
||||
@@ -23,21 +23,24 @@ immutable Shape
|
||||
# end
|
||||
end
|
||||
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
|
||||
|
||||
function shape_coords(shape::Shape)
|
||||
function coords(shape::Shape)
|
||||
shape.x, shape.y
|
||||
end
|
||||
|
||||
function shape_coords(shapes::AVec{Shape})
|
||||
function 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, shape_coords(shapes[1]))
|
||||
x, y = map(copy, coords(shapes[1]))
|
||||
for shape in shapes[2:end]
|
||||
nanappend!(x, shape.x)
|
||||
nanappend!(y, shape.y)
|
||||
@@ -72,13 +75,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)[1:end-2])
|
||||
Shape(weave(outercircle, innercircle))
|
||||
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)[1:end-1])
|
||||
Shape(partialcircle(z, z + 2π, n+1, radius))
|
||||
end
|
||||
|
||||
|
||||
@@ -88,7 +91,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])[1:end-2])
|
||||
ordering=Vector[outercircle,innercircle,outercircle]))
|
||||
end
|
||||
|
||||
|
||||
@@ -110,6 +113,8 @@ const _shape_keys = Symbol[
|
||||
:xcross,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:rtriangle,
|
||||
:ltriangle,
|
||||
:pentagon,
|
||||
:heptagon,
|
||||
:octagon,
|
||||
@@ -127,8 +132,10 @@ const _shapes = KW(
|
||||
:circle => makeshape(20),
|
||||
:rect => makeshape(4, offset=-0.25),
|
||||
:diamond => makeshape(4),
|
||||
:utriangle => makeshape(3),
|
||||
:dtriangle => makeshape(3, offset=0.5),
|
||||
:utriangle => makeshape(3, offset=0.5),
|
||||
:dtriangle => makeshape(3, offset=-0.5),
|
||||
:rtriangle => makeshape(3, offset=0.0),
|
||||
:ltriangle => makeshape(3, offset=1.0),
|
||||
:pentagon => makeshape(5),
|
||||
:hexagon => makeshape(6),
|
||||
:heptagon => makeshape(7),
|
||||
@@ -143,12 +150,14 @@ 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
|
||||
function center(shape::Shape)
|
||||
x, y = shape_coords(shape)
|
||||
x, y = coords(shape)
|
||||
n = length(x)
|
||||
A, Cx, Cy = 0.0, 0.0, 0.0
|
||||
for i=1:n
|
||||
@@ -165,8 +174,8 @@ function center(shape::Shape)
|
||||
Cx / 6A, Cy / 6A
|
||||
end
|
||||
|
||||
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = shape_coords(shape)
|
||||
function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(sx)
|
||||
sx[i] = (sx[i] - cx) * x + cx
|
||||
@@ -175,13 +184,13 @@ function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
shape
|
||||
end
|
||||
|
||||
function Base.scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
function scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
shapecopy = deepcopy(shape)
|
||||
scale!(shape, x, y, c)
|
||||
scale!(shapecopy, x, y, c)
|
||||
end
|
||||
|
||||
function translate!(shape::Shape, x::Real, y::Real = x)
|
||||
sx, sy = shape_coords(shape)
|
||||
sx, sy = coords(shape)
|
||||
for i=1:length(sx)
|
||||
sx[i] += x
|
||||
sy[i] += y
|
||||
@@ -191,7 +200,7 @@ end
|
||||
|
||||
function translate(shape::Shape, x::Real, y::Real = x)
|
||||
shapecopy = deepcopy(shape)
|
||||
translate!(shape, x, y)
|
||||
translate!(shapecopy, x, y)
|
||||
end
|
||||
|
||||
function rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
|
||||
@@ -208,7 +217,7 @@ function rotate(x::Real, y::Real, θ::Real, c = center(shape))
|
||||
end
|
||||
|
||||
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
||||
x, y = shape_coords(shape)
|
||||
x, y = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(x)
|
||||
xi = rotate_x(x[i], y[i], Θ, cx, cy)
|
||||
@@ -226,7 +235,7 @@ end
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
||||
immutable Font
|
||||
type Font
|
||||
family::AbstractString
|
||||
pointsize::Int
|
||||
halign::Symbol
|
||||
@@ -239,7 +248,7 @@ end
|
||||
function font(args...)
|
||||
|
||||
# defaults
|
||||
family = "Helvetica"
|
||||
family = "sans-serif"
|
||||
pointsize = 14
|
||||
halign = :hcenter
|
||||
valign = :vcenter
|
||||
@@ -283,6 +292,17 @@ function font(args...)
|
||||
Font(family, pointsize, halign, valign, rotation, color)
|
||||
end
|
||||
|
||||
function scalefontsize(k::Symbol, factor::Number)
|
||||
f = default(k)
|
||||
f.pointsize = round(Int, factor * f.pointsize)
|
||||
default(k, f)
|
||||
end
|
||||
function scalefontsizes(factor::Number)
|
||||
for k in (:titlefont, :guidefont, :tickfont, :legendfont)
|
||||
scalefontsize(k, factor)
|
||||
end
|
||||
end
|
||||
|
||||
"Wrap a string with font info"
|
||||
immutable PlotText
|
||||
str::AbstractString
|
||||
@@ -296,11 +316,7 @@ function text(str, args...)
|
||||
PlotText(string(str), font(args...))
|
||||
end
|
||||
|
||||
|
||||
annotations(::Void) = []
|
||||
annotations(anns::AVec) = anns
|
||||
annotations(anns) = Any[anns]
|
||||
|
||||
Base.length(t::PlotText) = length(t.str)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@@ -314,9 +330,9 @@ immutable Stroke
|
||||
end
|
||||
|
||||
function stroke(args...; alpha = nothing)
|
||||
width = nothing
|
||||
color = nothing
|
||||
style = nothing
|
||||
width = 1
|
||||
color = :black
|
||||
style = :solid
|
||||
|
||||
for arg in args
|
||||
T = typeof(arg)
|
||||
@@ -350,8 +366,8 @@ immutable Brush
|
||||
end
|
||||
|
||||
function brush(args...; alpha = nothing)
|
||||
size = nothing
|
||||
color = nothing
|
||||
size = 1
|
||||
color = :black
|
||||
|
||||
for arg in args
|
||||
T = typeof(arg)
|
||||
@@ -376,13 +392,116 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
type 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
|
||||
|
||||
type 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
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
"type which represents z-values for colors and sizes (and anything else that might come up)"
|
||||
immutable ZValues
|
||||
values::Vector{Float64}
|
||||
zrange::Tuple{Float64,Float64}
|
||||
end
|
||||
|
||||
function zvalues{T<:Real}(values::AVec{T}, zrange::Tuple{T,T} = (minimum(values), maximum(values)))
|
||||
function zvalues{T<:Real}(values::AVec{T}, zrange::Tuple{T,T} = (ignoreNaN_minimum(values), ignoreNaN_maximum(values)))
|
||||
ZValues(collect(float(values)), map(Float64, zrange))
|
||||
end
|
||||
|
||||
@@ -402,7 +521,7 @@ Base.Array(surf::Surface) = surf.surf
|
||||
for f in (:length, :size)
|
||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||
end
|
||||
Base.copy(surf::Surface) = Surface{typeof(surf.surf)}(copy(surf.surf))
|
||||
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
||||
Base.eltype{T}(surf::Surface{T}) = eltype(T)
|
||||
|
||||
function expand_extrema!(a::Axis, surf::Surface)
|
||||
@@ -452,19 +571,25 @@ Base.eltype{T}(vol::Volume{T}) = T
|
||||
# style is :open or :closed (for now)
|
||||
immutable Arrow
|
||||
style::Symbol
|
||||
side::Symbol # :head (default), :tail, or :both
|
||||
headlength::Float64
|
||||
headwidth::Float64
|
||||
end
|
||||
|
||||
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
|
||||
style = arg
|
||||
if arg in (:head, :tail, :both)
|
||||
side = arg
|
||||
else
|
||||
style = arg
|
||||
end
|
||||
elseif T <: Number
|
||||
# first we apply to both, but if there's more, then only change width after the first number
|
||||
headwidth = Float64(arg)
|
||||
@@ -478,7 +603,7 @@ function arrow(args...)
|
||||
warn("Skipped arrow arg $arg")
|
||||
end
|
||||
end
|
||||
Arrow(style, headlength, headwidth)
|
||||
Arrow(style, side, headlength, headwidth)
|
||||
end
|
||||
|
||||
|
||||
@@ -508,54 +633,34 @@ end
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
type BezierCurve{T <: FixedSizeArrays.Vec}
|
||||
control_points::Vector{T}
|
||||
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
|
||||
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
|
||||
|
||||
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
||||
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
||||
# 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
|
||||
|
||||
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||
@deprecate curve_points coords
|
||||
|
||||
coords(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(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
|
||||
[]
|
||||
function directed_curve(args...; kw...)
|
||||
error("directed_curve has been moved to PlotRecipes")
|
||||
end
|
||||
|
||||
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
|
||||
function extrema_plus_buffer(v, buffmult = 0.2)
|
||||
vmin,vmax = ignoreNaN_extrema(v)
|
||||
vdiff = vmax-vmin
|
||||
buffer = vdiff * buffmult
|
||||
vmin - buffer, vmax + buffer
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# This should cut down on boilerplate code and allow more focused dispatch on type
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
|
||||
const FuncOrFuncs = @compat(Union{Function, AVec{Function}})
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
|
||||
|
||||
+29
-3
@@ -40,7 +40,7 @@ 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.",
|
||||
[:(begin
|
||||
y = rand(100)
|
||||
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=(0,:auto), α=0.6)
|
||||
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")
|
||||
end)]
|
||||
),
|
||||
@@ -115,7 +115,8 @@ PlotExample("Line types",
|
||||
PlotExample("Line styles",
|
||||
"",
|
||||
[:(begin
|
||||
styles = filter(s -> s in Plots.supported_styles(), [:solid, :dash, :dot, :dashdot, :dashdotdot])'
|
||||
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))
|
||||
@@ -300,7 +301,32 @@ PlotExample("Boxplot and Violin series recipes",
|
||||
violin(singers, :VoicePart, :Height, line = 0, fill = (0.2, :blue))
|
||||
boxplot!(singers, :VoicePart, :Height, line = (2,:black), fill = (0.3, :orange))
|
||||
end)]
|
||||
)
|
||||
),
|
||||
|
||||
PlotExample("Animation with subplots",
|
||||
"The `layout` macro can be used to create an animation with subplots.",
|
||||
[:(begin
|
||||
l = @layout([[a; b] c])
|
||||
p = plot(plot([sin,cos],1,leg=false),
|
||||
scatter([atan,cos],1,leg=false),
|
||||
plot(log,1,xlims=(1,10π),ylims=(0,5),leg=false),layout=l)
|
||||
|
||||
anim = Animation()
|
||||
for x = linspace(1,10π,100)
|
||||
plot(push!(p,x,Float64[sin(x),cos(x),atan(x),cos(x),log(x)]))
|
||||
frame(anim)
|
||||
end
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Spy",
|
||||
"For a matrix `mat` with unique nonzeros `spy(mat)` returns a colorless plot. If `mat` has various different nonzero values, a colorbar is added. The colorbar can be disabled with `legend = nothing`. As always, the marker shape and size can be changed with `spy(mat, markersize = 3, markershape = :star)`",
|
||||
[:(begin
|
||||
a = spdiagm((ones(50), ones(49), ones(49), ones(40), ones(40)),(0, 1, -1, 10, -10))
|
||||
b = spdiagm((1:50, 1:49, 1:49, 1:40, 1:40),(0, 1, -1, 10, -10))
|
||||
plot(spy(a, markershape = :dtriangle), spy(b), markersize = 3, title = ["Unique nonzeros" "Different nonzeros"])
|
||||
end)]
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
|
||||
+27
-1
@@ -96,6 +96,32 @@ 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
|
||||
type 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
|
||||
|
||||
@@ -678,7 +704,7 @@ function link_axes!(axes::Axis...)
|
||||
a1 = axes[1]
|
||||
for i=2:length(axes)
|
||||
a2 = axes[i]
|
||||
expand_extrema!(a1, extrema(a2))
|
||||
expand_extrema!(a1, ignoreNaN_extrema(a2))
|
||||
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
|
||||
a2[k] = a1[k]
|
||||
end
|
||||
|
||||
+32
-8
@@ -52,6 +52,16 @@ function tex(plt::Plot, fn::AbstractString)
|
||||
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)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -63,6 +73,7 @@ const _savemap = Dict(
|
||||
"ps" => ps,
|
||||
"eps" => eps,
|
||||
"tex" => tex,
|
||||
"html" => html,
|
||||
)
|
||||
|
||||
function getExtension(fn::AbstractString)
|
||||
@@ -111,6 +122,13 @@ savefig(fn::AbstractString) = savefig(current(), fn)
|
||||
|
||||
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)
|
||||
@@ -119,6 +137,13 @@ 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(
|
||||
@@ -129,6 +154,7 @@ const _mimeformats = Dict(
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg",
|
||||
"text/plain" => "txt",
|
||||
"application/x-tex" => "tex",
|
||||
)
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
@@ -245,7 +271,7 @@ function setup_ijulia()
|
||||
show(io, MIME("text/html"), plt)
|
||||
end
|
||||
end
|
||||
set_ijulia_output("text/html")
|
||||
@eval set_ijulia_output("text/html")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -276,6 +302,10 @@ function setup_atom()
|
||||
Media.render(pane, Atom.div(".fill", Atom.HTML(stringmime(MIME("text/html"), plt))))
|
||||
plt[:size] = sz
|
||||
end
|
||||
# special handling for PlotlyJS
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
end
|
||||
else
|
||||
#
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot)
|
||||
@@ -288,14 +318,8 @@ function setup_atom()
|
||||
# special handling for plotly... use PlotsDisplay
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
s = "PlotPane turned off. The plotly and plotlyjs backends cannot render in the PlotPane due to javascript issues."
|
||||
s = "PlotPane turned off. The plotly backend cannot render in the PlotPane due to javascript issues. Plotlyjs is similar to plotly and is compatible with the plot pane."
|
||||
Media.render(pane, Atom.div(Atom.HTML(s)))
|
||||
end
|
||||
|
||||
# special handling for PlotlyJS to pass through to that render method
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
|
||||
Plots.prepare_output(plt)
|
||||
Media.render(pane, plt.o)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+25
-11
@@ -153,7 +153,7 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
if get(kw, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
sx = [minimum(x), maximum(x)]
|
||||
sx = [ignoreNaN_minimum(x), ignoreNaN_maximum(x)]
|
||||
sy = β * sx + α
|
||||
push!(kw_list, merge(copy(kw), KW(
|
||||
:seriestype => :path,
|
||||
@@ -277,13 +277,24 @@ 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 = merge(d, get(sp_attrs, sp, KW()))
|
||||
attr = if !haskey(d, :subplot) || d[:subplot] == idx
|
||||
merge(d, get(sp_attrs, sp, KW()))
|
||||
else
|
||||
get(sp_attrs, sp, KW())
|
||||
end
|
||||
_update_subplot_args(plt, sp, attr, idx, false)
|
||||
end
|
||||
|
||||
@@ -333,14 +344,17 @@ 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])
|
||||
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)
|
||||
# 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)
|
||||
end
|
||||
|
||||
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
@@ -350,7 +364,7 @@ function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
expand_extrema!(sp[:xaxis], (0,w))
|
||||
expand_extrema!(sp[:yaxis], (0,h))
|
||||
sp[:yaxis].d[:flip] = true
|
||||
elseif !(st in (:pie, :histogram, :histogram2d))
|
||||
elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
end
|
||||
end
|
||||
|
||||
+5
-6
@@ -126,9 +126,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
|
||||
# finish up
|
||||
current(plt)
|
||||
if get(d, :show, default(:show))
|
||||
gui()
|
||||
end
|
||||
_do_plot_show(plt, get(d, :show, default(:show)))
|
||||
plt
|
||||
end
|
||||
|
||||
@@ -234,9 +232,10 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
current(plt)
|
||||
|
||||
# do we want to force display?
|
||||
if plt[:show]
|
||||
gui(plt)
|
||||
end
|
||||
# if plt[:show]
|
||||
# gui(plt)
|
||||
# end
|
||||
_do_plot_show(plt, plt[:show])
|
||||
|
||||
plt
|
||||
end
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
const _attribute_defaults = Dict(:Series => _series_defaults,
|
||||
:Subplot => _subplot_defaults,
|
||||
:Plot => _plot_defaults,
|
||||
:Axis => _axis_defaults)
|
||||
|
||||
attrtypes() = join(keys(_attribute_defaults), ", ")
|
||||
attributes(attrtype::Symbol) = sort(collect(keys(_attribute_defaults[attrtype])))
|
||||
|
||||
function lookup_aliases(attrtype, attribute)
|
||||
attribute = Symbol(attribute)
|
||||
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
|
||||
in(attribute, keys(_attribute_defaults[attrtype])) && return attribute
|
||||
error("There is no attribute named $attribute in $attrtype")
|
||||
end
|
||||
|
||||
function plotattr()
|
||||
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
|
||||
end
|
||||
|
||||
function plotattr(attrtype::Symbol)
|
||||
in(attrtype, keys(_attribute_defaults)) || error("Viable options are $(attrtypes())")
|
||||
println("Defined $attrtype attributes are:\n$(join(attributes(attrtype), ", "))")
|
||||
end
|
||||
|
||||
function plotattr(attribute::AbstractString)
|
||||
attribute = Symbol(attribute)
|
||||
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
|
||||
for (k, v) in _attribute_defaults
|
||||
if in(attribute, keys(v))
|
||||
return plotattr(k, "$attribute")
|
||||
end
|
||||
end
|
||||
error("There is no attribute named $attribute")
|
||||
end
|
||||
|
||||
function plotattr(attrtype::Symbol, attribute::AbstractString)
|
||||
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
|
||||
|
||||
attribute = Symbol(lookup_aliases(attrtype, attribute))
|
||||
|
||||
desc = get(_arg_desc, attribute, "")
|
||||
first_period_idx = findfirst(desc, '.')
|
||||
typedesc = desc[1:first_period_idx-1]
|
||||
desc = strip(desc[first_period_idx+1:end])
|
||||
als = keys(filter((_,v)->v==attribute, _keyAliases)) |> collect |> sort
|
||||
als = join(map(string,als), ", ")
|
||||
def = _attribute_defaults[attrtype][attribute]
|
||||
|
||||
|
||||
# Looks up the different elements and plots them
|
||||
println("$attribute ", typedesc == "" ? "" : "{$typedesc}", "\n",
|
||||
als == "" ? "" : "$als\n",
|
||||
"\n$desc\n",
|
||||
"$(attrtype) attribute, ", def == "" ? "" : " default: $def")
|
||||
end
|
||||
+382
-88
@@ -225,7 +225,7 @@ end
|
||||
fr = if yaxis[:scale] == :identity
|
||||
0.0
|
||||
else
|
||||
min(axis_limits(yaxis)[1], minimum(y))
|
||||
NaNMath.min(axis_limits(yaxis)[1], ignoreNaN_minimum(y))
|
||||
end
|
||||
end
|
||||
newx, newy = zeros(3n), zeros(3n)
|
||||
@@ -323,10 +323,11 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
nx, ny = length(x), length(y)
|
||||
procx, procy, xscale, yscale, baseline = _preprocess_barlike(d, x, y)
|
||||
nx, ny = length(procx), length(procy)
|
||||
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=x]
|
||||
x = if nx == ny
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=procx]
|
||||
procx = if nx == ny
|
||||
cv
|
||||
elseif nx == ny + 1
|
||||
0.5diff(cv) + cv[1:end-1]
|
||||
@@ -337,9 +338,9 @@ end
|
||||
# compute half-width of bars
|
||||
bw = d[:bar_width]
|
||||
hw = if bw == nothing
|
||||
0.5mean(diff(x))
|
||||
0.5ignoreNaN_mean(diff(procx))
|
||||
else
|
||||
Float64[0.5cycle(bw,i) for i=1:length(x)]
|
||||
Float64[0.5cycle(bw,i) for i=1:length(procx)]
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
@@ -347,20 +348,25 @@ end
|
||||
if fillto == nothing
|
||||
fillto = 0
|
||||
end
|
||||
if (yscale in _logScales) && !all(_is_positive, fillto)
|
||||
fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto)
|
||||
end
|
||||
|
||||
# create the bar shapes by adding x/y segments
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i=1:ny
|
||||
center = x[i]
|
||||
hwi = cycle(hw,i)
|
||||
yi = y[i]
|
||||
fi = cycle(fillto,i)
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
yi = procy[i]
|
||||
if !isnan(yi)
|
||||
center = procx[i]
|
||||
hwi = cycle(hw,i)
|
||||
fi = cycle(fillto,i)
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
end
|
||||
end
|
||||
|
||||
# widen limits out a bit
|
||||
expand_extrema!(axis, widen(extrema(xseg.pts)...))
|
||||
expand_extrema!(axis, widen(ignoreNaN_extrema(xseg.pts)...))
|
||||
|
||||
# switch back
|
||||
if !isvertical(d)
|
||||
@@ -378,106 +384,326 @@ end
|
||||
end
|
||||
@deps bar shape
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
|
||||
# edges from number of bins
|
||||
function calc_edges(v, bins::Integer)
|
||||
vmin, vmax = extrema(v)
|
||||
linspace(vmin, vmax, bins+1)
|
||||
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
||||
|
||||
_is_positive(x) = (x > 0) && !(x ≈ 0)
|
||||
|
||||
_positive_else_nan{T}(::Type{T}, x::Real) = _is_positive(x) ? T(x) : T(NaN)
|
||||
|
||||
function _scale_adjusted_values{T<:AbstractFloat}(::Type{T}, V::AbstractVector, scale::Symbol)
|
||||
if scale in _logScales
|
||||
[_positive_else_nan(T, x) for x in V]
|
||||
else
|
||||
[T(x) for x in V]
|
||||
end
|
||||
end
|
||||
|
||||
# just pass through arrays
|
||||
calc_edges(v, bins::AVec) = bins
|
||||
|
||||
# find the bucket index of this value
|
||||
function bucket_index(vi, edges)
|
||||
for (i,e) in enumerate(edges)
|
||||
if vi <= e
|
||||
return max(1,i-1)
|
||||
function _binbarlike_baseline{T<:Real}(min_value::T, scale::Symbol)
|
||||
if (scale in _logScales)
|
||||
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
|
||||
else
|
||||
zero(T)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _preprocess_binbarlike_weights{T<:AbstractFloat}(::Type{T}, w, wscale::Symbol)
|
||||
w_adj = _scale_adjusted_values(T, w, wscale)
|
||||
w_min = ignoreNaN_minimum(w_adj)
|
||||
w_max = ignoreNaN_maximum(w_adj)
|
||||
baseline = _binbarlike_baseline(w_min, wscale)
|
||||
w_adj, baseline
|
||||
end
|
||||
|
||||
function _preprocess_barlike(d, x, y)
|
||||
xscale = get(d, :xscale, :identity)
|
||||
yscale = get(d, :yscale, :identity)
|
||||
weights, baseline = _preprocess_binbarlike_weights(float(eltype(y)), y, yscale)
|
||||
x, weights, xscale, yscale, baseline
|
||||
end
|
||||
|
||||
function _preprocess_binlike(d, x, y)
|
||||
xscale = get(d, :xscale, :identity)
|
||||
yscale = get(d, :yscale, :identity)
|
||||
T = float(promote_type(eltype(x), eltype(y)))
|
||||
edge = T.(x)
|
||||
weights, baseline = _preprocess_binbarlike_weights(T, y, yscale)
|
||||
edge, weights, xscale, yscale, baseline
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||
if (d[:bar_width] == nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
seriestype := :bar
|
||||
()
|
||||
end
|
||||
@deps barbins bar
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||
xerror := diff(edge)/2
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
seriestype := :scatter
|
||||
()
|
||||
end
|
||||
@deps scatterbins scatter
|
||||
|
||||
|
||||
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
|
||||
log_scale_x = xscale in _logScales
|
||||
log_scale_y = yscale in _logScales
|
||||
|
||||
nbins = length(linearindices(weights))
|
||||
if length(linearindices(edge)) != nbins + 1
|
||||
error("Edge vector must be 1 longer than weight vector")
|
||||
end
|
||||
|
||||
x = eltype(edge)[]
|
||||
y = eltype(weights)[]
|
||||
|
||||
it_e, it_w = start(edge), start(weights)
|
||||
a, it_e = next(edge, it_e)
|
||||
last_w = eltype(weights)(NaN)
|
||||
i = 1
|
||||
while (!done(edge, it_e) && !done(edge, it_e))
|
||||
b, it_e = next(edge, it_e)
|
||||
w, it_w = next(weights, it_w)
|
||||
|
||||
if (log_scale_x && a ≈ 0)
|
||||
a = b/_logScaleBases[xscale]^3
|
||||
end
|
||||
|
||||
if isnan(w)
|
||||
if !isnan(last_w)
|
||||
push!(x, a)
|
||||
push!(y, baseline)
|
||||
end
|
||||
else
|
||||
if isnan(last_w)
|
||||
push!(x, a)
|
||||
push!(y, baseline)
|
||||
end
|
||||
push!(x, a)
|
||||
push!(y, w)
|
||||
push!(x, b)
|
||||
push!(y, w)
|
||||
end
|
||||
|
||||
a = b
|
||||
last_w = w
|
||||
end
|
||||
return length(edges)-1
|
||||
if (last_w != baseline)
|
||||
push!(x, a)
|
||||
push!(y, baseline)
|
||||
end
|
||||
|
||||
(x, y)
|
||||
end
|
||||
|
||||
function my_hist(v, bins; normed = false, weights = nothing)
|
||||
edges = calc_edges(v, bins)
|
||||
counts = zeros(length(edges)-1)
|
||||
|
||||
# add a weighted count
|
||||
for (i,vi) in enumerate(v)
|
||||
idx = bucket_index(vi, edges)
|
||||
counts[idx] += (weights == nothing ? 1.0 : weights[i])
|
||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||
axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis]
|
||||
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||
|
||||
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||
if !isvertical(d)
|
||||
xpts, ypts = ypts, xpts
|
||||
end
|
||||
|
||||
# normalize by bar area?
|
||||
norm_denom = normed ? sum(diff(edges) .* counts) : 1.0
|
||||
if norm_denom == 0
|
||||
norm_denom = 1.0
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
fillrange := nothing
|
||||
label := ""
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
markershape := :none
|
||||
xerror := :none
|
||||
yerror := :none
|
||||
end
|
||||
|
||||
edges, counts ./ norm_denom
|
||||
x := xpts
|
||||
y := ypts
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
Plots.@deps stepbins path
|
||||
|
||||
|
||||
function _auto_binning_nbins{N}(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto)
|
||||
_cl(x) = ceil(Int, NaNMath.max(x, one(x)))
|
||||
_iqr(v) = quantile(v, 0.75) - quantile(v, 0.25)
|
||||
_span(v) = ignoreNaN_maximum(v) - ignoreNaN_minimum(v)
|
||||
|
||||
n_samples = length(linearindices(first(vs)))
|
||||
# Estimator for number of samples in one row/column of bins along each axis:
|
||||
n = max(1, n_samples^(1/N))
|
||||
|
||||
v = vs[dim]
|
||||
|
||||
if mode == :auto
|
||||
30
|
||||
elseif mode == :sqrt # Square-root choice
|
||||
_cl(sqrt(n))
|
||||
elseif mode == :sturges # Sturges' formula
|
||||
_cl(log2(n)) + 1
|
||||
elseif mode == :rice # Rice Rule
|
||||
_cl(2 * n^(1/3))
|
||||
elseif mode == :scott # Scott's normal reference rule
|
||||
_cl(_span(v) / (3.5 * std(v) / n^(1/3)))
|
||||
elseif mode == :fd # Freedman–Diaconis rule
|
||||
_cl(_span(v) / (2 * _iqr(v) / n^(1/3)))
|
||||
else
|
||||
error("Unknown auto-binning mode $mode")
|
||||
end::Int
|
||||
end
|
||||
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) = binning
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::NTuple{N}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...))
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...))
|
||||
|
||||
_hist_norm_mode(mode::Symbol) = mode
|
||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
||||
|
||||
function _make_hist{N}(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing)
|
||||
edges = _hist_edges(vs, binning)
|
||||
h = float( weights == nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, vs, edges, closed = :left) :
|
||||
StatsBase.fit(StatsBase.Histogram, vs, weights, edges, closed = :left)
|
||||
)
|
||||
normalize!(h, mode = _hist_norm_mode(normed))
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram}}, x, y, z)
|
||||
edges, counts = my_hist(y, d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights])
|
||||
x := edges
|
||||
y := counts
|
||||
seriestype := :bar
|
||||
seriestype := :barhist
|
||||
()
|
||||
end
|
||||
@deps histogram bar
|
||||
@deps histogram barhist
|
||||
|
||||
@recipe function f(::Type{Val{:barhist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :barbins
|
||||
()
|
||||
end
|
||||
@deps barhist barbins
|
||||
|
||||
@recipe function f(::Type{Val{:stephist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :stepbins
|
||||
()
|
||||
end
|
||||
@deps stephist stepbins
|
||||
|
||||
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :scatterbins
|
||||
()
|
||||
end
|
||||
@deps scatterhist scatterbins
|
||||
|
||||
|
||||
@recipe function f{T, E}(h::StatsBase.Histogram{T, 1, E})
|
||||
seriestype --> :barbins
|
||||
|
||||
st_map = Dict(
|
||||
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
|
||||
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
|
||||
)
|
||||
seriestype := get(st_map, d[:seriestype], d[:seriestype])
|
||||
|
||||
if d[:seriestype] == :scatterbins
|
||||
# Workaround, error bars currently not set correctly by scatterbins
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1])/2
|
||||
seriestype := :scatter
|
||||
(Plots._bin_centers(edge), weights)
|
||||
else
|
||||
(h.edges[1], h.weights)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@recipe function f{H <: StatsBase.Histogram}(hv::AbstractVector{H})
|
||||
for h in hv
|
||||
@series begin
|
||||
h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histogram 2D
|
||||
|
||||
# if tuple, map out bins, otherwise use the same for both
|
||||
calc_edges_2d(x, y, bins) = calc_edges(x, bins), calc_edges(y, bins)
|
||||
calc_edges_2d{X,Y}(x, y, bins::Tuple{X,Y}) = calc_edges(x, bins[1]), calc_edges(y, bins[2])
|
||||
@recipe function f(::Type{Val{:bins2d}}, x, y, z)
|
||||
edge_x, edge_y, weights = x, y, z.surf
|
||||
|
||||
# the 2D version
|
||||
function my_hist_2d(x, y, bins; normed = false, weights = nothing)
|
||||
xedges, yedges = calc_edges_2d(x, y, bins)
|
||||
counts = zeros(length(yedges)-1, length(xedges)-1)
|
||||
|
||||
# add a weighted count
|
||||
for i=1:length(x)
|
||||
r = bucket_index(y[i], yedges)
|
||||
c = bucket_index(x[i], xedges)
|
||||
counts[r,c] += (weights == nothing ? 1.0 : weights[i])
|
||||
float_weights = float(weights)
|
||||
if is(float_weights, weights)
|
||||
float_weights = deepcopy(float_weights)
|
||||
end
|
||||
|
||||
# normalize to cubic area of the imaginary surface towers
|
||||
norm_denom = normed ? sum((diff(yedges) * diff(xedges)') .* counts) : 1.0
|
||||
if norm_denom == 0
|
||||
norm_denom = 1.0
|
||||
end
|
||||
|
||||
xedges, yedges, counts ./ norm_denom
|
||||
end
|
||||
|
||||
centers(v::AVec) = 0.5 * (v[1:end-1] + v[2:end])
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights])
|
||||
for (i,c) in enumerate(counts)
|
||||
for (i, c) in enumerate(float_weights)
|
||||
if c == 0
|
||||
counts[i] = NaN
|
||||
float_weights[i] = NaN
|
||||
end
|
||||
end
|
||||
x := centers(xedges)
|
||||
y := centers(yedges)
|
||||
z := Surface(counts)
|
||||
linewidth := 0
|
||||
|
||||
x := Plots._bin_centers(edge_x)
|
||||
y := Plots._bin_centers(edge_y)
|
||||
z := Surface(float_weights)
|
||||
|
||||
match_dimensions := true
|
||||
seriestype := :heatmap
|
||||
()
|
||||
end
|
||||
@deps histogram2d heatmap
|
||||
Plots.@deps bins2d heatmap
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
h = _make_hist((x, y), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.edges[2]
|
||||
z := Surface(h.weights)
|
||||
seriestype := :bins2d
|
||||
()
|
||||
end
|
||||
@deps histogram2d bins2d
|
||||
|
||||
|
||||
@recipe function f{T, E}(h::StatsBase.Histogram{T, 2, E})
|
||||
seriestype --> :bins2d
|
||||
(h.edges[1], h.edges[2], Surface(h.weights))
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -526,8 +752,7 @@ end
|
||||
|
||||
function error_coords(xorig, yorig, ebar)
|
||||
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
|
||||
x, y = zeros(0), zeros(0)
|
||||
|
||||
x, y = Array(float_extended_type(xorig), 0), Array(Float64, 0)
|
||||
# for each point, create a line segment from the bottom to the top of the errorbar
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
xi = cycle(xorig, i)
|
||||
@@ -694,7 +919,7 @@ end
|
||||
|
||||
# get the joined vector
|
||||
function get_xy(v::AVec{OHLC}, x = 1:length(v))
|
||||
xdiff = 0.3mean(abs(diff(x)))
|
||||
xdiff = 0.3ignoreNaN_mean(abs(diff(x)))
|
||||
x_out, y_out = zeros(0), zeros(0)
|
||||
for (i,ohlc) in enumerate(v)
|
||||
ox,oy = get_xy(ohlc, x[i], xdiff)
|
||||
@@ -751,6 +976,9 @@ end
|
||||
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
|
||||
seriestype := :spy
|
||||
mat = g.args[1]
|
||||
if length(unique(mat[mat .!= 0])) < 2
|
||||
legend --> nothing
|
||||
end
|
||||
n,m = size(mat)
|
||||
Plots.SliceIt, 1:m, 1:n, Surface(mat)
|
||||
end
|
||||
@@ -759,8 +987,8 @@ end
|
||||
yflip := true
|
||||
aspect_ratio := 1
|
||||
rs, cs, zs = findnz(z.surf)
|
||||
xlim := extrema(cs)
|
||||
ylim := extrema(rs)
|
||||
xlim := ignoreNaN_extrema(cs)
|
||||
ylim := ignoreNaN_extrema(rs)
|
||||
if d[:markershape] == :none
|
||||
markershape := :circle
|
||||
end
|
||||
@@ -774,6 +1002,7 @@ end
|
||||
y := rs
|
||||
z := nothing
|
||||
seriestype := :scatter
|
||||
grid --> false
|
||||
()
|
||||
end
|
||||
|
||||
@@ -781,7 +1010,7 @@ end
|
||||
|
||||
"Adds a+bx... straight line over the current plot"
|
||||
function abline!(plt::Plot, a, b; kw...)
|
||||
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
|
||||
plot!(plt, [ignoreNaN_extrema(plt)...], x -> b + a*x; kw...)
|
||||
end
|
||||
|
||||
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
@@ -790,5 +1019,70 @@ abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
# -------------------------------------------------
|
||||
# Dates
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int,dt), dt -> string(convert(Date,dt)))
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int,dt), dt -> string(convert(DateTime,dt)))
|
||||
dateformatter(dt) = string(convert(Date, dt))
|
||||
datetimeformatter(dt) = string(convert(DateTime, dt))
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int, dt), dateformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int, dt), datetimeformatter)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
@recipe function f{T<:Number}(A::Array{Complex{T}})
|
||||
xguide --> "Re(x)"
|
||||
yguide --> "Im(x)"
|
||||
real.(A), imag.(A)
|
||||
end
|
||||
|
||||
# Splits a complex matrix to its real and complex parts
|
||||
# Reals defaults solid, imaginary defaults dashed
|
||||
# Label defaults are changed to match the real-imaginary reference / indexing
|
||||
@recipe function f{T<:Real,T2}(x::AbstractArray{T},y::Array{Complex{T2}})
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x,real.(y),imag.(y)
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
# Color Gradients
|
||||
|
||||
@userplot ShowLibrary
|
||||
@recipe function f(cl::ShowLibrary)
|
||||
if !(length(cl.args) == 1 && isa(cl.args[1], Symbol))
|
||||
error("showlibrary takes the name of a color library as a Symbol")
|
||||
end
|
||||
|
||||
library = PlotUtils.color_libraries[cl.args[1]]
|
||||
z = sqrt.((1:15)*(1:20)')
|
||||
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
legend := false
|
||||
|
||||
layout --> length(library.lib)
|
||||
|
||||
i = 0
|
||||
for grad in sort(collect(keys(library.lib)))
|
||||
@series begin
|
||||
seriescolor := cgrad(grad, cl.args[1])
|
||||
title := string(grad)
|
||||
subplot := i += 1
|
||||
z
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@userplot ShowGradient
|
||||
@recipe function f(grad::ShowGradient)
|
||||
if !(length(grad.args) == 1 && isa(grad.args[1], Symbol))
|
||||
error("showgradient takes the name of a color gradient as a Symbol")
|
||||
end
|
||||
z = sqrt.((1:15)*(1:20)')
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
legend := false
|
||||
seriescolor := grad.args[1]
|
||||
title := string(grad.args[1])
|
||||
z
|
||||
end
|
||||
|
||||
+44
-16
@@ -6,7 +6,7 @@
|
||||
# This should cut down on boilerplate code and allow more focused dispatch on type
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
typealias FuncOrFuncs Union{Function, AVec{Function}}
|
||||
typealias FuncOrFuncs{F} Union{F, Vector{F}, Matrix{F}}
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
|
||||
@@ -96,8 +96,8 @@ nobigs(v) = v
|
||||
end
|
||||
|
||||
# not allowed
|
||||
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{F<:Function}(x::Void, y::FuncOrFuncs{F}, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz{F<:Function}(x::Void, y::Void, z::FuncOrFuncs{F}) = 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!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
@@ -257,12 +257,23 @@ 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(d,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||
|
||||
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
||||
@recipe function f{T<:Union{Integer,AbstractFloat}}(mat::AMat{T})
|
||||
if all3D(d)
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(d)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
SliceIt, nothing, mat, nothing
|
||||
@@ -274,6 +285,7 @@ end
|
||||
if all3D(d)
|
||||
mat = fmt.data
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(d)
|
||||
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
|
||||
else
|
||||
SliceIt, nothing, fmt, nothing
|
||||
@@ -305,9 +317,10 @@ end
|
||||
# # images - colors
|
||||
|
||||
@recipe function f{T<:Colorant}(mat::AMat{T})
|
||||
n, m = size(mat)
|
||||
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
n, m = size(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
@@ -322,18 +335,18 @@ end
|
||||
|
||||
@recipe function f(shape::Shape)
|
||||
seriestype --> :shape
|
||||
shape_coords(shape)
|
||||
coords(shape)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AVec{Shape})
|
||||
seriestype --> :shape
|
||||
shape_coords(shapes)
|
||||
coords(shapes)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AMat{Shape})
|
||||
seriestype --> :shape
|
||||
for j in 1:size(shapes,2)
|
||||
@series shape_coords(vec(shapes[:,j]))
|
||||
@series coords(vec(shapes[:,j]))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -341,9 +354,13 @@ end
|
||||
|
||||
# function without range... use the current range of the x-axis
|
||||
|
||||
@recipe function f(f::FuncOrFuncs)
|
||||
@recipe function f{F<:Function}(f::FuncOrFuncs{F})
|
||||
plt = d[:plot_object]
|
||||
xmin,xmax = axis_limits(plt[1][:xaxis])
|
||||
xmin, xmax = try
|
||||
axis_limits(plt[1][:xaxis])
|
||||
catch
|
||||
-5, 5
|
||||
end
|
||||
f, xmin, xmax
|
||||
end
|
||||
|
||||
@@ -356,8 +373,9 @@ 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, x)
|
||||
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
|
||||
@recipe function f{F<:Function}(f::FuncOrFuncs{F}, x)
|
||||
F2 = typeof(x)
|
||||
@assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here
|
||||
x, f
|
||||
end
|
||||
|
||||
@@ -386,6 +404,7 @@ end
|
||||
# seriestype := :path3d
|
||||
# end
|
||||
# end
|
||||
wrap_surfaces(d)
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@@ -395,6 +414,7 @@ end
|
||||
@recipe function f(x::AVec, y::AVec, zf::Function)
|
||||
# x = X <: Number ? sort(x) : x
|
||||
# y = Y <: Number ? sort(y) : y
|
||||
wrap_surfaces(d)
|
||||
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
end
|
||||
|
||||
@@ -405,6 +425,7 @@ end
|
||||
if !like_surface(get(d, :seriestype, :none))
|
||||
d[:seriestype] = :contour
|
||||
end
|
||||
wrap_surfaces(d)
|
||||
SliceIt, x, y, Surface(z)
|
||||
end
|
||||
|
||||
@@ -416,16 +437,23 @@ end
|
||||
|
||||
#
|
||||
# # special handling... xmin/xmax with parametric function(s)
|
||||
@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)
|
||||
@recipe function f(f::Function, xmin::Number, xmax::Number)
|
||||
xs = adapted_grid(f, (xmin, xmax))
|
||||
xs, f
|
||||
end
|
||||
@recipe function f{F<:Function}(fs::AbstractArray{F}, xmin::Number, xmax::Number)
|
||||
xs = Any[adapted_grid(f, (xmin, xmax)) for f in fs]
|
||||
xs, fs
|
||||
end
|
||||
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n)
|
||||
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec)
|
||||
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec)
|
||||
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
|
||||
end
|
||||
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints = 200)
|
||||
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200)
|
||||
fx, fy, fz, linspace(umin, umax, numPoints)
|
||||
end
|
||||
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot]
|
||||
function should_add_to_legend(series::Series)
|
||||
series.d[:primary] && series.d[:label] != "" &&
|
||||
!(series.d[:seriestype] in (
|
||||
:hexbin,:histogram2d,:hline,:vline,
|
||||
:hexbin,:bins2d,:histogram2d,:hline,:vline,
|
||||
:contour,:contourf,:contour3d,:surface,:wireframe,
|
||||
:heatmap, :pie, :image
|
||||
))
|
||||
|
||||
+4
-2
@@ -2,7 +2,8 @@
|
||||
function theme(s::Symbol; kw...)
|
||||
# reset?
|
||||
if s == :none || s == :default
|
||||
PlotUtils._default_gradient[] = :inferno
|
||||
PlotUtils.clibrary(:Plots)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
|
||||
default(;
|
||||
bg = :white,
|
||||
bglegend = :match,
|
||||
@@ -23,7 +24,8 @@ function theme(s::Symbol; kw...)
|
||||
# update the default gradient and other defaults
|
||||
thm = PlotThemes._themes[s]
|
||||
if thm.gradient != nothing
|
||||
PlotUtils._default_gradient[] = PlotThemes.gradient_name(s)
|
||||
PlotUtils.clibrary(:misc)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = PlotThemes.gradient_name(s))
|
||||
end
|
||||
default(;
|
||||
bg = thm.bg_secondary,
|
||||
|
||||
+12
-4
@@ -2,9 +2,9 @@
|
||||
# TODO: I declare lots of types here because of the lacking ability to do forward declarations in current Julia
|
||||
# I should move these to the relevant files when something like "extern" is implemented
|
||||
|
||||
typealias AVec AbstractVector
|
||||
typealias AMat AbstractMatrix
|
||||
typealias KW Dict{Symbol,Any}
|
||||
const AVec = AbstractVector
|
||||
const AMat = AbstractMatrix
|
||||
const KW = Dict{Symbol,Any}
|
||||
|
||||
immutable PlotsDisplay <: Display end
|
||||
|
||||
@@ -62,7 +62,7 @@ Extrema() = Extrema(Inf, -Inf)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
typealias SubplotMap Dict{Any, Subplot}
|
||||
const SubplotMap = Dict{Any, Subplot}
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
@@ -90,10 +90,18 @@ 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))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
+42
-31
@@ -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 = extrema(data)
|
||||
lo, hi = ignoreNaN_extrema(data)
|
||||
edges = collect(linspace(lo, hi, nbins+1))
|
||||
midpoints = calcMidpoints(edges)
|
||||
buckets = Int[max(2, min(searchsortedfirst(edges, x), length(edges)))-1 for x in data]
|
||||
@@ -109,7 +109,7 @@ function regressionXY(x, y)
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
|
||||
# make a line segment
|
||||
regx = [minimum(x), maximum(x)]
|
||||
regx = [ignoreNaN_minimum(x), ignoreNaN_maximum(x)]
|
||||
regy = β * regx + α
|
||||
regx, regy
|
||||
end
|
||||
@@ -228,25 +228,34 @@ 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{T}(x::AbstractArray{T}) = Union{T,Float64}
|
||||
float_extended_type{T<:Real}(x::AbstractArray{T}) = Float64
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
nop() = nothing
|
||||
notimpl() = error("This has not been implemented yet")
|
||||
|
||||
Base.cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
||||
Base.cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
|
||||
isnothing(x::Void) = true
|
||||
isnothing(x) = false
|
||||
|
||||
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(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
||||
cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
|
||||
|
||||
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, indices::AVec{Int}) = fill(v, length(indices))
|
||||
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(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
|
||||
Base.cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
|
||||
cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
cycle(v, indices::AVec{Int}) = fill(v, length(indices))
|
||||
|
||||
cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
|
||||
cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
|
||||
|
||||
makevec(v::AVec) = v
|
||||
makevec{T}(v::T) = T[v]
|
||||
@@ -256,7 +265,7 @@ maketuple(x::Real) = (x,x)
|
||||
maketuple{T,S}(x::Tuple{T,S}) = x
|
||||
|
||||
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
||||
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
|
||||
mapFuncOrFuncs{F<:Function}(fs::AVec{F}, u::AVec) = [map(f, u) for f in fs]
|
||||
|
||||
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]
|
||||
@@ -274,9 +283,9 @@ unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[
|
||||
# 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 = extrema(x)
|
||||
lims[1] = min(lims[1], e1)
|
||||
lims[2] = max(lims[2], e2)
|
||||
e1, e2 = ignoreNaN_extrema(x)
|
||||
lims[1] = NaNMath.min(lims[1], e1)
|
||||
lims[2] = NaNMath.max(lims[2], e2)
|
||||
# catch err
|
||||
# warn(err)
|
||||
end
|
||||
@@ -325,23 +334,23 @@ sortedkeys(d::Dict) = sort(collect(keys(d)))
|
||||
|
||||
"create an (n+1) list of the outsides of heatmap rectangles"
|
||||
function heatmap_edges(v::AVec)
|
||||
vmin, vmax = extrema(v)
|
||||
vmin, vmax = ignoreNaN_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 = extrema(x)
|
||||
ymin, ymax = extrema(y)
|
||||
r = 0.5 * min(xmax - xmin, ymax - ymin)
|
||||
extrema(r)
|
||||
xmin, xmax = ignoreNaN_extrema(x)
|
||||
ymin, ymax = ignoreNaN_extrema(y)
|
||||
r = 0.5 * NaNMath.min(xmax - xmin, ymax - ymin)
|
||||
ignoreNaN_extrema(r)
|
||||
end
|
||||
|
||||
function convert_to_polar(x, y, r_extrema = calc_r_extrema(x, y))
|
||||
rmin, rmax = r_extrema
|
||||
phi, r = x, y
|
||||
r = 0.5 * (r - rmin) / (rmax - rmin)
|
||||
r = (r - rmin) / (rmax - rmin)
|
||||
n = max(length(phi), length(r))
|
||||
x = zeros(n)
|
||||
y = zeros(n)
|
||||
@@ -460,7 +469,7 @@ ok(tup::Tuple) = ok(tup...)
|
||||
# compute one side of a fill range from a ribbon
|
||||
function make_fillrange_side(y, rib)
|
||||
frs = zeros(length(y))
|
||||
for (i, (yi, ri)) in enumerate(zip(y, cycle(rib)))
|
||||
for (i, (yi, ri)) in enumerate(zip(y, Base.cycle(rib)))
|
||||
frs[i] = yi + ri
|
||||
end
|
||||
frs
|
||||
@@ -490,6 +499,8 @@ zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
makekw(; kw...) = KW(kw)
|
||||
|
||||
wraptuple(x::Tuple) = x
|
||||
wraptuple(x) = (x,)
|
||||
|
||||
@@ -634,7 +645,7 @@ end
|
||||
# used in updating an existing series
|
||||
|
||||
extendSeriesByOne(v::UnitRange{Int}, n::Int = 1) = isempty(v) ? (1:n) : (minimum(v):maximum(v)+n)
|
||||
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + maximum(v))
|
||||
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + ignoreNaN_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)
|
||||
@@ -714,28 +725,28 @@ Base.push!(series::Series, xi, yi, zi) = (push_x!(series,xi); push_y!(series,yi)
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
function update!(series::Series; kw...)
|
||||
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 update")
|
||||
warn("unused key $k in series attr")
|
||||
end
|
||||
end
|
||||
_series_updated(series[:subplot].plt, series)
|
||||
series
|
||||
end
|
||||
|
||||
function update!(sp::Subplot; kw...)
|
||||
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 update")
|
||||
warn("unused key $k in subplot attr")
|
||||
end
|
||||
end
|
||||
sp
|
||||
@@ -860,9 +871,9 @@ mm2px(mm::Real) = float(px / MM_PER_PX)
|
||||
|
||||
|
||||
"Smallest x in plot"
|
||||
xmin(plt::Plot) = minimum([minimum(series.d[:x]) for series in plt.series_list])
|
||||
xmin(plt::Plot) = ignoreNaN_minimum([ignoreNaN_minimum(series.d[:x]) for series in plt.series_list])
|
||||
"Largest x in plot"
|
||||
xmax(plt::Plot) = maximum([maximum(series.d[:x]) for series in plt.series_list])
|
||||
xmax(plt::Plot) = ignoreNaN_maximum([ignoreNaN_maximum(series.d[:x]) for series in plt.series_list])
|
||||
|
||||
"Extrema of x-values in plot"
|
||||
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))
|
||||
ignoreNaN_extrema(plt::Plot) = (xmin(plt), xmax(plt))
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ try
|
||||
end
|
||||
|
||||
|
||||
using Plots
|
||||
# using Plots # reexported by StatPlots
|
||||
using StatPlots
|
||||
using FactCheck
|
||||
using Glob
|
||||
@@ -24,7 +24,7 @@ default(size=(500,300))
|
||||
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
||||
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
||||
|
||||
const _current_plots_version = v"0.9.6"
|
||||
const _current_plots_version = v"0.11.3"
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||
|
||||
@@ -5,9 +5,11 @@ set -ex
|
||||
sudo apt-get -qq update
|
||||
# sudo apt-get install -y wkhtmltopdf
|
||||
|
||||
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
|
||||
sudo apt-get install -y xfonts-75dpi xfonts-base
|
||||
wget https://downloads.wkhtmltopdf.org/0.12/0.12.2/wkhtmltox-0.12.2_linux-precise-amd64.deb
|
||||
sudo dpkg -i wkhtmltox-0.12.2_linux-precise-amd64.deb
|
||||
# wget http://download.gna.org/wkhtmltopdf/0.12/0.12.2/wkhtmltox-0.12.2_linux-trusty-amd64.deb
|
||||
# sudo dpkg -i wkhtmltox-0.12.2_linux-trusty-amd64.deb
|
||||
wkhtmltoimage http://www.google.com test.png
|
||||
ls
|
||||
|
||||
|
||||
+61
-18
@@ -19,31 +19,74 @@ img_eps = isinteractive() ? 1e-2 : 10e-2
|
||||
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], eps=img_eps)
|
||||
# end
|
||||
|
||||
facts("PyPlot") do
|
||||
@fact pyplot() --> Plots.PyPlotBackend()
|
||||
@fact backend() --> Plots.PyPlotBackend()
|
||||
|
||||
image_comparison_facts(:pyplot, skip=[25,30], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("GR") do
|
||||
@fact gr() --> Plots.GRBackend()
|
||||
@fact backend() --> Plots.GRBackend()
|
||||
|
||||
if is_linux() && isinteractive()
|
||||
image_comparison_facts(:gr, skip=[2,25,30], eps=img_eps)
|
||||
end
|
||||
image_comparison_facts(:gr, eps=img_eps)
|
||||
end
|
||||
|
||||
facts("Plotly") do
|
||||
@fact plotly() --> Plots.PlotlyBackend()
|
||||
@fact backend() --> Plots.PlotlyBackend()
|
||||
|
||||
# # until png generation is reliable on OSX, just test on linux
|
||||
# @static is_linux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
|
||||
facts("PyPlot") do
|
||||
@fact pyplot() --> Plots.PyPlotBackend()
|
||||
@fact backend() --> Plots.PyPlotBackend()
|
||||
|
||||
image_comparison_facts(:pyplot, eps=img_eps)
|
||||
end
|
||||
|
||||
|
||||
# The plotlyjs testimages return a connection error on travis:
|
||||
# connect: connection refused (ECONNREFUSED)
|
||||
|
||||
# facts("PlotlyJS") do
|
||||
# @fact plotlyjs() --> Plots.PlotlyJSBackend()
|
||||
# @fact backend() --> Plots.PlotlyJSBackend()
|
||||
#
|
||||
# if is_linux() && isinteractive()
|
||||
# image_comparison_facts(:plotlyjs,
|
||||
# skip=[
|
||||
# 2, # animation (skipped for speed)
|
||||
# 27, # (polar plots) takes very long / not working
|
||||
# 31, # animation (skipped for speed)
|
||||
# ],
|
||||
# eps=img_eps)
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
# InspectDR returns that error on travis:
|
||||
# ERROR: LoadError: InitError: Cannot open display:
|
||||
# in Gtk.GLib.GError(::Gtk.##229#230) at /home/travis/.julia/v0.5/Gtk/src/GLib/gerror.jl:17
|
||||
|
||||
# facts("InspectDR") do
|
||||
# @fact inspectdr() --> Plots.InspectDRBackend()
|
||||
# @fact backend() --> Plots.InspectDRBackend()
|
||||
#
|
||||
# image_comparison_facts(:inspectdr,
|
||||
# skip=[
|
||||
# 2, # animation
|
||||
# 6, # heatmap not defined
|
||||
# 10, # heatmap not defined
|
||||
# 22, # contour not defined
|
||||
# 23, # pie not defined
|
||||
# 27, # polar plot not working
|
||||
# 28, # heatmap not defined
|
||||
# 31, # animation
|
||||
# ],
|
||||
# eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
# facts("Plotly") do
|
||||
# @fact plotly() --> Plots.PlotlyBackend()
|
||||
# @fact backend() --> Plots.PlotlyBackend()
|
||||
#
|
||||
# # # until png generation is reliable on OSX, just test on linux
|
||||
# # @static is_linux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
# facts("Immerse") do
|
||||
# @fact immerse() --> Plots.ImmerseBackend()
|
||||
# @fact backend() --> Plots.ImmerseBackend()
|
||||
@@ -67,7 +110,7 @@ facts("UnicodePlots") do
|
||||
@fact backend() --> Plots.UnicodePlotsBackend()
|
||||
|
||||
# lets just make sure it runs without error
|
||||
@fact isa(plot(rand(10)), Plot) --> true
|
||||
@fact isa(plot(rand(10)), Plots.Plot) --> true
|
||||
end
|
||||
|
||||
|
||||
@@ -75,15 +118,15 @@ end
|
||||
facts("Axes") do
|
||||
p = plot()
|
||||
axis = p.subplots[1][:xaxis]
|
||||
@fact typeof(axis) --> Axis
|
||||
@fact typeof(axis) --> Plots.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 Plots.ignoreNaN_extrema(axis) --> (0.5,1.5)
|
||||
@fact axis[:discrete_map] --> Dict{Any,Any}(:yo => 2, "HI" => 1)
|
||||
|
||||
Plots.discrete_value!(axis, ["x$i" for i=1:5])
|
||||
Plots.discrete_value!(axis, ["x$i" for i=0:2])
|
||||
@fact extrema(axis) --> (0.5, 7.5)
|
||||
@fact Plots.ignoreNaN_extrema(axis) --> (0.5, 7.5)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Pkg.clone("ImageMagick")
|
||||
# Pkg.build("ImageMagick")
|
||||
Pkg.clone("ImageMagick")
|
||||
Pkg.build("ImageMagick")
|
||||
|
||||
# Pkg.clone("GR")
|
||||
# Pkg.build("GR")
|
||||
Pkg.clone("GR")
|
||||
Pkg.build("GR")
|
||||
|
||||
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
|
||||
|
||||
@@ -11,11 +11,12 @@ Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
|
||||
Pkg.clone("StatPlots")
|
||||
Pkg.checkout("PlotUtils")
|
||||
|
||||
# Pkg.clone("https://github.com/JunoLab/Blink.jl.git")
|
||||
# Pkg.clone("Blink")
|
||||
# Pkg.build("Blink")
|
||||
# import Blink
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")
|
||||
# Pkg.add("Rsvg")
|
||||
# Pkg.add("PlotlyJS")
|
||||
|
||||
# Pkg.checkout("RecipesBase")
|
||||
# Pkg.clone("VisualRegressionTests")
|
||||
@@ -25,4 +26,6 @@ ENV["PYTHON"] = ""
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.build("PyPlot")
|
||||
|
||||
# Pkg.add("InspectDR")
|
||||
|
||||
Pkg.test("Plots"; coverage=false)
|
||||
|
||||
Reference in New Issue
Block a user