Compare commits
341 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42c3827ded | |||
| 26e4b4efda | |||
| 2169ab3bb8 | |||
| 85dad8db88 | |||
| 8120c34064 | |||
| 1f1d1f70a1 | |||
| 373d868a17 | |||
| 834ba9bc61 | |||
| 3ffec5aed9 | |||
| ef20c1d684 | |||
| 697a59568c | |||
| e58fb935b0 | |||
| 044d23f8a5 | |||
| 1604d867c3 | |||
| 011db481c3 | |||
| c404cdc38a | |||
| 20689af7dd | |||
| 9ea0585d71 | |||
| 614c5f694b | |||
| fa36aae068 | |||
| 3e85232310 | |||
| 894928d7ca | |||
| afb1486f86 | |||
| 2799bd18e1 | |||
| 43c2a885b9 | |||
| be7feb0eaa | |||
| e3d653a8d9 | |||
| b9f9d8160d | |||
| 141b5c604d | |||
| 8a0fc00435 | |||
| a3a8cb9368 | |||
| 96c66b33a2 | |||
| 62fad3724f | |||
| ca2b6c87ef | |||
| 20348645a3 | |||
| a9b39fad64 | |||
| 1c556ade57 | |||
| e08957f5ec | |||
| e76ded69e6 | |||
| 7e740861d8 | |||
| f28e65a26c | |||
| a29f0c820b | |||
| b2bc30cfed | |||
| 6ce235bb68 | |||
| 479ab989df | |||
| 8d490b9e52 | |||
| 949f11a8c9 | |||
| d4896e1978 | |||
| c4020080b3 | |||
| 5a68003d16 | |||
| 9fcc3cdc24 | |||
| ee80ce5ae6 | |||
| 456c4019c4 | |||
| f65627a505 | |||
| 38d1fc15fe | |||
| 5eead5b0fa | |||
| 4223bd57f1 | |||
| 5bfa653858 | |||
| 5342764caf | |||
| 04a44a82c9 | |||
| c28820f39c | |||
| 546955a78b | |||
| d1e9cc5800 | |||
| 4ef2fb9933 | |||
| d7bc8adf21 | |||
| b5f44efc69 | |||
| 5e8abea222 | |||
| f291454ccb | |||
| bac6637b46 | |||
| 866ac9fb72 | |||
| 8208f6947f | |||
| dcd50a7c62 | |||
| 781aa86568 | |||
| 3facc116c8 | |||
| 9178c3b087 | |||
| 5c6c85a22f | |||
| b8adb4d229 | |||
| 093118da8b | |||
| 0aa760119f | |||
| 60b3a1583d | |||
| 593c47b938 | |||
| 0f6bb7ce6f | |||
| 048c2541e6 | |||
| f3e7d1a43b | |||
| 5df24d6683 | |||
| 73779898b7 | |||
| 7200b6adc6 | |||
| d48f15ab4d | |||
| 2747671935 | |||
| d86f24dd35 | |||
| 60edccade5 | |||
| 540460cae9 | |||
| df44d86f46 | |||
| c287b446c9 | |||
| 1db36c05a8 | |||
| 2c884ab5ab | |||
| cd0dfc956d | |||
| 03392a5f03 | |||
| a7da999bd7 | |||
| 47f21b1652 | |||
| 7c91d10c79 | |||
| fac2f5494b | |||
| bfcdb7e840 | |||
| 21d55db3b4 | |||
| 18e49888fe | |||
| 7ba2f61361 | |||
| 407df158ef | |||
| eab46ba9c4 | |||
| 39318e5e61 | |||
| ac1ce03fe5 | |||
| 4fa697ec75 | |||
| 027961bffa | |||
| 7dfcbabe0c | |||
| dfc7658426 | |||
| 04c6726b76 | |||
| 8fdbbe4c16 | |||
| 09fceab75b | |||
| 8df4c27285 | |||
| e38820b74a | |||
| c052e731f8 | |||
| 3636860262 | |||
| ce7421bef4 | |||
| 23b51c4c3e | |||
| 48d802fc40 | |||
| 51e8f3ff6e | |||
| 7917397bf3 | |||
| e0c0052b63 | |||
| 15a4b50ce4 | |||
| a51a8abf27 | |||
| 45747e3635 | |||
| 64b8e15918 | |||
| f4b716b255 | |||
| 1115ff83bb | |||
| c24550c756 | |||
| b5cdfe30ea | |||
| a10e594da1 | |||
| 63aee387f0 | |||
| ab9de1e199 | |||
| e60c463328 | |||
| 537d74196b | |||
| a8bbbe8582 | |||
| 29471a7aac | |||
| 894a4a5567 | |||
| f7cd5276f0 | |||
| 95b8c71083 | |||
| 52c82e6fe2 | |||
| cef1abd6aa | |||
| 91da261128 | |||
| 1c70346a61 | |||
| 2de017086d | |||
| d6253c72d4 | |||
| e5007b6c87 | |||
| bbc18549b3 | |||
| b6855b6fba | |||
| 70db97d576 | |||
| f632f2f39e | |||
| b23f968d57 | |||
| ea88877a11 | |||
| 1de3decede | |||
| 78045fd2ab | |||
| 7c8898158b | |||
| 3fc5f87388 | |||
| 623ca1effc | |||
| da05e7fe5e | |||
| f3734d4c64 | |||
| 9ab458e109 | |||
| d4c72e6f78 | |||
| 1f58ef5140 | |||
| 69d574c587 | |||
| a027fdded7 | |||
| 443df0501d | |||
| d68fe5f80d | |||
| ebf868bae3 | |||
| 8a9b3286e9 | |||
| a0455561b6 | |||
| 518ccf5a74 | |||
| 54f09c58cb | |||
| 029ad1045a | |||
| 2f55fb4d84 | |||
| add724eee9 | |||
| 07324ae892 | |||
| ffc05e971f | |||
| 30f42fd1d4 | |||
| f284857438 | |||
| 06ab514472 | |||
| 85b65acbe0 | |||
| 4d1d9de22f | |||
| a68ec9b191 | |||
| e386636d66 | |||
| cc3311b8eb | |||
| d3d1181769 | |||
| 001abf684c | |||
| 92a65296b5 | |||
| 3a7887d003 | |||
| 3334c4a87a | |||
| 228ce768e0 | |||
| eafc1a784c | |||
| fd3a4d0a0e | |||
| 77d679b63b | |||
| 730d764c6e | |||
| 6f1ef82e89 | |||
| d4da910f19 | |||
| a0a0773bd2 | |||
| 9ce8d138d5 | |||
| 0041b034e0 | |||
| d781c437de | |||
| 2fe21b8547 | |||
| dbc90c9417 | |||
| 431d60a58a | |||
| fcfcc9a94a | |||
| 870fb4e2df | |||
| e3612b973f | |||
| 75f4ca928f | |||
| 22fd25b30f | |||
| 610ddb09ff | |||
| 33602aca11 | |||
| a207856e99 | |||
| 80e8b5b5e4 | |||
| 273996aa91 | |||
| 646b88c5be | |||
| 88cfd82a25 | |||
| 2f8436be87 | |||
| a6237cdda3 | |||
| eb93a767fc | |||
| 41852412ca | |||
| 224b2b7323 | |||
| ec939f9f9a | |||
| eced15b712 | |||
| f8fb473981 | |||
| f2bcb77cfb | |||
| dc5da28176 | |||
| be43e64743 | |||
| 454630dace | |||
| c9df244333 | |||
| 2649d97d69 | |||
| bcb9ac5501 | |||
| 76d1e26f7d | |||
| ba13ab9926 | |||
| c43b34b67f | |||
| 3739c2881b | |||
| bc13b81351 | |||
| 86a56e487a | |||
| 031058cd0c | |||
| 85040d6104 | |||
| efbf74b44c | |||
| 774fc42e8e | |||
| 692cfe4f52 | |||
| 8b34d4417b | |||
| c20a7b3c69 | |||
| ff6e83c55e | |||
| 6aa56fd30f | |||
| f5a6f129dd | |||
| 5405c1d240 | |||
| 455f85e230 | |||
| fa899a67a7 | |||
| 56cf25eebf | |||
| 1a490551e6 | |||
| 4514802e35 | |||
| a9062b0611 | |||
| eb6b30aab1 | |||
| 922abd7ad3 | |||
| c4596dda75 | |||
| 168b77326b | |||
| f674bba861 | |||
| 6b2e65e738 | |||
| 51021c799d | |||
| 01cb036976 | |||
| ea7867429f | |||
| c5300cd7be | |||
| ea34c7d226 | |||
| 537e04989f | |||
| d447c5dc25 | |||
| 73944fc9bf | |||
| 01e994e0f1 | |||
| af3f3f436c | |||
| 56e90a266a | |||
| 1fc59ed522 | |||
| 3a3b1a6c17 | |||
| 242ce807df | |||
| 734967152c | |||
| 8023e15bb9 | |||
| ceb58a6fe1 | |||
| ae48bd1abb | |||
| d27fe2fbad | |||
| 970686efaf | |||
| 8df46ec0e9 | |||
| 864057a6ed | |||
| 92ca4cb1d1 | |||
| aabf811fe7 | |||
| 9b0a645a05 | |||
| 24b5e7ff09 | |||
| 48969ecee3 | |||
| 6ae0912e0e | |||
| e8062d544d | |||
| 07332aa4ac | |||
| 1e2b3559f2 | |||
| 8bfaa14edb | |||
| d4e87d3fe8 | |||
| 460348c0b6 | |||
| 264e8c735e | |||
| c4b04e2327 | |||
| dc1389bd1a | |||
| 51e38c80d8 | |||
| a33f5fabd1 | |||
| e6c8344970 | |||
| 2207ae6617 | |||
| cdc6707ad3 | |||
| 448fdaecbd | |||
| 4aa79aed6f | |||
| c8fcc373f8 | |||
| 1410141b46 | |||
| 76efabb5f3 | |||
| 36effc6dc4 | |||
| 8d5412a38f | |||
| 465b5d20b4 | |||
| 60a22b1897 | |||
| 09a685e977 | |||
| 31da90865c | |||
| 9ea98bcb56 | |||
| 4f9804d24e | |||
| 0a88518188 | |||
| ccae6373be | |||
| 31e5d07a45 | |||
| 056fee4b28 | |||
| 5a47864d73 | |||
| 1c7da432f1 | |||
| 3d3ceacaab | |||
| 35df39a44b | |||
| c23d6d62e7 | |||
| 243d4a7ac3 | |||
| 50254d06d7 | |||
| 475b46f516 | |||
| f4573f7e87 | |||
| 7fb1a4ec0f | |||
| 4299aa0ff8 | |||
| 4eb7e3810e | |||
| 3e7e7e9ac0 | |||
| 9e1058ec0a | |||
| 426a9eb6d3 | |||
| f322b9ac1c | |||
| e13fec08ca |
@@ -1,3 +1,6 @@
|
||||
*.jl.cov
|
||||
*.jl.*.cov
|
||||
*.jl.mem
|
||||
.DS_Store
|
||||
examples/.ipynb_checkpoints/*
|
||||
examples/meetup/.ipynb_checkpoints/*
|
||||
|
||||
@@ -5,9 +5,16 @@ os:
|
||||
- osx
|
||||
julia:
|
||||
- 0.4
|
||||
#- nightly
|
||||
notifications:
|
||||
email: false
|
||||
email: true
|
||||
# uncomment the following lines to override the default test script
|
||||
script:
|
||||
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
||||
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots"); Pkg.add("Gadfly"); Pkg.test("Plots"; coverage=true)'
|
||||
- julia -e 'Pkg.clone("https://github.com/tbreloff/Images.jl.git"); Pkg.checkout("Images","tom_imgcompare");'
|
||||
- julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
|
||||
- julia -e 'Pkg.clone("Cairo"); Pkg.build("Cairo")'
|
||||
- julia -e 'ENV["PYTHON"] = ""; Pkg.clone("PyPlot"); Pkg.build("PyPlot")'
|
||||
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots")'
|
||||
- julia -e 'Pkg.test("Plots"; coverage=false)'
|
||||
# - julia -e 'cd(Pkg.dir("Plots")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(process_folder())'
|
||||
|
||||
@@ -1,224 +1,406 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/tbreloff/Plots.jl)
|
||||
[](http://pkg.julialang.org/?pkg=Plots&ver=0.3)
|
||||
[](http://pkg.julialang.org/?pkg=Plots&ver=0.4)
|
||||
<!-- [](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
|
||||
<!-- [](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
|
||||
|
||||
Plotting interface and wrapper for several plotting packages.
|
||||
#### Author: Thomas Breloff (@tbreloff)
|
||||
|
||||
Plots is a plotting API and toolset. My goals with the package are:
|
||||
|
||||
- **Intuitive**. Start generating complex plots without reading volumes of documentation. Commands should "just work".
|
||||
- **Concise**. Less code means fewer mistakes and more efficient development/analysis.
|
||||
- **Flexible**. Produce your favorite plots from your favorite package, but quicker and simpler.
|
||||
- **Consistent**. Don't commit to one graphics package. Use the same code and access the strengths of all backends.
|
||||
- **Lightweight**. Very few dependencies, since backends are loaded and initialized dynamically.
|
||||
|
||||
Use the preprocessing pipeline in Plots to fully describe your visualization before it calls the backend code. This maintains modularity and allows for efficient separation of front end code, algorithms, and backend graphics. New graphical backends can be added with minimal effort.
|
||||
|
||||
Check out the [summary graphs](https://github.com/tbreloff/ExamplePlots.jl/tree/master/img/supported/supported.md) for the features that each backend supports.
|
||||
|
||||
Please add wishlist items, bugs, or any other comments/questions to the issues list.
|
||||
|
||||
## Examples
|
||||
## Examples for each implemented backend:
|
||||
|
||||
- [Qwt.jl](docs/qwt_examples.md)
|
||||
- [Gadfly.jl](docs/gadfly_examples.md)
|
||||
- [Gadfly.jl/Immerse.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/gadfly_examples.md)
|
||||
- [PyPlot.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/pyplot_examples.md)
|
||||
- [UnicodePlots.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/unicodeplots_examples.md)
|
||||
- [Qwt.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/qwt_examples.md)
|
||||
|
||||
Also check out the many [IJulia notebooks](http://nbviewer.ipython.org/github/tbreloff/ExamplePlots.jl/tree/master/examples/) with many examples.
|
||||
|
||||
## Installation
|
||||
|
||||
First, clone the package, and get any plotting packages you need:
|
||||
First, add the package
|
||||
|
||||
```julia
|
||||
Pkg.add("Plots")
|
||||
|
||||
# if you want the latest features:
|
||||
Pkg.checkout("Plots")
|
||||
|
||||
# or for the bleeding edge:
|
||||
Pkg.checkout("Plots", "dev")
|
||||
```
|
||||
Pkg.clone("https://github.com/tbreloff/Plots.jl.git")
|
||||
Pkg.add("Gadfly") # [optional]
|
||||
Pkg.clone("https://github.com/tbreloff/Qwt.jl.git") # [optional] requires pyqt and pyqwt
|
||||
|
||||
then get any plotting packages you need (obviously, you should get at least one backend).
|
||||
|
||||
```julia
|
||||
Pkg.add("Gadfly")
|
||||
Pkg.add("Immerse")
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.add("UnicodePlots")
|
||||
Pkg.clone("https://github.com/tbreloff/Qwt.jl.git")
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
Load it in. The underlying plotting backends are not imported until `plotter()` is called (which happens
|
||||
on your first call to `plot`). This means that you don't need any backends to be installed when you call `using Plots`.
|
||||
For now, the default backend is Gadfly.
|
||||
Load it in. The underlying plotting backends are not imported until `backend()` is called (which happens
|
||||
on your first call to `plot` or `subplot`). This means that you don't need any backends to be installed when you call `using Plots`.
|
||||
|
||||
```
|
||||
Plots will try to figure out a good default backend for you automatically based on what backends are installed.
|
||||
|
||||
```julia
|
||||
using Plots
|
||||
```
|
||||
|
||||
Do a plot in Gadfly, then save a png:
|
||||
Do a plot in Gadfly (inspired by [this example](http://gadflyjl.org/geom_point.html)), then save a png:
|
||||
|
||||
```
|
||||
plot(rand(10,2); marker = :rect)
|
||||
savepng(Plots.IMG_DIR * "gadfly1.png")
|
||||
```
|
||||
```julia
|
||||
gadfly() # switch to Gadfly as a backend
|
||||
dataframes() # turn on support for DataFrames inputs
|
||||
|
||||
which saves:
|
||||
# load some data
|
||||
using RDatasets
|
||||
iris = dataset("datasets", "iris");
|
||||
|
||||
# This will bring up a browser window with the plot. Add a semicolon at the end to skip display.
|
||||
scatter(iris, :SepalLength, :SepalWidth, group=:Species, m=([:+ :d :s], 12), smooth=0.99, bg=:black)
|
||||
|
||||
# save a png (equivalent to png("gadfly1.png") and savefig("gadfly1.png"))
|
||||
png("gadfly1")
|
||||
```
|
||||
|
||||

|
||||
|
||||
## API
|
||||
|
||||
Do a plot in Qwt, then save a png:
|
||||
Call `backend(backend::Symbol)` or the shorthands (`gadfly()`, `qwt()`, `unicodeplots()`, etc) to set the current plotting backend.
|
||||
Subsequent commands are converted into the relevant plotting commands for that package:
|
||||
|
||||
```
|
||||
plotter!(:qwt) # switches the backend to Qwt
|
||||
plot(rand(10,2); marker = :rect)
|
||||
savepng(Plots.IMG_DIR * "qwt1.png")
|
||||
```
|
||||
|
||||
which saves:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## plot and plotter! interface (WIP)
|
||||
|
||||
The main plot command. Call `plotter!(:module)` to set the current plotting backend.
|
||||
Commands are converted into the relevant plotting commands for that package:
|
||||
|
||||
```
|
||||
plotter!(:gadfly)
|
||||
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
|
||||
plotter!(:qwt)
|
||||
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
|
||||
```julia
|
||||
gadfly()
|
||||
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
|
||||
qwt()
|
||||
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
|
||||
```
|
||||
|
||||
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
|
||||
|
||||
```
|
||||
plot(args...; kw...) # creates a new plot window, and sets it to be the `currentPlot`
|
||||
plot!(args...; kw...) # adds to the `currentPlot`
|
||||
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
|
||||
```julia
|
||||
plot(args...; kw...) # creates a new plot window, and sets it to be the `current`
|
||||
plot!(args...; kw...) # adds to the `current`
|
||||
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
|
||||
```
|
||||
|
||||
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
|
||||
Here are some various args to supply, and the implicit mapping (AVec == AbstractVector and AMat == AbstractMatrix):
|
||||
There are many ways to pass in data to the plot functions... some examples:
|
||||
|
||||
```
|
||||
plot(y::AVec; kw...) # one line... x = 1:length(y)
|
||||
plot(x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
|
||||
plot(y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
|
||||
plot(x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
|
||||
plot(x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
|
||||
plot(x::AVec, f::Function; kw...) # one line, y = f(x)
|
||||
plot(x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
|
||||
plot(x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
|
||||
plot(y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
|
||||
plot(x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
|
||||
plot(x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
|
||||
plot(n::Integer; kw...) # n lines, all empty (for updating plots)
|
||||
- Vector-like (subtypes of AbstractArray{T,1})
|
||||
- Matrix-like (subtypes of AbstractArray{T,2})
|
||||
- Vectors of Vectors
|
||||
- Functions
|
||||
- Vectors of Functions
|
||||
- DataFrames with column symbols (initialize with `dataframes()`)
|
||||
|
||||
# TODO: how do we handle NA values in dataframes?
|
||||
plot(df::DataFrame; kw...) # one line per DataFrame column, labels == names(df)
|
||||
plot(df::DataFrame, columns; kw...) # one line per column, but on a subset of column names
|
||||
In general, you can pass in a `y` only, or an `x` and `y`, both of whatever type(s) you want, and Plots will slice up the data as needed.
|
||||
For matrices, data is split by columns. For functions, data is mapped. For DataFrames, a Symbol/Symbols in place of x/y will map to
|
||||
the relevant column(s).
|
||||
|
||||
Here are some example usages... remember you can always use `plot!` to update an existing plot, and that, unless specified, you will update the `current()`.
|
||||
|
||||
```julia
|
||||
plot() # empty plot object
|
||||
plot(4) # initialize with 4 empty series
|
||||
plot(rand(10)) # plot 1 series... x = 1:10
|
||||
plot(rand(10,5)) # plot 5 series... x = 1:10
|
||||
plot(rand(10), rand(10)) # plot 1 series
|
||||
plot(rand(10,5), rand(10)) # plot 5 series... y is the same for all
|
||||
plot(sin, rand(10)) # y = sin(x)
|
||||
plot(rand(10), sin) # same... y = sin(x)
|
||||
plot([sin,cos], 0:0.1:π) # plot 2 series, sin(x) and cos(x)
|
||||
plot([sin,cos], 0, π) # plot sin and cos on the range [0, π]
|
||||
plot(1:10, Any[rand(10), sin]) # plot 2 series, y = rand(10) for the first, y = sin(x) for the second... x = 1:10 for both
|
||||
plot(dataset("Ecdat", "Airline"), :Cost) # plot from a DataFrame (call `dataframes()` first to import DataFrames and initialize)
|
||||
```
|
||||
|
||||
[TODO] You can swap out `plot` for `subplot`. Each line will go into a separate plot. Use the layout keyword:
|
||||
All plot methods accept a number of keyword arguments (see the tables below), which follow some rules:
|
||||
- Many arguments have aliases which are replaced during preprocessing. `c` is the same as `color`, `m` is the same as `marker`, etc. You can choose how verbose you'd like to be. (see the tables below)
|
||||
- There are some special arguments (`xaxis`, `yaxis`, `line`, `marker`, `fill` and the aliases `l`, `m`, `f`) which magically set many related things at once. (see the __Tip__ below)
|
||||
- If the argument is a "matrix-type", then each column will map to a series, cycling through columns if there are fewer columns than series. Anything else will apply the argument value to every series.
|
||||
- Many arguments accept many different types... for example the `color` (also `markercolor`, `fillcolor`, etc) argument will accept strings or symbols with a color name, or any `Colors.Colorant`, or a `ColorScheme`, or a symbol representing a `ColorGradient`, or an AbstractVector of colors/symbols/etc...
|
||||
|
||||
You can update certain plot settings after plot creation (not supported on all backends):
|
||||
|
||||
```julia
|
||||
plot!(title = "New Title", xlabel = "New xlabel", ylabel = "New ylabel")
|
||||
plot!(xlims = (0, 5.5), ylims = (-2.2, 6), xticks = 0:0.5:10, yticks = [0,1,5,10])
|
||||
|
||||
# using shorthands:
|
||||
xaxis!("mylabel", :log10, :flip)
|
||||
```
|
||||
y = rand(100,3)
|
||||
subplot(y; layout=(2,2), kw...) # creates 3 lines going into 3 separate plots, laid out on a 2x2 grid (last row is filled with plot #3)
|
||||
subplot(y; layout=(1,3), kw...) # again 3 plots, all in the same row
|
||||
subplot(y; layout=[1,[2,3]]) # pass a nested Array to fully specify the layout. here the first plot will take up the first row,
|
||||
# and the others will share the second row
|
||||
|
||||
With `subplot`, create multiple plots at once, with flexible layout options:
|
||||
|
||||
```julia
|
||||
y = rand(100,3)
|
||||
subplot(y; n = 3) # create an automatic grid, and let it figure out the shape
|
||||
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows
|
||||
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns
|
||||
subplot(y; layout = [1, 2]) # explicit layout. Lists the number of plots in each row
|
||||
```
|
||||
|
||||
__Tip__: You can call `subplot!(args...; kw...)` to add to an existing subplot.
|
||||
|
||||
__Tip__: Calling `subplot!` on a `Plot` object, or `plot!` on a `Subplot` object will throw an error.
|
||||
|
||||
Shorthands:
|
||||
|
||||
```
|
||||
scatter(args...; kw...) = plot(args...; kw..., linetype = :none, marker = :hexagon)
|
||||
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :none, marker = :hexagon)
|
||||
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
|
||||
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
|
||||
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
|
||||
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
|
||||
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
|
||||
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
|
||||
```julia
|
||||
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
|
||||
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
|
||||
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
|
||||
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
|
||||
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
|
||||
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
|
||||
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
|
||||
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
|
||||
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
|
||||
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
|
||||
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
|
||||
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
|
||||
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
|
||||
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
|
||||
|
||||
title!(s::AbstractString) = plot!(title = s)
|
||||
xlabel!(s::AbstractString) = plot!(xlabel = s)
|
||||
ylabel!(s::AbstractString) = plot!(ylabel = s)
|
||||
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(xlims = lims)
|
||||
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(ylims = lims)
|
||||
xticks!{T<:Real}(v::AVec{T}) = plot!(xticks = v)
|
||||
yticks!{T<:Real}(v::AVec{T}) = plot!(yticks = v)
|
||||
xflip!(flip::Bool = true) = plot!(xflip = flip)
|
||||
yflip!(flip::Bool = true) = plot!(yflip = flip)
|
||||
xaxis!(args...) = plot!(xaxis = args)
|
||||
yaxis!(args...) = plot!(yaxis = args)
|
||||
annotate!(anns) = plot!(annotation = anns)
|
||||
```
|
||||
|
||||
Some keyword arguments you can set:
|
||||
### Keyword arguments:
|
||||
|
||||
Keyword | Default | Type | Aliases
|
||||
---- | ---- | ---- | ----
|
||||
`:annotation` | `nothing` | Series | `:ann`, `:annotate`, `:annotations`, `:anns`
|
||||
`:axis` | `left` | Series | `:axiss`
|
||||
`:background_color` | `RGB{U8}(1.0,1.0,1.0)` | Plot | `:background`, `:bg`, `:bg_color`, `:bgcolor`
|
||||
`:color` | `auto` | Series | `:c`, `:colors`
|
||||
`:color_palette` | `auto` | Plot | `:palette`
|
||||
`:fill` | `nothing` | Series | `:area`, `:f`
|
||||
`:fillcolor` | `match` | Series | `:fc`, `:fcolor`, `:fillcolors`
|
||||
`:fillopacity` | `nothing` | Series | `:fillopacitys`, `:fo`
|
||||
`:fillrange` | `nothing` | Series | `:fillranges`, `:fillrng`
|
||||
`:foreground_color` | `auto` | Plot | `:fg`, `:fg_color`, `:fgcolor`, `:foreground`
|
||||
`:grid` | `true` | Plot |
|
||||
`:group` | `nothing` | Series | `:g`, `:groups`
|
||||
`:guidefont` | `Plots.Font("Helvetica",11,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
|
||||
`:label` | `AUTO` | Series | `:lab`, `:labels`
|
||||
`:layout` | `nothing` | Plot |
|
||||
`:legend` | `true` | Plot | `:leg`
|
||||
`:legendfont` | `Plots.Font("Helvetica",8,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
|
||||
`:line` | `nothing` | Series | `:l`
|
||||
`:lineopacity` | `nothing` | Series | `:lineopacitys`, `:lo`
|
||||
`:linestyle` | `solid` | Series | `:linestyles`, `:ls`, `:s`, `:style`
|
||||
`:linetype` | `path` | Series | `:linetypes`, `:lt`, `:t`, `:type`
|
||||
`:linewidth` | `1` | Series | `:linewidths`, `:lw`, `:w`, `:width`
|
||||
`:link` | `false` | Plot |
|
||||
`:linkfunc` | `nothing` | Plot |
|
||||
`:linkx` | `false` | Plot | `:xlink`
|
||||
`:linky` | `false` | Plot | `:ylink`
|
||||
`:marker` | `nothing` | Series | `:m`, `:mark`
|
||||
`:markercolor` | `match` | Series | `:markercolors`, `:mc`, `:mcolor`
|
||||
`:markeropacity` | `nothing` | Series | `:alpha`, `:markeropacitys`, `:mo`, `:opacity`
|
||||
`:markershape` | `none` | Series | `:markershapes`, `:shape`
|
||||
`:markersize` | `6` | Series | `:markersizes`, `:ms`, `:msize`
|
||||
`:n` | `-1` | Plot |
|
||||
`:nbins` | `100` | Series | `:nb`, `:nbin`, `:nbinss`
|
||||
`:nc` | `-1` | Plot |
|
||||
`:nr` | `-1` | Plot |
|
||||
`:pos` | `(0,0)` | Plot |
|
||||
`:show` | `false` | Plot | `:display`, `:gui`
|
||||
`:size` | `(500,300)` | Plot | `:windowsize`, `:wsize`
|
||||
`:smooth` | `false` | Series | `:reg`, `:regression`, `:smooths`
|
||||
`:tickfont` | `Plots.Font("Helvetica",8,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
|
||||
`:title` | `` | Plot |
|
||||
`:windowtitle` | `Plots.jl` | Plot | `:wtitle`
|
||||
`:xaxis` | `nothing` | Plot |
|
||||
`:xflip` | `false` | Plot |
|
||||
`:xlabel` | `` | Plot | `:xlab`
|
||||
`:xlims` | `auto` | Plot | `:xlim`, `:xlimit`, `:xlimits`
|
||||
`:xscale` | `identity` | Plot |
|
||||
`:xticks` | `auto` | Plot | `:xtick`
|
||||
`:yaxis` | `nothing` | Plot |
|
||||
`:yflip` | `false` | Plot |
|
||||
`:ylabel` | `` | Plot | `:ylab`
|
||||
`:ylims` | `auto` | Plot | `:ylim`, `:ylimit`, `:ylimits`
|
||||
`:yrightlabel` | `` | Plot | `:y2lab`, `:y2label`, `:ylab2`, `:ylabel2`, `:ylabelright`, `:ylabr`, `:yrlab`
|
||||
`:yscale` | `identity` | Plot |
|
||||
`:yticks` | `auto` | Plot | `:ytick`
|
||||
`:z` | `nothing` | Series | `:zs`
|
||||
|
||||
|
||||
### Plot types:
|
||||
|
||||
Type | Desc | Aliases
|
||||
---- | ---- | ----
|
||||
`:none` | No line | `:n`, `:no`
|
||||
`:line` | Lines with sorted x-axis | `:l`
|
||||
`:path` | Lines | `:p`
|
||||
`:steppre` | Step plot (vertical then horizontal) | `:stepinv`, `:stepinverted`, `:stepsinv`, `:stepsinverted`
|
||||
`:steppost` | Step plot (horizontal then vertical) | `:stair`, `:stairs`, `:step`, `:steps`
|
||||
`:sticks` | Vertical lines | `:stem`, `:stems`
|
||||
`:scatter` | Points, no lines | `:dots`
|
||||
`:heatmap` | Colored regions by density |
|
||||
`:hexbin` | Similar to heatmap |
|
||||
`:hist` | Histogram (doesn't use x) | `:histogram`
|
||||
`:bar` | Bar plot (centered on x values) |
|
||||
`:hline` | Horizontal line (doesn't use x) |
|
||||
`:vline` | Vertical line (doesn't use x) |
|
||||
`:ohlc` | Open/High/Low/Close chart (expects y is AbstractVector{Plots.OHLC}) |
|
||||
|
||||
|
||||
### Line styles:
|
||||
|
||||
Type | Aliases
|
||||
---- | ----
|
||||
`:auto` | `:a`
|
||||
`:solid` | `:s`
|
||||
`:dash` | `:d`
|
||||
`:dot` |
|
||||
`:dashdot` | `:dd`
|
||||
`:dashdotdot` | `:ddd`
|
||||
|
||||
|
||||
### Markers:
|
||||
|
||||
Type | Aliases
|
||||
---- | ----
|
||||
`:none` | `:n`, `:no`
|
||||
`:auto` | `:a`
|
||||
`:cross` | `:+`, `:plus`
|
||||
`:diamond` | `:d`
|
||||
`:dtriangle` | `:V`, `:downtri`, `:downtriangle`, `:dt`, `:dtri`, `:v`
|
||||
`:ellipse` | `:c`, `:circle`
|
||||
`:heptagon` | `:hep`
|
||||
`:hexagon` | `:h`, `:hex`
|
||||
`:octagon` | `:o`, `:oct`
|
||||
`:pentagon` | `:p`, `:pent`
|
||||
`:rect` | `:r`, `:sq`, `:square`
|
||||
`:star4` |
|
||||
`:star5` | `:s`, `:star`, `:star1`
|
||||
`:star6` |
|
||||
`:star7` |
|
||||
`:star8` | `:s2`, `:star2`
|
||||
`:utriangle` | `:^`, `:uptri`, `:uptriangle`, `:ut`, `:utri`
|
||||
`:xcross` | `:X`, `:x`
|
||||
|
||||
|
||||
__Tip__: With supported backends, you can pass a `Plots.Shape` object for the `marker`/`markershape` arguments. `Shape` takes a vector of 2-tuples in the constructor, defining the points of the polygon's shape in a unit-scaled coordinate space. To make a square, for example, you could do `Shape([(1,1),(1,-1),(-1,-1),(-1,1)])`
|
||||
|
||||
__Tip__: You can see the default value for a given argument with `default(arg::Symbol)`, and set the default value with `default(arg::Symbol, value)` or `default(; kw...)`. For example set the default window size and whether we should show a legend with `default(size=(600,400), leg=false)`.
|
||||
|
||||
__Tip__: There are some helper arguments you can set: `xaxis`, `yaxis`, `line`, `marker`, `fill`. These go through special preprocessing to extract values into individual arguments. The order doesn't matter, and if you pass a single value it's equivalent to wrapping it in a Tuple. Examples:
|
||||
|
||||
```
|
||||
axis # :left or :right
|
||||
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
|
||||
label # string or symbol, applies to that line, may go in a legend
|
||||
width # width of a line
|
||||
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar
|
||||
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
|
||||
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
|
||||
markercolor # same choices as `color`, or :match will set the color to be the same as `color`
|
||||
markersize # size of the marker
|
||||
nbins # number of bins for heatmap/hexbin and histograms
|
||||
heatmap_c # color cutoffs for Qwt heatmaps
|
||||
fillto # fillto value for area plots
|
||||
title # string or symbol, title of the plot
|
||||
xlabel # string or symbol, label on the bottom (x) axis
|
||||
ylabel # string or symbol, label on the left (y) axis
|
||||
yrightlabel # string or symbol, label on the right (y) axis
|
||||
reg # true or false, add a regression line for each line
|
||||
size # (Int,Int), resize the enclosing window
|
||||
pos # (Int,Int), move the enclosing window to this position
|
||||
windowtitle # string or symbol, set the title of the enclosing windowtitle
|
||||
screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
||||
show # true or false, show the plot (in case you don't want the window to pop up right away)
|
||||
plot(y, xaxis = ("mylabel", :log, :flip, (-1,1))) # this sets the `xlabel`, `xscale`, `xflip`, and `xlims` arguments automatically
|
||||
plot(y, line = (:bar, :blue, :dot, 10)) # this sets the `linetype`, `color`, `linestyle`, and `linewidth` arguments automatically
|
||||
plot(y, marker = (:rect, :red, 10)) # this sets the `markershape`, `markercolor`, and `markersize` arguments automatically
|
||||
plot(y, fill = (:green, 10)) # this sets the `fillcolor` and `fillrange` arguments automatically
|
||||
# Note: `fillrange` can be:
|
||||
a number (fill to horizontal line)
|
||||
a vector of numbers (different for each data point)
|
||||
a tuple of vectors (fill a band)
|
||||
```
|
||||
|
||||
If you don't include a keyword argument, these are the defaults:
|
||||
|
||||
```
|
||||
axis = :left
|
||||
color = :auto
|
||||
label = automatically generated (y1, y2, ...., or y1 (R), y2 (R) for the right axis)
|
||||
width = 1
|
||||
linetype = :line
|
||||
linestype = :solid
|
||||
marker = :none
|
||||
markercolor = :match
|
||||
markersize = 3
|
||||
nbins = 100
|
||||
heatmap_c = (0.15, 0.5)
|
||||
fillto = nothing
|
||||
title = ""
|
||||
xlabel = ""
|
||||
ylabel = ""
|
||||
yrightlabel = ""
|
||||
reg = false
|
||||
size = (800,600)
|
||||
pos = (0,0)
|
||||
windowtitle = ""
|
||||
screen = 1
|
||||
show = true
|
||||
__Tip__: When plotting multiple lines, you can set all series to use the same value, or pass in a matrix to cycle through values. Example:
|
||||
|
||||
```julia
|
||||
plot(rand(100,4); color = [:red RGB(0,0,1)], # (Matrix) lines 1 and 3 are red, lines 2 and 4 are blue
|
||||
axis = :auto, # lines 1 and 3 are on the left axis, lines 2 and 4 are on the right
|
||||
markershape = [:rect, :star] # (Vector) ALL lines are passed the vector [:rect, :star1]
|
||||
width = 5) # all lines have a width of 5
|
||||
```
|
||||
|
||||
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
|
||||
(yes I know it's not gramatically correct, but it's easy to use and implement)
|
||||
__Tip__: Not all features are supported for each backend, but you can see what's supported by calling the functions: `supportedArgs()`, `supportedAxes()`, `supportedTypes()`, `supportedStyles()`, `supportedMarkers()`, `subplotSupported()`
|
||||
|
||||
```
|
||||
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
|
||||
```
|
||||
__Tip__: Call `gui()` to display the plot in a window. Interactivity depends on backend. Plotting at the REPL (without semicolon) implicitly calls `gui()`.
|
||||
|
||||
# TODO
|
||||
### Animations
|
||||
|
||||
Animations are created in 3 steps (see example #2):
|
||||
- Initialize an `Animation` object.
|
||||
- Save each frame of the animation with `frame(anim)`.
|
||||
- Convert the frames to an animated gif with `gif(anim, filename, fps=15)`
|
||||
|
||||
|
||||
## TODO features:
|
||||
|
||||
- [x] Plot vectors/matrices/functions
|
||||
- [ ] Plot DataFrames
|
||||
- [ ] Subplots
|
||||
- [ ] Histograms
|
||||
- [x] Plot DataFrames
|
||||
- [x] Histograms
|
||||
- [x] Grouping
|
||||
- [x] Annotations
|
||||
- [x] Scales
|
||||
- [x] Categorical Inputs (strings, etc... for hist, bar? or can split one series into multiple?)
|
||||
- [x] Custom markers
|
||||
- [x] Animations
|
||||
- [x] Subplots
|
||||
- [ ] Contours
|
||||
- [ ] Boxplots
|
||||
- [ ] 3D plotting
|
||||
- [ ] Scenes/Drawing
|
||||
- [ ] Graphs
|
||||
- [ ] Interactivity (GUIs)
|
||||
|
||||
## TODO backends:
|
||||
|
||||
- [x] Gadfly.jl
|
||||
- [ ] PyPlot.jl
|
||||
- [ ] Winston.jl
|
||||
- [ ] Gaston.jl
|
||||
- [ ] GLPlot.jl
|
||||
- [x] Immerse.jl
|
||||
- [x] PyPlot.jl
|
||||
- [x] UnicodePlots.jl
|
||||
- [x] Qwt.jl
|
||||
- [x] Winston.jl (deprecated)
|
||||
- [ ] GLPlot.jl
|
||||
- [ ] Bokeh.jl
|
||||
- [ ] Vega.jl
|
||||
- [ ] Gaston.jl
|
||||
- [ ] Plotly.jl
|
||||
- [ ] GoogleCharts.jl
|
||||
- [ ] Vega.jl
|
||||
- [ ] PLplot.jl
|
||||
- [ ] TextPlots.jl
|
||||
- [ ] ASCIIPlots.jl
|
||||
- [ ] Sparklines.jl
|
||||
- [ ] UnicodePlots.jl
|
||||
- [ ] Hinton.jl
|
||||
- [ ] ImageTerm.jl
|
||||
- [ ] GraphViz.jl
|
||||
- [ ] TikzGraphs.jl
|
||||
- [ ] GraphLayout.jl
|
||||
|
||||
# Backends
|
||||
## More information on backends (both supported and unsupported)
|
||||
|
||||
See the wiki at: https://github.com/JuliaPlot/juliaplot_docs/wiki
|
||||
|
||||
# Author
|
||||
|
||||
Thomas Breloff (@tbreloff)
|
||||
|
||||
|
||||
@@ -3,100 +3,223 @@ module PlotExamples
|
||||
|
||||
using Plots
|
||||
using Colors
|
||||
using Compat
|
||||
|
||||
const DOCDIR = Pkg.dir("Plots") * "/docs"
|
||||
const IMGDIR = Pkg.dir("Plots") * "/img"
|
||||
|
||||
doc"""
|
||||
"""
|
||||
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
|
||||
"""
|
||||
type PlotExample
|
||||
header::String
|
||||
desc::String
|
||||
header::@compat(AbstractString)
|
||||
desc::@compat(AbstractString)
|
||||
exprs::Vector{Expr}
|
||||
end
|
||||
|
||||
|
||||
|
||||
# the examples we'll run for each
|
||||
const examples = PlotExample[
|
||||
PlotExample("Lines",
|
||||
"A simple line plot of the 3 columns.",
|
||||
[:(plot(rand(100,3)))]),
|
||||
PlotExample("Functions",
|
||||
"Plot multiple functions",
|
||||
[:(plot(0:0.01:4π, [sin,cos]))]),
|
||||
"A simple line plot of the columns.",
|
||||
[
|
||||
:(plot(Plots.fakedata(50,5), w=3))
|
||||
]),
|
||||
PlotExample("Functions, adding data, and animations",
|
||||
"Plot multiple functions. You can also put the function first, or use the form `plot(f, xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data: `x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with `push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the animation.",
|
||||
[
|
||||
:(p = plot([sin,cos], zeros(0), leg=false)),
|
||||
:(anim = Animation()),
|
||||
:(for x in linspace(0, 10π, 200)
|
||||
push!(p, x, Float64[sin(x), cos(x)])
|
||||
frame(anim)
|
||||
end)
|
||||
]),
|
||||
PlotExample("Parametric plots",
|
||||
"Plot function pair (x(u), y(u)).",
|
||||
[
|
||||
:(plot(sin, x->sin(2x), 0, 2π, line=4, leg=false, fill=(0,:orange)))
|
||||
]),
|
||||
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.",
|
||||
[
|
||||
:(y = rand(100)),
|
||||
:(plot(0:10:100,rand(11,4),lab="lines",w=3, palette=:grays, fill=(0.5,:auto))),
|
||||
:(scatter!(y, z=abs(y-.5), m=(10,:heat), lab="grad"))
|
||||
]),
|
||||
PlotExample("Global",
|
||||
"Change the guides/background without a separate call.",
|
||||
[:(plot(rand(10); title="TITLE", xlabel="XLABEL", ylabel="YLABEL", background_color = RGB(0.5,0.5,0.5)))]),
|
||||
"Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow you to pass a tuple or value which will be mapped to the relevant args automatically. The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`, `yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`",
|
||||
[
|
||||
:(plot(rand(20,3), xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2), leg=false)),
|
||||
:(title!("TITLE")),
|
||||
:(yaxis!("YLABEL", :log10))
|
||||
]),
|
||||
PlotExample("Two-axis",
|
||||
"Use the `axis` or `axiss` arguments.\n\nNote: This is only supported with Qwt right now",
|
||||
[:(plot(Vector[randn(100), randn(100)*100]; axiss = [:left,:right]))]),
|
||||
PlotExample("Vectors w/ pluralized args",
|
||||
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).",
|
||||
[:(plot(Vector[rand(10), rand(20)]; marker=:ellipse, markersize=8, colors=[:red,:blue]))]),
|
||||
"Use the `axis` arguments.\n\nNote: Currently only supported with Qwt and PyPlot",
|
||||
[
|
||||
:(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT"))
|
||||
]),
|
||||
PlotExample("Arguments",
|
||||
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (marker/markersize) with arguments unique to each series (colors). Special arguments `line`, `marker`, and `fill` will automatically figure out what arguments to set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with `line`.) Note that we pass a matrix of colors, and this applies the colors to each series.",
|
||||
[
|
||||
:(plot(Vector[rand(10), rand(20)], marker=(:ellipse,8), line=(:dot,3,[:black :orange])))
|
||||
]),
|
||||
PlotExample("Build plot in pieces",
|
||||
"Start with a base plot...",
|
||||
[:(plot(rand(100)/3; reg=true, fillto=0))]),
|
||||
[
|
||||
:(plot(rand(100)/3, reg=true, fill=(0,:green)))
|
||||
]),
|
||||
PlotExample("",
|
||||
"and add to it later.",
|
||||
[:(scatter!(rand(100); markersize=6, color=:blue))]),
|
||||
[
|
||||
:(scatter!(rand(100), markersize=6, c=:orange))
|
||||
]),
|
||||
PlotExample("Heatmaps",
|
||||
"",
|
||||
[:(heatmap(randn(10000),randn(10000); nbins=200))]),
|
||||
PlotExample("Lots of line types",
|
||||
"Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar) \nNote: some may not work with all backends",
|
||||
[:(plot(rand(20,4); linetypes=[:line, :step, :sticks, :dots]))]),
|
||||
[
|
||||
:(heatmap(randn(10000),randn(10000), nbins=100))
|
||||
]),
|
||||
PlotExample("Line types",
|
||||
"",
|
||||
[
|
||||
:(types = intersect(supportedTypes(), [:line, :path, :steppre, :steppost, :sticks, :scatter])'),
|
||||
:(n = length(types)),
|
||||
:(x = Vector[sort(rand(20)) for i in 1:n]),
|
||||
:(y = rand(20,n)),
|
||||
:(plot(x, y, line=(types,3), lab=map(string,types), ms=15))
|
||||
]),
|
||||
PlotExample("Line styles",
|
||||
"",
|
||||
[
|
||||
:(styles = setdiff(supportedStyles(), [:auto])'),
|
||||
:(plot(cumsum(randn(20,length(styles)),1), style=:auto, label=map(string,styles), w=5))
|
||||
]),
|
||||
PlotExample("Marker types",
|
||||
"",
|
||||
[
|
||||
:(markers = setdiff(supportedMarkers(), [:none,:auto,Shape])'),
|
||||
:(n = length(markers)),
|
||||
:(x = linspace(0,10,n+2)[2:end-1]),
|
||||
:(y = repmat(reverse(x)', n, 1)),
|
||||
:(scatter(x, y, m=(8,:auto), lab=map(string,markers), bg=:linen))
|
||||
]),
|
||||
PlotExample("Bar",
|
||||
"x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
|
||||
[:(bar(randn(1000)))]),
|
||||
[
|
||||
:(bar(randn(999)))
|
||||
]),
|
||||
PlotExample("Histogram",
|
||||
"note: fillto isn't supported on all backends",
|
||||
[:(histogram(randn(1000); nbins=50, fillto=20))]),
|
||||
"",
|
||||
[
|
||||
:(histogram(randn(1000), nbins=50))
|
||||
]),
|
||||
PlotExample("Subplots",
|
||||
"""
|
||||
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
|
||||
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
|
||||
number of columns `nc`), or you can set the layout directly with `layout`.
|
||||
|
||||
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
|
||||
number of columns `nc`), or you can set the layout directly with `layout`.
|
||||
""",
|
||||
[:(subplot(randn(100,5); layout=[1,1,3], linetypes=[:line,:hist,:dots,:step,:bar], nbins=10, legend=false))]),
|
||||
[
|
||||
:(subplot(randn(100,5), layout=[1,1,3], t=[:line :hist :scatter :step :bar], nbins=10, leg=false))
|
||||
]),
|
||||
PlotExample("Adding to subplots",
|
||||
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
|
||||
[:(subplot(randn(100,5); n=4))]),
|
||||
[
|
||||
:(subplot(Plots.fakedata(100,10), n=4, palette=[:grays :blues :heat :lightrainbow], bg=[:orange :pink :darkblue :black]))
|
||||
]),
|
||||
PlotExample("",
|
||||
"",
|
||||
[:(subplot!(randn(100,3)))]),
|
||||
|
||||
[
|
||||
:(subplot!(Plots.fakedata(100,10)))
|
||||
]),
|
||||
PlotExample("Open/High/Low/Close",
|
||||
"Create an OHLC chart. Pass in a vector of OHLC objects as your `y` argument. Adjust the tick width with arg `markersize`.",
|
||||
[
|
||||
:(n=20),
|
||||
:(hgt=rand(n)+1),
|
||||
:(bot=randn(n)),
|
||||
:(openpct=rand(n)),
|
||||
:(closepct=rand(n)),
|
||||
:(y = [OHLC(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i], closepct[i]*hgt[i]+bot[i]) for i in 1:n]),
|
||||
:(ohlc(y; markersize=8))
|
||||
]),
|
||||
PlotExample("Annotations",
|
||||
"Currently only text annotations are supported. Pass in a tuple or vector-of-tuples: (x,y,text). `annotate!(ann)` is shorthand for `plot!(; annotation=ann)`",
|
||||
[
|
||||
:(y = rand(10)),
|
||||
:(plot(y, ann=(3,y[3],text("this is #3",:left)))),
|
||||
:(annotate!([(5,y[5],text("this is #5",16,:red,:center)),
|
||||
(10,y[10],text("this is #10",:right,20,"courier"))]))
|
||||
]),
|
||||
PlotExample("Custom Markers",
|
||||
"A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and the size is expected to be rougly the area of the unit circle.",
|
||||
[
|
||||
:(verts = [(-1.0,1.0),(-1.28,0.6),(-0.2,-1.4),(0.2,-1.4),(1.28,0.6),(1.0,1.0),
|
||||
(-1.0,1.0),(-0.2,-0.6),(0.0,-0.2),(-0.4,0.6),(1.28,0.6),(0.2,-1.4),
|
||||
(-0.2,-1.4),(0.6,0.2),(-0.2,0.2),(0.0,-0.2),(0.2,0.2),(-0.2,-0.6)])
|
||||
:(plot(0.1:0.2:0.9, 0.7rand(5)+0.15,
|
||||
l=(3,:dash,:lightblue),
|
||||
m=(Shape(verts),30,RGBA(0,0,0,0.2)),
|
||||
bg=:pink, fg=:darkblue,
|
||||
xlim = (0,1), ylim=(0,1), leg=false))
|
||||
])
|
||||
|
||||
]
|
||||
|
||||
|
||||
function createStringOfMarkDownCodeValues(arr, prefix = "")
|
||||
string("`", prefix, join(sort(map(string, arr)), "`, `$prefix"), "`")
|
||||
end
|
||||
createStringOfMarkDownSymbols(arr) = isempty(arr) ? "" : createStringOfMarkDownCodeValues(arr, ":")
|
||||
|
||||
|
||||
function generate_markdown(pkgname::Symbol)
|
||||
|
||||
# set up the plotter, and don't show the plots by default
|
||||
plotter!(pkgname)
|
||||
plotDefault!(:show, false)
|
||||
# set up the backend, and don't show the plots by default
|
||||
pkg = backend(pkgname)
|
||||
# default(:show, false)
|
||||
|
||||
# mkdir if necessary
|
||||
try
|
||||
mkdir("$IMGDIR/$pkgname")
|
||||
end
|
||||
|
||||
# open the markdown file
|
||||
md = open("$DOCDIR/$(pkgname)_examples.md", "w")
|
||||
|
||||
write(md, "## Examples for backend: $pkgname\n\n")
|
||||
|
||||
write(md, "### Initialize\n\n```julia\nusing Plots\n$(pkgname)()\n```\n\n")
|
||||
|
||||
|
||||
for (i,example) in enumerate(examples)
|
||||
|
||||
try
|
||||
|
||||
# we want to always produce consistent results
|
||||
srand(1234)
|
||||
|
||||
# run the code
|
||||
map(eval, example.exprs)
|
||||
|
||||
# save the png
|
||||
imgname = "$(pkgname)_example_$i.png"
|
||||
savepng("$IMGDIR/$imgname")
|
||||
# # save the png
|
||||
# imgname = "$(pkgname)_example_$i.png"
|
||||
|
||||
# NOTE: uncomment this to overwrite the images as well
|
||||
if i == 2
|
||||
imgname = "$(pkgname)_example_$i.gif"
|
||||
gif(anim, "$IMGDIR/$pkgname/$imgname", fps=15)
|
||||
else
|
||||
imgname = "$(pkgname)_example_$i.png"
|
||||
png("$IMGDIR/$pkgname/$imgname")
|
||||
end
|
||||
|
||||
# write out the header, description, code block, and image link
|
||||
write(md, "### $(example.header)\n\n")
|
||||
write(md, "$(example.desc)\n\n")
|
||||
write(md, "```julia\n$(join(map(string, example.exprs), "\n"))\n```\n\n")
|
||||
write(md, "\n\n")
|
||||
write(md, "\n\n")
|
||||
|
||||
catch ex
|
||||
# TODO: put error info into markdown?
|
||||
@@ -106,12 +229,154 @@ function generate_markdown(pkgname::Symbol)
|
||||
#
|
||||
end
|
||||
|
||||
write(md, "- Supported arguments: $(createStringOfMarkDownCodeValues(supportedArgs(pkg)))\n")
|
||||
write(md, "- Supported values for axis: $(createStringOfMarkDownSymbols(supportedAxes(pkg)))\n")
|
||||
write(md, "- Supported values for linetype: $(createStringOfMarkDownSymbols(supportedTypes(pkg)))\n")
|
||||
write(md, "- Supported values for linestyle: $(createStringOfMarkDownSymbols(supportedStyles(pkg)))\n")
|
||||
write(md, "- Supported values for marker: $(createStringOfMarkDownSymbols(supportedMarkers(pkg)))\n")
|
||||
write(md, "- Is `subplot`/`subplot!` supported? $(subplotSupported(pkg) ? "Yes" : "No")\n\n")
|
||||
|
||||
write(md, "(Automatically generated: $(now()))")
|
||||
close(md)
|
||||
|
||||
end
|
||||
|
||||
|
||||
# make and display one plot
|
||||
function test_examples(pkgname::Symbol, idx::Int; debug = true)
|
||||
Plots._debugMode.on = debug
|
||||
println("Testing plot: $pkgname:$idx:$(examples[idx].header)")
|
||||
backend(pkgname)
|
||||
backend()
|
||||
map(eval, examples[idx].exprs)
|
||||
plt = current()
|
||||
gui(plt)
|
||||
plt
|
||||
end
|
||||
|
||||
# generate all plots and create a dict mapping idx --> plt
|
||||
function test_examples(pkgname::Symbol; debug = false)
|
||||
Plots._debugMode.on = debug
|
||||
plts = Dict()
|
||||
for i in 1:length(examples)
|
||||
# if examples[i].header == "Subplots" && !subplotSupported()
|
||||
# break
|
||||
# end
|
||||
|
||||
try
|
||||
plt = test_examples(pkgname, i, debug=debug)
|
||||
plts[i] = plt
|
||||
catch ex
|
||||
# TODO: put error info into markdown?
|
||||
warn("Example $pkgname:$i:$(examples[i].header) failed with: $ex")
|
||||
end
|
||||
end
|
||||
plts
|
||||
end
|
||||
|
||||
# axis # :left or :right
|
||||
# color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl
|
||||
# # Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
|
||||
# label # string or symbol, applies to that line, may go in a legend
|
||||
# width # width of a line
|
||||
# linetype # :line, :step, :stepinverted, :sticks, :scatter, :none, :heatmap, :hexbin, :hist, :bar
|
||||
# linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
|
||||
# marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle,
|
||||
# # :cross, :xcross, :star1, :star2, :hexagon
|
||||
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
|
||||
# markersize # size of the marker
|
||||
# nbins # number of bins for heatmap/hexbin and histograms
|
||||
# heatmap_c # color cutoffs for Qwt heatmaps
|
||||
# fill # fill value for area plots
|
||||
# title # string or symbol, title of the plot
|
||||
# xlabel # string or symbol, label on the bottom (x) axis
|
||||
# ylabel # string or symbol, label on the left (y) axis
|
||||
# yrightlabel # string or symbol, label on the right (y) axis
|
||||
# reg # true or false, add a regression line for each line
|
||||
# size # (Int,Int), resize the enclosing window
|
||||
# pos # (Int,Int), move the enclosing window to this position
|
||||
# windowtitle # string or symbol, set the title of the enclosing windowtitle
|
||||
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
||||
|
||||
|
||||
|
||||
@compat const _ltdesc = Dict(
|
||||
:none => "No line",
|
||||
:line => "Lines with sorted x-axis",
|
||||
:path => "Lines",
|
||||
:steppre => "Step plot (vertical then horizontal)",
|
||||
:steppost => "Step plot (horizontal then vertical)",
|
||||
:sticks => "Vertical lines",
|
||||
:scatter => "Points, no lines",
|
||||
:heatmap => "Colored regions by density",
|
||||
:hexbin => "Similar to heatmap",
|
||||
:hist => "Histogram (doesn't use x)",
|
||||
:bar => "Bar plot (centered on x values)",
|
||||
:hline => "Horizontal line (doesn't use x)",
|
||||
:vline => "Vertical line (doesn't use x)",
|
||||
:ohlc => "Open/High/Low/Close chart (expects y is AbstractVector{Plots.OHLC})",
|
||||
)
|
||||
|
||||
function buildReadme()
|
||||
readme = readall("$DOCDIR/readme_template.md")
|
||||
|
||||
# build keyword arg table
|
||||
table = "Keyword | Default | Type | Aliases \n---- | ---- | ---- | ----\n"
|
||||
allseries = merge(Plots._seriesDefaults, @compat(Dict(:line=>nothing, :marker=>nothing, :fill=>nothing)))
|
||||
allplots = merge(Plots._plotDefaults, @compat(Dict(:xaxis=>nothing, :yaxis=>nothing)))
|
||||
alldefs = merge(allseries, allplots)
|
||||
for k in Plots.sortedkeys(alldefs)
|
||||
# for d in (Plots._seriesDefaults, Plots._plotDefaults)
|
||||
# for k in Plots.sortedkeys(d)
|
||||
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._keyAliases, k))
|
||||
table = string(table, "`:$k` | `$(alldefs[k])` | $(haskey(allseries,k) ? "Series" : "Plot") | $aliasstr \n")
|
||||
# end
|
||||
end
|
||||
readme = replace(readme, "[[KEYWORD_ARGS_TABLE]]", table)
|
||||
|
||||
# build linetypes table
|
||||
table = "Type | Desc | Aliases\n---- | ---- | ----\n"
|
||||
for lt in Plots._allTypes
|
||||
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._typeAliases, lt))
|
||||
table = string(table, "`:$lt` | $(_ltdesc[lt]) | $aliasstr \n")
|
||||
end
|
||||
readme = replace(readme, "[[LINETYPES_TABLE]]", table)
|
||||
|
||||
# build linestyles table
|
||||
table = "Type | Aliases\n---- | ----\n"
|
||||
for s in Plots._allStyles
|
||||
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._styleAliases, s))
|
||||
table = string(table, "`:$s` | $aliasstr \n")
|
||||
end
|
||||
readme = replace(readme, "[[LINESTYLES_TABLE]]", table)
|
||||
|
||||
# build markers table
|
||||
table = "Type | Aliases\n---- | ----\n"
|
||||
for s in Plots._allMarkers
|
||||
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._markerAliases, s))
|
||||
table = string(table, "`:$s` | $aliasstr \n")
|
||||
end
|
||||
readme = replace(readme, "[[MARKERS_TABLE]]", table)
|
||||
|
||||
readme_fn = Pkg.dir("Plots") * "/README.md"
|
||||
f = open(readme_fn, "w")
|
||||
write(f, readme)
|
||||
close(f)
|
||||
|
||||
gadfly()
|
||||
Plots.dumpSupportGraphs()
|
||||
end
|
||||
|
||||
default(size=(500,300))
|
||||
|
||||
# run it!
|
||||
map(generate_markdown, (:qwt, :gadfly))
|
||||
# note: generate separately so it's easy to comment out
|
||||
# @osx_only generate_markdown(:unicodeplots)
|
||||
# generate_markdown(:qwt)
|
||||
# generate_markdown(:gadfly)
|
||||
# generate_markdown(:pyplot)
|
||||
# generate_markdown(:immerse)
|
||||
# generate_markdown(:winston)
|
||||
|
||||
|
||||
end # module
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
### Lines
|
||||
|
||||
A simple line plot of the 3 columns.
|
||||
|
||||
```julia
|
||||
plot(rand(100,3))
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Functions
|
||||
|
||||
Plot multiple functions
|
||||
|
||||
```julia
|
||||
plot(0:0.01:4π,[sin,cos])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Global
|
||||
|
||||
Change the guides/background without a separate call.
|
||||
|
||||
```julia
|
||||
plot(rand(10); title="TITLE",xlabel="XLABEL",ylabel="YLABEL",background_color=RGB(0.5,0.5,0.5))
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Two-axis
|
||||
|
||||
Use the `axis` or `axiss` arguments.
|
||||
|
||||
Note: This is only supported with Qwt right now
|
||||
|
||||
```julia
|
||||
plot(Vector[randn(100),randn(100) * 100]; axiss=[:left,:right])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Vectors w/ pluralized args
|
||||
|
||||
Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).
|
||||
|
||||
```julia
|
||||
plot(Vector[rand(10),rand(20)]; marker=:ellipse,markersize=8,colors=[:red,:blue])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Build plot in pieces
|
||||
|
||||
Start with a base plot...
|
||||
|
||||
```julia
|
||||
plot(rand(100) / 3; reg=true,fillto=0)
|
||||
```
|
||||
|
||||

|
||||
|
||||
###
|
||||
|
||||
and add to it later.
|
||||
|
||||
```julia
|
||||
scatter!(rand(100); markersize=6,color=:blue)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Heatmaps
|
||||
|
||||
|
||||
|
||||
```julia
|
||||
heatmap(randn(10000),randn(10000); nbins=200)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Lots of line types
|
||||
|
||||
Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
|
||||
Note: some may not work with all backends
|
||||
|
||||
```julia
|
||||
plot(rand(20,4); linetypes=[:line,:step,:sticks,:dots])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Bar
|
||||
|
||||
x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)
|
||||
|
||||
```julia
|
||||
bar(randn(1000))
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Histogram
|
||||
|
||||
note: fillto isn't supported on all backends
|
||||
|
||||
```julia
|
||||
histogram(randn(1000); nbins=50,fillto=20)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Subplots
|
||||
|
||||
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
|
||||
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
|
||||
number of columns `nc`), or you can set the layout directly with `layout`.
|
||||
|
||||
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
|
||||
|
||||
|
||||
```julia
|
||||
subplot(randn(100,5); layout=[1,1,3],linetypes=[:line,:hist,:dots,:step,:bar],nbins=10,legend=false)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Adding to subplots
|
||||
|
||||
Note here the automatic grid layout, as well as the order in which new series are added to the plots.
|
||||
|
||||
```julia
|
||||
subplot(randn(100,5); n=4)
|
||||
```
|
||||
|
||||

|
||||
|
||||
###
|
||||
|
||||
|
||||
|
||||
```julia
|
||||
subplot!(randn(100,3))
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
### Lines
|
||||
|
||||
A simple line plot of the 3 columns.
|
||||
|
||||
```julia
|
||||
plot(rand(100,3))
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Functions
|
||||
|
||||
Plot multiple functions
|
||||
|
||||
```julia
|
||||
plot(0:0.01:4π,[sin,cos])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Global
|
||||
|
||||
Change the guides/background without a separate call.
|
||||
|
||||
```julia
|
||||
plot(rand(10); title="TITLE",xlabel="XLABEL",ylabel="YLABEL",background_color=RGB(0.5,0.5,0.5))
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Two-axis
|
||||
|
||||
Use the `axis` or `axiss` arguments.
|
||||
|
||||
Note: This is only supported with Qwt right now
|
||||
|
||||
```julia
|
||||
plot(Vector[randn(100),randn(100) * 100]; axiss=[:left,:right])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Vectors w/ pluralized args
|
||||
|
||||
Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).
|
||||
|
||||
```julia
|
||||
plot(Vector[rand(10),rand(20)]; marker=:ellipse,markersize=8,colors=[:red,:blue])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Build plot in pieces
|
||||
|
||||
Start with a base plot...
|
||||
|
||||
```julia
|
||||
plot(rand(100) / 3; reg=true,fillto=0)
|
||||
```
|
||||
|
||||

|
||||
|
||||
###
|
||||
|
||||
and add to it later.
|
||||
|
||||
```julia
|
||||
scatter!(rand(100); markersize=6,color=:blue)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Heatmaps
|
||||
|
||||
|
||||
|
||||
```julia
|
||||
heatmap(randn(10000),randn(10000); nbins=200)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Lots of line types
|
||||
|
||||
Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
|
||||
Note: some may not work with all backends
|
||||
|
||||
```julia
|
||||
plot(rand(20,4); linetypes=[:line,:step,:sticks,:dots])
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Bar
|
||||
|
||||
x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)
|
||||
|
||||
```julia
|
||||
bar(randn(1000))
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Histogram
|
||||
|
||||
note: fillto isn't supported on all backends
|
||||
|
||||
```julia
|
||||
histogram(randn(1000); nbins=50,fillto=20)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Subplots
|
||||
|
||||
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
|
||||
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
|
||||
number of columns `nc`), or you can set the layout directly with `layout`.
|
||||
|
||||
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
|
||||
|
||||
|
||||
```julia
|
||||
subplot(randn(100,5); layout=[1,1,3],linetypes=[:line,:hist,:dots,:step,:bar],nbins=10,legend=false)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Adding to subplots
|
||||
|
||||
Note here the automatic grid layout, as well as the order in which new series are added to the plots.
|
||||
|
||||
```julia
|
||||
subplot(randn(100,5); n=4)
|
||||
```
|
||||
|
||||

|
||||
|
||||
###
|
||||
|
||||
|
||||
|
||||
```julia
|
||||
subplot!(randn(100,3))
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/tbreloff/Plots.jl)
|
||||
[](http://pkg.julialang.org/?pkg=Plots&ver=0.3)
|
||||
[](http://pkg.julialang.org/?pkg=Plots&ver=0.4)
|
||||
<!-- [](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
|
||||
<!-- [](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
|
||||
|
||||
#### Author: Thomas Breloff (@tbreloff)
|
||||
|
||||
Plots is a plotting API and toolset. My goals with the package are:
|
||||
|
||||
- **Intuitive**. Start generating complex plots without reading volumes of documentation. Commands should "just work".
|
||||
- **Concise**. Less code means fewer mistakes and more efficient development/analysis.
|
||||
- **Flexible**. Produce your favorite plots from your favorite package, but quicker and simpler.
|
||||
- **Consistent**. Don't commit to one graphics package. Use the same code and access the strengths of all backends.
|
||||
- **Lightweight**. Very few dependencies, since backends are loaded and initialized dynamically.
|
||||
|
||||
Use the preprocessing pipeline in Plots to fully describe your visualization before it calls the backend code. This maintains modularity and allows for efficient separation of front end code, algorithms, and backend graphics. New graphical backends can be added with minimal effort.
|
||||
|
||||
Check out the [summary graphs](https://github.com/tbreloff/ExamplePlots.jl/tree/master/img/supported/supported.md) for the features that each backend supports.
|
||||
|
||||
Please add wishlist items, bugs, or any other comments/questions to the issues list.
|
||||
|
||||
## Examples for each implemented backend:
|
||||
|
||||
- [Gadfly.jl/Immerse.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/gadfly_examples.md)
|
||||
- [PyPlot.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/pyplot_examples.md)
|
||||
- [UnicodePlots.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/unicodeplots_examples.md)
|
||||
- [Qwt.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/qwt_examples.md)
|
||||
|
||||
Also check out the many [IJulia notebooks](http://nbviewer.ipython.org/github/tbreloff/ExamplePlots.jl/tree/master/examples/) with many examples.
|
||||
|
||||
## Installation
|
||||
|
||||
First, add the package
|
||||
|
||||
```julia
|
||||
Pkg.add("Plots")
|
||||
|
||||
# if you want the latest features:
|
||||
Pkg.checkout("Plots")
|
||||
|
||||
# or for the bleeding edge:
|
||||
Pkg.checkout("Plots", "dev")
|
||||
```
|
||||
|
||||
then get any plotting packages you need (obviously, you should get at least one backend).
|
||||
|
||||
```julia
|
||||
Pkg.add("Gadfly")
|
||||
Pkg.add("Immerse")
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.add("UnicodePlots")
|
||||
Pkg.clone("https://github.com/tbreloff/Qwt.jl.git")
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
Load it in. The underlying plotting backends are not imported until `backend()` is called (which happens
|
||||
on your first call to `plot` or `subplot`). This means that you don't need any backends to be installed when you call `using Plots`.
|
||||
|
||||
Plots will try to figure out a good default backend for you automatically based on what backends are installed.
|
||||
|
||||
```julia
|
||||
using Plots
|
||||
```
|
||||
|
||||
Do a plot in Gadfly (inspired by [this example](http://gadflyjl.org/geom_point.html)), then save a png:
|
||||
|
||||
```julia
|
||||
gadfly() # switch to Gadfly as a backend
|
||||
dataframes() # turn on support for DataFrames inputs
|
||||
|
||||
# load some data
|
||||
using RDatasets
|
||||
iris = dataset("datasets", "iris");
|
||||
|
||||
# This will bring up a browser window with the plot. Add a semicolon at the end to skip display.
|
||||
scatter(iris, :SepalLength, :SepalWidth, group=:Species, m=([:+ :d :s], 12), smooth=0.99, bg=:black)
|
||||
|
||||
# save a png (equivalent to png("gadfly1.png") and savefig("gadfly1.png"))
|
||||
png("gadfly1")
|
||||
```
|
||||
|
||||

|
||||
|
||||
## API
|
||||
|
||||
Call `backend(backend::Symbol)` or the shorthands (`gadfly()`, `qwt()`, `unicodeplots()`, etc) to set the current plotting backend.
|
||||
Subsequent commands are converted into the relevant plotting commands for that package:
|
||||
|
||||
```julia
|
||||
gadfly()
|
||||
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
|
||||
qwt()
|
||||
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
|
||||
```
|
||||
|
||||
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
|
||||
|
||||
```julia
|
||||
plot(args...; kw...) # creates a new plot window, and sets it to be the `current`
|
||||
plot!(args...; kw...) # adds to the `current`
|
||||
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
|
||||
```
|
||||
|
||||
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
|
||||
There are many ways to pass in data to the plot functions... some examples:
|
||||
|
||||
- Vector-like (subtypes of AbstractArray{T,1})
|
||||
- Matrix-like (subtypes of AbstractArray{T,2})
|
||||
- Vectors of Vectors
|
||||
- Functions
|
||||
- Vectors of Functions
|
||||
- DataFrames with column symbols (initialize with `dataframes()`)
|
||||
|
||||
In general, you can pass in a `y` only, or an `x` and `y`, both of whatever type(s) you want, and Plots will slice up the data as needed.
|
||||
For matrices, data is split by columns. For functions, data is mapped. For DataFrames, a Symbol/Symbols in place of x/y will map to
|
||||
the relevant column(s).
|
||||
|
||||
Here are some example usages... remember you can always use `plot!` to update an existing plot, and that, unless specified, you will update the `current()`.
|
||||
|
||||
```julia
|
||||
plot() # empty plot object
|
||||
plot(4) # initialize with 4 empty series
|
||||
plot(rand(10)) # plot 1 series... x = 1:10
|
||||
plot(rand(10,5)) # plot 5 series... x = 1:10
|
||||
plot(rand(10), rand(10)) # plot 1 series
|
||||
plot(rand(10,5), rand(10)) # plot 5 series... y is the same for all
|
||||
plot(sin, rand(10)) # y = sin(x)
|
||||
plot(rand(10), sin) # same... y = sin(x)
|
||||
plot([sin,cos], 0:0.1:π) # plot 2 series, sin(x) and cos(x)
|
||||
plot([sin,cos], 0, π) # plot sin and cos on the range [0, π]
|
||||
plot(1:10, Any[rand(10), sin]) # plot 2 series, y = rand(10) for the first, y = sin(x) for the second... x = 1:10 for both
|
||||
plot(dataset("Ecdat", "Airline"), :Cost) # plot from a DataFrame (call `dataframes()` first to import DataFrames and initialize)
|
||||
```
|
||||
|
||||
All plot methods accept a number of keyword arguments (see the tables below), which follow some rules:
|
||||
- Many arguments have aliases which are replaced during preprocessing. `c` is the same as `color`, `m` is the same as `marker`, etc. You can choose how verbose you'd like to be. (see the tables below)
|
||||
- There are some special arguments (`xaxis`, `yaxis`, `line`, `marker`, `fill` and the aliases `l`, `m`, `f`) which magically set many related things at once. (see the __Tip__ below)
|
||||
- If the argument is a "matrix-type", then each column will map to a series, cycling through columns if there are fewer columns than series. Anything else will apply the argument value to every series.
|
||||
- Many arguments accept many different types... for example the `color` (also `markercolor`, `fillcolor`, etc) argument will accept strings or symbols with a color name, or any `Colors.Colorant`, or a `ColorScheme`, or a symbol representing a `ColorGradient`, or an AbstractVector of colors/symbols/etc...
|
||||
|
||||
You can update certain plot settings after plot creation (not supported on all backends):
|
||||
|
||||
```julia
|
||||
plot!(title = "New Title", xlabel = "New xlabel", ylabel = "New ylabel")
|
||||
plot!(xlims = (0, 5.5), ylims = (-2.2, 6), xticks = 0:0.5:10, yticks = [0,1,5,10])
|
||||
|
||||
# using shorthands:
|
||||
xaxis!("mylabel", :log10, :flip)
|
||||
```
|
||||
|
||||
With `subplot`, create multiple plots at once, with flexible layout options:
|
||||
|
||||
```julia
|
||||
y = rand(100,3)
|
||||
subplot(y; n = 3) # create an automatic grid, and let it figure out the shape
|
||||
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows
|
||||
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns
|
||||
subplot(y; layout = [1, 2]) # explicit layout. Lists the number of plots in each row
|
||||
```
|
||||
|
||||
__Tip__: You can call `subplot!(args...; kw...)` to add to an existing subplot.
|
||||
|
||||
__Tip__: Calling `subplot!` on a `Plot` object, or `plot!` on a `Subplot` object will throw an error.
|
||||
|
||||
Shorthands:
|
||||
|
||||
```julia
|
||||
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
|
||||
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
|
||||
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
|
||||
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
|
||||
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
|
||||
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
|
||||
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
|
||||
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
|
||||
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
|
||||
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
|
||||
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
|
||||
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
|
||||
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
|
||||
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
|
||||
|
||||
title!(s::AbstractString) = plot!(title = s)
|
||||
xlabel!(s::AbstractString) = plot!(xlabel = s)
|
||||
ylabel!(s::AbstractString) = plot!(ylabel = s)
|
||||
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(xlims = lims)
|
||||
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(ylims = lims)
|
||||
xticks!{T<:Real}(v::AVec{T}) = plot!(xticks = v)
|
||||
yticks!{T<:Real}(v::AVec{T}) = plot!(yticks = v)
|
||||
xflip!(flip::Bool = true) = plot!(xflip = flip)
|
||||
yflip!(flip::Bool = true) = plot!(yflip = flip)
|
||||
xaxis!(args...) = plot!(xaxis = args)
|
||||
yaxis!(args...) = plot!(yaxis = args)
|
||||
annotate!(anns) = plot!(annotation = anns)
|
||||
```
|
||||
|
||||
### Keyword arguments:
|
||||
|
||||
[[KEYWORD_ARGS_TABLE]]
|
||||
|
||||
### Plot types:
|
||||
|
||||
[[LINETYPES_TABLE]]
|
||||
|
||||
### Line styles:
|
||||
|
||||
[[LINESTYLES_TABLE]]
|
||||
|
||||
### Markers:
|
||||
|
||||
[[MARKERS_TABLE]]
|
||||
|
||||
__Tip__: With supported backends, you can pass a `Plots.Shape` object for the `marker`/`markershape` arguments. `Shape` takes a vector of 2-tuples in the constructor, defining the points of the polygon's shape in a unit-scaled coordinate space. To make a square, for example, you could do `Shape([(1,1),(1,-1),(-1,-1),(-1,1)])`
|
||||
|
||||
__Tip__: You can see the default value for a given argument with `default(arg::Symbol)`, and set the default value with `default(arg::Symbol, value)` or `default(; kw...)`. For example set the default window size and whether we should show a legend with `default(size=(600,400), leg=false)`.
|
||||
|
||||
__Tip__: There are some helper arguments you can set: `xaxis`, `yaxis`, `line`, `marker`, `fill`. These go through special preprocessing to extract values into individual arguments. The order doesn't matter, and if you pass a single value it's equivalent to wrapping it in a Tuple. Examples:
|
||||
|
||||
```
|
||||
plot(y, xaxis = ("mylabel", :log, :flip, (-1,1))) # this sets the `xlabel`, `xscale`, `xflip`, and `xlims` arguments automatically
|
||||
plot(y, line = (:bar, :blue, :dot, 10)) # this sets the `linetype`, `color`, `linestyle`, and `linewidth` arguments automatically
|
||||
plot(y, marker = (:rect, :red, 10)) # this sets the `markershape`, `markercolor`, and `markersize` arguments automatically
|
||||
plot(y, fill = (:green, 10)) # this sets the `fillcolor` and `fillrange` arguments automatically
|
||||
# Note: `fillrange` can be:
|
||||
a number (fill to horizontal line)
|
||||
a vector of numbers (different for each data point)
|
||||
a tuple of vectors (fill a band)
|
||||
```
|
||||
|
||||
__Tip__: When plotting multiple lines, you can set all series to use the same value, or pass in a matrix to cycle through values. Example:
|
||||
|
||||
```julia
|
||||
plot(rand(100,4); color = [:red RGB(0,0,1)], # (Matrix) lines 1 and 3 are red, lines 2 and 4 are blue
|
||||
axis = :auto, # lines 1 and 3 are on the left axis, lines 2 and 4 are on the right
|
||||
markershape = [:rect, :star] # (Vector) ALL lines are passed the vector [:rect, :star1]
|
||||
width = 5) # all lines have a width of 5
|
||||
```
|
||||
|
||||
__Tip__: Not all features are supported for each backend, but you can see what's supported by calling the functions: `supportedArgs()`, `supportedAxes()`, `supportedTypes()`, `supportedStyles()`, `supportedMarkers()`, `subplotSupported()`
|
||||
|
||||
__Tip__: Call `gui()` to display the plot in a window. Interactivity depends on backend. Plotting at the REPL (without semicolon) implicitly calls `gui()`.
|
||||
|
||||
### Animations
|
||||
|
||||
Animations are created in 3 steps (see example #2):
|
||||
- Initialize an `Animation` object.
|
||||
- Save each frame of the animation with `frame(anim)`.
|
||||
- Convert the frames to an animated gif with `gif(anim, filename, fps=15)`
|
||||
|
||||
|
||||
## TODO features:
|
||||
|
||||
- [x] Plot vectors/matrices/functions
|
||||
- [x] Plot DataFrames
|
||||
- [x] Histograms
|
||||
- [x] Grouping
|
||||
- [x] Annotations
|
||||
- [x] Scales
|
||||
- [x] Categorical Inputs (strings, etc... for hist, bar? or can split one series into multiple?)
|
||||
- [x] Custom markers
|
||||
- [x] Animations
|
||||
- [x] Subplots
|
||||
- [ ] Contours
|
||||
- [ ] Boxplots
|
||||
- [ ] 3D plotting
|
||||
- [ ] Scenes/Drawing
|
||||
- [ ] Graphs
|
||||
- [ ] Interactivity (GUIs)
|
||||
|
||||
## TODO backends:
|
||||
|
||||
- [x] Gadfly.jl
|
||||
- [x] Immerse.jl
|
||||
- [x] PyPlot.jl
|
||||
- [x] UnicodePlots.jl
|
||||
- [x] Qwt.jl
|
||||
- [x] Winston.jl (deprecated)
|
||||
- [ ] GLPlot.jl
|
||||
- [ ] Bokeh.jl
|
||||
- [ ] Vega.jl
|
||||
- [ ] Gaston.jl
|
||||
- [ ] Plotly.jl
|
||||
- [ ] GoogleCharts.jl
|
||||
- [ ] PLplot.jl
|
||||
- [ ] TextPlots.jl
|
||||
- [ ] ASCIIPlots.jl
|
||||
- [ ] Sparklines.jl
|
||||
- [ ] Hinton.jl
|
||||
- [ ] ImageTerm.jl
|
||||
- [ ] GraphViz.jl
|
||||
- [ ] TikzGraphs.jl
|
||||
- [ ] GraphLayout.jl
|
||||
|
||||
## More information on backends (both supported and unsupported)
|
||||
|
||||
See the wiki at: https://github.com/JuliaPlot/juliaplot_docs/wiki
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,23 @@
|
||||
## Supported keyword arguments
|
||||
|
||||

|
||||
|
||||
## Supported plot types
|
||||
|
||||

|
||||
|
||||
## Supported markers
|
||||
|
||||

|
||||
|
||||
## Supported line styles
|
||||
|
||||

|
||||
|
||||
## Supported scales
|
||||
|
||||

|
||||
|
||||
## Supported axes
|
||||
|
||||

|
||||
@@ -0,0 +1,4 @@
|
||||
git checkout master
|
||||
git merge --ff-only dev
|
||||
git push origin master
|
||||
git checkout dev
|
||||
@@ -1,33 +1,97 @@
|
||||
__precompile__()
|
||||
|
||||
if VERSION >= v"0.4-"
|
||||
__precompile__()
|
||||
end
|
||||
|
||||
module Plots
|
||||
|
||||
using Colors
|
||||
using Compat
|
||||
using Reexport
|
||||
@reexport using Colors
|
||||
|
||||
export
|
||||
plotter,
|
||||
plot,
|
||||
subplot,
|
||||
|
||||
plotter!,
|
||||
plot!,
|
||||
# plot_display,
|
||||
# plot_display!,
|
||||
subplot,
|
||||
subplot!,
|
||||
|
||||
currentPlot,
|
||||
plotDefault,
|
||||
current,
|
||||
default,
|
||||
|
||||
scatter,
|
||||
bar,
|
||||
histogram,
|
||||
heatmap,
|
||||
|
||||
currentPlot!,
|
||||
plotDefault!,
|
||||
scatter!,
|
||||
bar,
|
||||
bar!,
|
||||
histogram,
|
||||
histogram!,
|
||||
heatmap,
|
||||
heatmap!,
|
||||
sticks,
|
||||
sticks!,
|
||||
hline,
|
||||
hline!,
|
||||
vline,
|
||||
vline!,
|
||||
ohlc,
|
||||
ohlc!,
|
||||
|
||||
savepng
|
||||
title!,
|
||||
xlabel!,
|
||||
ylabel!,
|
||||
xlims!,
|
||||
ylims!,
|
||||
xticks!,
|
||||
yticks!,
|
||||
annotate!,
|
||||
xflip!,
|
||||
yflip!,
|
||||
xaxis!,
|
||||
yaxis!,
|
||||
|
||||
savefig,
|
||||
png,
|
||||
gui,
|
||||
|
||||
backend,
|
||||
backends,
|
||||
aliases,
|
||||
dataframes,
|
||||
|
||||
Shape,
|
||||
text,
|
||||
font,
|
||||
OHLC,
|
||||
|
||||
colorscheme,
|
||||
ColorScheme,
|
||||
ColorGradient,
|
||||
ColorVector,
|
||||
ColorWrapper,
|
||||
ColorFunction,
|
||||
ColorZFunction,
|
||||
getColor,
|
||||
getColorZ,
|
||||
|
||||
debugplots,
|
||||
|
||||
supportedArgs,
|
||||
supportedAxes,
|
||||
supportedTypes,
|
||||
supportedStyles,
|
||||
supportedMarkers,
|
||||
subplotSupported,
|
||||
|
||||
Animation,
|
||||
frame,
|
||||
gif,
|
||||
|
||||
# recipes
|
||||
PlotRecipe,
|
||||
EllipseRecipe,
|
||||
spy,
|
||||
corrplot
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -39,38 +103,109 @@ const IMG_DIR = Pkg.dir("Plots") * "/img/"
|
||||
|
||||
include("types.jl")
|
||||
include("utils.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
include("qwt.jl")
|
||||
include("gadfly.jl")
|
||||
include("colors.jl")
|
||||
include("plotter.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
include("args.jl")
|
||||
include("plot.jl")
|
||||
include("subplot.jl")
|
||||
include("recipes.jl")
|
||||
include("animation.jl")
|
||||
include("output.jl")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
scatter(args...; kw...) = plot(args...; kw..., linetype = :none, marker = :hexagon)
|
||||
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :none, marker = :hexagon)
|
||||
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
|
||||
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
|
||||
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
|
||||
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
|
||||
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
|
||||
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
|
||||
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
|
||||
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
|
||||
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
|
||||
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
|
||||
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
|
||||
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
|
||||
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
|
||||
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
|
||||
|
||||
|
||||
title!(s::@compat(AbstractString); kw...) = plot!(; title = s, kw...)
|
||||
xlabel!(s::@compat(AbstractString); kw...) = plot!(; xlabel = s, kw...)
|
||||
ylabel!(s::@compat(AbstractString); kw...) = plot!(; ylabel = s, kw...)
|
||||
xlims!{T<:Real,S<:Real}(lims::@compat(Tuple{T,S}); kw...) = plot!(; xlims = lims, kw...)
|
||||
ylims!{T<:Real,S<:Real}(lims::@compat(Tuple{T,S}); kw...) = plot!(; ylims = lims, kw...)
|
||||
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
|
||||
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
|
||||
xticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; xticks = v, kw...)
|
||||
yticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; yticks = v, kw...)
|
||||
xticks!{T<:Real,S<:@compat(AbstractString)}(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; xticks = (ticks,labels), kw...)
|
||||
yticks!{T<:Real,S<:@compat(AbstractString)}(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; yticks = (ticks,labels), kw...)
|
||||
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
|
||||
annotate!{T<:Tuple}(anns::AVec{T}; kw...) = plot!(; annotation = anns, kw...)
|
||||
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
|
||||
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
|
||||
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
||||
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
||||
|
||||
title!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; title = s, kw...)
|
||||
xlabel!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; xlabel = s, kw...)
|
||||
ylabel!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; ylabel = s, kw...)
|
||||
xlims!{T<:Real,S<:Real}(plt::Plot, lims::@compat(Tuple{T,S}); kw...) = plot!(plt; xlims = lims, kw...)
|
||||
ylims!{T<:Real,S<:Real}(plt::Plot, lims::@compat(Tuple{T,S}); kw...) = plot!(plt; ylims = 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...)
|
||||
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<:@compat(AbstractString)}(plt::Plot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
|
||||
yticks!{T<:Real,S<:@compat(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...)
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
savepng(args...; kw...) = savepng(currentPlot(), args...; kw...)
|
||||
savepng(plt::PlottingObject, args...; kw...) = savepng(plt.plotter, plt, args...; kw...)
|
||||
try
|
||||
import DataFrames
|
||||
dataframes()
|
||||
end
|
||||
|
||||
# const CURRENT_BACKEND = pickDefaultBackend()
|
||||
|
||||
# for be in backends()
|
||||
# try
|
||||
# backend(be)
|
||||
# backend()
|
||||
# catch err
|
||||
# @show err
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
function __init__()
|
||||
global const CURRENT_BACKEND = pickDefaultBackend()
|
||||
# global CURRENT_BACKEND
|
||||
# println("[Plots.jl] Default backend: ", CURRENT_BACKEND.sym)
|
||||
|
||||
# # auto init dataframes if the import statement doesn't error out
|
||||
# try
|
||||
# @eval import DataFrames
|
||||
# dataframes()
|
||||
# end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
immutable Animation{P<:PlottingObject}
|
||||
plt::P
|
||||
dir::ASCIIString
|
||||
frames::Vector{ASCIIString}
|
||||
end
|
||||
|
||||
function Animation(plt::PlottingObject)
|
||||
Animation(plt, mktempdir(), ASCIIString[])
|
||||
end
|
||||
Animation() = Animation(current())
|
||||
|
||||
function frame(anim::Animation)
|
||||
i = length(anim.frames) + 1
|
||||
filename = @sprintf("%06d.png", i)
|
||||
png(anim.plt, joinpath(anim.dir, filename))
|
||||
push!(anim.frames, filename)
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
"Wraps the location of an animated gif so that it can be displayed"
|
||||
immutable AnimatedGif
|
||||
filename::ASCIIString
|
||||
end
|
||||
|
||||
function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Integer = 20)
|
||||
fn = abspath(fn)
|
||||
|
||||
try
|
||||
|
||||
# high quality
|
||||
speed = round(Int, 100 / fps)
|
||||
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png $fn`)
|
||||
|
||||
catch err
|
||||
warn("Tried to create gif using convert (ImageMagick), but got error: $err\nWill try ffmpeg, but it's lower quality...)")
|
||||
|
||||
# low quality
|
||||
run(`ffmpeg -v 0 -framerate $fps -i $(anim.dir)/%06d.png -y $fn`)
|
||||
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
|
||||
end
|
||||
|
||||
info("Saved animation to ", fn)
|
||||
AnimatedGif(fn)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
|
||||
function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
||||
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
|
||||
end
|
||||
@@ -1,133 +1,677 @@
|
||||
|
||||
|
||||
# const COLORS = [:black, :blue, :green, :red, :darkGray, :darkCyan, :darkYellow, :darkMagenta,
|
||||
# :darkBlue, :darkGreen, :darkRed, :gray, :cyan, :yellow, :magenta]
|
||||
const COLORS = distinguishable_colors(20)
|
||||
const NUMCOLORS = length(COLORS)
|
||||
const _allAxes = [:auto, :left, :right]
|
||||
@compat const _axesAliases = Dict(
|
||||
:a => :auto,
|
||||
:l => :left,
|
||||
:r => :right
|
||||
)
|
||||
|
||||
# these are valid choices... first one is default value if unset
|
||||
const LINE_AXES = (:left, :right)
|
||||
const LINE_TYPES = (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
|
||||
const LINE_STYLES = (:solid, :dash, :dot, :dashdot, :dashdotdot)
|
||||
const LINE_MARKERS = (:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon)
|
||||
const _allTypes = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
|
||||
:heatmap, :hexbin, :hist, :bar, :hline, :vline, :ohlc, :contour]
|
||||
@compat const _typeAliases = Dict(
|
||||
:n => :none,
|
||||
:no => :none,
|
||||
:l => :line,
|
||||
:p => :path,
|
||||
:stepinv => :steppre,
|
||||
:stepsinv => :steppre,
|
||||
:stepinverted => :steppre,
|
||||
:stepsinverted => :steppre,
|
||||
:step => :steppost,
|
||||
:steps => :steppost,
|
||||
:stair => :steppost,
|
||||
:stairs => :steppost,
|
||||
:stem => :sticks,
|
||||
:stems => :sticks,
|
||||
:dots => :scatter,
|
||||
:histogram => :hist,
|
||||
:contours => :contours,
|
||||
)
|
||||
|
||||
const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
@compat const _styleAliases = Dict(
|
||||
:a => :auto,
|
||||
:s => :solid,
|
||||
:d => :dash,
|
||||
:dd => :dashdot,
|
||||
:ddd => :dashdotdot,
|
||||
)
|
||||
|
||||
# const _allMarkers = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle,
|
||||
# :cross, :xcross, :star5, :star8, :hexagon, :octagon, Shape]
|
||||
const _allMarkers = vcat(:none, :auto, sort(collect(keys(_shapes))))
|
||||
@compat const _markerAliases = Dict(
|
||||
:n => :none,
|
||||
:no => :none,
|
||||
:a => :auto,
|
||||
:circle => :ellipse,
|
||||
:c => :ellipse,
|
||||
:square => :rect,
|
||||
:sq => :rect,
|
||||
:r => :rect,
|
||||
:d => :diamond,
|
||||
:^ => :utriangle,
|
||||
:ut => :utriangle,
|
||||
:utri => :utriangle,
|
||||
:uptri => :utriangle,
|
||||
:uptriangle => :utriangle,
|
||||
:v => :dtriangle,
|
||||
:V => :dtriangle,
|
||||
:dt => :dtriangle,
|
||||
:dtri => :dtriangle,
|
||||
:downtri => :dtriangle,
|
||||
:downtriangle => :dtriangle,
|
||||
:+ => :cross,
|
||||
:plus => :cross,
|
||||
:x => :xcross,
|
||||
:X => :xcross,
|
||||
:star => :star5,
|
||||
:s => :star5,
|
||||
:star1 => :star5,
|
||||
:s2 => :star8,
|
||||
:star2 => :star8,
|
||||
:p => :pentagon,
|
||||
:pent => :pentagon,
|
||||
:h => :hexagon,
|
||||
:hex => :hexagon,
|
||||
:hep => :heptagon,
|
||||
:o => :octagon,
|
||||
:oct => :octagon,
|
||||
)
|
||||
|
||||
const _allScales = [:identity, :log, :log2, :log10, :asinh, :sqrt]
|
||||
@compat const _scaleAliases = Dict(
|
||||
:none => :identity,
|
||||
:ln => :log,
|
||||
)
|
||||
|
||||
supportedAxes(::PlottingPackage) = _allAxes
|
||||
supportedTypes(::PlottingPackage) = _allTypes
|
||||
supportedStyles(::PlottingPackage) = _allStyles
|
||||
supportedMarkers(::PlottingPackage) = _allMarkers
|
||||
supportedScales(::PlottingPackage) = _allScales
|
||||
subplotSupported(::PlottingPackage) = true
|
||||
|
||||
supportedAxes() = supportedAxes(backend())
|
||||
supportedTypes() = supportedTypes(backend())
|
||||
supportedStyles() = supportedStyles(backend())
|
||||
supportedMarkers() = supportedMarkers(backend())
|
||||
supportedScales() = supportedScales(backend())
|
||||
subplotSupported() = subplotSupported(backend())
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
const PLOT_DEFAULTS = Dict{Symbol, Any}()
|
||||
const _seriesDefaults = Dict{Symbol, Any}()
|
||||
|
||||
# series-specific
|
||||
PLOT_DEFAULTS[:axis] = :left
|
||||
PLOT_DEFAULTS[:color] = :auto
|
||||
PLOT_DEFAULTS[:label] = "AUTO"
|
||||
PLOT_DEFAULTS[:width] = 1
|
||||
PLOT_DEFAULTS[:linetype] = :line
|
||||
PLOT_DEFAULTS[:linestyle] = :solid
|
||||
PLOT_DEFAULTS[:marker] = :none
|
||||
PLOT_DEFAULTS[:markercolor] = :match
|
||||
PLOT_DEFAULTS[:markersize] = 3
|
||||
PLOT_DEFAULTS[:nbins] = 100 # number of bins for heatmaps and hists
|
||||
PLOT_DEFAULTS[:heatmap_c] = (0.15, 0.5)
|
||||
PLOT_DEFAULTS[:fillto] = nothing # fills in the area
|
||||
PLOT_DEFAULTS[:reg] = false # regression line?
|
||||
_seriesDefaults[:axis] = :left
|
||||
_seriesDefaults[:color] = :auto
|
||||
_seriesDefaults[:label] = "AUTO"
|
||||
_seriesDefaults[:linetype] = :path
|
||||
_seriesDefaults[:linestyle] = :solid
|
||||
_seriesDefaults[:linewidth] = 1
|
||||
_seriesDefaults[:lineopacity] = nothing
|
||||
_seriesDefaults[:markershape] = :none
|
||||
_seriesDefaults[:markercolor] = :match
|
||||
_seriesDefaults[:markeropacity] = nothing
|
||||
_seriesDefaults[:markersize] = 6
|
||||
_seriesDefaults[:fillrange] = nothing # ribbons, areas, etc
|
||||
_seriesDefaults[:fillcolor] = :match
|
||||
_seriesDefaults[:fillopacity] = nothing
|
||||
# _seriesDefaults[:ribbon] = nothing
|
||||
# _seriesDefaults[:ribboncolor] = :match
|
||||
_seriesDefaults[:nbins] = 30 # number of bins for heatmaps and hists
|
||||
# _seriesDefaults[:heatmap_c] = (0.15, 0.5) # TODO: this should be replaced with a ColorGradient
|
||||
# _seriesDefaults[:fill] = nothing # fills in the area
|
||||
_seriesDefaults[:smooth] = false # regression line?
|
||||
_seriesDefaults[:group] = nothing # groupby vector
|
||||
_seriesDefaults[:annotation] = nothing # annotation tuple(s)... (x,y,annotation)
|
||||
_seriesDefaults[:z] = nothing # depth for contour, color scale, etc
|
||||
# _seriesDefaults[:args] = [] # additional args to pass to the backend
|
||||
# _seriesDefaults[:kwargs] = [] # additional keyword args to pass to the backend
|
||||
# # note: can be Vector{Dict} or Vector{Tuple}
|
||||
_seriesDefaults[:surface] = nothing
|
||||
_seriesDefaults[:nlevels] = 15
|
||||
|
||||
|
||||
const _plotDefaults = Dict{Symbol, Any}()
|
||||
|
||||
# plot globals
|
||||
PLOT_DEFAULTS[:title] = ""
|
||||
PLOT_DEFAULTS[:xlabel] = ""
|
||||
PLOT_DEFAULTS[:ylabel] = ""
|
||||
PLOT_DEFAULTS[:yrightlabel] = ""
|
||||
PLOT_DEFAULTS[:legend] = true
|
||||
# PLOT_DEFAULTS[:background_color] = nothing
|
||||
PLOT_DEFAULTS[:xticks] = true
|
||||
PLOT_DEFAULTS[:yticks] = true
|
||||
PLOT_DEFAULTS[:size] = (600,400)
|
||||
_plotDefaults[:title] = ""
|
||||
_plotDefaults[:xlabel] = ""
|
||||
_plotDefaults[:ylabel] = ""
|
||||
_plotDefaults[:yrightlabel] = ""
|
||||
_plotDefaults[:legend] = true
|
||||
_plotDefaults[:background_color] = colorant"white"
|
||||
_plotDefaults[:foreground_color] = :auto
|
||||
_plotDefaults[:xlims] = :auto
|
||||
_plotDefaults[:ylims] = :auto
|
||||
_plotDefaults[:xticks] = :auto
|
||||
_plotDefaults[:yticks] = :auto
|
||||
_plotDefaults[:xscale] = :identity
|
||||
_plotDefaults[:yscale] = :identity
|
||||
_plotDefaults[:xflip] = false
|
||||
_plotDefaults[:yflip] = false
|
||||
_plotDefaults[:size] = (600,400)
|
||||
_plotDefaults[:pos] = (0,0)
|
||||
_plotDefaults[:windowtitle] = "Plots.jl"
|
||||
_plotDefaults[:show] = false
|
||||
_plotDefaults[:layout] = nothing
|
||||
_plotDefaults[:n] = -1
|
||||
_plotDefaults[:nr] = -1
|
||||
_plotDefaults[:nc] = -1
|
||||
_plotDefaults[:color_palette] = :auto
|
||||
_plotDefaults[:link] = false
|
||||
_plotDefaults[:linkx] = false
|
||||
_plotDefaults[:linky] = false
|
||||
_plotDefaults[:linkfunc] = nothing
|
||||
_plotDefaults[:tickfont] = font(8)
|
||||
_plotDefaults[:guidefont] = font(11)
|
||||
_plotDefaults[:legendfont] = font(8)
|
||||
_plotDefaults[:grid] = true
|
||||
|
||||
|
||||
|
||||
# TODO: x/y scales
|
||||
|
||||
const _allArgs = sort(collect(union(keys(_seriesDefaults), keys(_plotDefaults))))
|
||||
supportedArgs(::PlottingPackage) = _allArgs
|
||||
supportedArgs() = supportedArgs(backend())
|
||||
|
||||
|
||||
@compat const _argNotes = Dict(
|
||||
:color => "Series color. To have different marker and/or fill colors, optionally set the markercolor and fillcolor args.",
|
||||
:z => "Determines the depth. For color gradients, we expect 0 ≤ z ≤ 1.",
|
||||
# :heatmap_c => "For Qwt heatmaps only... will be deprecated eventually.",
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
plotDefault(sym::Symbol) = PLOT_DEFAULTS[sym]
|
||||
function plotDefault!(sym::Symbol, val)
|
||||
PLOT_DEFAULTS[sym] = val
|
||||
makeplural(s::Symbol) = symbol(string(s,"s"))
|
||||
|
||||
autopick(arr::AVec, idx::Integer) = arr[mod1(idx,length(arr))]
|
||||
autopick(notarr, idx::Integer) = notarr
|
||||
|
||||
autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
|
||||
autopick_ignore_none_auto(notarr, idx::Integer) = notarr
|
||||
|
||||
function aliasesAndAutopick(d::Dict, sym::Symbol, aliases::Dict, options::AVec, plotIndex::Int)
|
||||
if d[sym] == :auto
|
||||
d[sym] = autopick_ignore_none_auto(options, plotIndex)
|
||||
elseif haskey(aliases, d[sym])
|
||||
d[sym] = aliases[d[sym]]
|
||||
end
|
||||
end
|
||||
|
||||
function aliases(aliasMap::Dict, val)
|
||||
# sort(vcat(val, collect(keys(filter((k,v)-> v==val, aliasMap)))))
|
||||
sortedkeys(filter((k,v)-> v==val, aliasMap))
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
makeplural(s::Symbol) = Symbol(string(s,"s"))
|
||||
autocolor(idx::Integer) = COLORS[mod1(idx,NUMCOLORS)]
|
||||
# Alternate args
|
||||
|
||||
@compat const _keyAliases = Dict(
|
||||
:c => :color,
|
||||
:lab => :label,
|
||||
:l => :line,
|
||||
:w => :linewidth,
|
||||
:width => :linewidth,
|
||||
:lw => :linewidth,
|
||||
:lo => :lineopacity,
|
||||
:type => :linetype,
|
||||
:lt => :linetype,
|
||||
:t => :linetype,
|
||||
:style => :linestyle,
|
||||
:s => :linestyle,
|
||||
:ls => :linestyle,
|
||||
:m => :marker,
|
||||
:mark => :marker,
|
||||
:shape => :markershape,
|
||||
:mc => :markercolor,
|
||||
:mcolor => :markercolor,
|
||||
:ms => :markersize,
|
||||
:msize => :markersize,
|
||||
:mo => :markeropacity,
|
||||
:opacity => :markeropacity,
|
||||
:alpha => :markeropacity,
|
||||
:f => :fill,
|
||||
:area => :fill,
|
||||
:fillrng => :fillrange,
|
||||
:fc => :fillcolor,
|
||||
:fcolor => :fillcolor,
|
||||
:fo => :fillopacity,
|
||||
:g => :group,
|
||||
:nb => :nbins,
|
||||
:nbin => :nbins,
|
||||
:rib => :ribbon,
|
||||
:ann => :annotation,
|
||||
:anns => :annotation,
|
||||
:annotate => :annotation,
|
||||
:annotations => :annotation,
|
||||
:xlab => :xlabel,
|
||||
:ylab => :ylabel,
|
||||
:yrlab => :yrightlabel,
|
||||
:ylabr => :yrightlabel,
|
||||
:y2lab => :yrightlabel,
|
||||
:ylab2 => :yrightlabel,
|
||||
:ylabelright => :yrightlabel,
|
||||
:ylabel2 => :yrightlabel,
|
||||
:y2label => :yrightlabel,
|
||||
:leg => :legend,
|
||||
:bg => :background_color,
|
||||
:bgcolor => :background_color,
|
||||
:bg_color => :background_color,
|
||||
:background => :background_color,
|
||||
:fg => :foreground_color,
|
||||
:fgcolor => :foreground_color,
|
||||
:fg_color => :foreground_color,
|
||||
:foreground => :foreground_color,
|
||||
:regression => :smooth,
|
||||
:reg => :smooth,
|
||||
:xlim => :xlims,
|
||||
:xlimit => :xlims,
|
||||
:xlimits => :xlims,
|
||||
:ylim => :ylims,
|
||||
:ylimit => :ylims,
|
||||
:ylimits => :ylims,
|
||||
:xtick => :xticks,
|
||||
:ytick => :yticks,
|
||||
:windowsize => :size,
|
||||
:wsize => :size,
|
||||
:wtitle => :windowtitle,
|
||||
:gui => :show,
|
||||
:display => :show,
|
||||
:palette => :color_palette,
|
||||
:xlink => :linkx,
|
||||
:ylink => :linky,
|
||||
)
|
||||
|
||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
||||
# note: if plt is nothing, we aren't doing anything with the color anyways
|
||||
function getRGBColor(c, n::Int = 0)
|
||||
|
||||
# auto-assign a color based on plot index
|
||||
if c == :auto && n > 0
|
||||
c = autocolor(n)
|
||||
end
|
||||
|
||||
# convert it from a symbol/string
|
||||
if isa(c, Symbol)
|
||||
c = string(c)
|
||||
end
|
||||
if isa(c, String)
|
||||
c = parse(Colorant, c)
|
||||
end
|
||||
|
||||
# should be a RGB now... either it was passed in, generated automatically, or created from a string
|
||||
@assert isa(c, RGB)
|
||||
|
||||
# return the RGB
|
||||
c
|
||||
# add all pluralized forms to the _keyAliases dict
|
||||
for arg in keys(_seriesDefaults)
|
||||
_keyAliases[makeplural(arg)] = arg
|
||||
end
|
||||
|
||||
|
||||
# note: idx is the index of this series within this call, n is the index of the series from all calls to plot/subplot
|
||||
function getPlotKeywordArgs(kw, idx::Int, n::Int)
|
||||
d = Dict(kw)
|
||||
|
||||
# default to a white background, but only on the initial call (so we don't change the background automatically)
|
||||
if haskey(d, :background_color)
|
||||
d[:background_color] = getRGBColor(d[:background_color])
|
||||
elseif n == 0
|
||||
d[:background_color] = colorant"white"
|
||||
end
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# fill in d with either 1) plural value, 2) value, 3) default
|
||||
for k in keys(PLOT_DEFAULTS)
|
||||
plural = makeplural(k)
|
||||
# if haskey(d, plural)
|
||||
# d[k] = d[plural][idx]
|
||||
if !haskey(d, k)
|
||||
if n == 0 || k != :size
|
||||
d[k] = haskey(d, plural) ? d[plural][idx] : PLOT_DEFAULTS[k]
|
||||
end
|
||||
end
|
||||
delete!(d, plural)
|
||||
end
|
||||
# update the defaults globally
|
||||
|
||||
"""
|
||||
`default(key)` returns the current default value for that key
|
||||
`default(key, value)` sets the current default value for that key
|
||||
`default(; kw...)` will set the current default value for each key/value pair
|
||||
"""
|
||||
|
||||
|
||||
# handle plot initialization differently
|
||||
if n == 0
|
||||
delete!(d, :x)
|
||||
delete!(d, :y)
|
||||
function default(k::Symbol)
|
||||
k = get(_keyAliases, k, k)
|
||||
if haskey(_seriesDefaults, k)
|
||||
return _seriesDefaults[k]
|
||||
elseif haskey(_plotDefaults, k)
|
||||
return _plotDefaults[k]
|
||||
else
|
||||
# once the plot is created, we can get line/marker colors
|
||||
|
||||
# update color
|
||||
d[:color] = getRGBColor(d[:color], n)
|
||||
|
||||
# update markercolor
|
||||
mc = d[:markercolor]
|
||||
mc = (mc == :match ? d[:color] : getRGBColor(mc, n))
|
||||
d[:markercolor] = mc
|
||||
|
||||
# set label
|
||||
label = d[:label]
|
||||
d[:label] = string(label == "AUTO" ? "y_$n" : label, d[:axis] == :left ? "" : " (R)")
|
||||
error("Unknown key: ", k)
|
||||
end
|
||||
end
|
||||
|
||||
function default(k::Symbol, v)
|
||||
k = get(_keyAliases, k, k)
|
||||
if haskey(_seriesDefaults, k)
|
||||
_seriesDefaults[k] = v
|
||||
elseif haskey(_plotDefaults, k)
|
||||
_plotDefaults[k] = v
|
||||
else
|
||||
error("Unknown key: ", k)
|
||||
end
|
||||
end
|
||||
|
||||
function default(; kw...)
|
||||
for (k,v) in kw
|
||||
default(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
wraptuple(x::@compat(Tuple)) = x
|
||||
wraptuple(x) = (x,)
|
||||
|
||||
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
|
||||
trueOrAllTrue(f::Function, x) = f(x)
|
||||
|
||||
function handleColors!(d::Dict, arg, csym::Symbol)
|
||||
try
|
||||
if arg == :auto
|
||||
d[csym] = :auto
|
||||
else
|
||||
c = colorscheme(arg)
|
||||
d[csym] = c
|
||||
end
|
||||
return true
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
# given one value (:log, or :flip, or (-1,1), etc), set the appropriate arg
|
||||
# TODO: use trueOrAllTrue for subplots which can pass vectors for these
|
||||
function processAxisArg(d::Dict, axisletter::@compat(AbstractString), arg)
|
||||
T = typeof(arg)
|
||||
# if T <: Symbol
|
||||
|
||||
arg = get(_scaleAliases, arg, arg)
|
||||
|
||||
if arg in _allScales
|
||||
d[symbol(axisletter * "scale")] = arg
|
||||
|
||||
elseif arg in (:flip, :invert, :inverted)
|
||||
d[symbol(axisletter * "flip")] = true
|
||||
|
||||
elseif T <: @compat(AbstractString)
|
||||
d[symbol(axisletter * "label")] = arg
|
||||
|
||||
# xlims/ylims
|
||||
elseif (T <: Tuple || T <: AVec) && length(arg) == 2
|
||||
d[symbol(axisletter * "lims")] = arg
|
||||
|
||||
# xticks/yticks
|
||||
elseif T <: AVec
|
||||
d[symbol(axisletter * "ticks")] = arg
|
||||
|
||||
elseif arg == nothing
|
||||
d[symbol(axisletter * "ticks")] = []
|
||||
|
||||
else
|
||||
warn("Skipped $(axisletter)axis arg $arg")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function processLineArg(d::Dict, arg)
|
||||
|
||||
# linetype
|
||||
if trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
|
||||
d[:linetype] = arg
|
||||
|
||||
# linestyle
|
||||
elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
|
||||
d[:linestyle] = arg
|
||||
|
||||
# linewidth
|
||||
elseif trueOrAllTrue(a -> typeof(a) <: Integer, arg)
|
||||
d[:linewidth] = arg
|
||||
|
||||
# lineopacity
|
||||
elseif trueOrAllTrue(a -> typeof(a) <: Real && a >= 0 && a <= 1, arg)
|
||||
d[:lineopacity] = arg
|
||||
|
||||
# color
|
||||
elseif !handleColors!(d, arg, :color)
|
||||
warn("Skipped line arg $arg.")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function processMarkerArg(d::Dict, arg)
|
||||
|
||||
# markershape
|
||||
if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg)
|
||||
d[:markershape] = arg
|
||||
elseif trueOrAllTrue(a -> isa(a, Shape), arg)
|
||||
d[:markershape] = arg
|
||||
|
||||
# markersize
|
||||
elseif trueOrAllTrue(a -> typeof(a) <: Integer, arg)
|
||||
d[:markersize] = arg
|
||||
|
||||
# lineopacity
|
||||
elseif trueOrAllTrue(a -> typeof(a) <: Real && a >= 0 && a <= 1, arg)
|
||||
d[:markeropacity] = arg
|
||||
|
||||
# markercolor
|
||||
elseif !handleColors!(d, arg, :markercolor)
|
||||
warn("Skipped marker arg $arg.")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function processFillArg(d::Dict, arg)
|
||||
if !handleColors!(d, arg, :fillcolor)
|
||||
d[:fillrange] = arg
|
||||
end
|
||||
end
|
||||
|
||||
"Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
|
||||
function preprocessArgs!(d::Dict)
|
||||
replaceAliases!(d, _keyAliases)
|
||||
|
||||
# handle axis args
|
||||
for axisletter in ("x", "y")
|
||||
asym = symbol(axisletter * "axis")
|
||||
for arg in wraptuple(get(d, asym, ()))
|
||||
processAxisArg(d, axisletter, arg)
|
||||
end
|
||||
delete!(d, asym)
|
||||
end
|
||||
|
||||
# handle line args
|
||||
for arg in wraptuple(get(d, :line, ()))
|
||||
processLineArg(d, arg)
|
||||
end
|
||||
delete!(d, :line)
|
||||
|
||||
# handle marker args... default to ellipse if shape not set
|
||||
anymarker = false
|
||||
for arg in wraptuple(get(d, :marker, ()))
|
||||
processMarkerArg(d, arg)
|
||||
anymarker = true
|
||||
end
|
||||
delete!(d, :marker)
|
||||
if anymarker && !haskey(d, :markershape)
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
# handle fill
|
||||
for arg in wraptuple(get(d, :fill, ()))
|
||||
processFillArg(d, arg)
|
||||
end
|
||||
delete!(d, :fill)
|
||||
|
||||
# handle subplot links
|
||||
if haskey(d, :link)
|
||||
l = d[:link]
|
||||
if isa(l, Bool)
|
||||
d[:linkx] = l
|
||||
d[:linky] = l
|
||||
elseif isa(l, Function)
|
||||
d[:linkx] = true
|
||||
d[:linky] = true
|
||||
d[:linkfunc] = l
|
||||
else
|
||||
warn("Unhandled/invalid link $l. Should be a Bool or a function mapping (row,column) -> (linkx, linky), where linkx/y can be Bool or Void (nothing)")
|
||||
end
|
||||
delete!(d, :link)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
"A special type that will break up incoming data into groups, and allow for easier creation of grouped plots"
|
||||
type GroupBy
|
||||
groupLabels::Vector{UTF8String} # length == numGroups
|
||||
groupIds::Vector{Vector{Int}} # list of indices for each group
|
||||
end
|
||||
|
||||
|
||||
# this is when given a vector-type of values to group by
|
||||
function extractGroupArgs(v::AVec, args...)
|
||||
groupLabels = sort(collect(unique(v)))
|
||||
n = length(groupLabels)
|
||||
if n > 20
|
||||
error("Too many group labels. n=$n Is that intended?")
|
||||
end
|
||||
groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels]
|
||||
GroupBy(map(string, groupLabels), groupIds)
|
||||
end
|
||||
|
||||
|
||||
# expecting a mapping of "group label" to "group indices"
|
||||
function extractGroupArgs{T, V<:AVec{Int}}(idxmap::Dict{T,V}, args...)
|
||||
groupLabels = sortedkeys(idxmap)
|
||||
groupIds = VecI[collect(idxmap[k]) for k in groupLabels]
|
||||
GroupBy(groupLabels, groupIds)
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function warnOnUnsupportedArgs(pkg::PlottingPackage, d::Dict)
|
||||
for k in sortedkeys(d)
|
||||
if (!(k in supportedArgs(pkg))
|
||||
&& k != :subplot
|
||||
&& d[k] != default(k))
|
||||
warn("Keyword argument $k not supported with $pkg. Choose from: $(supportedArgs(pkg))")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
|
||||
(d[:axis] in supportedAxes(pkg)
|
||||
|| warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))"))
|
||||
(d[:linetype] == :none
|
||||
|| d[:linetype] in supportedTypes(pkg)
|
||||
|| warn("linetype $(d[:linetype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))"))
|
||||
(d[:linestyle] in supportedStyles(pkg)
|
||||
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))"))
|
||||
(d[:markershape] == :none
|
||||
|| d[:markershape] in supportedMarkers(pkg)
|
||||
|| (Shape in supportedMarkers(pkg) && typeof(d[:markershape]) <: Shape)
|
||||
|| warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))"))
|
||||
end
|
||||
|
||||
function warnOnUnsupportedScales(pkg::PlottingPackage, d::Dict)
|
||||
for k in (:xscale, :yscale)
|
||||
if haskey(d, k)
|
||||
d[k] in supportedScales(pkg) || warn("scale $(d[k]) is unsupported with $pkg. Choose from: $(supportedScales(pkg))")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# 1-row matrices will give an element
|
||||
# multi-row matrices will give a column
|
||||
# anything else is returned as-is
|
||||
# getArgValue(v::Tuple, idx::Int) = v[mod1(idx, length(v))]
|
||||
function getArgValue(v::AMat, idx::Int)
|
||||
c = mod1(idx, size(v,2))
|
||||
size(v,1) == 1 ? v[1,c] : v[:,c]
|
||||
end
|
||||
getArgValue(v, idx) = v
|
||||
|
||||
|
||||
# given an argument key (k), we want to extract the argument value for this index.
|
||||
# if nothing is set (or container is empty), return the default.
|
||||
function setDictValue(d_in::Dict, d_out::Dict, k::Symbol, idx::Int, defaults::Dict)
|
||||
if haskey(d_in, k) && !(typeof(d_in[k]) <: @compat(Union{AbstractArray, Tuple}) && isempty(d_in[k]))
|
||||
d_out[k] = getArgValue(d_in[k], idx)
|
||||
else
|
||||
d_out[k] = defaults[k]
|
||||
end
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# build the argument dictionary for the plot
|
||||
function getPlotArgs(pkg::PlottingPackage, kw, idx::Int; set_defaults = true)
|
||||
kwdict = Dict(kw)
|
||||
d = Dict()
|
||||
|
||||
# add defaults?
|
||||
if set_defaults
|
||||
for k in keys(_plotDefaults)
|
||||
setDictValue(kwdict, d, k, idx, _plotDefaults)
|
||||
end
|
||||
end
|
||||
|
||||
for k in (:xscale, :yscale)
|
||||
if haskey(_scaleAliases, d[k])
|
||||
d[k] = _scaleAliases[d[k]]
|
||||
end
|
||||
end
|
||||
|
||||
# convert color
|
||||
handlePlotColors(pkg, d)
|
||||
|
||||
# no need for these
|
||||
delete!(d, :x)
|
||||
delete!(d, :y)
|
||||
|
||||
d
|
||||
end
|
||||
|
||||
|
||||
|
||||
# build the argument dictionary for a series
|
||||
function getSeriesArgs(pkg::PlottingPackage, initargs::Dict, kw, commandIndex::Int, plotIndex::Int, globalIndex::Int) # TODO, pass in initargs, not plt
|
||||
kwdict = Dict(kw)
|
||||
d = Dict()
|
||||
|
||||
# add defaults?
|
||||
for k in keys(_seriesDefaults)
|
||||
setDictValue(kwdict, d, k, commandIndex, _seriesDefaults)
|
||||
end
|
||||
|
||||
# groupby args?
|
||||
for k in (:idxfilter, :numUncounted, :dataframe)
|
||||
if haskey(kwdict, k)
|
||||
d[k] = kwdict[k]
|
||||
end
|
||||
end
|
||||
|
||||
if haskey(_typeAliases, d[:linetype])
|
||||
d[:linetype] = _typeAliases[d[:linetype]]
|
||||
end
|
||||
|
||||
aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex)
|
||||
aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex)
|
||||
aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex)
|
||||
|
||||
# update color
|
||||
d[:color] = getSeriesRGBColor(d[:color], initargs, plotIndex)
|
||||
|
||||
# update markercolor
|
||||
mc = d[:markercolor]
|
||||
mc = (mc == :match ? d[:color] : getSeriesRGBColor(mc, initargs, plotIndex))
|
||||
d[:markercolor] = mc
|
||||
|
||||
# update fillcolor
|
||||
mc = d[:fillcolor]
|
||||
mc = (mc == :match ? d[:color] : getSeriesRGBColor(mc, initargs, plotIndex))
|
||||
d[:fillcolor] = mc
|
||||
|
||||
# set label
|
||||
label = d[:label]
|
||||
label = (label == "AUTO" ? "y$globalIndex" : label)
|
||||
if d[:axis] == :right && !(length(label) >= 4 && label[end-3:end] != " (R)")
|
||||
label = string(label, " (R)")
|
||||
end
|
||||
d[:label] = label
|
||||
|
||||
warnOnUnsupported(pkg, d)
|
||||
|
||||
|
||||
d
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,642 @@
|
||||
|
||||
# https://github.com/dcjones/Gadfly.jl
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function createGadflyPlotObject(d::Dict)
|
||||
gplt = Gadfly.Plot()
|
||||
gplt.mapping = Dict()
|
||||
gplt.data_source = DataFrames.DataFrame()
|
||||
gplt.layers = gplt.layers[1:0]
|
||||
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
|
||||
Gadfly.Guide.ylabel(d[:ylabel]),
|
||||
Gadfly.Guide.title(d[:title])]
|
||||
gplt
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function getLineGeom(d::Dict)
|
||||
lt = d[:linetype]
|
||||
xbins, ybins = maketuple(d[:nbins])
|
||||
if lt == :hexbin
|
||||
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
|
||||
elseif lt == :heatmap
|
||||
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
|
||||
elseif lt == :hist
|
||||
Gadfly.Geom.histogram(bincount = xbins)
|
||||
elseif lt == :path
|
||||
Gadfly.Geom.path
|
||||
elseif lt in (:bar, :sticks)
|
||||
Gadfly.Geom.bar
|
||||
elseif lt == :steppost
|
||||
Gadfly.Geom.step
|
||||
elseif lt == :steppre
|
||||
Gadfly.Geom.step(direction = :vh)
|
||||
elseif lt == :hline
|
||||
Gadfly.Geom.hline(color = getColor(d[:color]), size = d[:linewidth] * Gadfly.px)
|
||||
elseif lt == :vline
|
||||
Gadfly.Geom.vline(color = getColor(d[:color]), size = d[:linewidth] * Gadfly.px)
|
||||
elseif lt == :contour
|
||||
Gadfly.Geom.contour(levels = d[:nlevels])
|
||||
else
|
||||
nothing
|
||||
end
|
||||
end
|
||||
|
||||
function getGadflyLineTheme(d::Dict)
|
||||
|
||||
lc = getColor(d[:color])
|
||||
α = d[:lineopacity]
|
||||
if α != nothing
|
||||
lc = RGBA(lc, α)
|
||||
end
|
||||
|
||||
fc = getColor(d[:fillcolor])
|
||||
α = d[:fillopacity]
|
||||
if α != nothing
|
||||
fc = RGBA(fc, α)
|
||||
end
|
||||
|
||||
Gadfly.Theme(;
|
||||
default_color = lc,
|
||||
line_width = (d[:linetype] == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
|
||||
line_style = Gadfly.get_stroke_vector(d[:linestyle]),
|
||||
lowlight_color = x->RGB(fc), # fill/ribbon
|
||||
lowlight_opacity = alpha(fc), # fill/ribbon
|
||||
bar_highlight = RGB(lc), # bars
|
||||
)
|
||||
end
|
||||
|
||||
# add a line as a new layer
|
||||
function addGadflyLine!(plt::Plot, d::Dict, geoms...)
|
||||
gplt = getGadflyContext(plt)
|
||||
gfargs = vcat(geoms...,
|
||||
getGadflyLineTheme(d))
|
||||
kwargs = Dict()
|
||||
lt = d[:linetype]
|
||||
|
||||
# add a fill?
|
||||
if d[:fillrange] != nothing && lt != :contour
|
||||
fillmin, fillmax = map(makevec, maketuple(d[:fillrange]))
|
||||
nmin, nmax = length(fillmin), length(fillmax)
|
||||
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
|
||||
kwargs[:ymax] = Float64[max(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
|
||||
push!(gfargs, Gadfly.Geom.ribbon)
|
||||
end
|
||||
|
||||
# h/vlines
|
||||
if lt == :hline
|
||||
kwargs[:yintercept] = d[:y]
|
||||
elseif lt == :vline
|
||||
kwargs[:xintercept] = d[:y]
|
||||
elseif lt == :sticks
|
||||
w = 0.01 * mean(diff(d[:x]))
|
||||
kwargs[:xmin] = d[:x] - w
|
||||
kwargs[:xmax] = d[:x] + w
|
||||
elseif lt == :contour
|
||||
d[:y] = reverse(d[:y])
|
||||
kwargs[:z] = d[:surface]
|
||||
end
|
||||
|
||||
# add the layer
|
||||
x = d[d[:linetype] == :hist ? :y : :x]
|
||||
Gadfly.layer(gfargs...; x = x, y = d[:y], kwargs...)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function getMarkerGeom(d::Dict)
|
||||
shape = d[:markershape]
|
||||
gadflyshape(isa(shape, Shape) ? shape : _shapes[shape])
|
||||
end
|
||||
|
||||
|
||||
function getGadflyMarkerTheme(d::Dict, initargs::Dict)
|
||||
c = getColor(d[:markercolor])
|
||||
α = d[:markeropacity]
|
||||
if α != nothing
|
||||
c = RGBA(RGB(c), α)
|
||||
end
|
||||
|
||||
fg = getColor(initargs[:foreground_color])
|
||||
Gadfly.Theme(
|
||||
default_color = c,
|
||||
default_point_size = d[:markersize] * Gadfly.px,
|
||||
discrete_highlight_color = c -> RGB(fg),
|
||||
highlight_width = d[:linewidth] * Gadfly.px,
|
||||
)
|
||||
end
|
||||
|
||||
function addGadflyMarker!(plt::Plot, d::Dict, initargs::Dict, geoms...)
|
||||
gfargs = vcat(geoms...,
|
||||
getGadflyMarkerTheme(d, initargs),
|
||||
getMarkerGeom(d))
|
||||
kwargs = Dict()
|
||||
|
||||
# handle continuous color scales for the markers
|
||||
z = d[:z]
|
||||
if z != nothing && typeof(z) <: AVec
|
||||
kwargs[:color] = z
|
||||
if !isa(d[:markercolor], ColorGradient)
|
||||
d[:markercolor] = colorscheme(:bluesreds)
|
||||
end
|
||||
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:markercolor], p))))
|
||||
end
|
||||
|
||||
Gadfly.layer(gfargs...; x = d[:x], y = d[:y], kwargs...)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function addToGadflyLegend(plt::Plot, d::Dict)
|
||||
|
||||
# add the legend?
|
||||
if plt.initargs[:legend]
|
||||
gplt = getGadflyContext(plt)
|
||||
|
||||
# add the legend if needed
|
||||
if all(g -> !isa(g, Gadfly.Guide.ManualColorKey), gplt.guides)
|
||||
unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", @compat(AbstractString)[], Color[]))
|
||||
end
|
||||
|
||||
# now add the series to the legend
|
||||
for guide in gplt.guides
|
||||
if isa(guide, Gadfly.Guide.ManualColorKey)
|
||||
# TODO: there's a BUG in gadfly if you pass in the same color more than once,
|
||||
# since gadfly will call unique(colors), but doesn't also merge the rows that match
|
||||
# Should ensure from this side that colors which are the same are merged together
|
||||
|
||||
c = getColor(d[d[:markershape] == :none ? :color : :markercolor])
|
||||
foundit = false
|
||||
|
||||
# extend the label if we found this color
|
||||
for i in 1:length(guide.colors)
|
||||
if RGB(c) == guide.colors[i]
|
||||
guide.labels[i] *= ", " * d[:label]
|
||||
foundit = true
|
||||
end
|
||||
end
|
||||
|
||||
# didn't find the color, so add a new entry into the legend
|
||||
if !foundit
|
||||
push!(guide.labels, d[:label])
|
||||
push!(guide.colors, c)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
getGadflySmoothing(smooth::Bool) = smooth ? [Gadfly.Geom.smooth(method=:lm)] : Any[]
|
||||
getGadflySmoothing(smooth::Real) = [Gadfly.Geom.smooth(method=:loess, smoothing=float(smooth))]
|
||||
|
||||
|
||||
function addGadflySeries!(plt::Plot, d::Dict)
|
||||
|
||||
layers = Gadfly.Layer[]
|
||||
|
||||
# add a regression line?
|
||||
# TODO: make more flexible
|
||||
smooth = getGadflySmoothing(d[:smooth])
|
||||
|
||||
# lines
|
||||
geom = getLineGeom(d)
|
||||
if geom != nothing
|
||||
prepend!(layers, addGadflyLine!(plt, d, geom, smooth...))
|
||||
|
||||
# don't add a regression for markers too
|
||||
smooth = Any[]
|
||||
end
|
||||
|
||||
# special handling for ohlc and scatter
|
||||
lt = d[:linetype]
|
||||
if lt == :ohlc
|
||||
error("Haven't re-implemented after refactoring")
|
||||
elseif lt == :scatter && d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
# markers
|
||||
if d[:markershape] != :none
|
||||
prepend!(layers, addGadflyMarker!(plt, d, plt.initargs, smooth...))
|
||||
end
|
||||
|
||||
lt in (:hist, :heatmap, :hexbin, :contour) || addToGadflyLegend(plt, d)
|
||||
|
||||
# now save the layers that apply to this series
|
||||
d[:gadflylayers] = layers
|
||||
prepend!(getGadflyContext(plt).layers, layers)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# NOTE: I'm leaving this here and commented out just in case I want to implement again... it was hacky code to create multi-colored line segments
|
||||
|
||||
# # colorgroup
|
||||
# z = d[:z]
|
||||
|
||||
# # handle line segments of different colors
|
||||
# cscheme = d[:color]
|
||||
# if isa(cscheme, ColorVector)
|
||||
# # create a color scale, and set the color group to the index of the color
|
||||
# push!(gplt.scales, Gadfly.Scale.color_discrete_manual(cscheme.v...))
|
||||
|
||||
# # this is super weird, but... oh well... for some reason this creates n separate line segments...
|
||||
# # create a list of vertices that go: [x1,x2,x2,x3,x3, ... ,xi,xi, ... xn,xn] (same for y)
|
||||
# # then the vector passed to the "color" keyword should be a vector: [1,1,2,2,3,3,4,4, ..., i,i, ... , n,n]
|
||||
# csindices = Int[mod1(i,length(cscheme.v)) for i in 1:length(d[:y])]
|
||||
# cs = collect(repmat(csindices', 2, 1))[1:end-1]
|
||||
# grp = collect(repmat((1:length(d[:y]))', 2, 1))[1:end-1]
|
||||
# d[:x], d[:y] = map(createSegments, (d[:x], d[:y]))
|
||||
# colorgroup = [(:color, cs), (:group, grp)]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function addGadflyTicksGuide(gplt, ticks, isx::Bool)
|
||||
ticks == :auto && return
|
||||
|
||||
# remove the ticks?
|
||||
if ticks in (:none, false, nothing)
|
||||
return addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
||||
end
|
||||
|
||||
ttype = ticksType(ticks)
|
||||
|
||||
# just the values... put ticks here, but use standard labels
|
||||
if ttype == :ticks
|
||||
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
|
||||
replaceType(gplt.guides, gtype(ticks = collect(ticks)))
|
||||
|
||||
# set the ticks and the labels
|
||||
elseif ttype == :ticks_and_labels
|
||||
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
|
||||
replaceType(gplt.guides, gtype(ticks = collect(ticks[1])))
|
||||
|
||||
# TODO add xtick_label function (given tick, return label??)
|
||||
# Scale.x_discrete(; labels=nothing, levels=nothing, order=nothing)
|
||||
filterGadflyScale(gplt, isx)
|
||||
gfunc = isx ? Gadfly.Scale.x_discrete : Gadfly.Scale.y_discrete
|
||||
labelmap = Dict(zip(ticks...))
|
||||
labelfunc = val -> labelmap[val]
|
||||
push!(gplt.scales, gfunc(levels = ticks[1], labels = labelfunc))
|
||||
|
||||
else
|
||||
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
|
||||
end
|
||||
end
|
||||
|
||||
continuousAndSameAxis(scale, isx::Bool) = isa(scale, Gadfly.Scale.ContinuousScale) && scale.vars[1] == (isx ? :x : :y)
|
||||
filterGadflyScale(gplt, isx::Bool) = filter!(scale -> !continuousAndSameAxis(scale, isx), gplt.scales)
|
||||
|
||||
|
||||
function getGadflyScaleFunction(d::Dict, isx::Bool)
|
||||
scalekey = isx ? :xscale : :yscale
|
||||
hasScaleKey = haskey(d, scalekey)
|
||||
if hasScaleKey
|
||||
scale = d[scalekey]
|
||||
scale == :log && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey
|
||||
scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey
|
||||
scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey
|
||||
scale == :asinh && return isx ? Gadfly.Scale.x_asinh : Gadfly.Scale.y_asinh, hasScaleKey
|
||||
scale == :sqrt && return isx ? Gadfly.Scale.x_sqrt : Gadfly.Scale.y_sqrt, hasScaleKey
|
||||
end
|
||||
isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous, hasScaleKey
|
||||
end
|
||||
|
||||
|
||||
function addGadflyLimitsScale(gplt, d::Dict, isx::Bool)
|
||||
|
||||
# get the correct scale function
|
||||
gfunc, hasScaleKey = getGadflyScaleFunction(d, isx)
|
||||
|
||||
# do we want to add min/max limits for the axis?
|
||||
limsym = isx ? :xlims : :ylims
|
||||
limargs = Any[]
|
||||
lims = get(d, limsym, :auto)
|
||||
lims == :auto && return
|
||||
|
||||
if limsType(lims) == :limits
|
||||
push!(limargs, (:minvalue, min(lims...)))
|
||||
push!(limargs, (:maxvalue, max(lims...)))
|
||||
else
|
||||
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
|
||||
end
|
||||
|
||||
# replace any current scales with this one
|
||||
if hasScaleKey || !isempty(limargs)
|
||||
filterGadflyScale(gplt, isx)
|
||||
push!(gplt.scales, gfunc(; limargs...))
|
||||
end
|
||||
|
||||
lims
|
||||
end
|
||||
|
||||
function updateGadflyAxisFlips(gplt, d::Dict, xlims, ylims)
|
||||
if isa(gplt.coord, Gadfly.Coord.Cartesian)
|
||||
gplt.coord = Gadfly.Coord.cartesian(
|
||||
gplt.coord.xvars,
|
||||
gplt.coord.yvars;
|
||||
xmin = xlims == nothing ? gplt.coord.xmin : minimum(xlims),
|
||||
xmax = xlims == nothing ? gplt.coord.xmax : maximum(xlims),
|
||||
ymin = ylims == nothing ? gplt.coord.ymin : minimum(ylims),
|
||||
ymax = ylims == nothing ? gplt.coord.ymax : maximum(ylims),
|
||||
xflip = get(d, :xflip, gplt.coord.xflip),
|
||||
yflip = get(d, :yflip, gplt.coord.yflip),
|
||||
fixed = gplt.coord.fixed,
|
||||
aspect_ratio = gplt.coord.aspect_ratio,
|
||||
raster = gplt.coord.raster
|
||||
)
|
||||
else
|
||||
gplt.coord = Gadfly.Coord.Cartesian(
|
||||
xflip = get(d, :xflip, false),
|
||||
yflip = get(d, :yflip, false)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function findGuideAndSet(gplt, t::DataType, args...; kw...) #s::@compat(AbstractString))
|
||||
for (i,guide) in enumerate(gplt.guides)
|
||||
if isa(guide, t)
|
||||
gplt.guides[i] = t(args...; kw...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function updateGadflyGuides(plt::Plot, d::Dict)
|
||||
gplt = getGadflyContext(plt)
|
||||
haskey(d, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(d[:title]))
|
||||
haskey(d, :xlabel) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xlabel]))
|
||||
haskey(d, :ylabel) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:ylabel]))
|
||||
|
||||
xlims = addGadflyLimitsScale(gplt, d, true)
|
||||
ylims = addGadflyLimitsScale(gplt, d, false)
|
||||
|
||||
ticks = get(d, :xticks, :auto)
|
||||
if ticks == :none
|
||||
handleLinkInner(plt, true)
|
||||
else
|
||||
addGadflyTicksGuide(gplt, ticks, true)
|
||||
end
|
||||
ticks = get(d, :yticks, :auto)
|
||||
if ticks == :none
|
||||
handleLinkInner(plt, false)
|
||||
else
|
||||
addGadflyTicksGuide(gplt, ticks, false)
|
||||
end
|
||||
# haskey(d, :yticks) && addGadflyTicksGuide(gplt, d[:yticks], false)
|
||||
|
||||
updateGadflyAxisFlips(gplt, d, xlims, ylims)
|
||||
end
|
||||
|
||||
function updateGadflyPlotTheme(plt::Plot, d::Dict)
|
||||
kwargs = Dict()
|
||||
|
||||
# # get the full initargs, overriding any new settings
|
||||
# # TODO: should this be part of the main `plot` command in plot.jl???
|
||||
# d = merge!(plt.initargs, d)
|
||||
|
||||
# hide the legend?
|
||||
if !get(d, :legend, true)
|
||||
kwargs[:key_position] = :none
|
||||
end
|
||||
|
||||
if !get(d, :grid, true)
|
||||
kwargs[:grid_color] = getColor(d[:background_color])
|
||||
end
|
||||
|
||||
# fonts
|
||||
tfont, gfont, lfont = d[:tickfont], d[:guidefont], d[:legendfont]
|
||||
|
||||
fg = getColor(d[:foreground_color])
|
||||
getGadflyContext(plt).theme = Gadfly.Theme(;
|
||||
background_color = getColor(d[:background_color]),
|
||||
minor_label_color = fg,
|
||||
minor_label_font = tfont.family,
|
||||
minor_label_font_size = tfont.pointsize * Gadfly.pt,
|
||||
major_label_color = fg,
|
||||
major_label_font = gfont.family,
|
||||
major_label_font_size = gfont.pointsize * Gadfly.pt,
|
||||
key_title_color = fg,
|
||||
key_title_font = gfont.family,
|
||||
key_title_font_size = gfont.pointsize * Gadfly.pt,
|
||||
key_label_color = fg,
|
||||
key_label_font = lfont.family,
|
||||
key_label_font_size = lfont.pointsize * Gadfly.pt,
|
||||
plot_padding = 1 * Gadfly.mm,
|
||||
kwargs...
|
||||
)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
function createGadflyAnnotationObject(x, y, val::@compat(AbstractString))
|
||||
Gadfly.Guide.annotation(Compose.compose(
|
||||
Compose.context(),
|
||||
Compose.text(x, y, val)
|
||||
))
|
||||
end
|
||||
|
||||
function createGadflyAnnotationObject(x, y, txt::PlotText)
|
||||
halign = (txt.font.halign == :hcenter ? Compose.hcenter : (txt.font.halign == :left ? Compose.hleft : Compose.hright))
|
||||
valign = (txt.font.valign == :vcenter ? Compose.vcenter : (txt.font.valign == :top ? Compose.vtop : Compose.vbottom))
|
||||
rotations = (txt.font.rotation == 0.0 ? [] : [Compose.Rotation(txt.font.rotation, Compose.Point(Compose.x_measure(x), Compose.y_measure(y)))])
|
||||
Gadfly.Guide.annotation(Compose.compose(
|
||||
Compose.context(),
|
||||
Compose.text(x, y, txt.str, halign, valign, rotations...),
|
||||
Compose.font(string(txt.font.family)),
|
||||
Compose.fontsize(txt.font.pointsize * Gadfly.pt),
|
||||
Compose.stroke(txt.font.color),
|
||||
Compose.fill(txt.font.color)
|
||||
))
|
||||
end
|
||||
|
||||
function addAnnotations{X,Y,V}(plt::Plot{GadflyPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
function plot(pkg::GadflyPackage; kw...)
|
||||
d = Dict(kw)
|
||||
gplt = createGadflyPlotObject(d)
|
||||
Plot(gplt, pkg, 0, d, Dict[])
|
||||
end
|
||||
|
||||
|
||||
# plot one data series
|
||||
function plot!(::GadflyPackage, plt::Plot; kw...)
|
||||
d = Dict(kw)
|
||||
addGadflySeries!(plt, d)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
|
||||
function updatePlotItems(plt::Plot{GadflyPackage}, d::Dict)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
# TODO: need to save all the layer indices which apply to this series
|
||||
function getGadflyMappings(plt::Plot, i::Integer)
|
||||
@assert i > 0 && i <= plt.n
|
||||
mappings = [l.mapping for l in plt.seriesargs[i][:gadflylayers]]
|
||||
end
|
||||
|
||||
function Base.getindex(plt::Plot{GadflyPackage}, i::Integer)
|
||||
mapping = getGadflyMappings(plt, i)[1]
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{GadflyPackage}, xy::Tuple, i::Integer)
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
# create the underlying object (each backend will do this differently)
|
||||
function buildSubplotObject!(subplt::Subplot{GadflyPackage}, isbefore::Bool)
|
||||
isbefore && return false # wait until after plotting to create the subplots
|
||||
subplt.o = nothing
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
function handleLinkInner(plt::Plot{GadflyPackage}, isx::Bool)
|
||||
gplt = getGadflyContext(plt)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
|
||||
end
|
||||
|
||||
function expandLimits!(lims, plt::Plot{GadflyPackage}, isx::Bool)
|
||||
for l in getGadflyContext(plt).layers
|
||||
expandLimits!(lims, l.mapping[isx ? :x : :y])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
getGadflyContext(plt::Plot{GadflyPackage}) = plt.o
|
||||
getGadflyContext(subplt::Subplot{GadflyPackage}) = buildGadflySubplotContext(subplt)
|
||||
|
||||
# create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
|
||||
function buildGadflySubplotContext(subplt::Subplot)
|
||||
rows = Any[]
|
||||
row = Any[]
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
|
||||
# add the Plot object to the row
|
||||
push!(row, getGadflyContext(subplt.plts[i]))
|
||||
|
||||
# add the row
|
||||
if c == ncols(subplt.layout, r)
|
||||
push!(rows, Gadfly.hstack(row...))
|
||||
row = Any[]
|
||||
end
|
||||
end
|
||||
|
||||
# stack the rows
|
||||
Gadfly.vstack(rows...)
|
||||
end
|
||||
|
||||
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
|
||||
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.initargs[:size]...)
|
||||
setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getinitargs(subplt, 1)[:size]...)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
function dowritemime{P<:GadflyOrImmerse}(io::IO, func, plt::PlottingObject{P})
|
||||
gplt = getGadflyContext(plt)
|
||||
setGadflyDisplaySize(plt)
|
||||
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
|
||||
end
|
||||
|
||||
getGadflyWriteFunc(::MIME"image/png") = Gadfly.PNG
|
||||
getGadflyWriteFunc(::MIME"image/svg+xml") = Gadfly.SVG
|
||||
# getGadflyWriteFunc(::MIME"text/html") = Gadfly.SVGJS
|
||||
getGadflyWriteFunc(::MIME"application/pdf") = Gadfly.PDF
|
||||
getGadflyWriteFunc(::MIME"application/postscript") = Gadfly.PS
|
||||
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
|
||||
|
||||
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript")
|
||||
@eval function Base.writemime{P<:GadflyOrImmerse}(io::IO, ::$mime, plt::PlottingObject{P})
|
||||
func = getGadflyWriteFunc($mime())
|
||||
dowritemime(io, func, plt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{GadflyPackage})
|
||||
setGadflyDisplaySize(plt.initargs[:size]...)
|
||||
display(plt.o)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{GadflyPackage})
|
||||
setGadflyDisplaySize(getinitargs(subplt,1)[:size]...)
|
||||
ctx = buildGadflySubplotContext(subplt)
|
||||
|
||||
|
||||
# taken from Gadfly since I couldn't figure out how to do it directly
|
||||
|
||||
filename = string(Gadfly.tempname(), ".html")
|
||||
output = open(filename, "w")
|
||||
|
||||
plot_output = IOBuffer()
|
||||
Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
|
||||
Compose.default_graphic_height, false), ctx)
|
||||
plotsvg = takebuf_string(plot_output)
|
||||
|
||||
write(output,
|
||||
"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Gadfly Plot</title>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script charset="utf-8">
|
||||
$(readall(Compose.snapsvgjs))
|
||||
</script>
|
||||
<script charset="utf-8">
|
||||
$(readall(Gadfly.gadflyjs))
|
||||
</script>
|
||||
$(plotsvg)
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
close(output)
|
||||
Gadfly.open_file(filename)
|
||||
end
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
|
||||
# Geometry which displays arbitrary shapes at given (x, y) positions.
|
||||
immutable ShapeGeometry <: Gadfly.GeometryElement
|
||||
vertices::AbstractVector{@compat(Tuple{Float64,Float64})}
|
||||
tag::Symbol
|
||||
|
||||
function ShapeGeometry(shape; tag::Symbol=Gadfly.Geom.empty_tag)
|
||||
new(shape, tag)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: add for PR
|
||||
# const shape = ShapeGeometry
|
||||
|
||||
|
||||
function Gadfly.element_aesthetics(::ShapeGeometry)
|
||||
[:x, :y, :size, :color]
|
||||
end
|
||||
|
||||
|
||||
# Generate a form for a shape geometry.
|
||||
#
|
||||
# Args:
|
||||
# geom: shape geometry.
|
||||
# theme: the plot's theme.
|
||||
# aes: aesthetics.
|
||||
#
|
||||
# Returns:
|
||||
# A compose Form.
|
||||
#
|
||||
function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics)
|
||||
|
||||
# TODO: add for PR
|
||||
# Gadfly.assert_aesthetics_defined("Geom.shape", aes, :x, :y)
|
||||
# Gadfly.assert_aesthetics_equal_length("Geom.shape", aes,
|
||||
# element_aesthetics(geom)...)
|
||||
|
||||
default_aes = Gadfly.Aesthetics()
|
||||
default_aes.color = Gadfly.DataFrames.PooledDataArray(RGBA{Float32}[theme.default_color])
|
||||
default_aes.size = Compose.Measure[theme.default_point_size]
|
||||
aes = Gadfly.inherit(aes, default_aes)
|
||||
|
||||
lw_hover_scale = 10
|
||||
lw_ratio = theme.line_width / aes.size[1]
|
||||
|
||||
aes_x, aes_y = Gadfly.concretize(aes.x, aes.y)
|
||||
|
||||
ctx = Compose.compose!(
|
||||
Compose.context(),
|
||||
make_polygon(geom, aes.x, aes.y, aes.size),
|
||||
Compose.fill(aes.color),
|
||||
Compose.linewidth(theme.highlight_width))
|
||||
|
||||
if aes.color_key_continuous != nothing && aes.color_key_continuous
|
||||
Compose.compose!(ctx,
|
||||
Compose.stroke(map(theme.continuous_highlight_color, aes.color)))
|
||||
else
|
||||
Compose.compose!(ctx,
|
||||
Compose.stroke(map(theme.discrete_highlight_color, aes.color)),
|
||||
Compose.svgclass([Gadfly.svg_color_class_from_label(Gadfly.escape_id(aes.color_label([c])[1]))
|
||||
for c in aes.color]))
|
||||
end
|
||||
|
||||
return Compose.compose!(Compose.context(order=4), Compose.svgclass("geometry"), ctx)
|
||||
end
|
||||
|
||||
function gadflyshape(sv::Shape)
|
||||
ShapeGeometry([(x,-y) for (x,y) in sv.vertices])
|
||||
end
|
||||
|
||||
|
||||
# create a Compose context given a ShapeGeometry and the xs/ys/sizes
|
||||
function make_polygon(geom::ShapeGeometry, xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
|
||||
n = max(length(xs), length(ys), length(rs))
|
||||
T = @compat(Tuple{Compose.Measure, Compose.Measure})
|
||||
polys = Array(Vector{T}, n)
|
||||
for i in 1:n
|
||||
x = Compose.x_measure(xs[mod1(i, length(xs))])
|
||||
y = Compose.y_measure(ys[mod1(i, length(ys))])
|
||||
r = rs[mod1(i, length(rs))]
|
||||
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in geom.vertices]
|
||||
end
|
||||
Gadfly.polygon(polys, geom.tag)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
|
||||
# https://github.com/JuliaGraphics/Immerse.jl
|
||||
|
||||
# immutable ImmersePackage <: PlottingPackage end
|
||||
|
||||
# export immerse
|
||||
# immerse() = backend(:immerse)
|
||||
|
||||
|
||||
# supportedArgs(::ImmersePackage) = supportedArgs(GadflyPackage())
|
||||
# supportedAxes(::ImmersePackage) = supportedAxes(GadflyPackage())
|
||||
# supportedTypes(::ImmersePackage) = supportedTypes(GadflyPackage())
|
||||
# supportedStyles(::ImmersePackage) = supportedStyles(GadflyPackage())
|
||||
# supportedMarkers(::ImmersePackage) = supportedMarkers(GadflyPackage())
|
||||
# supportedScales(::ImmersePackage) = supportedScales(GadflyPackage())
|
||||
|
||||
|
||||
function createImmerseFigure(d::Dict)
|
||||
w,h = d[:size]
|
||||
figidx = Immerse.figure(; name = d[:windowtitle], width = w, height = h)
|
||||
Immerse.Figure(figidx)
|
||||
end
|
||||
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
function plot(pkg::ImmersePackage; kw...)
|
||||
d = Dict(kw)
|
||||
|
||||
# create the underlying Gadfly.Plot object
|
||||
gplt = createGadflyPlotObject(d)
|
||||
|
||||
# save both the Immerse.Figure and the Gadfly.Plot
|
||||
Plot((nothing,gplt), pkg, 0, d, Dict[])
|
||||
end
|
||||
|
||||
|
||||
# plot one data series
|
||||
function plot!(::ImmersePackage, plt::Plot; kw...)
|
||||
d = Dict(kw)
|
||||
addGadflySeries!(plt, d)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
function updatePlotItems(plt::Plot{ImmersePackage}, d::Dict)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function addAnnotations{X,Y,V}(plt::Plot{ImmersePackage}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{ImmersePackage}, i::Integer)
|
||||
mapping = getGadflyMappings(plt, i)[1]
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{ImmersePackage}, xy::Tuple, i::Integer)
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
function buildSubplotObject!(subplt::Subplot{ImmersePackage}, isbefore::Bool)
|
||||
return false
|
||||
# isbefore && return false
|
||||
end
|
||||
|
||||
function showSubplotObject(subplt::Subplot{ImmersePackage})
|
||||
# create the Gtk window with vertical box vsep
|
||||
d = getinitargs(subplt,1)
|
||||
w,h = d[:size]
|
||||
vsep = Gtk.GtkBoxLeaf(:v)
|
||||
win = Gtk.GtkWindowLeaf(vsep, d[:windowtitle], w, h)
|
||||
|
||||
figindices = []
|
||||
row = Gtk.GtkBoxLeaf(:h)
|
||||
push!(vsep, row)
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
plt = subplt.plts[i]
|
||||
|
||||
# get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
|
||||
box, toolbar, canvas = Immerse.createPlotGuiComponents()
|
||||
|
||||
# add the plot's box to the row
|
||||
push!(row, box)
|
||||
|
||||
# create the figure and store the index returned for destruction later
|
||||
figidx = Immerse.figure(canvas)
|
||||
push!(figindices, figidx)
|
||||
|
||||
fig = Immerse.figure(figidx)
|
||||
plt.o = (fig, plt.o[2])
|
||||
|
||||
# add the row
|
||||
if c == ncols(subplt.layout, r)
|
||||
row = Gtk.GtkBoxLeaf(:h)
|
||||
push!(vsep, row)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# destructor... clean up plots
|
||||
Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
|
||||
|
||||
subplt.o = win
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
function handleLinkInner(plt::Plot{ImmersePackage}, isx::Bool)
|
||||
gplt = getGadflyContext(plt)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
|
||||
end
|
||||
|
||||
function expandLimits!(lims, plt::Plot{ImmersePackage}, isx::Bool)
|
||||
for l in getGadflyContext(plt).layers
|
||||
expandLimits!(lims, l.mapping[isx ? :x : :y])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
getGadflyContext(plt::Plot{ImmersePackage}) = plt.o[2]
|
||||
getGadflyContext(subplt::Subplot{ImmersePackage}) = buildGadflySubplotContext(subplt)
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{ImmersePackage})
|
||||
|
||||
fig, gplt = plt.o
|
||||
if fig == nothing
|
||||
fig = createImmerseFigure(plt.initargs)
|
||||
Gtk.on_signal_destroy((x...) -> (Immerse.dropfig(Immerse._display, fig.figno); plt.o = (nothing,gplt)), fig.canvas)
|
||||
plt.o = (fig, gplt)
|
||||
end
|
||||
|
||||
Immerse.figure(fig.figno; displayfig = false)
|
||||
display(gplt)
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{ImmersePackage})
|
||||
|
||||
# if we haven't created the window yet, do it
|
||||
if subplt.o == nothing
|
||||
showSubplotObject(subplt)
|
||||
end
|
||||
|
||||
# display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
|
||||
for plt in subplt.plts
|
||||
fig, gplt = plt.o
|
||||
Immerse.figure(fig.figno; displayfig = false)
|
||||
display(gplt)
|
||||
end
|
||||
|
||||
# o is the window... show it
|
||||
showall(subplt.o)
|
||||
end
|
||||
@@ -0,0 +1,672 @@
|
||||
|
||||
# https://github.com/stevengj/PyPlot.jl
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# convert colorant to 4-tuple RGBA
|
||||
getPyPlotColor(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
|
||||
getPyPlotColor(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α))
|
||||
getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
|
||||
# getPyPlotColor(c, alpha) = getPyPlotColor(colorscheme(c, alpha))
|
||||
|
||||
function getPyPlotColorMap(c::ColorGradient, α=nothing)
|
||||
# c = ColorGradient(c.colors, c.values, alpha=α)
|
||||
# pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", map(getPyPlotColor, getColorVector(c)))
|
||||
pyvals = [(c.values[i], getPyPlotColor(c.colors[i], α)) for i in 1:length(c.colors)]
|
||||
pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
|
||||
end
|
||||
|
||||
# anything else just gets a redsblue gradient
|
||||
getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:redsblues), α)
|
||||
|
||||
# get the style (solid, dashed, etc)
|
||||
function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol)
|
||||
linetype == :none && return " "
|
||||
linestyle == :solid && return "-"
|
||||
linestyle == :dash && return "--"
|
||||
linestyle == :dot && return ":"
|
||||
linestyle == :dashdot && return "-."
|
||||
warn("Unknown linestyle $linestyle")
|
||||
return "-"
|
||||
end
|
||||
|
||||
function getPyPlotMarker(marker::Shape)
|
||||
n = length(marker.vertices)
|
||||
mat = zeros(n+1,2)
|
||||
for (i,vert) in enumerate(marker.vertices)
|
||||
mat[i,1] = vert[1]
|
||||
mat[i,2] = vert[2]
|
||||
end
|
||||
mat[n+1,:] = mat[1,:]
|
||||
pypath.pymember("Path")(mat)
|
||||
# marker.vertices
|
||||
end
|
||||
|
||||
# get the marker shape
|
||||
function getPyPlotMarker(marker::Symbol)
|
||||
marker == :none && return " "
|
||||
marker == :ellipse && return "o"
|
||||
marker == :rect && return "s"
|
||||
marker == :diamond && return "D"
|
||||
marker == :utriangle && return "^"
|
||||
marker == :dtriangle && return "v"
|
||||
marker == :cross && return "+"
|
||||
marker == :xcross && return "x"
|
||||
marker == :star5 && return "*"
|
||||
marker == :pentagon && return "p"
|
||||
marker == :hexagon && return "h"
|
||||
marker == :octagon && return "8"
|
||||
haskey(_shapes, marker) && return getPyPlotMarker(_shapes[marker])
|
||||
|
||||
warn("Unknown marker $marker")
|
||||
return "o"
|
||||
end
|
||||
|
||||
# pass through
|
||||
function getPyPlotMarker(marker::@compat(AbstractString))
|
||||
@assert length(marker) == 1
|
||||
marker
|
||||
end
|
||||
|
||||
function getPyPlotStepStyle(linetype::Symbol)
|
||||
linetype == :steppost && return "steps-post"
|
||||
linetype == :steppre && return "steps-pre"
|
||||
return "default"
|
||||
end
|
||||
|
||||
|
||||
immutable PyPlotFigWrapper
|
||||
fig
|
||||
end
|
||||
|
||||
immutable PyPlotAxisWrapper
|
||||
ax
|
||||
fig
|
||||
end
|
||||
|
||||
getfig(wrap::@compat(Union{PyPlotAxisWrapper,PyPlotFigWrapper})) = wrap.fig
|
||||
|
||||
|
||||
|
||||
# get a reference to the correct axis
|
||||
function getLeftAxis(wrap::PyPlotFigWrapper)
|
||||
axes = wrap.fig.o[:axes]
|
||||
if isempty(axes)
|
||||
return wrap.fig.o[:add_subplot](111)
|
||||
end
|
||||
axes[1]
|
||||
end
|
||||
getLeftAxis(wrap::PyPlotAxisWrapper) = wrap.ax
|
||||
getLeftAxis(plt::Plot{PyPlotPackage}) = getLeftAxis(plt.o)
|
||||
getRightAxis(x) = getLeftAxis(x)[:twinx]()
|
||||
getAxis(plt::Plot{PyPlotPackage}, axis::Symbol) = (axis == :right ? getRightAxis : getLeftAxis)(plt)
|
||||
|
||||
# left axis is PyPlot.<func>, right axis is "f.axes[0].twinx().<func>"
|
||||
function getPyPlotFunction(plt::Plot, axis::Symbol, linetype::Symbol)
|
||||
|
||||
# in the 2-axis case we need to get: <rightaxis>[:<func>]
|
||||
ax = getAxis(plt, axis)
|
||||
ax[:set_ylabel](plt.initargs[:yrightlabel])
|
||||
fmap = @compat Dict(
|
||||
:hist => :hist,
|
||||
:sticks => :bar,
|
||||
:bar => :bar,
|
||||
:heatmap => :hexbin,
|
||||
:hexbin => :hexbin,
|
||||
:scatter => :scatter,
|
||||
:contour => :contour,
|
||||
)
|
||||
return ax[get(fmap, linetype, :plot)]
|
||||
end
|
||||
|
||||
function updateAxisColors(ax, fgcolor)
|
||||
for loc in ("bottom", "top", "left", "right")
|
||||
ax[:spines][loc][:set_color](fgcolor)
|
||||
end
|
||||
for axis in ("x", "y")
|
||||
ax[:tick_params](axis=axis, colors=fgcolor, which="both")
|
||||
end
|
||||
for axis in (:yaxis, :xaxis)
|
||||
ax[axis][:label][:set_color](fgcolor)
|
||||
end
|
||||
ax[:title][:set_color](fgcolor)
|
||||
end
|
||||
|
||||
|
||||
function handleSmooth(plt::Plot{PyPlotPackage}, ax, d::Dict, smooth::Bool)
|
||||
if smooth
|
||||
xs, ys = regressionXY(d[:x], d[:y])
|
||||
ax[:plot](xs, ys,
|
||||
# linestyle = getPyPlotLineStyle(:path, :dashdot),
|
||||
color = getPyPlotColor(d[:color]),
|
||||
linewidth = 2
|
||||
)
|
||||
end
|
||||
end
|
||||
handleSmooth(plt::Plot{PyPlotPackage}, ax, d::Dict, smooth::Real) = handleSmooth(plt, ax, d, true)
|
||||
|
||||
|
||||
nop() = nothing
|
||||
|
||||
|
||||
makePyPlotCurrent(wrap::PyPlotFigWrapper) = PyPlot.figure(wrap.fig.o[:number])
|
||||
makePyPlotCurrent(wrap::PyPlotAxisWrapper) = nothing #PyPlot.sca(wrap.ax.o)
|
||||
makePyPlotCurrent(plt::Plot{PyPlotPackage}) = makePyPlotCurrent(plt.o)
|
||||
|
||||
|
||||
function preparePlotUpdate(plt::Plot{PyPlotPackage})
|
||||
makePyPlotCurrent(plt)
|
||||
end
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# TODO:
|
||||
# fillto # might have to use barHack/histogramHack??
|
||||
# reg # true or false, add a regression line for each line
|
||||
# pos # (Int,Int), move the enclosing window to this position
|
||||
# windowtitle # string or symbol, set the title of the enclosing windowtitle
|
||||
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
||||
# show # true or false, show the plot (in case you don't want the window to pop up right away)
|
||||
|
||||
function plot(pkg::PyPlotPackage; kw...)
|
||||
# create the figure
|
||||
d = Dict(kw)
|
||||
|
||||
# standalone plots will create a figure, but not if part of a subplot (do it later)
|
||||
if haskey(d, :subplot)
|
||||
wrap = nothing
|
||||
else
|
||||
w,h = map(px2inch, d[:size])
|
||||
bgcolor = getPyPlotColor(d[:background_color])
|
||||
wrap = PyPlotFigWrapper(PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = 96))
|
||||
end
|
||||
|
||||
plt = Plot(wrap, pkg, 0, d, Dict[])
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
function plot!(pkg::PyPlotPackage, plt::Plot; kw...)
|
||||
d = Dict(kw)
|
||||
|
||||
ax = getAxis(plt, d[:axis])
|
||||
lt = d[:linetype]
|
||||
if !(lt in supportedTypes(pkg))
|
||||
error("linetype $(lt) is unsupported in PyPlot. Choose from: $(supportedTypes(pkg))")
|
||||
end
|
||||
|
||||
color = getPyPlotColor(d[:color], d[:lineopacity])
|
||||
|
||||
|
||||
if lt == :sticks
|
||||
d,_ = sticksHack(;d...)
|
||||
|
||||
elseif lt == :scatter
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
elseif lt in (:hline,:vline)
|
||||
linewidth = d[:linewidth]
|
||||
linecolor = color
|
||||
linestyle = getPyPlotLineStyle(lt, d[:linestyle])
|
||||
for yi in d[:y]
|
||||
func = ax[lt == :hline ? :axhline : axvline]
|
||||
func(yi, linewidth=d[:linewidth], color=linecolor, linestyle=linestyle)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
lt = d[:linetype]
|
||||
extra_kwargs = Dict()
|
||||
|
||||
plotfunc = getPyPlotFunction(plt, d[:axis], lt)
|
||||
|
||||
# we have different args depending on plot type
|
||||
if lt in (:hist, :sticks, :bar)
|
||||
|
||||
# NOTE: this is unsupported because it does the wrong thing... it shifts the whole axis
|
||||
# extra_kwargs[:bottom] = d[:fill]
|
||||
|
||||
if lt == :hist
|
||||
extra_kwargs[:bins] = d[:nbins]
|
||||
else
|
||||
extra_kwargs[:linewidth] = (lt == :sticks ? 0.1 : 0.9)
|
||||
end
|
||||
|
||||
elseif lt in (:heatmap, :hexbin)
|
||||
extra_kwargs[:gridsize] = d[:nbins]
|
||||
extra_kwargs[:cmap] = getPyPlotColorMap(d[:color])
|
||||
|
||||
elseif lt == :contour
|
||||
extra_kwargs[:cmap] = getPyPlotColorMap(d[:color])
|
||||
extra_kwargs[:linewidths] = d[:linewidth]
|
||||
extra_kwargs[:linestyles] = getPyPlotLineStyle(lt, d[:linestyle])
|
||||
# TODO: will need to call contourf to fill in the contours
|
||||
|
||||
else
|
||||
|
||||
extra_kwargs[:linestyle] = getPyPlotLineStyle(lt, d[:linestyle])
|
||||
extra_kwargs[:marker] = getPyPlotMarker(d[:markershape])
|
||||
|
||||
if lt == :scatter
|
||||
extra_kwargs[:s] = d[:markersize]^2
|
||||
c = d[:markercolor]
|
||||
if isa(c, ColorGradient) && d[:z] != nothing
|
||||
extra_kwargs[:c] = convert(Vector{Float64}, d[:z])
|
||||
extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeropacity])
|
||||
else
|
||||
extra_kwargs[:c] = getPyPlotColor(c, d[:markeropacity])
|
||||
end
|
||||
else
|
||||
extra_kwargs[:markersize] = d[:markersize]
|
||||
extra_kwargs[:markerfacecolor] = getPyPlotColor(d[:markercolor], d[:markeropacity])
|
||||
extra_kwargs[:markeredgecolor] = getPyPlotColor(plt.initargs[:foreground_color])
|
||||
extra_kwargs[:markeredgewidth] = d[:linewidth]
|
||||
extra_kwargs[:drawstyle] = getPyPlotStepStyle(lt)
|
||||
end
|
||||
end
|
||||
|
||||
# set these for all types
|
||||
if lt != :contour
|
||||
extra_kwargs[:color] = color
|
||||
extra_kwargs[:linewidth] = d[:linewidth]
|
||||
extra_kwargs[:label] = d[:label]
|
||||
end
|
||||
|
||||
# do the plot
|
||||
d[:serieshandle] = if lt == :hist
|
||||
plotfunc(d[:y]; extra_kwargs...)[1]
|
||||
elseif lt == :contour
|
||||
# NOTE: x/y are backwards in pyplot, so we switch the x and y args (also y is reversed),
|
||||
# and take the transpose of the surface matrix
|
||||
x, y = d[:y], d[:x]
|
||||
surf = d[:surface]'
|
||||
handle = plotfunc(x, y, surf, d[:nlevels]; extra_kwargs...)
|
||||
if d[:fillrange] != nothing
|
||||
handle = ax[:contourf](x, y, surf, d[:nlevels]; cmap = getPyPlotColorMap(d[:fillcolor], d[:fillopacity]))
|
||||
end
|
||||
handle
|
||||
elseif lt in (:scatter, :heatmap, :hexbin)
|
||||
plotfunc(d[:x], d[:y]; extra_kwargs...)
|
||||
else
|
||||
plotfunc(d[:x], d[:y]; extra_kwargs...)[1]
|
||||
end
|
||||
|
||||
handleSmooth(plt, ax, d, d[:smooth])
|
||||
|
||||
# add the colorbar legend
|
||||
if plt.initargs[:legend] && haskey(extra_kwargs, :cmap)
|
||||
PyPlot.colorbar(d[:serieshandle])
|
||||
end
|
||||
|
||||
# this sets the bg color inside the grid
|
||||
ax[:set_axis_bgcolor](getPyPlotColor(plt.initargs[:background_color]))
|
||||
|
||||
fillrange = d[:fillrange]
|
||||
if fillrange != nothing && lt != :contour
|
||||
fillcolor = getPyPlotColor(d[:fillcolor], d[:fillopacity])
|
||||
if typeof(fillrange) <: @compat(Union{Real, AVec})
|
||||
ax[:fill_between](d[:x], fillrange, d[:y], facecolor = fillcolor)
|
||||
else
|
||||
ax[:fill_between](d[:x], fillrange..., facecolor = fillcolor)
|
||||
end
|
||||
end
|
||||
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
|
||||
function Base.getindex(plt::Plot{PyPlotPackage}, i::Integer)
|
||||
series = plt.seriesargs[i][:serieshandle]
|
||||
try
|
||||
return series[:get_data]()
|
||||
catch
|
||||
xy = series[:get_offsets]()
|
||||
return vec(xy[:,1]), vec(xy[:,2])
|
||||
end
|
||||
# series[:relim]()
|
||||
# mapping = getGadflyMappings(plt, i)[1]
|
||||
# mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{PyPlotPackage}, xy::Tuple, i::Integer)
|
||||
series = plt.seriesargs[i][:serieshandle]
|
||||
try
|
||||
series[:set_data](xy...)
|
||||
catch
|
||||
series[:set_offsets](hcat(xy...))
|
||||
end
|
||||
|
||||
ax = series[:axes]
|
||||
if plt.initargs[:xlims] == :auto
|
||||
xmin, xmax = ax[:get_xlim]()
|
||||
ax[:set_xlim](min(xmin, minimum(xy[1])), max(xmax, maximum(xy[1])))
|
||||
end
|
||||
if plt.initargs[:ylims] == :auto
|
||||
ymin, ymax = ax[:get_ylim]()
|
||||
ax[:set_ylim](min(ymin, minimum(xy[2])), max(ymax, maximum(xy[2])))
|
||||
end
|
||||
|
||||
# getLeftAxis(plt)[:relim]()
|
||||
# getRightAxis(plt)[:relim]()
|
||||
# for mapping in getGadflyMappings(plt, i)
|
||||
# mapping[:x], mapping[:y] = xy
|
||||
# end
|
||||
plt
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
function addPyPlotLims(ax, lims, isx::Bool)
|
||||
lims == :auto && return
|
||||
ltype = limsType(lims)
|
||||
if ltype == :limits
|
||||
ax[isx ? :set_xlim : :set_ylim](lims...)
|
||||
else
|
||||
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
|
||||
end
|
||||
end
|
||||
|
||||
function addPyPlotTicks(ax, ticks, isx::Bool)
|
||||
ticks == :auto && return
|
||||
if ticks == :none || ticks == nothing
|
||||
ticks = zeros(0)
|
||||
end
|
||||
|
||||
ttype = ticksType(ticks)
|
||||
if ttype == :ticks
|
||||
ax[isx ? :set_xticks : :set_yticks](ticks)
|
||||
elseif ttype == :ticks_and_labels
|
||||
ax[isx ? :set_xticks : :set_yticks](ticks...)
|
||||
else
|
||||
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
|
||||
end
|
||||
end
|
||||
|
||||
usingRightAxis(plt::Plot{PyPlotPackage}) = any(args -> args[:axis] in (:right,:auto), plt.seriesargs)
|
||||
|
||||
function updatePlotItems(plt::Plot{PyPlotPackage}, d::Dict)
|
||||
figorax = plt.o
|
||||
ax = getLeftAxis(figorax)
|
||||
# PyPlot.sca(ax)
|
||||
|
||||
# title and axis labels
|
||||
# haskey(d, :title) && PyPlot.title(d[:title])
|
||||
haskey(d, :title) && ax[:set_title](d[:title])
|
||||
haskey(d, :xlabel) && ax[:set_xlabel](d[:xlabel])
|
||||
if haskey(d, :ylabel)
|
||||
ax[:set_ylabel](d[:ylabel])
|
||||
end
|
||||
if usingRightAxis(plt) && get(d, :yrightlabel, "") != ""
|
||||
rightax = getRightAxis(figorax)
|
||||
rightax[:set_ylabel](d[:yrightlabel])
|
||||
end
|
||||
|
||||
# scales
|
||||
haskey(d, :xscale) && applyPyPlotScale(ax, d[:xscale], true)
|
||||
haskey(d, :yscale) && applyPyPlotScale(ax, d[:yscale], false)
|
||||
|
||||
# limits and ticks
|
||||
haskey(d, :xlims) && addPyPlotLims(ax, d[:xlims], true)
|
||||
haskey(d, :ylims) && addPyPlotLims(ax, d[:ylims], false)
|
||||
haskey(d, :xticks) && addPyPlotTicks(ax, d[:xticks], true)
|
||||
haskey(d, :yticks) && addPyPlotTicks(ax, d[:yticks], false)
|
||||
|
||||
if get(d, :xflip, false)
|
||||
ax[:invert_xaxis]()
|
||||
end
|
||||
if get(d, :yflip, false)
|
||||
ax[:invert_yaxis]()
|
||||
end
|
||||
|
||||
axes = [getLeftAxis(figorax)]
|
||||
if usingRightAxis(plt)
|
||||
push!(axes, getRightAxis(figorax))
|
||||
end
|
||||
|
||||
# font sizes
|
||||
for ax in axes
|
||||
# haskey(d, :yrightlabel) || continue
|
||||
|
||||
|
||||
# guides
|
||||
sz = get(d, :guidefont, plt.initargs[:guidefont]).pointsize
|
||||
ax[:title][:set_fontsize](sz)
|
||||
ax[:xaxis][:label][:set_fontsize](sz)
|
||||
ax[:yaxis][:label][:set_fontsize](sz)
|
||||
|
||||
# ticks
|
||||
sz = get(d, :tickfont, plt.initargs[:tickfont]).pointsize
|
||||
for sym in (:get_xticklabels, :get_yticklabels)
|
||||
for lab in ax[sym]()
|
||||
lab[:set_fontsize](sz)
|
||||
end
|
||||
end
|
||||
|
||||
# grid
|
||||
if get(d, :grid, false)
|
||||
ax[:xaxis][:grid](true)
|
||||
ax[:yaxis][:grid](true)
|
||||
ax[:set_axisbelow](true)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function applyPyPlotScale(ax, scaleType::Symbol, isx::Bool)
|
||||
func = ax[isx ? :set_xscale : :set_yscale]
|
||||
scaleType == :identity && return func("linear")
|
||||
scaleType == :log && return func("log", basex = e, basey = e)
|
||||
scaleType == :log2 && return func("log", basex = 2, basey = 2)
|
||||
scaleType == :log10 && return func("log", basex = 10, basey = 10)
|
||||
warn("Unhandled scaleType: ", scaleType)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
function createPyPlotAnnotationObject(plt::Plot{PyPlotPackage}, x, y, val::@compat(AbstractString))
|
||||
ax = getLeftAxis(plt)
|
||||
ax[:annotate](val, xy = (x,y))
|
||||
end
|
||||
|
||||
|
||||
function createPyPlotAnnotationObject(plt::Plot{PyPlotPackage}, x, y, val::PlotText)
|
||||
ax = getLeftAxis(plt)
|
||||
ax[:annotate](val.str,
|
||||
xy = (x,y),
|
||||
family = val.font.family,
|
||||
color = getPyPlotColor(val.font.color),
|
||||
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
|
||||
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
|
||||
rotation = val.font.rotation * 180 / π,
|
||||
size = val.font.pointsize
|
||||
)
|
||||
end
|
||||
|
||||
function addAnnotations{X,Y,V}(plt::Plot{PyPlotPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
createPyPlotAnnotationObject(plt, ann...)
|
||||
end
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# NOTE: pyplot needs to build before
|
||||
function buildSubplotObject!(subplt::Subplot{PyPlotPackage}, isbefore::Bool)
|
||||
l = subplt.layout
|
||||
|
||||
w,h = map(px2inch, getinitargs(subplt,1)[:size])
|
||||
bgcolor = getPyPlotColor(getinitargs(subplt,1)[:background_color])
|
||||
fig = PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = 96)
|
||||
|
||||
nr = nrows(l)
|
||||
for (i,(r,c)) in enumerate(l)
|
||||
|
||||
# add the plot to the figure
|
||||
nc = ncols(l, r)
|
||||
fakeidx = (r-1) * nc + c
|
||||
ax = fig[:add_subplot](nr, nc, fakeidx)
|
||||
|
||||
subplt.plts[i].o = PyPlotAxisWrapper(ax, fig)
|
||||
end
|
||||
|
||||
subplt.o = PyPlotFigWrapper(fig)
|
||||
true
|
||||
end
|
||||
|
||||
# this will be called internally, when creating a subplot from existing plots
|
||||
# NOTE: if I ever need to "Rebuild a "ubplot from individual Plot's"... this is what I should use!
|
||||
function subplot(plts::AVec{Plot{PyPlotPackage}}, layout::SubplotLayout, d::Dict)
|
||||
validateSubplotSupported()
|
||||
|
||||
p = length(layout)
|
||||
n = sum([plt.n for plt in plts])
|
||||
|
||||
pkg = PyPlotPackage()
|
||||
newplts = Plot{PyPlotPackage}[plot(pkg; subplot=true, plt.initargs...) for plt in plts]
|
||||
|
||||
subplt = Subplot(nothing, newplts, PyPlotPackage(), p, n, layout, d, true, false, false, (r,c) -> (nothing,nothing))
|
||||
|
||||
preprocessSubplot(subplt, d)
|
||||
buildSubplotObject!(subplt, true)
|
||||
|
||||
for (i,plt) in enumerate(plts)
|
||||
for seriesargs in plt.seriesargs
|
||||
_plot_from_subplot!(newplts[i]; seriesargs...)
|
||||
end
|
||||
end
|
||||
|
||||
postprocessSubplot(subplt, d)
|
||||
|
||||
subplt
|
||||
end
|
||||
|
||||
|
||||
function handleLinkInner(plt::Plot{PyPlotPackage}, isx::Bool)
|
||||
if isx
|
||||
plot!(plt, xticks=zeros(0), xlabel="")
|
||||
else
|
||||
plot!(plt, yticks=zeros(0), ylabel="")
|
||||
end
|
||||
end
|
||||
|
||||
function expandLimits!(lims, plt::Plot{PyPlotPackage}, isx::Bool)
|
||||
pltlims = plt.o.ax[isx ? :get_xbound : :get_ybound]()
|
||||
expandLimits!(lims, pltlims)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# function addPyPlotLegend(plt::Plot)
|
||||
function addPyPlotLegend(plt::Plot, ax)
|
||||
if plt.initargs[:legend]
|
||||
# gotta do this to ensure both axes are included
|
||||
args = filter(x -> !(x[:linetype] in (:hist,:hexbin,:heatmap,:hline,:vline,:contour)), plt.seriesargs)
|
||||
if length(args) > 0
|
||||
ax[:legend]([d[:serieshandle] for d in args],
|
||||
[d[:label] for d in args],
|
||||
loc="best",
|
||||
fontsize = plt.initargs[:legendfont].pointsize
|
||||
# framealpha = 0.6
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function finalizePlot(plt::Plot{PyPlotPackage})
|
||||
ax = getLeftAxis(plt)
|
||||
addPyPlotLegend(plt, ax)
|
||||
updateAxisColors(ax, getPyPlotColor(plt.initargs[:foreground_color]))
|
||||
PyPlot.draw()
|
||||
end
|
||||
|
||||
function finalizePlot(subplt::Subplot{PyPlotPackage})
|
||||
fig = subplt.o.fig
|
||||
for (i,plt) in enumerate(subplt.plts)
|
||||
ax = getLeftAxis(plt)
|
||||
addPyPlotLegend(plt, ax)
|
||||
updateAxisColors(ax, getPyPlotColor(plt.initargs[:foreground_color]))
|
||||
end
|
||||
PyPlot.draw()
|
||||
end
|
||||
|
||||
# # allow for writing any supported mime
|
||||
# for mime in keys(PyPlot.aggformats)
|
||||
# @eval function Base.writemime(io::IO, m::MIME{symbol{$mime}}, plt::Plot{PyPlotPackage})
|
||||
# finalizePlot(plt)
|
||||
# writemime(io, m, getfig(plt.o))
|
||||
# end
|
||||
# end
|
||||
|
||||
# function Base.writemime(io::IO, m::@compat(Union{MIME"image/svg+xml", MIME"image/png"}, plt::Plot{PyPlotPackage})
|
||||
# finalizePlot(plt)
|
||||
# writemime(io, m, getfig(plt.o))
|
||||
# end
|
||||
|
||||
|
||||
# NOTE: to bring up a GUI window in IJulia, need some extra steps
|
||||
function Base.display(::PlotsDisplay, plt::PlottingObject{PyPlotPackage})
|
||||
finalizePlot(plt)
|
||||
if isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
|
||||
display(getfig(plt.o))
|
||||
else
|
||||
# # PyPlot.ion()
|
||||
# PyPlot.figure(getfig(plt.o).o[:number])
|
||||
# PyPlot.draw_if_interactive()
|
||||
# # PyPlot.ioff()
|
||||
end
|
||||
# PyPlot.plt[:show](block=false)
|
||||
getfig(plt.o)[:show]()
|
||||
end
|
||||
|
||||
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{PyPlotPackage})
|
||||
# finalizePlot(subplt)
|
||||
# PyPlot.ion()
|
||||
# PyPlot.figure(getfig(subplt.o).o[:number])
|
||||
# PyPlot.draw_if_interactive()
|
||||
# PyPlot.ioff()
|
||||
# # display(getfig(subplt.o))
|
||||
# end
|
||||
|
||||
# # allow for writing any supported mime
|
||||
# for mime in (MIME"image/png", MIME"application/pdf", MIME"application/postscript")
|
||||
# @eval function Base.writemime(io::IO, ::$mime, plt::PlottingObject{PyPlotPackage})
|
||||
# finalizePlot(plt)
|
||||
# writemime(io, $mime(), getfig(plt.o))
|
||||
# end
|
||||
# end
|
||||
|
||||
const _pyplot_mimeformats = @compat Dict(
|
||||
"application/eps" => "eps",
|
||||
"image/eps" => "eps",
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"application/postscript" => "ps",
|
||||
# "image/svg+xml" => "svg"
|
||||
)
|
||||
|
||||
|
||||
for (mime, fmt) in _pyplot_mimeformats
|
||||
@eval function Base.writemime(io::IO, ::MIME{symbol($mime)}, plt::PlottingObject{PyPlotPackage})
|
||||
finalizePlot(plt)
|
||||
fig = getfig(plt.o)
|
||||
fig.o["canvas"][:print_figure](io,
|
||||
format=$fmt,
|
||||
bbox_inches="tight",
|
||||
facecolor = fig.o["get_facecolor"](),
|
||||
edgecolor = "none"
|
||||
# edgecolor = fig.o["get_edgecolor"]()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# function Base.writemime(io::IO, m::MIME"image/png", subplt::Subplot{PyPlotPackage})
|
||||
# finalizePlot(subplt)
|
||||
# writemime(io, m, getfig(subplt.o))
|
||||
# end
|
||||
@@ -0,0 +1,283 @@
|
||||
|
||||
# https://github.com/tbreloff/Qwt.jl
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
@compat const _qwtAliases = Dict(
|
||||
:nbins => :heatmap_n,
|
||||
:fillrange => :fillto,
|
||||
:linewidth => :width,
|
||||
:markershape => :marker,
|
||||
:hexbin => :heatmap,
|
||||
:path => :line,
|
||||
:steppost => :step,
|
||||
:steppre => :stepinverted,
|
||||
:star5 => :star1,
|
||||
:star8 => :star2,
|
||||
)
|
||||
|
||||
function fixcolors(d::Dict)
|
||||
for (k,v) in d
|
||||
if typeof(v) <: ColorScheme
|
||||
d[k] = getColor(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function replaceQwtAliases(d, s)
|
||||
if haskey(_qwtAliases, d[s])
|
||||
d[s] = _qwtAliases[d[s]]
|
||||
end
|
||||
end
|
||||
|
||||
function adjustQwtKeywords(plt::Plot{QwtPackage}, iscreating::Bool; kw...)
|
||||
d = Dict(kw)
|
||||
lt = d[:linetype]
|
||||
if lt == :scatter
|
||||
d[:linetype] = :none
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
elseif lt in (:hline, :vline)
|
||||
addLineMarker(plt, d)
|
||||
d[:linetype] = :none
|
||||
d[:markershape] = :ellipse
|
||||
d[:markersize] = 1
|
||||
if lt == :vline
|
||||
d[:x], d[:y] = d[:y], d[:x]
|
||||
end
|
||||
|
||||
elseif !iscreating && lt == :bar
|
||||
d = barHack(; kw...)
|
||||
elseif !iscreating && lt == :hist
|
||||
d = barHack(; histogramHack(; kw...)...)
|
||||
end
|
||||
|
||||
replaceQwtAliases(d, :linetype)
|
||||
replaceQwtAliases(d, :markershape)
|
||||
|
||||
for k in keys(d)
|
||||
if haskey(_qwtAliases, k)
|
||||
d[_qwtAliases[k]] = d[k]
|
||||
end
|
||||
end
|
||||
|
||||
d[:x] = collect(d[:x])
|
||||
d[:y] = collect(d[:y])
|
||||
|
||||
d
|
||||
end
|
||||
|
||||
function plot(pkg::QwtPackage; kw...)
|
||||
d = Dict(kw)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot")
|
||||
o = Qwt.plot(zeros(0,0); d..., show=false)
|
||||
plt = Plot(o, pkg, 0, d, Dict[])
|
||||
plt
|
||||
end
|
||||
|
||||
function plot!(::QwtPackage, plt::Plot; kw...)
|
||||
d = adjustQwtKeywords(plt, false; kw...)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot!")
|
||||
Qwt.oplot(plt.o; d...)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function updateLimsAndTicks(plt::Plot{QwtPackage}, d::Dict, isx::Bool)
|
||||
lims = get(d, isx ? :xlims : :ylims, nothing)
|
||||
ticks = get(d, isx ? :xticks : :yticks, nothing)
|
||||
w = plt.o.widget
|
||||
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
|
||||
|
||||
if typeof(lims) <: @compat(Union{Tuple,AVec}) && length(lims) == 2
|
||||
if isx
|
||||
plt.o.autoscale_x = false
|
||||
else
|
||||
plt.o.autoscale_y = false
|
||||
end
|
||||
w[:setAxisScale](axisid, lims...)
|
||||
end
|
||||
|
||||
if typeof(ticks) <: Range
|
||||
if isx
|
||||
plt.o.autoscale_x = false
|
||||
else
|
||||
plt.o.autoscale_y = false
|
||||
end
|
||||
w[:setAxisScale](axisid, float(minimum(ticks)), float(maximum(ticks)), float(step(ticks)))
|
||||
elseif !(ticks in (nothing, :none, :auto))
|
||||
warn("Only Range types are supported for Qwt xticks/yticks. typeof(ticks)=$(typeof(ticks))")
|
||||
end
|
||||
|
||||
# change the scale
|
||||
scalesym = isx ? :xscale : :yscale
|
||||
if haskey(d, scalesym)
|
||||
scaletype = d[scalesym]
|
||||
scaletype == :identity && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLinearScaleEngine())
|
||||
# scaletype == :log && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(e))
|
||||
# scaletype == :log2 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(2))
|
||||
scaletype == :log10 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLog10ScaleEngine())
|
||||
scaletype in supportedScales() || warn("Unsupported scale type: ", scaletype)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function updatePlotItems(plt::Plot{QwtPackage}, d::Dict)
|
||||
haskey(d, :title) && Qwt.title(plt.o, d[:title])
|
||||
haskey(d, :xlabel) && Qwt.xlabel(plt.o, d[:xlabel])
|
||||
haskey(d, :ylabel) && Qwt.ylabel(plt.o, d[:ylabel])
|
||||
updateLimsAndTicks(plt, d, true)
|
||||
updateLimsAndTicks(plt, d, false)
|
||||
end
|
||||
|
||||
function updatePositionAndSize(plt::PlottingObject{QwtPackage}, d::Dict)
|
||||
haskey(d, :size) && Qwt.resizewidget(plt.o, d[:size]...)
|
||||
haskey(d, :pos) && Qwt.movewidget(plt.o, d[:pos]...)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# curve.setPen(Qt.QPen(Qt.QColor(color), linewidth, self.getLineStyle(linestyle)))
|
||||
function addLineMarker(plt::Plot{QwtPackage}, d::Dict)
|
||||
for yi in d[:y]
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
ishorizontal = (d[:linetype] == :hline)
|
||||
marker[:setLineStyle](ishorizontal ? 1 : 2)
|
||||
marker[ishorizontal ? :setYValue : :setXValue](yi)
|
||||
qcolor = Qwt.convertRGBToQColor(getColor(d[:color]))
|
||||
linestyle = plt.o.widget[:getLineStyle](string(d[:linestyle]))
|
||||
marker[:setLinePen](Qwt.QT.QPen(qcolor, d[:linewidth], linestyle))
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
# marker[:setValue](x, y)
|
||||
# marker[:setLabel](Qwt.QWT.QwtText(val))
|
||||
# marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
function createQwtAnnotation(plt::Plot, x, y, val::PlotText)
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
marker[:setValue](x, y)
|
||||
qwttext = Qwt.QWT.QwtText(val.str)
|
||||
qwttext[:setFont](Qwt.QT.QFont(val.font.family, val.font.pointsize))
|
||||
qwttext[:setColor](Qwt.convertRGBToQColor(getColor(val.font.color)))
|
||||
marker[:setLabel](qwttext)
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
function createQwtAnnotation(plt::Plot, x, y, val::@compat(AbstractString))
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
marker[:setValue](x, y)
|
||||
marker[:setLabel](Qwt.QWT.QwtText(val))
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
function addAnnotations{X,Y,V}(plt::Plot{QwtPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
createQwtAnnotation(plt, ann...)
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{QwtPackage}, i::Int)
|
||||
series = plt.o.lines[i]
|
||||
series.x, series.y
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{QwtPackage}, xy::Tuple, i::Integer)
|
||||
series = plt.o.lines[i]
|
||||
series.x, series.y = xy
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# savepng(::QwtPackage, plt::PlottingObject, fn::@compat(AbstractString), args...) = Qwt.savepng(plt.o, fn)
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# create the underlying object (each backend will do this differently)
|
||||
function buildSubplotObject!(subplt::Subplot{QwtPackage}, isbefore::Bool)
|
||||
isbefore && return false
|
||||
i = 0
|
||||
rows = Any[]
|
||||
row = Any[]
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
push!(row, subplt.plts[i].o)
|
||||
if c == ncols(subplt.layout, r)
|
||||
push!(rows, Qwt.hsplitter(row...))
|
||||
row = Any[]
|
||||
end
|
||||
end
|
||||
# for rowcnt in subplt.layout.rowcounts
|
||||
# push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
|
||||
# i += rowcnt
|
||||
# end
|
||||
subplt.o = Qwt.vsplitter(rows...)
|
||||
# Qwt.resizewidget(subplt.o, getinitargs(subplt,1)[:size]...)
|
||||
# Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
|
||||
true
|
||||
end
|
||||
|
||||
function handleLinkInner(plt::Plot{QwtPackage}, isx::Bool)
|
||||
warn("handleLinkInner isn't implemented for qwt")
|
||||
end
|
||||
|
||||
function expandLimits!(lims, plt::Plot{QwtPackage}, isx::Bool)
|
||||
for series in plt.o.lines
|
||||
expandLimits!(lims, isx ? series.x : series.y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function handleLinkInner(plt::Plot{QwtPackage}, isx::Bool)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtPackage})
|
||||
Qwt.refresh(plt.o)
|
||||
Qwt.savepng(plt.o, "/tmp/dfskjdhfkh.png")
|
||||
write(io, readall("/tmp/dfskjdhfkh.png"))
|
||||
end
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtPackage})
|
||||
for plt in subplt.plts
|
||||
Qwt.refresh(plt.o)
|
||||
end
|
||||
Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
|
||||
write(io, readall("/tmp/dfskjdhfkh.png"))
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{QwtPackage})
|
||||
Qwt.refresh(plt.o)
|
||||
Qwt.showwidget(plt.o)
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{QwtPackage})
|
||||
for plt in subplt.plts
|
||||
Qwt.refresh(plt.o)
|
||||
end
|
||||
# iargs = getinitargs(subplt,1)
|
||||
# # iargs = subplt.initargs
|
||||
# Qwt.resizewidget(subplt.o, iargs[:size]...)
|
||||
# Qwt.movewidget(subplt.o, iargs[:pos]...)
|
||||
Qwt.showwidget(subplt.o)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
|
||||
supportedArgs(::GadflyPackage) = [
|
||||
:annotation,
|
||||
# :axis,
|
||||
:background_color,
|
||||
:color,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillopacity,
|
||||
:foreground_color,
|
||||
:group,
|
||||
:label,
|
||||
:layout,
|
||||
:legend,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:lineopacity,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeropacity,
|
||||
:n,
|
||||
:nbins,
|
||||
:nc,
|
||||
:nr,
|
||||
# :pos,
|
||||
:smooth,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
:yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
:xflip,
|
||||
:yflip,
|
||||
:z,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:surface,
|
||||
:nlevels,
|
||||
]
|
||||
supportedAxes(::GadflyPackage) = [:auto, :left]
|
||||
supportedTypes(::GadflyPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
supportedStyles(::GadflyPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::GadflyPackage) = vcat(_allMarkers, Shape)
|
||||
supportedScales(::GadflyPackage) = [:identity, :log, :log2, :log10, :asinh, :sqrt]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
supportedArgs(::ImmersePackage) = supportedArgs(GadflyPackage())
|
||||
supportedAxes(::ImmersePackage) = supportedAxes(GadflyPackage())
|
||||
supportedTypes(::ImmersePackage) = supportedTypes(GadflyPackage())
|
||||
supportedStyles(::ImmersePackage) = supportedStyles(GadflyPackage())
|
||||
supportedMarkers(::ImmersePackage) = supportedMarkers(GadflyPackage())
|
||||
supportedScales(::ImmersePackage) = supportedScales(GadflyPackage())
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
supportedArgs(::PyPlotPackage) = [
|
||||
:annotation,
|
||||
:axis,
|
||||
:background_color,
|
||||
:color,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:foreground_color,
|
||||
:group,
|
||||
:label,
|
||||
:layout,
|
||||
:legend,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:n,
|
||||
:nbins,
|
||||
:nc,
|
||||
:nr,
|
||||
# :pos,
|
||||
:smooth,
|
||||
# :ribbon,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
:yrightlabel,
|
||||
:yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
:xflip,
|
||||
:yflip,
|
||||
:z,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:surface,
|
||||
:nlevels,
|
||||
:fillopacity,
|
||||
:lineopacity,
|
||||
:markeropacity,
|
||||
]
|
||||
supportedAxes(::PyPlotPackage) = _allAxes
|
||||
supportedTypes(::PyPlotPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
supportedStyles(::PyPlotPackage) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
# supportedMarkers(::PyPlotPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon]
|
||||
supportedMarkers(::PyPlotPackage) = vcat(_allMarkers, Shape)
|
||||
supportedScales(::PyPlotPackage) = [:identity, :log, :log2, :log10]
|
||||
subplotSupported(::PyPlotPackage) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
supportedArgs(::QwtPackage) = [
|
||||
:annotation,
|
||||
# :args,
|
||||
:axis,
|
||||
:background_color,
|
||||
:color,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:foreground_color,
|
||||
:group,
|
||||
# :heatmap_c,
|
||||
# :kwargs,
|
||||
:label,
|
||||
:layout,
|
||||
:legend,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:n,
|
||||
:nbins,
|
||||
:nc,
|
||||
:nr,
|
||||
:pos,
|
||||
:smooth,
|
||||
# :ribbon,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
:yrightlabel,
|
||||
:yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
]
|
||||
supportedTypes(::QwtPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline]
|
||||
supportedMarkers(::QwtPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
|
||||
supportedScales(::QwtPackage) = [:identity, :log10]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
supportedArgs(::UnicodePlotsPackage) = [
|
||||
# :annotation,
|
||||
# :args,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
# :color,
|
||||
# :fill,
|
||||
# :foreground_color,
|
||||
:group,
|
||||
# :heatmap_c,
|
||||
# :kwargs,
|
||||
:label,
|
||||
# :layout,
|
||||
:legend,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
# :linewidth,
|
||||
:markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# :n,
|
||||
:nbins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
# :reg,
|
||||
# :ribbon,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
]
|
||||
supportedAxes(::UnicodePlotsPackage) = [:auto, :left]
|
||||
supportedTypes(::UnicodePlotsPackage) = [:none, :line, :path, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline]
|
||||
supportedStyles(::UnicodePlotsPackage) = [:auto, :solid]
|
||||
supportedMarkers(::UnicodePlotsPackage) = [:none, :auto, :ellipse]
|
||||
supportedScales(::UnicodePlotsPackage) = [:identity]
|
||||
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
supportedArgs(::WinstonPackage) = [
|
||||
:annotation,
|
||||
# :args,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
:color,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
# :foreground_color,
|
||||
:group,
|
||||
# :heatmap_c,
|
||||
# :kwargs,
|
||||
:label,
|
||||
# :layout,
|
||||
:legend,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
# :n,
|
||||
:nbins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
:smooth,
|
||||
# :ribbon,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
]
|
||||
supportedAxes(::WinstonPackage) = [:auto, :left]
|
||||
supportedTypes(::WinstonPackage) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
|
||||
supportedStyles(::WinstonPackage) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supportedMarkers(::WinstonPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
||||
supportedScales(::WinstonPackage) = [:identity, :log10]
|
||||
subplotSupported(::WinstonPackage) = false
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,104 @@
|
||||
|
||||
# TODO: find/replace all [PkgName] with CamelCase, all [pkgname] with lowercase
|
||||
|
||||
# [WEBSITE]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# supportedArgs(::[PkgName]Package) = _allArgs
|
||||
supportedArgs(::[PkgName]Package) = [
|
||||
:annotation,
|
||||
# :args,
|
||||
:axis,
|
||||
:background_color,
|
||||
:color,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:foreground_color,
|
||||
:group,
|
||||
# :heatmap_c,
|
||||
# :kwargs,
|
||||
:label,
|
||||
:layout,
|
||||
:legend,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:n,
|
||||
:nbins,
|
||||
:nc,
|
||||
:nr,
|
||||
# :pos,
|
||||
:smooth,
|
||||
# :ribbon,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
:yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
]
|
||||
supportedAxes(::[PkgName]Package) = _allAxes
|
||||
supportedTypes(::[PkgName]Package) = _allTypes
|
||||
supportedStyles(::[PkgName]Package) = _allStyles
|
||||
supportedMarkers(::[PkgName]Package) = _allMarkers
|
||||
supportedScales(::[PkgName]Package) = _allScales
|
||||
subplotSupported(::[PkgName]Package) = false
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function plot(pkg::[PkgName]Package; kw...)
|
||||
d = Dict(kw)
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
Plot(o, pkg, 0, d, Dict[])
|
||||
end
|
||||
|
||||
|
||||
function plot!(::[PkgName]Package, plt::Plot; kw...)
|
||||
d = Dict(kw)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function updatePlotItems(plt::Plot{[PkgName]Package}, d::Dict)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function buildSubplotObject!(subplt::Subplot{[PkgName]Package})
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{[PkgName]Package})
|
||||
# TODO: write a png to io
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{[PkgName]Package})
|
||||
# TODO: display/show the plot
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]Package})
|
||||
# TODO: display/show the subplot
|
||||
end
|
||||
@@ -0,0 +1,196 @@
|
||||
|
||||
# https://github.com/Evizero/UnicodePlots.jl
|
||||
|
||||
# -------------------------------
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
# figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
|
||||
sargs = plt.seriesargs
|
||||
iargs = plt.initargs
|
||||
|
||||
# get the x/y limits
|
||||
if get(iargs, :xlims, :auto) == :auto
|
||||
xlim = [Inf, -Inf]
|
||||
for d in sargs
|
||||
expandLimits!(xlim, d[:x])
|
||||
end
|
||||
else
|
||||
xmin, xmax = iargs[:xlims]
|
||||
xlim = [xmin, xmax]
|
||||
end
|
||||
|
||||
if get(iargs, :ylims, :auto) == :auto
|
||||
ylim = [Inf, -Inf]
|
||||
for d in sargs
|
||||
expandLimits!(ylim, d[:y])
|
||||
end
|
||||
else
|
||||
ymin, ymax = iargs[:ylims]
|
||||
ylim = [ymin, ymax]
|
||||
end
|
||||
|
||||
# we set x/y to have a single point, since we need to create the plot with some data.
|
||||
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
|
||||
x = Float64[xlim[1]]
|
||||
y = Float64[ylim[1]]
|
||||
|
||||
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||
width, height = iargs[:size]
|
||||
o = UnicodePlots.createPlotWindow(x, y; width = width,
|
||||
height = height,
|
||||
title = iargs[:title],
|
||||
# labels = iargs[:legend],
|
||||
xlim = xlim,
|
||||
ylim = ylim)
|
||||
|
||||
# set the axis labels
|
||||
UnicodePlots.xlabel!(o, iargs[:xlabel])
|
||||
UnicodePlots.ylabel!(o, iargs[:ylabel])
|
||||
|
||||
# now use the ! functions to add to the plot
|
||||
for d in sargs
|
||||
addUnicodeSeries!(o, d, iargs[:legend], xlim, ylim)
|
||||
end
|
||||
|
||||
# save the object
|
||||
plt.o = o
|
||||
end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, d::Dict, addlegend::Bool, xlim, ylim)
|
||||
|
||||
# get the function, or special handling for step/bar/hist
|
||||
lt = d[:linetype]
|
||||
|
||||
# handle hline/vline separately
|
||||
if lt in (:hline,:vline)
|
||||
for yi in d[:y]
|
||||
if lt == :hline
|
||||
UnicodePlots.lineplot!(o, xlim, [yi,yi])
|
||||
else
|
||||
UnicodePlots.lineplot!(o, [yi,yi], ylim)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
stepstyle = :post
|
||||
if lt == :path
|
||||
func = UnicodePlots.lineplot!
|
||||
elseif lt == :scatter || d[:markershape] != :none
|
||||
func = UnicodePlots.scatterplot!
|
||||
elseif lt == :steppost
|
||||
func = UnicodePlots.stairs!
|
||||
elseif lt == :steppre
|
||||
func = UnicodePlots.stairs!
|
||||
stepstyle = :pre
|
||||
else
|
||||
error("Linestyle $lt not supported by UnicodePlots")
|
||||
end
|
||||
|
||||
# get the series data and label
|
||||
x, y = [collect(float(d[s])) for s in (:x, :y)]
|
||||
label = addlegend ? d[:label] : ""
|
||||
|
||||
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
|
||||
color = d[:color] in UnicodePlots.autoColors ? d[:color] : :auto
|
||||
|
||||
# add the series
|
||||
func(o, x, y; color = color, name = label, style = stepstyle)
|
||||
end
|
||||
|
||||
|
||||
function handlePlotColors(::UnicodePlotsPackage, d::Dict)
|
||||
# TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
|
||||
d[:color_palette] = [RGB(0,0,0)]
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
|
||||
function plot(pkg::UnicodePlotsPackage; kw...)
|
||||
plt = Plot(nothing, pkg, 0, Dict(kw), Dict[])
|
||||
|
||||
# do we want to give a new default size?
|
||||
if !haskey(plt.initargs, :size) || plt.initargs[:size] == _plotDefaults[:size]
|
||||
plt.initargs[:size] = (60,20)
|
||||
end
|
||||
|
||||
plt
|
||||
end
|
||||
|
||||
function plot!(::UnicodePlotsPackage, plt::Plot; kw...)
|
||||
d = Dict(kw)
|
||||
if d[:linetype] in (:sticks, :bar)
|
||||
d = barHack(; d...)
|
||||
elseif d[:linetype] == :hist
|
||||
d = barHack(; histogramHack(; d...)...)
|
||||
end
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
function updatePlotItems(plt::Plot{UnicodePlotsPackage}, d::Dict)
|
||||
for k in (:title, :xlabel, :ylabel, :xlims, :ylims)
|
||||
if haskey(d, k)
|
||||
plt.initargs[k] = d[k]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
|
||||
function png(plt::PlottingObject{UnicodePlotsPackage}, fn::@compat(AbstractString))
|
||||
fn = addExtension(fn, "png")
|
||||
|
||||
# make some whitespace and show the plot
|
||||
println("\n\n\n\n\n\n")
|
||||
gui(plt)
|
||||
|
||||
@osx_only begin
|
||||
# BEGIN HACK
|
||||
|
||||
# wait while the plot gets drawn
|
||||
sleep(0.5)
|
||||
|
||||
# use osx screen capture when my terminal is maximized and cursor starts at the bottom (I know, right?)
|
||||
# TODO: compute size of plot to adjust these numbers (or maybe implement something good??)
|
||||
run(`screencapture -R50,600,700,420 $fn`)
|
||||
|
||||
# END HACK (phew)
|
||||
return
|
||||
end
|
||||
|
||||
error("Can only savepng on osx with UnicodePlots (though even then I wouldn't do it)")
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# we don't do very much for subplots... just stack them vertically
|
||||
|
||||
function buildSubplotObject!(subplt::Subplot{UnicodePlotsPackage}, isbefore::Bool)
|
||||
isbefore && return false
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsPackage})
|
||||
rebuildUnicodePlot!(plt)
|
||||
show(plt.o)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsPackage})
|
||||
for plt in subplt.plts
|
||||
gui(plt)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
|
||||
# https://github.com/nolta/Winston.jl
|
||||
|
||||
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
## dictionaries for conversion of Plots.jl names to Winston ones.
|
||||
@compat const winston_linestyle = Dict(:solid=>"solid",
|
||||
:dash=>"dash",
|
||||
:dot=>"dotted",
|
||||
:dashdot=>"dotdashed"
|
||||
)
|
||||
|
||||
@compat const winston_marker = Dict(:none=>".",
|
||||
:rect => "square",
|
||||
:ellipse=>"circle",
|
||||
:diamond=>"diamond",
|
||||
:utriangle=>"triangle",
|
||||
:dtriangle=>"down-triangle",
|
||||
:cross => "plus",
|
||||
:xcross => "cross",
|
||||
:star5 => "asterisk"
|
||||
)
|
||||
|
||||
function preparePlotUpdate(plt::Plot{WinstonPackage})
|
||||
Winston.ghf(plt.o)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function plot(pkg::WinstonPackage; kw...)
|
||||
d = Dict(kw)
|
||||
wplt = Winston.FramedPlot(title = d[:title], xlabel = d[:xlabel], ylabel = d[:ylabel])
|
||||
|
||||
Plot(wplt, pkg, 0, d, Dict[])
|
||||
end
|
||||
|
||||
copy_remove(d::Dict, s::Symbol) = delete!(copy(d), s)
|
||||
|
||||
function addRegressionLineWinston(d::Dict, wplt)
|
||||
xs, ys = regressionXY(d[:x], d[:y])
|
||||
Winston.add(wplt, Winston.Curve(xs, ys, kind="dotted"))
|
||||
end
|
||||
|
||||
function getWinstonItems(plt::Plot)
|
||||
if isa(plt.o, Winston.FramedPlot)
|
||||
wplt = plt.o
|
||||
window, canvas = nothing, nothing
|
||||
else
|
||||
window, canvas, wplt = plt.o
|
||||
end
|
||||
window, canvas, wplt
|
||||
end
|
||||
|
||||
function plot!(::WinstonPackage, plt::Plot; kw...)
|
||||
d = Dict(kw)
|
||||
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
|
||||
# until we call it normally, do the hack
|
||||
if d[:linetype] == :bar
|
||||
d = barHack(;d...)
|
||||
end
|
||||
|
||||
|
||||
e = Dict()
|
||||
e[:color] = getColor(d[:color])
|
||||
e[:linewidth] = d[:linewidth]
|
||||
e[:kind] = winston_linestyle[d[:linestyle]]
|
||||
e[:symbolkind] = winston_marker[d[:markershape]]
|
||||
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
|
||||
e[:symbolsize] = d[:markersize] / 5
|
||||
|
||||
# pos # (Int,Int), move the enclosing window to this position
|
||||
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
||||
|
||||
|
||||
|
||||
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar
|
||||
if d[:linetype] == :none
|
||||
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
|
||||
|
||||
elseif d[:linetype] == :path
|
||||
x, y = d[:x], d[:y]
|
||||
Winston.add(wplt, Winston.Curve(x, y; e...))
|
||||
|
||||
fillrange = d[:fillrange]
|
||||
if fillrange != nothing
|
||||
if isa(fillrange, AbstractVector)
|
||||
y2 = fillrange
|
||||
else
|
||||
y2 = Float64[fillrange for yi in y]
|
||||
end
|
||||
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(d[:fillcolor])))
|
||||
end
|
||||
|
||||
elseif d[:linetype] == :scatter
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
# elseif d[:linetype] == :step
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:linetype] == :stepinverted
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:linetype] == :sticks
|
||||
Winston.add(wplt, Winston.Stems(d[:x], d[:y]; e...))
|
||||
|
||||
# elseif d[:linetype] == :dots
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:linetype] == :heatmap
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:linetype] == :hexbin
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:linetype] == :hist
|
||||
hst = hist(d[:y], d[:nbins])
|
||||
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :nbins)...))
|
||||
|
||||
# elseif d[:linetype] == :bar
|
||||
# # fn = Winston.XXX
|
||||
|
||||
else
|
||||
error("linetype $(d[:linetype]) not supported by Winston.")
|
||||
|
||||
end
|
||||
|
||||
|
||||
# markershape
|
||||
if d[:markershape] != :none
|
||||
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
|
||||
end
|
||||
|
||||
|
||||
# optionally add a regression line
|
||||
d[:smooth] && d[:linetype] != :hist && addRegressionLineWinston(d, wplt)
|
||||
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@compat const _winstonNames = Dict(
|
||||
:xlims => :xrange,
|
||||
:ylims => :yrange,
|
||||
:xscale => :xlog,
|
||||
:yscale => :ylog,
|
||||
)
|
||||
|
||||
function updatePlotItems(plt::Plot{WinstonPackage}, d::Dict)
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
for k in (:xlabel, :ylabel, :title, :xlims, :ylims)
|
||||
if haskey(d, k)
|
||||
Winston.setattr(wplt, string(get(_winstonNames, k, k)), d[k])
|
||||
end
|
||||
end
|
||||
|
||||
for k in (:xscale, :yscale)
|
||||
if haskey(d, k)
|
||||
islogscale = d[k] == :log10
|
||||
Winston.setattr(wplt, (k == :xscale ? :xlog : :ylog), islogscale)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function createWinstonAnnotationObject(plt::Plot{WinstonPackage}, x, y, val::@compat(AbstractString))
|
||||
Winston.text(x, y, val)
|
||||
end
|
||||
|
||||
function addAnnotations{X,Y,V}(plt::Plot{WinstonPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
createWinstonAnnotationObject(plt, ann...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function buildSubplotObject!(subplt::Subplot{WinstonPackage}, isbefore::Bool)
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function addWinstonLegend(plt::Plot, wplt)
|
||||
if plt.initargs[:legend]
|
||||
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
|
||||
end
|
||||
end
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{WinstonPackage})
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
addWinstonLegend(plt, wplt)
|
||||
writemime(io, "image/png", wplt)
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{WinstonPackage})
|
||||
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
|
||||
if window == nothing
|
||||
if Winston.output_surface != :gtk
|
||||
error("Gtk is the only supported display for Winston in Plots. Set `output_surface = gtk` in src/Winston.ini")
|
||||
end
|
||||
# initialize window
|
||||
w,h = plt.initargs[:size]
|
||||
canvas = Gtk.GtkCanvasLeaf()
|
||||
window = Gtk.GtkWindowLeaf(canvas, plt.initargs[:windowtitle], w, h)
|
||||
plt.o = (window, canvas, wplt)
|
||||
end
|
||||
|
||||
addWinstonLegend(plt, wplt)
|
||||
|
||||
Winston.display(canvas, wplt)
|
||||
Gtk.showall(window)
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonPackage})
|
||||
# TODO: display/show the Subplot object
|
||||
end
|
||||
@@ -0,0 +1,426 @@
|
||||
|
||||
# note: I found this list of hex values in a comment by Tatarize here: http://stackoverflow.com/a/12224359
|
||||
const _masterColorList = [
|
||||
0xFFFFFF,
|
||||
0x000000,
|
||||
0x0000FF,
|
||||
0x00FF00,
|
||||
0xFF0000,
|
||||
0x01FFFE,
|
||||
0xFFA6FE,
|
||||
0xFFDB66,
|
||||
0x006401,
|
||||
0x010067,
|
||||
0x95003A,
|
||||
0x007DB5,
|
||||
0xFF00F6,
|
||||
0xFFEEE8,
|
||||
0x774D00,
|
||||
0x90FB92,
|
||||
0x0076FF,
|
||||
0xD5FF00,
|
||||
0xFF937E,
|
||||
0x6A826C,
|
||||
0xFF029D,
|
||||
0xFE8900,
|
||||
0x7A4782,
|
||||
0x7E2DD2,
|
||||
0x85A900,
|
||||
0xFF0056,
|
||||
0xA42400,
|
||||
0x00AE7E,
|
||||
0x683D3B,
|
||||
0xBDC6FF,
|
||||
0x263400,
|
||||
0xBDD393,
|
||||
0x00B917,
|
||||
0x9E008E,
|
||||
0x001544,
|
||||
0xC28C9F,
|
||||
0xFF74A3,
|
||||
0x01D0FF,
|
||||
0x004754,
|
||||
0xE56FFE,
|
||||
0x788231,
|
||||
0x0E4CA1,
|
||||
0x91D0CB,
|
||||
0xBE9970,
|
||||
0x968AE8,
|
||||
0xBB8800,
|
||||
0x43002C,
|
||||
0xDEFF74,
|
||||
0x00FFC6,
|
||||
0xFFE502,
|
||||
0x620E00,
|
||||
0x008F9C,
|
||||
0x98FF52,
|
||||
0x7544B1,
|
||||
0xB500FF,
|
||||
0x00FF78,
|
||||
0xFF6E41,
|
||||
0x005F39,
|
||||
0x6B6882,
|
||||
0x5FAD4E,
|
||||
0xA75740,
|
||||
0xA5FFD2,
|
||||
0xFFB167,
|
||||
0x009BFF,
|
||||
0xE85EBE,
|
||||
]
|
||||
|
||||
|
||||
function darken(c, v=0.1)
|
||||
rgba = convert(RGBA, c)
|
||||
r = max(0, min(rgba.r - v, 1))
|
||||
g = max(0, min(rgba.g - v, 1))
|
||||
b = max(0, min(rgba.b - v, 1))
|
||||
RGBA(r,g,b,rgba.alpha)
|
||||
end
|
||||
function lighten(c, v=0.3)
|
||||
darken(c, -v)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
|
||||
abstract ColorScheme
|
||||
|
||||
getColor(scheme::ColorScheme) = getColor(scheme, 1)
|
||||
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
|
||||
|
||||
colorscheme(scheme::ColorScheme) = scheme
|
||||
colorscheme(s::Symbol; kw...) = haskey(_gradients, s) ? ColorGradient(s; kw...) : ColorWrapper(convertColor(s); kw...)
|
||||
colorscheme{T<:Real}(s::Symbol, vals::AVec{T}; kw...) = ColorGradient(s, vals; kw...)
|
||||
colorscheme(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
|
||||
colorscheme{T<:Colorant}(cs::AVec{T}; kw...) = ColorGradient(cs; kw...)
|
||||
colorscheme(f::Function; kw...) = ColorFunction(f; kw...)
|
||||
colorscheme(v::AVec; kw...) = ColorVector(v; kw...)
|
||||
colorscheme(m::AMat; kw...) = size(m,1) == 1 ? map(c->colorscheme(c; kw...), m) : [colorscheme(m[:,i]; kw...) for i in 1:size(m,2)]'
|
||||
colorscheme(c::Colorant; kw...) = ColorWrapper(c; kw...)
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
|
||||
convertColor(c::@compat(Union{AbstractString, Symbol})) = parse(Colorant, string(c))
|
||||
convertColor(c::Colorant) = c
|
||||
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
|
||||
convertColor(c::ColorScheme) = c
|
||||
|
||||
function convertColor(c, α::Real)
|
||||
c = convertColor(c)
|
||||
RGBA(RGB(c), α)
|
||||
end
|
||||
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
|
||||
convertColor(c, α::@compat(Void)) = convertColor(c)
|
||||
|
||||
# backup... try to convert
|
||||
getColor(c) = convertColor(c)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"]
|
||||
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
|
||||
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
|
||||
|
||||
@compat const _gradients = Dict(
|
||||
:blues => [colorant"lightblue", colorant"darkblue"],
|
||||
:reds => [colorant"lightpink", colorant"darkred"],
|
||||
:greens => [colorant"lightgreen", colorant"darkgreen"],
|
||||
:redsblues => [colorant"darkred", RGB(0.8,0.85,0.8), colorant"darkblue"],
|
||||
:bluesreds => [colorant"darkblue", RGB(0.8,0.85,0.8), colorant"darkred"],
|
||||
:heat => [colorant"lightyellow", colorant"orange", colorant"darkred"],
|
||||
:grays => [RGB(.95,.95,.95),RGB(.05,.05,.05)],
|
||||
:rainbow => _rainbowColors,
|
||||
:lightrainbow => map(lighten, _rainbowColors),
|
||||
:darkrainbow => map(darken, _rainbowColors),
|
||||
:darktest => _testColors,
|
||||
:lighttest => map(c -> lighten(c, 0.3), _testColors),
|
||||
)
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
|
||||
immutable ColorGradient <: ColorScheme
|
||||
colors::Vector{Colorant}
|
||||
values::Vector{Float64}
|
||||
|
||||
function ColorGradient{T<:Colorant,S<:Real}(cs::AVec{T}, vals::AVec{S} = 0:1; alpha = nothing)
|
||||
if length(cs) == length(vals)
|
||||
return new(convertColor(cs,alpha), collect(vals))
|
||||
end
|
||||
|
||||
# otherwise interpolate evenly between the minval and maxval
|
||||
minval, maxval = minimum(vals), maximum(vals)
|
||||
vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
|
||||
new(convertColor(cs,alpha), vs)
|
||||
end
|
||||
end
|
||||
|
||||
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
|
||||
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:1; kw...)
|
||||
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
|
||||
|
||||
# if we passed in the right number of values, create the gradient directly
|
||||
cs = _gradients[s]
|
||||
ColorGradient(cs, vals; kw...)
|
||||
end
|
||||
|
||||
function ColorGradient{T<:Real}(cs::AVec{Symbol}, vals::AVec{T} = 0:1; kw...)
|
||||
ColorGradient(map(convertColor, cs), vals; kw...)
|
||||
end
|
||||
|
||||
getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]
|
||||
|
||||
function getColorZ(gradient::ColorGradient, z::Real)
|
||||
cs = gradient.colors
|
||||
vs = gradient.values
|
||||
n = length(cs)
|
||||
@assert n > 0 && n == length(vs)
|
||||
|
||||
# can we just return the first color?
|
||||
if z <= vs[1] || n == 1
|
||||
return cs[1]
|
||||
end
|
||||
|
||||
# find the bounding colors and interpolate
|
||||
for i in 2:n
|
||||
if z <= vs[i]
|
||||
return interpolate_rgb(cs[i-1], cs[i], (z - vs[i-1]) / (vs[i] - vs[i-1]))
|
||||
end
|
||||
end
|
||||
|
||||
# if we get here, return the last color
|
||||
cs[end]
|
||||
end
|
||||
|
||||
getColorVector(gradient::ColorGradient) = gradient.colors
|
||||
|
||||
# for 0.3
|
||||
Colors.RGBA(c::Colorant) = RGBA(red(c), green(c), blue(c), alpha(c))
|
||||
Colors.RGB(c::Colorant) = RGB(red(c), green(c), blue(c))
|
||||
|
||||
function interpolate_rgb(c1::Colorant, c2::Colorant, w::Real)
|
||||
rgb1 = RGBA(c1)
|
||||
rgb2 = RGBA(c2)
|
||||
r = interpolate(rgb1.r, rgb2.r, w)
|
||||
g = interpolate(rgb1.g, rgb2.g, w)
|
||||
b = interpolate(rgb1.b, rgb2.b, w)
|
||||
a = interpolate(rgb1.alpha, rgb2.alpha, w)
|
||||
RGBA(r, g, b, a)
|
||||
end
|
||||
|
||||
|
||||
function interpolate(v1::Real, v2::Real, w::Real)
|
||||
(1-w) * v1 + w * v2
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a function, taking an index and returning a Colorant"
|
||||
immutable ColorFunction <: ColorScheme
|
||||
f::Function
|
||||
end
|
||||
|
||||
getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a function, taking an z-value and returning a Colorant"
|
||||
immutable ColorZFunction <: ColorScheme
|
||||
f::Function
|
||||
end
|
||||
|
||||
getColorZ(scheme::ColorFunction, z::Real) = scheme.f(z)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a vector of colors... may be vector of Symbol/String/Colorant"
|
||||
immutable ColorVector <: ColorScheme
|
||||
v::Vector{Colorant}
|
||||
ColorVector(v::AVec; alpha = nothing) = new(convertColor(v,alpha))
|
||||
end
|
||||
|
||||
getColor(scheme::ColorVector, idx::Int) = convertColor(scheme.v[mod1(idx, length(scheme.v))])
|
||||
getColorVector(scheme::ColorVector) = scheme.v
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a single color"
|
||||
immutable ColorWrapper <: ColorScheme
|
||||
c::RGBA
|
||||
ColorWrapper(c::Colorant; alpha = nothing) = new(convertColor(c, alpha))
|
||||
end
|
||||
|
||||
ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Colorant, s), alpha))
|
||||
|
||||
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
|
||||
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
|
||||
isbackgrounddark(bgcolor::Color) = Lab(bgcolor).l < 0.5
|
||||
|
||||
# move closer to lighter/darker depending on background value
|
||||
function adjustAway(val, bgval, vmin=0., vmax=100.)
|
||||
if bgval < 0.5 * (vmax+vmin)
|
||||
tmp = max(val, bgval)
|
||||
return 0.5 * (tmp + max(tmp, vmax))
|
||||
else
|
||||
tmp = min(val, bgval)
|
||||
return 0.5 * (tmp + min(tmp, vmin))
|
||||
end
|
||||
end
|
||||
|
||||
# borrowed from http://stackoverflow.com/a/1855903:
|
||||
lightnessLevel(c::Colorant) = 0.299 * red(c) + 0.587 * green(c) + 0.114 * blue(c)
|
||||
|
||||
isdark(c::Colorant) = lightnessLevel(c) < 0.5
|
||||
islight(c::Colorant) = !isdark(c)
|
||||
|
||||
function convertHexToRGB(h::Unsigned)
|
||||
mask = 0x0000FF
|
||||
RGB([(x & mask) / 0xFF for x in (h >> 16, h >> 8, h)]...)
|
||||
end
|
||||
|
||||
const _allColors = map(convertHexToRGB, _masterColorList)
|
||||
const _darkColors = filter(isdark, _allColors)
|
||||
const _lightColors = filter(islight, _allColors)
|
||||
const _sortedColorsForDarkBackground = vcat(_lightColors, reverse(_darkColors[2:end]))
|
||||
const _sortedColorsForLightBackground = vcat(_darkColors, reverse(_lightColors[2:end]))
|
||||
|
||||
const _defaultNumColors = 17
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
# Methods to automatically generate gradients for color selection based on
|
||||
# background color and a short list of seed colors
|
||||
|
||||
# here are some magic constants that could be changed if you really want
|
||||
const _lightness_darkbg = [80.0]
|
||||
const _lightness_lightbg = [60.0]
|
||||
const _lch_c_const = [60]
|
||||
|
||||
function adjust_lch(color, l, c)
|
||||
lch = convert(LCHab, color)
|
||||
convert(RGB, LCHab(l, c, lch.h))
|
||||
end
|
||||
|
||||
function lightness_from_background(bgcolor)
|
||||
bglight = convert(LCHab, bgcolor).l
|
||||
bglight < 50.0 ? _lightness_darkbg[1] : _lightness_lightbg[1]
|
||||
end
|
||||
|
||||
function gradient_from_list(cs)
|
||||
zvalues = Plots.get_zvalues(length(cs))
|
||||
indices = sortperm(zvalues)
|
||||
sorted_colors = map(RGBA, cs[indices])
|
||||
sorted_zvalues = zvalues[indices]
|
||||
ColorGradient(sorted_colors, sorted_zvalues)
|
||||
end
|
||||
|
||||
function generate_colorgradient(bgcolor = colorant"white";
|
||||
color_bases = color_bases=[colorant"steelblue",colorant"orangered"],
|
||||
lightness = lightness_from_background(bgcolor),
|
||||
chroma = _lch_c_const[1],
|
||||
n = _defaultNumColors)
|
||||
seed_colors = vcat(bgcolor, map(c -> adjust_lch(c, lightness, chroma), color_bases))
|
||||
colors = distinguishable_colors(n,
|
||||
seed_colors,
|
||||
lchoices=Float64[lightness],
|
||||
cchoices=Float64[chroma],
|
||||
hchoices=linspace(0, 340, 20)
|
||||
)[2:end]
|
||||
gradient_from_list(colors)
|
||||
end
|
||||
|
||||
function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
|
||||
grad = if palette == :auto
|
||||
generate_colorgradient(bgcolor)
|
||||
else
|
||||
ColorGradient(palette)
|
||||
end
|
||||
zrng = get_zvalues(numcolors)
|
||||
RGBA[getColorZ(grad, z) for z in zrng]
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function getpctrange(n::Int)
|
||||
n > 0 || error()
|
||||
n == 1 && return zeros(1)
|
||||
zs = [0.0, 1.0]
|
||||
for i in 3:n
|
||||
sorted = sort(zs)
|
||||
diffs = diff(sorted)
|
||||
widestj = 0
|
||||
widest = 0.0
|
||||
for (j,d) in enumerate(diffs)
|
||||
if d > widest
|
||||
widest = d
|
||||
widestj = j
|
||||
end
|
||||
end
|
||||
push!(zs, sorted[widestj] + 0.5 * diffs[widestj])
|
||||
end
|
||||
zs
|
||||
end
|
||||
|
||||
function get_zvalues(n::Int)
|
||||
offsets = getpctrange(ceil(Int,n/4)+1)/4
|
||||
offsets = vcat(offsets[1], offsets[3:end])
|
||||
zvalues = Float64[]
|
||||
for offset in offsets
|
||||
append!(zvalues, offset + [0.0, 0.5, 0.25, 0.75])
|
||||
end
|
||||
vcat(zvalues[1], 1.0, zvalues[2:n-1])
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
# TODO: try to use the algorithms from https://github.com/timothyrenner/ColorBrewer.jl
|
||||
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
|
||||
|
||||
# function getBackgroundRGBColor(c, d::Dict)
|
||||
function handlePlotColors(::PlottingPackage, d::Dict)
|
||||
if :background_color in supportedArgs()
|
||||
bgcolor = convertColor(d[:background_color])
|
||||
else
|
||||
bgcolor = _plotDefaults[:background_color]
|
||||
if d[:background_color] != _plotDefaults[:background_color]
|
||||
warn("Cannot set background_color with backend $(backend())")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100)
|
||||
|
||||
|
||||
# set the foreground color (text, ticks, gridlines) to be white or black depending
|
||||
# on how dark the background is.
|
||||
fgcolor = get(d, :foreground_color, :auto)
|
||||
fgcolor = if fgcolor == :auto
|
||||
isdark(bgcolor) ? colorant"white" : colorant"black"
|
||||
else
|
||||
convertColor(fgcolor)
|
||||
end
|
||||
|
||||
# bgcolor
|
||||
d[:background_color] = colorscheme(bgcolor)
|
||||
d[:foreground_color] = colorscheme(fgcolor)
|
||||
end
|
||||
|
||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
||||
function getSeriesRGBColor(c, initargs::Dict, n::Int)
|
||||
|
||||
if c == :auto
|
||||
c = autopick(initargs[:color_palette], n)
|
||||
end
|
||||
|
||||
# c should now be a subtype of ColorScheme
|
||||
colorscheme(c)
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
|
||||
# TODO:
|
||||
"""
|
||||
- load Contours.jl similar to DataFrames
|
||||
- method to build grid from x/y/z vectors
|
||||
- method to wrap contours creation
|
||||
- method to plot contours as custom shapes (TODO: create Stroke and Fill types and add markerstroke/markerfill args)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# # ----------------------------------------------------------
|
||||
# # ----------------------------------------------------------
|
||||
|
||||
|
||||
# immutable Vertex
|
||||
# x::Float64
|
||||
# y::Float64
|
||||
# z::Float64
|
||||
# end
|
||||
|
||||
# immutable Edge
|
||||
# v::Vertex
|
||||
# u::Vertex
|
||||
# end
|
||||
|
||||
# # ----------------------------------------------------------
|
||||
|
||||
# # one rectangle's z-values and the center vertex
|
||||
# # z is ordered: topleft, topright, bottomright, bottomleft
|
||||
# immutable GridRect
|
||||
# z::Vector{Float64}
|
||||
# center::Vertex
|
||||
# data::Vector{Vertex}
|
||||
# end
|
||||
|
||||
|
||||
|
||||
# type Grid
|
||||
# xs::Vector{Float64}
|
||||
# ys::Vector{Float64}
|
||||
# rects::Matrix{GridRect}
|
||||
# end
|
||||
|
||||
# function splitDataEvenly(v::AbstractVector{Float64}, n::Int)
|
||||
# vs = sort(v)
|
||||
|
||||
# end
|
||||
|
||||
# # the goal here is to create the vertical and horizontal partitions
|
||||
# # which define the grid, so that the data is somewhat evenly split
|
||||
# function bucketData(x, y, z)
|
||||
|
||||
# end
|
||||
|
||||
|
||||
# function buildGrid(x, y, z)
|
||||
# # create
|
||||
# end
|
||||
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
|
||||
# https://github.com/dcjones/Gadfly.jl
|
||||
|
||||
immutable GadflyPackage <: PlottingPackage end
|
||||
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
function plot(pkg::GadflyPackage; kw...)
|
||||
@eval import DataFrames
|
||||
|
||||
plt = Gadfly.Plot()
|
||||
plt.mapping = Dict()
|
||||
plt.data_source = DataFrames.DataFrame()
|
||||
plt.layers = plt.layers[1:0]
|
||||
|
||||
# add the title, axis labels, and theme
|
||||
d = Dict(kw)
|
||||
|
||||
plt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
|
||||
Gadfly.Guide.ylabel(d[:ylabel]),
|
||||
Gadfly.Guide.title(d[:title])]
|
||||
|
||||
# add the legend?
|
||||
if d[:legend]
|
||||
unshift!(plt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
|
||||
end
|
||||
|
||||
plt.theme = Gadfly.Theme(background_color = (haskey(d, :background_color) ? d[:background_color] : colorant"white"))
|
||||
|
||||
Plot(plt, pkg, 0)
|
||||
end
|
||||
|
||||
function getGeomFromLineType(linetype::Symbol, nbins::Int)
|
||||
linetype == :line && return Gadfly.Geom.line
|
||||
linetype == :dots && return Gadfly.Geom.point
|
||||
linetype == :bar && return Gadfly.Geom.bar
|
||||
linetype == :step && return Gadfly.Geom.step
|
||||
linetype == :hist && return Gadfly.Geom.histogram(bincount=nbins)
|
||||
linetype == :none && return Gadfly.Geom.point # change this? are we usually pairing no line with scatterplots?
|
||||
linetype == :sticks && return Gadfly.Geom.bar
|
||||
error("linetype $linetype not currently supported with Gadfly")
|
||||
end
|
||||
|
||||
function getGeoms(linetype::Symbol, marker::Symbol, nbins::Int)
|
||||
geoms = []
|
||||
|
||||
# handle heatmaps (hexbins) specially
|
||||
if linetype in (:heatmap,:hexbin)
|
||||
push!(geoms, Gadfly.Geom.hexbin(xbincount=nbins, ybincount=nbins))
|
||||
else
|
||||
|
||||
# for other linetypes, get the correct Geom
|
||||
push!(geoms, getGeomFromLineType(linetype, nbins))
|
||||
|
||||
# for any marker, add Geom.point
|
||||
if marker != :none
|
||||
push!(geoms, Gadfly.Geom.point)
|
||||
end
|
||||
end
|
||||
|
||||
geoms
|
||||
end
|
||||
|
||||
|
||||
# plot one data series
|
||||
function plot!(::GadflyPackage, plt::Plot; kw...)
|
||||
d = Dict(kw)
|
||||
|
||||
gfargs = []
|
||||
|
||||
# add the Geoms
|
||||
append!(gfargs, getGeoms(d[:linetype], d[:marker], d[:nbins]))
|
||||
|
||||
# set color, line width, and point size
|
||||
theme = Gadfly.Theme(default_color = d[:color],
|
||||
line_width = d[:width] * Gadfly.px,
|
||||
default_point_size = d[:markersize] * Gadfly.px)
|
||||
push!(gfargs, theme)
|
||||
|
||||
# add a regression line?
|
||||
if d[:reg]
|
||||
push!(gfargs, Gadfly.Geom.smooth(method=:lm))
|
||||
end
|
||||
|
||||
# for histograms, set x=y
|
||||
x = d[d[:linetype] == :hist ? :y : :x]
|
||||
|
||||
# add to the legend
|
||||
if length(plt.o.guides) > 0 && isa(plt.o.guides[1], Gadfly.Guide.ManualColorKey)
|
||||
push!(plt.o.guides[1].labels, d[:label])
|
||||
push!(plt.o.guides[1].colors, d[:color])
|
||||
end
|
||||
|
||||
if d[:axis] != :left
|
||||
warn("Gadly only supports one y axis")
|
||||
end
|
||||
|
||||
# add the layer to the Gadfly.Plot
|
||||
prepend!(plt.o.layers, Gadfly.layer(unique(gfargs)...; x = x, y = d[:y]))
|
||||
plt
|
||||
end
|
||||
|
||||
function Base.display(::GadflyPackage, plt::Plot)
|
||||
display(plt.o)
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
function savepng(::GadflyPackage, plt::PlottingObject, fn::String;
|
||||
w = 6 * Gadfly.inch,
|
||||
h = 4 * Gadfly.inch)
|
||||
Gadfly.draw(Gadfly.PNG(fn, w, h), plt.o)
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# # create the underlying object (each backend will do this differently)
|
||||
# o = buildSubplotObject(plts, pkg, layout)
|
||||
|
||||
function buildSubplotObject!(::GadflyPackage, subplt::Subplot)
|
||||
i = 0
|
||||
rows = []
|
||||
for rowcnt in subplt.layout.rowcounts
|
||||
push!(rows, Gadfly.hstack([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
|
||||
i += rowcnt
|
||||
end
|
||||
subplt.o = Gadfly.vstack(rows...)
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::GadflyPackage, subplt::Subplot)
|
||||
display(subplt.o)
|
||||
end
|
||||
@@ -0,0 +1,104 @@
|
||||
|
||||
|
||||
defaultOutputFormat(plt::PlottingObject) = "png"
|
||||
|
||||
function png(plt::PlottingObject, fn::@compat(AbstractString))
|
||||
fn = addExtension(fn, "png")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("image/png"), plt)
|
||||
close(io)
|
||||
end
|
||||
png(fn::@compat(AbstractString)) = png(current(), fn)
|
||||
|
||||
function svg(plt::PlottingObject, fn::@compat(AbstractString))
|
||||
fn = addExtension(fn, "svg")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("image/svg+xml"), plt)
|
||||
close(io)
|
||||
end
|
||||
svg(fn::@compat(AbstractString)) = svg(current(), fn)
|
||||
|
||||
|
||||
function pdf(plt::PlottingObject, fn::@compat(AbstractString))
|
||||
fn = addExtension(fn, "pdf")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("application/pdf"), plt)
|
||||
close(io)
|
||||
end
|
||||
pdf(fn::@compat(AbstractString)) = pdf(current(), fn)
|
||||
|
||||
|
||||
function ps(plt::PlottingObject, fn::@compat(AbstractString))
|
||||
fn = addExtension(fn, "ps")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("application/postscript"), plt)
|
||||
close(io)
|
||||
end
|
||||
ps(fn::@compat(AbstractString)) = ps(current(), fn)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
@compat const _savemap = Dict(
|
||||
"png" => png,
|
||||
"svg" => svg,
|
||||
"pdf" => pdf,
|
||||
"ps" => ps,
|
||||
)
|
||||
|
||||
function getExtension(fn::@compat(AbstractString))
|
||||
pieces = split(fn, ".")
|
||||
length(pieces) > 1 || error("Can't extract file extension: ", fn)
|
||||
ext = pieces[end]
|
||||
haskey(_savemap, ext) || error("Invalid file extension: ", fn)
|
||||
ext
|
||||
end
|
||||
|
||||
function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
|
||||
try
|
||||
oldext = getExtension(fn)
|
||||
if oldext == ext
|
||||
return fn
|
||||
else
|
||||
return "$fn.$ext"
|
||||
end
|
||||
catch
|
||||
return "$fn.$ext"
|
||||
end
|
||||
end
|
||||
|
||||
function savefig(plt::PlottingObject, fn::@compat(AbstractString))
|
||||
|
||||
# get the extension
|
||||
local ext
|
||||
try
|
||||
ext = getExtension(fn)
|
||||
catch
|
||||
# if we couldn't extract the extension, add the default
|
||||
ext = defaultOutputFormat(plt)
|
||||
fn = addExtension(fn, ext)
|
||||
end
|
||||
|
||||
# save it
|
||||
func = get(_savemap, ext) do
|
||||
error("Unsupported extension $ext with filename ", fn)
|
||||
end
|
||||
func(plt, fn)
|
||||
end
|
||||
savefig(fn::@compat(AbstractString)) = savefig(current(), fn)
|
||||
|
||||
|
||||
# savepng(args...; kw...) = savepng(current(), args...; kw...)
|
||||
# savepng(plt::PlottingObject, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
gui(plt::PlottingObject = current()) = display(PlotsDisplay(), plt)
|
||||
|
||||
|
||||
# override the REPL display to open a gui window
|
||||
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::PlottingObject) = gui(plt)
|
||||
@@ -6,94 +6,67 @@ const CURRENT_PLOT = CurrentPlot(Nullable{PlottingObject}())
|
||||
|
||||
isplotnull() = isnull(CURRENT_PLOT.nullableplot)
|
||||
|
||||
function currentPlot()
|
||||
function current()
|
||||
if isplotnull()
|
||||
error("No current plot/subplot")
|
||||
end
|
||||
get(CURRENT_PLOT.nullableplot)
|
||||
end
|
||||
currentPlot!(plot::PlottingObject) = (CURRENT_PLOT.nullableplot = Nullable(plot))
|
||||
current(plot::PlottingObject) = (CURRENT_PLOT.nullableplot = Nullable(plot))
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
Base.string(plt::Plot) = "Plot{$(plt.plotter) n=$(plt.n)}"
|
||||
Base.string(plt::Plot) = "Plot{$(plt.backend) n=$(plt.n)}"
|
||||
Base.print(io::IO, plt::Plot) = print(io, string(plt))
|
||||
Base.show(io::IO, plt::Plot) = print(io, string(plt))
|
||||
|
||||
getplot(plt::Plot) = plt
|
||||
getinitargs(plt::Plot, idx::Int = 1) = plt.initargs
|
||||
convertSeriesIndex(plt::Plot, n::Int) = n
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
doc"""
|
||||
The main plot command. Call `plotter!(:module)` to set the current plotting backend.
|
||||
Commands are converted into the relevant plotting commands for that package:
|
||||
"""
|
||||
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
|
||||
|
||||
```
|
||||
plotter!(:gadfly)
|
||||
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
|
||||
plotter!(:qwt)
|
||||
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
|
||||
```
|
||||
|
||||
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
|
||||
|
||||
```
|
||||
plot(args...; kw...) # creates a new plot window, and sets it to be the currentPlot
|
||||
plot!(args...; kw...) # adds to the `currentPlot`
|
||||
plot(args...; kw...) # creates a new plot window, and sets it to be the current
|
||||
plot!(args...; kw...) # adds to the `current`
|
||||
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
|
||||
```
|
||||
|
||||
There are lots of ways to pass in data... just try it and it will likely work as expected.
|
||||
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
|
||||
When you pass in matrices, it splits by columns. See the documentation for more info.
|
||||
|
||||
Some keyword arguments you can set:
|
||||
|
||||
```
|
||||
axis # :left or :right
|
||||
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
|
||||
label # string or symbol, applies to that line, may go in a legend
|
||||
width # width of a line
|
||||
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap
|
||||
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
|
||||
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
|
||||
markercolor # same choices as `color`
|
||||
markersize # size of the marker
|
||||
nbins # number of bins for heatmap/hexbin and histograms
|
||||
heatmap_c # color cutoffs for Qwt heatmaps
|
||||
fillto # fillto value for area plots
|
||||
title # string or symbol, title of the plot
|
||||
xlabel # string or symbol, label on the bottom (x) axis
|
||||
ylabel # string or symbol, label on the left (y) axis
|
||||
yrightlabel # string or symbol, label on the right (y) axis
|
||||
reg # true or false, add a regression line for each line
|
||||
size # (Int,Int), resize the enclosing window
|
||||
pos # (Int,Int), move the enclosing window to this position
|
||||
windowtitle # string or symbol, set the title of the enclosing windowtitle
|
||||
screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
||||
show # true or false, show the plot (in case you don't want the window to pop up right away)
|
||||
```
|
||||
|
||||
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
|
||||
(yes I know it's not gramatically correct, but it's easy to use and implement)
|
||||
|
||||
```
|
||||
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# -------------------------
|
||||
|
||||
# this creates a new plot with args/kw and sets it to be the current plot
|
||||
function plot(args...; kw...)
|
||||
plt = plot(plotter(); getPlotKeywordArgs(kw, 1, 0)...) # create a new, blank plot
|
||||
plot!(plt, args...; kw...) # add to it
|
||||
pkg = backend()
|
||||
d = Dict(kw)
|
||||
preprocessArgs!(d)
|
||||
dumpdict(d, "After plot preprocessing")
|
||||
|
||||
plotargs = getPlotArgs(pkg, d, 1)
|
||||
dumpdict(plotargs, "Plot args")
|
||||
plt = plot(pkg; plotargs...) # create a new, blank plot
|
||||
|
||||
delete!(d, :background_color)
|
||||
plot!(plt, args...; d...) # add to it
|
||||
end
|
||||
|
||||
# this adds to the current plot
|
||||
|
||||
|
||||
# this adds to the current plot, or creates a new plot if none are current
|
||||
function plot!(args...; kw...)
|
||||
plot!(currentPlot(), args...; kw...)
|
||||
local plt
|
||||
try
|
||||
plt = current()
|
||||
catch
|
||||
return plot(args...; kw...)
|
||||
end
|
||||
plot!(current(), args...; kw...)
|
||||
end
|
||||
|
||||
# not allowed:
|
||||
@@ -103,312 +76,357 @@ end
|
||||
|
||||
# this adds to a specific plot... most plot commands will flow through here
|
||||
function plot!(plt::Plot, args...; kw...)
|
||||
d = Dict(kw)
|
||||
preprocessArgs!(d)
|
||||
|
||||
# # increment n if we're going directly to the package's plot method
|
||||
# if length(args) == 0
|
||||
# plt.n += 1
|
||||
# end
|
||||
dumpdict(d, "After plot! preprocessing")
|
||||
|
||||
# plot!(plt.plotter, plt, args...; kw...)
|
||||
warnOnUnsupportedArgs(plt.backend, d)
|
||||
|
||||
# grouping
|
||||
groupargs = get(d, :group, nothing) == nothing ? [] : [extractGroupArgs(d[:group], args...)]
|
||||
|
||||
kwList = createKWargsList(plt, args...; kw...)
|
||||
for (i,d) in enumerate(kwList)
|
||||
# just in case the backend needs to set up the plot (make it current or something)
|
||||
preparePlotUpdate(plt)
|
||||
|
||||
# get the list of dictionaries, one per series
|
||||
kwList, xmeta, ymeta = createKWargsList(plt, groupargs..., args...; d...)
|
||||
|
||||
# if we were able to extract guide information from the series inputs, then update the plot
|
||||
# @show xmeta, ymeta
|
||||
updateDictWithMeta(d, plt.initargs, xmeta, true)
|
||||
updateDictWithMeta(d, plt.initargs, ymeta, false)
|
||||
|
||||
# now we can plot the series
|
||||
for (i,di) in enumerate(kwList)
|
||||
plt.n += 1
|
||||
plot!(plt.plotter, plt; d...)
|
||||
|
||||
setTicksFromStringVector(d, di, :x, :xticks)
|
||||
setTicksFromStringVector(d, di, :y, :yticks)
|
||||
|
||||
# remove plot args
|
||||
for k in keys(_plotDefaults)
|
||||
delete!(di, k)
|
||||
end
|
||||
|
||||
dumpdict(di, "Series $i")
|
||||
|
||||
plot!(plt.backend, plt; di...)
|
||||
end
|
||||
|
||||
currentPlot!(plt)
|
||||
addAnnotations(plt, d)
|
||||
|
||||
# do we want to show it?
|
||||
d = Dict(kw)
|
||||
warnOnUnsupportedScales(plt.backend, d)
|
||||
|
||||
|
||||
# add title, axis labels, ticks, etc
|
||||
if !haskey(d, :subplot)
|
||||
merge!(plt.initargs, d)
|
||||
dumpdict(plt.initargs, "Updating plot items")
|
||||
updatePlotItems(plt, plt.initargs)
|
||||
end
|
||||
|
||||
updatePositionAndSize(plt, d)
|
||||
|
||||
current(plt)
|
||||
|
||||
# NOTE: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
|
||||
# # do we want to show it?
|
||||
if haskey(d, :show) && d[:show]
|
||||
display(plt)
|
||||
gui()
|
||||
end
|
||||
|
||||
plt
|
||||
end
|
||||
|
||||
# show/update the plot
|
||||
function Base.display(plt::PlottingObject)
|
||||
display(plt.plotter, plt)
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# if x or y are a vector of strings, we should create a list of unique strings,
|
||||
# and map x/y to be the index of the string... then set the x/y tick labels
|
||||
function setTicksFromStringVector(d::Dict, di::Dict, sym::Symbol, ticksym::Symbol)
|
||||
# if the x or y values are strings, set ticks to the unique values, and x/y to the indices of the ticks
|
||||
|
||||
v = di[sym]
|
||||
isa(v, AbstractArray) || return
|
||||
|
||||
T = eltype(v)
|
||||
if T <: @compat(AbstractString) || (!isempty(T.types) && all(x -> x <: @compat(AbstractString), T.types))
|
||||
|
||||
ticks = unique(di[sym])
|
||||
di[sym] = Int[findnext(ticks, v, 1) for v in di[sym]]
|
||||
|
||||
if !haskey(d, ticksym) || d[ticksym] == :auto
|
||||
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
preparePlotUpdate(plt::Plot) = nothing
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# should we update the x/y label given the meta info during input slicing?
|
||||
function updateDictWithMeta(d::Dict, initargs::Dict, meta::Symbol, isx::Bool)
|
||||
lsym = isx ? :xlabel : :ylabel
|
||||
if initargs[lsym] == default(lsym)
|
||||
d[lsym] = string(meta)
|
||||
end
|
||||
end
|
||||
updateDictWithMeta(d::Dict, initargs::Dict, meta, isx::Bool) = nothing
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
annotations(::@compat(Void)) = []
|
||||
annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v
|
||||
annotations{X,Y,V}(t::@compat(Tuple{X,Y,V})) = [t]
|
||||
annotations(anns) = error("Expecting a tuple (or vector of tuples) for annotations: ",
|
||||
"(x, y, annotation)\n got: $(typeof(anns))")
|
||||
|
||||
function addAnnotations(plt::Plot, d::Dict)
|
||||
anns = annotations(get(d, :annotation, nothing))
|
||||
if !isempty(anns)
|
||||
addAnnotations(plt, anns)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
# create a new "createKWargsList" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
|
||||
# Special handling for: no args, xmin/xmax, parametric, dataframes
|
||||
# Then once inputs have been converted, build the series args, map functions, etc.
|
||||
# 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}})
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::@compat(Void); kw...) = Any[nothing], nothing
|
||||
|
||||
# fixed number of blank series
|
||||
convertToAnyVector(n::Integer; kw...) = Any[zeros(0) for i in 1:n], nothing
|
||||
|
||||
# numeric vector
|
||||
convertToAnyVector{T<:Real}(v::AVec{T}; kw...) = Any[v], nothing
|
||||
|
||||
# string vector
|
||||
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}; kw...) = Any[v], nothing
|
||||
|
||||
# numeric matrix
|
||||
convertToAnyVector{T<:Real}(v::AMat{T}; kw...) = Any[v[:,i] for i in 1:size(v,2)], nothing
|
||||
|
||||
# function
|
||||
convertToAnyVector(f::Function; kw...) = Any[f], nothing
|
||||
|
||||
# vector of OHLC
|
||||
convertToAnyVector(v::AVec{OHLC}; kw...) = Any[v], nothing
|
||||
|
||||
# list of things (maybe other vectors, functions, or something else)
|
||||
convertToAnyVector(v::AVec; kw...) = Any[vi for vi in v], nothing
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# in computeXandY, we take in any of the possible items, convert into proper x/y vectors, then return.
|
||||
# this is also where all the "set x to 1:length(y)" happens, and also where we assert on lengths.
|
||||
computeX(x::@compat(Void), y) = 1:length(y)
|
||||
computeX(x, y) = copy(x)
|
||||
computeY(x, y::Function) = map(y, x)
|
||||
computeY(x, y) = copy(y)
|
||||
function computeXandY(x, y)
|
||||
if x == nothing && isa(y, Function)
|
||||
error("If you want to plot the function `$y`, you need to define the x values somehow!")
|
||||
end
|
||||
x, y = computeX(x,y), computeY(x,y)
|
||||
@assert length(x) == length(y)
|
||||
x, y
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# create n=max(mx,my) series arguments. the shorter list is cycled through
|
||||
# note: everything should flow through this
|
||||
function createKWargsList(plt::PlottingObject, x, y; kw...)
|
||||
xs, xmeta = convertToAnyVector(x; kw...)
|
||||
ys, ymeta = convertToAnyVector(y; kw...)
|
||||
|
||||
# _debugMode.on && dumpcallstack()
|
||||
|
||||
mx = length(xs)
|
||||
my = length(ys)
|
||||
ret = Any[]
|
||||
for i in 1:max(mx, my)
|
||||
|
||||
# try to set labels using ymeta
|
||||
d = Dict(kw)
|
||||
if !haskey(d, :label) && ymeta != nothing
|
||||
if isa(ymeta, Symbol)
|
||||
d[:label] = string(ymeta)
|
||||
elseif isa(ymeta, AVec{Symbol})
|
||||
d[:label] = string(ymeta[mod1(i,length(ymeta))])
|
||||
end
|
||||
end
|
||||
|
||||
# build the series arg dict
|
||||
numUncounted = get(d, :numUncounted, 0)
|
||||
n = plt.n + i + numUncounted
|
||||
dumpdict(d, "before getSeriesArgs")
|
||||
d = getSeriesArgs(plt.backend, getinitargs(plt, n), d, i + numUncounted, convertSeriesIndex(plt, n), n)
|
||||
dumpdict(d, "after getSeriesArgs")
|
||||
d[:x], d[:y] = computeXandY(xs[mod1(i,mx)], ys[mod1(i,my)])
|
||||
|
||||
if haskey(d, :idxfilter)
|
||||
d[:x] = d[:x][d[:idxfilter]]
|
||||
d[:y] = d[:y][d[:idxfilter]]
|
||||
end
|
||||
|
||||
# for linetype `line`, need to sort by x values
|
||||
if d[:linetype] == :line
|
||||
# order by x
|
||||
indices = sortperm(d[:x])
|
||||
d[:x] = d[:x][indices]
|
||||
d[:y] = d[:y][indices]
|
||||
d[:linetype] = :path
|
||||
end
|
||||
|
||||
# cleanup those fields that were used only for generating kw args
|
||||
for k in (:idxfilter, :numUncounted, :dataframe)
|
||||
delete!(d, k)
|
||||
end
|
||||
|
||||
# add it to our series list
|
||||
push!(ret, d)
|
||||
end
|
||||
|
||||
ret, xmeta, ymeta
|
||||
end
|
||||
|
||||
# handle grouping
|
||||
function createKWargsList(plt::PlottingObject, groupby::GroupBy, args...; kw...)
|
||||
ret = Any[]
|
||||
for (i,glab) in enumerate(groupby.groupLabels)
|
||||
# TODO: don't automatically overwrite labels
|
||||
kwlist, xmeta, ymeta = createKWargsList(plt, args...; kw...,
|
||||
idxfilter = groupby.groupIds[i],
|
||||
label = string(glab),
|
||||
numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
|
||||
append!(ret, kwlist)
|
||||
end
|
||||
ret, nothing, nothing # TODO: handle passing meta through
|
||||
end
|
||||
|
||||
# pass it off to the x/y version
|
||||
function createKWargsList(plt::PlottingObject, y; kw...)
|
||||
createKWargsList(plt, nothing, y; kw...)
|
||||
end
|
||||
|
||||
# contours or surfaces... irregular data
|
||||
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zvec::AVec; kw...)
|
||||
error("TODO: contours or surfaces... irregular data")
|
||||
end
|
||||
|
||||
# contours or surfaces... function grid
|
||||
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zf::Function; kw...)
|
||||
# only allow sorted x/y for now
|
||||
# TODO: auto sort x/y/z properly
|
||||
@assert x == sort(x)
|
||||
@assert y == sort(y)
|
||||
surface = Float64[zf(xi, yi) for xi in x, yi in y]
|
||||
createKWargsList(plt, x, y, surface; kw...) # passes it to the zmat version
|
||||
end
|
||||
|
||||
# contours or surfaces... matrix grid
|
||||
function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec, y::AVec, zmat::AMat{T}; kw...)
|
||||
# only allow sorted x/y for now
|
||||
# TODO: auto sort x/y/z properly
|
||||
@assert x == sort(x)
|
||||
@assert y == sort(y)
|
||||
@assert size(zmat) == (length(x), length(y))
|
||||
surf = Array(Any,1,1)
|
||||
surf[1,1] = convert(Matrix{Float64}, zmat)
|
||||
createKWargsList(plt, x, y; kw..., surface = surf, linetype = :contour)
|
||||
end
|
||||
|
||||
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs; kw...)
|
||||
error("Can't pass a Function or Vector{Function} for y without also passing x")
|
||||
end
|
||||
|
||||
# list of functions
|
||||
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs, x; kw...)
|
||||
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
|
||||
createKWargsList(plt, x, f; kw...)
|
||||
end
|
||||
|
||||
# special handling... xmin/xmax with function(s)
|
||||
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs, xmin::Real, xmax::Real; kw...)
|
||||
width = plt.initargs[:size][1]
|
||||
x = collect(linspace(xmin, xmax, width)) # we don't need more than the width
|
||||
createKWargsList(plt, x, f; kw...)
|
||||
end
|
||||
|
||||
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
||||
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
|
||||
|
||||
# special handling... xmin/xmax with parametric function(s)
|
||||
createKWargsList{T<:Real}(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...)
|
||||
createKWargsList{T<:Real}(plt::PlottingObject, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...)
|
||||
createKWargsList(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Real, umax::Real, numPoints::Int = 1000; kw...) = createKWargsList(plt, fx, fy, linspace(umin, umax, numPoints); kw...)
|
||||
|
||||
|
||||
|
||||
doc"Build a vector of dictionaries which hold the keyword arguments for a call to plot!"
|
||||
|
||||
# no args... 1 series
|
||||
# special handling... no args... 1 series
|
||||
function createKWargsList(plt::PlottingObject; kw...)
|
||||
d = Dict(kw)
|
||||
@assert haskey(d, :y)
|
||||
if !haskey(d, :x)
|
||||
d[:x] = 1:length(d[:y])
|
||||
if !haskey(d, :y)
|
||||
# assume we just want to create an empty plot object which can be added to later
|
||||
return [], nothing, nothing
|
||||
# error("Called plot/subplot without args... must set y in the keyword args. Example: plot(; y=rand(10))")
|
||||
end
|
||||
[getPlotKeywordArgs(d, 1, plt.n + 1)]
|
||||
end
|
||||
|
||||
# create one series where y is vectors of numbers
|
||||
function createKWargsList{T<:Real}(plt::PlottingObject, y::AVec{T}; kw...)
|
||||
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
|
||||
d[:x] = 1:length(y)
|
||||
d[:y] = y
|
||||
[d]
|
||||
end
|
||||
|
||||
# create one series where x/y are vectors of numbers
|
||||
function createKWargsList{T<:Real,S<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec{S}; kw...)
|
||||
@assert length(x) == length(y)
|
||||
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
|
||||
d[:x] = x
|
||||
d[:y] = y
|
||||
[d]
|
||||
end
|
||||
|
||||
# create m series, 1 for each column of y
|
||||
function createKWargsList(plt::PlottingObject, y::AMat; kw...)
|
||||
n,m = size(y)
|
||||
ret = []
|
||||
for i in 1:m
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = 1:n
|
||||
d[:y] = y[:,i]
|
||||
push!(ret, d)
|
||||
|
||||
if haskey(d, :x)
|
||||
return createKWargsList(plt, d[:x], d[:y]; kw...)
|
||||
else
|
||||
return createKWargsList(plt, d[:y]; kw...)
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
# create m series, 1 for each column of y
|
||||
function createKWargsList(plt::PlottingObject, x::AVec, y::AMat; kw...)
|
||||
n,m = size(y)
|
||||
@assert length(x) == n
|
||||
ret = []
|
||||
for i in 1:m
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = x
|
||||
d[:y] = y[:,i]
|
||||
push!(ret, d)
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
"For DataFrame support. Imports DataFrames and defines the necessary methods which support them."
|
||||
function dataframes()
|
||||
@eval import DataFrames
|
||||
|
||||
@eval function createKWargsList(plt::PlottingObject, df::DataFrames.DataFrame, args...; kw...)
|
||||
createKWargsList(plt, args...; kw..., dataframe = df)
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
# create m series, 1 for each column of y
|
||||
function createKWargsList(plt::PlottingObject, x::AMat, y::AMat; kw...)
|
||||
@assert size(x) == size(y)
|
||||
n,m = size(y)
|
||||
ret = []
|
||||
for i in 1:m
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = x[:,i]
|
||||
d[:y] = y[:,i]
|
||||
push!(ret, d)
|
||||
# expecting the column name of a dataframe that was passed in... anything else should error
|
||||
@eval function extractGroupArgs(s::Symbol, df::DataFrames.DataFrame, args...)
|
||||
if haskey(df, s)
|
||||
return extractGroupArgs(df[s])
|
||||
else
|
||||
error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
|
||||
end
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
# create 1 series, y = f(x)
|
||||
function createKWargsList(plt::PlottingObject, x::AVec, f::Function; kw...)
|
||||
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
|
||||
d[:x] = x
|
||||
d[:y] = map(f, x)
|
||||
[d]
|
||||
end
|
||||
|
||||
# create m series, y = f(x), 1 for each column of x
|
||||
function createKWargsList(plt::PlottingObject, x::AMat, f::Function; kw...)
|
||||
n,m = size(x)
|
||||
ret = []
|
||||
for i in 1:m
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = x[:,i]
|
||||
d[:y] = map(f, d[:x])
|
||||
push!(ret, d)
|
||||
@eval function getDataFrameFromKW(; kw...)
|
||||
for (k,v) in kw
|
||||
if k == :dataframe
|
||||
return v
|
||||
end
|
||||
end
|
||||
error("Missing dataframe argument in arguments!")
|
||||
end
|
||||
ret
|
||||
|
||||
# the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
|
||||
@eval convertToAnyVector(s::Symbol; kw...) = Any[getDataFrameFromKW(;kw...)[s]], s
|
||||
@eval convertToAnyVector(v::AVec{Symbol}; kw...) = (df = getDataFrameFromKW(;kw...); Any[df[s] for s in v]), v
|
||||
end
|
||||
|
||||
# create m series, 1 for each item in y (assumes vectors of something other than numbers... functions? vectors?)
|
||||
function createKWargsList(plt::PlottingObject, y::AVec; kw...)
|
||||
m = length(y)
|
||||
ret = []
|
||||
for i in 1:m
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = 1:length(y[i])
|
||||
d[:y] = y[i]
|
||||
push!(ret, d)
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
function getyvec(x::AVec, y::AVec)
|
||||
@assert length(x) == length(y)
|
||||
y
|
||||
end
|
||||
getyvec(x::AVec, f::Function) = map(f, x)
|
||||
getyvec(x, y) = error("Couldn't create yvec from types: x ($(typeof(x))), y ($(typeof(y)))")
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# same, but given an x to use for all series
|
||||
function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec; kw...)
|
||||
m = length(y)
|
||||
ret = []
|
||||
for i in 1:m
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = x
|
||||
d[:y] = getyvec(x, y[i])
|
||||
push!(ret, d)
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
# same, but m series of (x[i],y[i])
|
||||
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec; kw...)
|
||||
@assert length(x) == length(y)
|
||||
m = length(y)
|
||||
ret = []
|
||||
for i in 1:m
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = x[i]
|
||||
d[:y] = getyvec(x[i], y[i])
|
||||
push!(ret, d)
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
# n empty series
|
||||
function createKWargsList(plt::PlottingObject, n::Integer; kw...)
|
||||
ret = []
|
||||
for i in 1:n
|
||||
d = getPlotKeywordArgs(kw, i, plt.n + i)
|
||||
d[:x] = zeros(0)
|
||||
d[:y] = zeros(0)
|
||||
push!(ret, d)
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
# TODO: handle DataFrames (might have NAs!)
|
||||
|
||||
# -------------------------
|
||||
|
||||
# # most calls should flow through here now... we create a Dict with the keyword args for each series, and plot them
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, args...; kw...)
|
||||
# kwList = createKWargsList(plt, args...; kw...)
|
||||
# for (i,d) in enumerate(kwList)
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; d...)
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# -------------------------
|
||||
|
||||
# # These methods are various ways to add to an existing plot
|
||||
|
||||
# function plot!{T<:Real}(pkg::PlottingPackage, plt::Plot, y::AVec{T}; kw...)
|
||||
# plt.n += 1
|
||||
# # plot!(pkg, plt; x = 1:length(y), y = y, getPlotKeywordArgs(kw, 1, plt)...)
|
||||
# end
|
||||
|
||||
# function plot!{T<:Real,S<:Real}(pkg::PlottingPackage, plt::Plot, x::AVec{T}, y::AVec{S}; kw...) # one line (will assert length(x) == length(y))
|
||||
# @assert length(x) == length(y)
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x=x, y=y, getPlotKeywordArgs(kw, 1, plt)...)
|
||||
# end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
|
||||
# n,m = size(y)
|
||||
# for i in 1:m
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = 1:n, y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
|
||||
# n,m = size(y)
|
||||
# for i in 1:m
|
||||
# @assert length(x) == n
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = x, y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
|
||||
# @assert size(x) == size(y)
|
||||
# for i in 1:size(x,2)
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = x[:,i], y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, f::Function; kw...) # one line, y = f(x)
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = x, y = map(f,x), getPlotKeywordArgs(kw, 1, plt)...)
|
||||
# end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
|
||||
# for i in 1:size(x,2)
|
||||
# xi = x[:,i]
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = xi, y = map(f, xi), getPlotKeywordArgs(kw, i, plt)...)
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# # function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
|
||||
# # for i in 1:length(fs)
|
||||
# # plt.n += 1
|
||||
# # plot!(pkg, plt; x = x, y = map(fs[i], x), getPlotKeywordArgs(kw, i, plt)...)
|
||||
# # end
|
||||
# # plt
|
||||
# # end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, y::AVec; kw...) # multiple lines, each with x = 1:length(y[i])
|
||||
# for i in 1:length(y)
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = 1:length(y[i]), y = y[i], getPlotKeywordArgs(kw, i, plt)...)
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# function plot!{T<:Real}(pkg::PlottingPackage, plt::Plot, x::AVec{T}, y::AVec; kw...) # multiple lines, will assert length(x) == length(y[i])
|
||||
# for i in 1:length(y)
|
||||
# if typeof(y[i]) <: AbstractVector
|
||||
# @assert length(x) == length(y[i])
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = x, y = y[i], getPlotKeywordArgs(kw, i, plt)...)
|
||||
# elseif typeof(y[i]) == Function
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = x, y = map(y[i], x), getPlotKeywordArgs(kw, 1, plt)...)
|
||||
# end
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, y::AVec; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
|
||||
# @assert length(x) == length(y)
|
||||
# for i in 1:length(x)
|
||||
# @assert length(x[i]) == length(y[i])
|
||||
# plt.n += 1
|
||||
# plot!(pkg, plt; x = x[i], y = y[i], getPlotKeywordArgs(kw, i, plt)...)
|
||||
# end
|
||||
# plt
|
||||
# end
|
||||
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot, n::Integer; kw...) # n lines, all empty (for updating plots)
|
||||
# for i in 1:n
|
||||
# plt.n += 1
|
||||
# plot(pkg, plt, x = zeros(0), y = zeros(0), getPlotKeywordArgs(kw, i, plt)...)
|
||||
# end
|
||||
# end
|
||||
|
||||
# -------------------------
|
||||
|
||||
# # this is the core method... add to a plot object using kwargs, with args already converted into kwargs
|
||||
# function plot!(pkg::PlottingPackage, plt::Plot; kw...)
|
||||
# plot!(pl, plt; kw...)
|
||||
# end
|
||||
|
||||
@@ -1,64 +1,255 @@
|
||||
|
||||
|
||||
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
|
||||
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
|
||||
Base.display(pkg::PlottingPackage, plt::Plot) = error("display($pkg, plt) is not implemented")
|
||||
immutable GadflyPackage <: PlottingPackage end
|
||||
immutable ImmersePackage <: PlottingPackage end
|
||||
immutable PyPlotPackage <: PlottingPackage end
|
||||
immutable QwtPackage <: PlottingPackage end
|
||||
immutable UnicodePlotsPackage <: PlottingPackage end
|
||||
immutable WinstonPackage <: PlottingPackage end
|
||||
|
||||
typealias GadflyOrImmerse @compat(Union{GadflyPackage, ImmersePackage})
|
||||
|
||||
export
|
||||
gadfly,
|
||||
immerse,
|
||||
pyplot,
|
||||
qwt,
|
||||
unicodeplots
|
||||
# winston
|
||||
|
||||
gadfly() = backend(:gadfly)
|
||||
immerse() = backend(:immerse)
|
||||
pyplot() = backend(:pyplot)
|
||||
qwt() = backend(:qwt)
|
||||
unicodeplots() = backend(:unicodeplots)
|
||||
# winston() = backend(:winston)
|
||||
|
||||
backend_name(::GadflyPackage) = :gadfly
|
||||
backend_name(::ImmersePackage) = :immerse
|
||||
backend_name(::PyPlotPackage) = :pyplot
|
||||
backend_name(::UnicodePlotsPackage) = :unicodeplots
|
||||
backend_name(::QwtPackage) = :qwt
|
||||
|
||||
include("backends/supported.jl")
|
||||
|
||||
include("backends/qwt.jl")
|
||||
include("backends/gadfly.jl")
|
||||
include("backends/unicodeplots.jl")
|
||||
include("backends/pyplot.jl")
|
||||
include("backends/immerse.jl")
|
||||
include("backends/winston.jl")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
const AVAILABLE_PACKAGES = [:qwt, :gadfly]
|
||||
const INITIALIZED_PACKAGES = Set{Symbol}()
|
||||
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
|
||||
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
|
||||
updatePlotItems(pkg::PlottingPackage, plt::Plot, d::Dict) = error("updatePlotItems($pkg, plt, d) is not implemented")
|
||||
# Base.display(pkg::PlottingPackage, plt::Plot) = error("display($pkg, plt) is not implemented")
|
||||
|
||||
type CurrentPackage
|
||||
updatePositionAndSize{P<:PlottingPackage}(plt::PlottingObject{P}, d::Dict) = nothing #error("updatePositionAndSize(plt,d) is not implemented for $P")
|
||||
|
||||
subplot(pkg::PlottingPackage; kw...) = error("subplot($pkg; kw...) is not implemented")
|
||||
subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
|
||||
# Base.display(pkg::PlottingPackage, subplt::Subplot) = error("display($pkg, subplt) is not implemented")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse]
|
||||
const INITIALIZED_BACKENDS = Set{Symbol}()
|
||||
backends() = BACKENDS
|
||||
|
||||
|
||||
function backendInstance(sym::Symbol)
|
||||
sym == :qwt && return QwtPackage()
|
||||
sym == :gadfly && return GadflyPackage()
|
||||
sym == :unicodeplots && return UnicodePlotsPackage()
|
||||
sym == :pyplot && return PyPlotPackage()
|
||||
sym == :immerse && return ImmersePackage()
|
||||
sym == :winston && return WinstonPackage()
|
||||
error("Unsupported backend $sym")
|
||||
end
|
||||
|
||||
|
||||
type CurrentBackend
|
||||
sym::Symbol
|
||||
pkg::PlottingPackage
|
||||
end
|
||||
# const CURRENT_PACKAGE = CurrentPackage(:qwt, QwtPackage())
|
||||
const CURRENT_PACKAGE = CurrentPackage(:gadfly, GadflyPackage())
|
||||
CurrentBackend(sym::Symbol) = CurrentBackend(sym, backendInstance(sym))
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function pickDefaultBackend()
|
||||
try
|
||||
if Pkg.installed("Immerse") != nothing
|
||||
return CurrentBackend(:immerse)
|
||||
end
|
||||
end
|
||||
try
|
||||
if Pkg.installed("Qwt") != nothing
|
||||
return CurrentBackend(:qwt)
|
||||
end
|
||||
end
|
||||
try
|
||||
if Pkg.installed("PyPlot") != nothing
|
||||
return CurrentBackend(:pyplot)
|
||||
end
|
||||
end
|
||||
try
|
||||
if Pkg.installed("Gadfly") != nothing
|
||||
return CurrentBackend(:gadfly)
|
||||
end
|
||||
end
|
||||
try
|
||||
if Pkg.installed("UnicodePlots") != nothing
|
||||
return CurrentBackend(:unicodeplots)
|
||||
end
|
||||
end
|
||||
try
|
||||
if Pkg.installed("Winston") != nothing
|
||||
return CurrentBackend(:winston)
|
||||
end
|
||||
end
|
||||
warn("You don't have any of the supported backends installed! Chose from ", backends())
|
||||
return CurrentBackend(:gadfly)
|
||||
end
|
||||
# const CURRENT_BACKEND = pickDefaultBackend()
|
||||
# println("[Plots.jl] Default backend: ", CURRENT_BACKEND.sym)
|
||||
|
||||
|
||||
doc"""
|
||||
# ---------------------------------------------------------
|
||||
|
||||
"""
|
||||
Returns the current plotting package name. Initializes package on first call.
|
||||
"""
|
||||
function plotter()
|
||||
function backend()
|
||||
# error()
|
||||
|
||||
currentPackageSymbol = CURRENT_PACKAGE.sym
|
||||
if !(currentPackageSymbol in INITIALIZED_PACKAGES)
|
||||
currentBackendSymbol = CURRENT_BACKEND.sym
|
||||
if !(currentBackendSymbol in INITIALIZED_BACKENDS)
|
||||
|
||||
# initialize
|
||||
print("[Plots.jl] Initializing package: $CURRENT_PACKAGE... ")
|
||||
if currentPackageSymbol == :qwt
|
||||
@eval import Qwt
|
||||
elseif currentPackageSymbol == :gadfly
|
||||
@eval import Gadfly
|
||||
println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym)
|
||||
if currentBackendSymbol == :qwt
|
||||
try
|
||||
@eval import Qwt
|
||||
@eval export Qwt
|
||||
catch err
|
||||
warn("Couldn't import Qwt. Install it with: Pkg.clone(\"https://github.com/tbreloff/Qwt.jl.git\")\n (Note: also requires pyqt and pyqwt).")
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
elseif currentBackendSymbol == :gadfly
|
||||
try
|
||||
@eval import Gadfly, Compose, DataFrames
|
||||
@eval export Gadfly, Compose, DataFrames
|
||||
@eval include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
|
||||
catch err
|
||||
warn("Couldn't import Gadfly. Install it with: Pkg.add(\"Gadfly\").")
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
elseif currentBackendSymbol == :unicodeplots
|
||||
try
|
||||
@eval import UnicodePlots
|
||||
@eval export UnicodePlots
|
||||
catch err
|
||||
warn("Couldn't import UnicodePlots. Install it with: Pkg.add(\"UnicodePlots\").")
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
elseif currentBackendSymbol == :pyplot
|
||||
try
|
||||
@eval import PyPlot
|
||||
@eval export PyPlot
|
||||
@eval const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
|
||||
@eval const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path"))
|
||||
# @eval const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar"))
|
||||
if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
|
||||
PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia
|
||||
|
||||
# # TODO: how the hell can I use PyQt4??
|
||||
# "pyqt4"=>:qt_pyqt4
|
||||
# PyPlot.backend[1] = "pyqt4"
|
||||
# PyPlot.gui[1] = :qt_pyqt4
|
||||
# PyPlot.switch_backend("Qt4Agg")
|
||||
|
||||
# only turn on the gui if we want it
|
||||
if PyPlot.gui != :none
|
||||
PyPlot.pygui(true)
|
||||
end
|
||||
|
||||
end
|
||||
catch err
|
||||
warn("Couldn't import PyPlot. Install it with: Pkg.add(\"PyPlot\").")
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
elseif currentBackendSymbol == :immerse
|
||||
try
|
||||
@eval import Immerse, Gadfly, Compose, Gtk
|
||||
@eval export Immerse, Gadfly, Compose, Gtk
|
||||
@eval include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
|
||||
catch err
|
||||
# error("Couldn't import Immerse. Install it with: Pkg.add(\"Immerse\").\n Error: ", err)
|
||||
warn("Couldn't import Immerse. Install it with: Pkg.add(\"Immerse\").")
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
elseif currentBackendSymbol == :winston
|
||||
warn("Winston support is deprecated and broken. Try another backend: $BACKENDS")
|
||||
try
|
||||
@eval ENV["WINSTON_OUTPUT"] = "gtk"
|
||||
@eval import Winston, Gtk
|
||||
@eval export Winston, Gtk
|
||||
catch err
|
||||
warn("Couldn't import Winston. Install it with: Pkg.add(\"Winston\").")
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
else
|
||||
error("Unknown plotter $currentPackageSymbol. Choose from: $AVAILABLE_PACKAGES")
|
||||
error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS")
|
||||
end
|
||||
push!(INITIALIZED_PACKAGES, currentPackageSymbol)
|
||||
println("done.")
|
||||
push!(INITIALIZED_BACKENDS, currentBackendSymbol)
|
||||
|
||||
end
|
||||
CURRENT_PACKAGE.pkg
|
||||
CURRENT_BACKEND.pkg
|
||||
end
|
||||
|
||||
doc"""
|
||||
Set the plot backend. Choose from: :qwt, :gadfly
|
||||
"""
|
||||
function plotter!(modname)
|
||||
Set the plot backend. Choose from: :qwt, :gadfly, :unicodeplots, :immerse, :pyplot
|
||||
"""
|
||||
function backend(pkg::PlottingPackage)
|
||||
|
||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
end
|
||||
|
||||
function backend(modname)
|
||||
|
||||
# set the PlottingPackage
|
||||
if modname == :qwt
|
||||
CURRENT_PACKAGE.pkg = QwtPackage()
|
||||
CURRENT_BACKEND.pkg = QwtPackage()
|
||||
elseif modname == :gadfly
|
||||
CURRENT_PACKAGE.pkg = GadflyPackage()
|
||||
CURRENT_BACKEND.pkg = GadflyPackage()
|
||||
elseif modname == :unicodeplots
|
||||
CURRENT_BACKEND.pkg = UnicodePlotsPackage()
|
||||
elseif modname == :pyplot
|
||||
CURRENT_BACKEND.pkg = PyPlotPackage()
|
||||
elseif modname == :immerse
|
||||
CURRENT_BACKEND.pkg = ImmersePackage()
|
||||
elseif modname == :winston
|
||||
CURRENT_BACKEND.pkg = WinstonPackage()
|
||||
else
|
||||
error("Unknown plotter $modname. Choose from: $AVAILABLE_PACKAGES")
|
||||
error("Unknown backend $modname. Choose from: $BACKENDS")
|
||||
end
|
||||
|
||||
# update the symbol
|
||||
CURRENT_PACKAGE.sym = modname
|
||||
CURRENT_BACKEND.sym = modname
|
||||
# println("[Plots.jl] Switched to backend: ", modname)
|
||||
|
||||
# return the package
|
||||
CURRENT_PACKAGE.pkg
|
||||
CURRENT_BACKEND.pkg
|
||||
end
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
# https://github.com/tbreloff/Qwt.jl
|
||||
|
||||
immutable QwtPackage <: PlottingPackage end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
function adjustQwtKeywords(iscreating::Bool; kw...)
|
||||
d = Dict(kw)
|
||||
d[:heatmap_n] = d[:nbins]
|
||||
|
||||
if d[:linetype] == :hexbin
|
||||
d[:linetype] = :heatmap
|
||||
elseif d[:linetype] == :dots
|
||||
d[:linetype] = :none
|
||||
d[:marker] = :hexagon
|
||||
elseif !iscreating && d[:linetype] == :bar
|
||||
return barHack(; kw...)
|
||||
elseif !iscreating && d[:linetype] == :hist
|
||||
return barHack(; histogramHack(; kw...)...)
|
||||
end
|
||||
d
|
||||
end
|
||||
|
||||
function plot(pkg::QwtPackage; kw...)
|
||||
kw = adjustQwtKeywords(true; kw...)
|
||||
plt = Plot(Qwt.plot(zeros(0,0); kw..., show=false), pkg, 0)
|
||||
plt
|
||||
end
|
||||
|
||||
function plot!(::QwtPackage, plt::Plot; kw...)
|
||||
kw = adjustQwtKeywords(false; kw...)
|
||||
Qwt.oplot(plt.o; kw...)
|
||||
end
|
||||
|
||||
function Base.display(::QwtPackage, plt::Plot)
|
||||
Qwt.refresh(plt.o)
|
||||
Qwt.showwidget(plt.o)
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
savepng(::QwtPackage, plt::PlottingObject, fn::String, args...) = Qwt.savepng(plt.o, fn)
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# # create the underlying object (each backend will do this differently)
|
||||
# o = buildSubplotObject(plts, pkg, layout)
|
||||
|
||||
function buildSubplotObject!(::QwtPackage, subplt::Subplot)
|
||||
i = 0
|
||||
rows = []
|
||||
for rowcnt in subplt.layout.rowcounts
|
||||
push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
|
||||
i += rowcnt
|
||||
end
|
||||
subplt.o = Qwt.vsplitter(rows...)
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::QwtPackage, subplt::Subplot)
|
||||
for plt in subplt.plts
|
||||
Qwt.refresh(plt.o)
|
||||
end
|
||||
Qwt.showwidget(subplt.o)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
|
||||
|
||||
# TODO: there should be a distinction between an object that will manage a full plot, vs a component of a plot.
|
||||
# the PlotRecipe as currently implemented is more of a "custom component"
|
||||
# a recipe should fully describe the plotting command(s) and call them, likewise for updating.
|
||||
# actually... maybe those should explicitly derive from PlottingObject???
|
||||
|
||||
abstract PlotRecipe
|
||||
|
||||
getRecipeXY(recipe::PlotRecipe) = Float64[], Float64[]
|
||||
getRecipeArgs(recipe::PlotRecipe) = ()
|
||||
|
||||
plot(recipe::PlotRecipe, args...; kw...) = plot(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
plot!(recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
function rotate(x::Real, y::Real, θ::Real; center = (0,0))
|
||||
cx = x - center[1]
|
||||
cy = y - center[2]
|
||||
xrot = cx * cos(θ) - cy * sin(θ)
|
||||
yrot = cy * cos(θ) + cx * sin(θ)
|
||||
xrot + center[1], yrot + center[2]
|
||||
end
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
type EllipseRecipe <: PlotRecipe
|
||||
w::Float64
|
||||
h::Float64
|
||||
x::Float64
|
||||
y::Float64
|
||||
θ::Float64
|
||||
end
|
||||
EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
|
||||
|
||||
# return x,y coords of a rotated ellipse, centered at the origin
|
||||
function rotatedEllipse(w, h, x, y, θ, rotθ)
|
||||
# # coord before rotation
|
||||
xpre = w * cos(θ)
|
||||
ypre = h * sin(θ)
|
||||
|
||||
# rotate and translate
|
||||
r = rotate(xpre, ypre, rotθ)
|
||||
x + r[1], y + r[2]
|
||||
end
|
||||
|
||||
function getRecipeXY(ep::EllipseRecipe)
|
||||
x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
|
||||
top = rotate(0, ep.h, ep.θ)
|
||||
right = rotate(ep.w, 0, ep.θ)
|
||||
linex = Float64[top[1], 0, right[1]] + ep.x
|
||||
liney = Float64[top[2], 0, right[2]] + ep.y
|
||||
Any[x, linex], Any[y, liney]
|
||||
end
|
||||
|
||||
function getRecipeArgs(ep::EllipseRecipe)
|
||||
[(:line, (3, [:dot :solid], [:red :blue], :path))]
|
||||
end
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
|
||||
"Correlation scatter matrix"
|
||||
function corrplot{T<:Real,S<:Real}(mat::AMat{T}, corrmat::AMat{S} = cor(mat);
|
||||
colors = :redsblues,
|
||||
labels = nothing, kw...)
|
||||
m = size(mat,2)
|
||||
centers = Float64[mean(extrema(mat[:,i])) for i in 1:m]
|
||||
|
||||
# might be a mistake?
|
||||
@assert m <= 20
|
||||
@assert size(corrmat) == (m,m)
|
||||
|
||||
# create a subplot grid, and a gradient from -1 to 1
|
||||
p = subplot(rand(0,m^2); n=m^2, leg=false, grid=false, kw...)
|
||||
cgrad = ColorGradient(colors, [-1,1])
|
||||
|
||||
# make all the plots
|
||||
for i in 1:m
|
||||
for j in 1:m
|
||||
idx = p.layout[i,j]
|
||||
plt = p.plts[idx]
|
||||
if i==j
|
||||
# histogram on diagonal
|
||||
histogram!(plt, mat[:,i], c=:black)
|
||||
i > 1 && plot!(plt, yticks = :none)
|
||||
elseif i < j
|
||||
# annotate correlation value in upper triangle
|
||||
mi, mj = centers[i], centers[j]
|
||||
plot!(plt, [mj], [mi],
|
||||
ann = (mj, mi, text(@sprintf("Corr:\n%0.3f", corrmat[i,j]), 15)),
|
||||
yticks=:none)
|
||||
else
|
||||
# scatter plots in lower triangle; color determined by correlation
|
||||
c = RGBA(RGB(getColorZ(cgrad, corrmat[i,j])), 0.3)
|
||||
scatter!(plt, mat[:,j], mat[:,i], w=0, ms=3, c=c, smooth=true)
|
||||
end
|
||||
|
||||
if labels != nothing && length(labels) >= m
|
||||
i == m && xlabel!(plt, string(labels[j]))
|
||||
j == 1 && ylabel!(plt, string(labels[i]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# link the axes
|
||||
subplot!(p, link = (r,c) -> (true, r!=c))
|
||||
end
|
||||
|
||||
|
||||
"Sparsity plot... heatmap of non-zero values of a matrix"
|
||||
function spy{T<:Real}(y::AMat{T}; kw...)
|
||||
I,J,V = findnz(y)
|
||||
heatmap(J, I; leg=false, yflip=true, nbins=size(y), kw...)
|
||||
end
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
|
||||
function subplotlayout(sz::@compat(Tuple{Int,Int}))
|
||||
GridLayout(sz...)
|
||||
end
|
||||
|
||||
# create a layout directly
|
||||
SubplotLayout(rowcounts::AbstractVector{Int}) = SubplotLayout(sum(rowcounts), rowcounts)
|
||||
function subplotlayout(rowcounts::AVec{Int})
|
||||
FlexLayout(sum(rowcounts), rowcounts)
|
||||
end
|
||||
|
||||
# create a layout given counts... nr/nc == -1 implies we figure out a good number automatically
|
||||
function SubplotLayout(numplts::Int, nr::Int, nc::Int)
|
||||
function subplotlayout(numplts::Int, nr::Int, nc::Int)
|
||||
|
||||
# figure out how many rows/columns we need
|
||||
if nr == -1
|
||||
@@ -18,6 +21,11 @@ function SubplotLayout(numplts::Int, nr::Int, nc::Int)
|
||||
nc = ceil(Int, numplts / nr)
|
||||
end
|
||||
|
||||
# if it's a perfect rectangle, just create a grid
|
||||
if numplts == nr * nc
|
||||
return GridLayout(nr, nc)
|
||||
end
|
||||
|
||||
# create the rowcounts vector
|
||||
i = 0
|
||||
rowcounts = Int[]
|
||||
@@ -27,26 +35,111 @@ function SubplotLayout(numplts::Int, nr::Int, nc::Int)
|
||||
i += cnt
|
||||
end
|
||||
|
||||
SubplotLayout(numplts, rowcounts)
|
||||
FlexLayout(numplts, rowcounts)
|
||||
end
|
||||
|
||||
|
||||
Base.length(layout::SubplotLayout) = layout.numplts
|
||||
|
||||
Base.length(layout::FlexLayout) = layout.numplts
|
||||
Base.start(layout::FlexLayout) = 1
|
||||
Base.done(layout::FlexLayout, state) = state > length(layout)
|
||||
function Base.next(layout::FlexLayout, state)
|
||||
r = 1
|
||||
c = 0
|
||||
for i = 1:state
|
||||
c += 1
|
||||
if c > layout.rowcounts[r]
|
||||
r += 1
|
||||
c = 1
|
||||
end
|
||||
end
|
||||
(r,c), state + 1
|
||||
end
|
||||
|
||||
nrows(layout::FlexLayout) = length(layout.rowcounts)
|
||||
ncols(layout::FlexLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row])
|
||||
|
||||
# get the plot index given row and column
|
||||
Base.getindex(layout::FlexLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c
|
||||
|
||||
Base.length(layout::GridLayout) = layout.nr * layout.nc
|
||||
Base.start(layout::GridLayout) = 1
|
||||
Base.done(layout::GridLayout, state) = state > length(layout)
|
||||
function Base.next(layout::GridLayout, state)
|
||||
r = div(state-1, layout.nc) + 1
|
||||
c = mod1(state, layout.nc)
|
||||
(r,c), state + 1
|
||||
end
|
||||
|
||||
nrows(layout::GridLayout) = layout.nr
|
||||
ncols(layout::GridLayout) = layout.nc
|
||||
ncols(layout::GridLayout, row::Int) = layout.nc
|
||||
|
||||
# get the plot index given row and column
|
||||
Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c
|
||||
|
||||
Base.getindex(subplt::Subplot, args...) = subplt.plts[subplt.layout[args...]]
|
||||
|
||||
# handle "linking" the subplot axes together
|
||||
# each backend should implement the handleLinkInner and expandLimits! methods
|
||||
function linkAxis(subplt::Subplot, isx::Bool)
|
||||
|
||||
# collect the list of plots and the expanded limits for those plots that should be linked on this axis
|
||||
includedPlots = Any[]
|
||||
# lims = [Inf, -Inf]
|
||||
lims = Dict{Int,Any}() # maps column to xlim
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
|
||||
# shouldlink will be a bool or nothing. if nothing, then use linkx/y (which is true if we get to this code)
|
||||
shouldlink = subplt.linkfunc(r,c)[isx ? 1 : 2]
|
||||
if shouldlink == nothing || shouldlink
|
||||
plt = subplt.plts[i]
|
||||
|
||||
# if we don't have this
|
||||
k = isx ? c : r
|
||||
if (firstone = !haskey(lims, k))
|
||||
lims[k] = [Inf, -Inf]
|
||||
end
|
||||
|
||||
isinner = (isx && r < nrows(subplt.layout)) || (!isx && !firstone)
|
||||
push!(includedPlots, (plt, isinner, k))
|
||||
|
||||
expandLimits!(lims[k], plt, isx)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# do the axis adjustments
|
||||
for (plt, isinner, k) in includedPlots
|
||||
if isinner
|
||||
handleLinkInner(plt, isx)
|
||||
end
|
||||
(isx ? xlims! : ylims!)(plt, lims[k]...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
Base.string(subplt::Subplot) = "Subplot{$(subplt.plotter) p=$(subplt.p) n=$(subplt.n)}"
|
||||
Base.string(subplt::Subplot) = "Subplot{$(subplt.backend) p=$(subplt.p) n=$(subplt.n)}"
|
||||
Base.print(io::IO, subplt::Subplot) = print(io, string(subplt))
|
||||
Base.show(io::IO, subplt::Subplot) = print(io, string(subplt))
|
||||
|
||||
getplot(subplt::Subplot) = subplt.plts[mod1(subplt.n, subplt.p)]
|
||||
getplot(subplt::Subplot, idx::Int = subplt.n) = subplt.plts[mod1(idx, subplt.p)]
|
||||
getinitargs(subplt::Subplot, idx::Int) = getplot(subplt, idx).initargs
|
||||
convertSeriesIndex(subplt::Subplot, n::Int) = ceil(Int, n / subplt.p)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
function validateSubplotSupported()
|
||||
if !subplotSupported()
|
||||
error(CURRENT_BACKEND.sym, " does not support the subplot/subplot! commands at this time. Try one of: ", join(filter(pkg->subplotSupported(backendInstance(pkg)), backends()),", "))
|
||||
end
|
||||
end
|
||||
|
||||
doc"""
|
||||
"""
|
||||
Create a series of plots:
|
||||
```
|
||||
y = rand(100,3)
|
||||
@@ -54,40 +147,180 @@ Create a series of plots:
|
||||
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows to 1 (so there are n columns)
|
||||
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns to 1 (so there are n rows)
|
||||
subplot(y; layout = [1, 2]) # explicit layout by row... plot #1 goes by itself in the first row, plots 2 and 3 split the 2nd row (note the n kw is unnecessary)
|
||||
subplot(plts, n; nr = -1, nc = -1) # build a layout from existing plots
|
||||
subplot(plts, layout) # build a layout from existing plots
|
||||
```
|
||||
"""
|
||||
function subplot(args...; kw...)
|
||||
validateSubplotSupported()
|
||||
d = Dict(kw)
|
||||
preprocessArgs!(d)
|
||||
|
||||
# figure out the layout
|
||||
if haskey(d, :layout)
|
||||
layout = SubplotLayout(d[:layout])
|
||||
layoutarg = get(d, :layout, nothing)
|
||||
if layoutarg != nothing
|
||||
layout = subplotlayout(layoutarg)
|
||||
else
|
||||
if !haskey(d, :n)
|
||||
n = get(d, :n, -1)
|
||||
if n < 0
|
||||
error("You must specify either layout or n when creating a subplot: ", d)
|
||||
end
|
||||
layout = SubplotLayout(d[:n], get(d, :nr, -1), get(d, :nc, -1))
|
||||
layout = subplotlayout(n, get(d, :nr, -1), get(d, :nc, -1))
|
||||
end
|
||||
|
||||
# initialize the individual plots
|
||||
pkg = plotter()
|
||||
kw0 = getPlotKeywordArgs(kw, 1, 0)
|
||||
plts = Plot[plot(pkg; kw0..., show=false) for i in 1:length(layout)]
|
||||
pkg = backend()
|
||||
plts = Plot{typeof(pkg)}[]
|
||||
for i in 1:length(layout)
|
||||
di = getPlotArgs(pkg, d, i)
|
||||
di[:subplot] = true
|
||||
dumpdict(di, "Plot args (subplot $i)")
|
||||
push!(plts, plot(pkg; di...))
|
||||
end
|
||||
|
||||
# create the object and do the plotting
|
||||
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout)
|
||||
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout, d, false, false, false, (r,c) -> (nothing,nothing))
|
||||
subplot!(subplt, args...; kw...)
|
||||
|
||||
subplt
|
||||
end
|
||||
|
||||
doc"""
|
||||
# ------------------------------------------------------------------------------------------------
|
||||
|
||||
# NOTE: for the subplot calls building from existing plots, we need the first plot to be separate to ensure dispatch calls this instead of the more general subplot(args...; kw...)
|
||||
|
||||
# grid layout
|
||||
function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...)
|
||||
d = Dict(kw)
|
||||
layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
|
||||
subplot(vcat(plt1, plts...), layout, d)
|
||||
end
|
||||
|
||||
# explicit layout
|
||||
function subplot{P,I<:Integer}(pltsPerRow::AVec{I}, plt1::Plot{P}, plts::Plot{P}...; kw...)
|
||||
layout = subplotlayout(pltsPerRow)
|
||||
subplot(vcat(plt1, plts...), layout, Dict(kw))
|
||||
end
|
||||
|
||||
# this will be called internally
|
||||
function subplot{P<:PlottingPackage}(plts::AVec{Plot{P}}, layout::SubplotLayout, d::Dict)
|
||||
validateSubplotSupported()
|
||||
p = length(layout)
|
||||
n = sum([plt.n for plt in plts])
|
||||
subplt = Subplot(nothing, collect(plts), P(), p, n, layout, Dict(), false, false, false, (r,c) -> (nothing,nothing))
|
||||
|
||||
# preprocessArgs!(d)
|
||||
|
||||
# #
|
||||
# for (i,plt) in enumerate(plts)
|
||||
# di = copy(plt.initargs)
|
||||
|
||||
# for ck in (:background_color, :foreground_color, :color_palette)
|
||||
# # if we have a value to override, do it
|
||||
# if haskey(d, ck)
|
||||
# di[ck] = get_mod(d[ck], i)
|
||||
# end
|
||||
|
||||
|
||||
|
||||
# # build a new dict from the initargs of the plots
|
||||
# iargs = Dict()
|
||||
# for k in keys(_plotDefaults)
|
||||
# iargs[k] = Any[plt.initargs[k] for plt in plts]'
|
||||
# end
|
||||
# merge!(iargs, d)
|
||||
|
||||
preprocessSubplot(subplt, d)
|
||||
postprocessSubplot(subplt, d)
|
||||
|
||||
subplt
|
||||
end
|
||||
|
||||
# TODO: hcat/vcat subplots and plots together arbitrarily
|
||||
|
||||
# ------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function preprocessSubplot(subplt::Subplot, d::Dict)
|
||||
validateSubplotSupported()
|
||||
preprocessArgs!(d)
|
||||
dumpdict(d, "After subplot! preprocessing")
|
||||
|
||||
# get the full initargs, overriding any new settings
|
||||
# TODO: subplt.initargs should probably be merged sooner and actually used
|
||||
# for color selection, etc. (i.e. if we overwrite the subplot palettes to [:heat :rainbow])
|
||||
# then we need to overwrite plt[1].initargs[:color_palette] to :heat before it's actually used
|
||||
# for color selection!
|
||||
|
||||
# first merge the new args into the subplot's initargs. then process the plot args and merge
|
||||
# those into the plot's initargs. (example... `palette = [:blues :reds]` goes into subplt.initargs,
|
||||
# then the ColorGradient for :blues/:reds is merged into plot 1/2 initargs, which is then used for color selection)
|
||||
for i in 1:length(subplt.layout)
|
||||
# di = getPlotArgs(backend(), subplt.initargs, i)
|
||||
# merge!(subplt.plts[i].initargs, di)
|
||||
subplt.plts[i].initargs = getPlotArgs(backend(), merge(subplt.plts[i].initargs, d), i)
|
||||
end
|
||||
merge!(subplt.initargs, d)
|
||||
|
||||
# process links. TODO: extract to separate function
|
||||
for s in (:linkx, :linky, :linkfunc)
|
||||
if haskey(d, s)
|
||||
setfield!(subplt, s, d[s])
|
||||
delete!(d, s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function postprocessSubplot(subplt::Subplot, d::Dict)
|
||||
# init (after plot creation)
|
||||
if !subplt.initialized
|
||||
subplt.initialized = buildSubplotObject!(subplt, false)
|
||||
end
|
||||
|
||||
# add title, axis labels, ticks, etc
|
||||
for (i,plt) in enumerate(subplt.plts)
|
||||
|
||||
# # # get the full initargs, overriding any new settings
|
||||
# # di = copy(merge(plt.initargs, d))
|
||||
# di = copy(d)
|
||||
|
||||
# for (k,v) in di
|
||||
# if typeof(v) <: AVec
|
||||
# di[k] = v[mod1(i, length(v))]
|
||||
# elseif typeof(v) <: AMat
|
||||
# m = size(v,2)
|
||||
# di[k] = (size(v,1) == 1 ? v[1, mod1(i, m)] : v[:, mod1(i, m)])
|
||||
# end
|
||||
# end
|
||||
|
||||
# di = merge!(plt.initargs, di)
|
||||
|
||||
di = plt.initargs
|
||||
|
||||
dumpdict(di, "Updating sp $i")
|
||||
updatePlotItems(plt, di)
|
||||
end
|
||||
|
||||
updatePositionAndSize(subplt, d)
|
||||
|
||||
# handle links
|
||||
subplt.linkx && linkAxis(subplt, true)
|
||||
subplt.linky && linkAxis(subplt, false)
|
||||
|
||||
# set this to be current
|
||||
current(subplt)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
Adds to a subplot.
|
||||
"""
|
||||
|
||||
# current subplot
|
||||
function subplot!(args...; kw...)
|
||||
subplot!(currentPlot(), args...; kw...)
|
||||
validateSubplotSupported()
|
||||
subplot!(current(), args...; kw...)
|
||||
end
|
||||
|
||||
|
||||
@@ -99,26 +332,87 @@ end
|
||||
|
||||
# # this adds to a specific subplot... most plot commands will flow through here
|
||||
function subplot!(subplt::Subplot, args...; kw...)
|
||||
kwList = createKWargsList(subplt, args...; kw...)
|
||||
for (i,d) in enumerate(kwList)
|
||||
subplt.n += 1
|
||||
plt = getplot(subplt) # get the Plot object where this series will be drawn
|
||||
plot!(plt; d...)
|
||||
end
|
||||
# validateSubplotSupported()
|
||||
|
||||
d = Dict(kw)
|
||||
preprocessSubplot(subplt, d)
|
||||
|
||||
# create the underlying object (each backend will do this differently)
|
||||
buildSubplotObject!(subplt.plotter, subplt)
|
||||
# note: we call it once before doing the individual plots, and once after
|
||||
# this is because some backends need to set up the subplots and then plot,
|
||||
# and others need to do it the other way around
|
||||
if !subplt.initialized
|
||||
subplt.initialized = buildSubplotObject!(subplt, true)
|
||||
end
|
||||
|
||||
# set this to be current
|
||||
currentPlot!(subplt)
|
||||
# handle grouping
|
||||
group = get(d, :group, nothing)
|
||||
if group == nothing
|
||||
groupargs = []
|
||||
else
|
||||
groupargs = [extractGroupArgs(d[:group], args...)]
|
||||
delete!(d, :group)
|
||||
end
|
||||
|
||||
# do we want to show it?
|
||||
d = Dict(kw)
|
||||
|
||||
kwList, xmeta, ymeta = createKWargsList(subplt, groupargs..., args...; d...)
|
||||
|
||||
# TODO: something useful with meta info?
|
||||
|
||||
for (i,di) in enumerate(kwList)
|
||||
|
||||
subplt.n += 1
|
||||
plt = getplot(subplt)
|
||||
plt.n += 1
|
||||
|
||||
# # update the plot's initargs for things such as palettes, etc
|
||||
# for (k,v) in subplt.initargs
|
||||
# haskey(_plotDefaults, k) || continue
|
||||
# if typeof(v) <: AVec
|
||||
# plt.initargs[k] = v[mod1(i, length(v))]
|
||||
# elseif typeof(v) <: AMat
|
||||
# m = size(v,2)
|
||||
# plt.initargs[k] = (size(v,1) == 1 ? v[1, mod1(i, m)] : v[:, mod1(i, m)])
|
||||
# end
|
||||
# end
|
||||
|
||||
# cleanup the dictionary that we pass into the plot! command
|
||||
di[:show] = false
|
||||
di[:subplot] = true
|
||||
for k in (:title, :xlabel, :xticks, :xlims, :xscale, :xflip,
|
||||
:ylabel, :yticks, :ylims, :yscale, :yflip)
|
||||
delete!(di, k)
|
||||
end
|
||||
dumpdict(di, "subplot! kwList $i")
|
||||
dumpdict(plt.initargs, "plt.initargs before plotting")
|
||||
|
||||
_plot_from_subplot!(plt; di...)
|
||||
end
|
||||
|
||||
postprocessSubplot(subplt, d)
|
||||
|
||||
# show it automatically?
|
||||
if haskey(d, :show) && d[:show]
|
||||
draw(subplt)
|
||||
gui()
|
||||
end
|
||||
|
||||
subplt
|
||||
end
|
||||
|
||||
|
||||
|
||||
function _plot_from_subplot!(plt::Plot, args...; kw...)
|
||||
d = Dict(kw)
|
||||
|
||||
setTicksFromStringVector(d, d, :x, :xticks)
|
||||
setTicksFromStringVector(d, d, :y, :yticks)
|
||||
|
||||
# dumpdict(d, "Plot from subplot")
|
||||
plot!(plt.backend, plt; d...)
|
||||
|
||||
addAnnotations(plt, d)
|
||||
warnOnUnsupportedScales(plt.backend, d)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,27 +2,193 @@
|
||||
typealias AVec AbstractVector
|
||||
typealias AMat AbstractMatrix
|
||||
|
||||
abstract PlottingObject
|
||||
immutable PlotsDisplay <: Display end
|
||||
|
||||
abstract PlottingPackage
|
||||
abstract PlottingObject{T<:PlottingPackage}
|
||||
|
||||
type Plot <: PlottingObject
|
||||
type Plot{T<:PlottingPackage} <: PlottingObject{T}
|
||||
o # the underlying object
|
||||
plotter::PlottingPackage
|
||||
n::Int # number of series
|
||||
backend::T
|
||||
n::Int # number of series
|
||||
|
||||
# store these just in case
|
||||
initargs::Dict
|
||||
seriesargs::Vector{Dict} # args for each series
|
||||
end
|
||||
|
||||
|
||||
type SubplotLayout
|
||||
abstract SubplotLayout
|
||||
|
||||
immutable GridLayout <: SubplotLayout
|
||||
nr::Int
|
||||
nc::Int
|
||||
end
|
||||
|
||||
immutable FlexLayout <: SubplotLayout
|
||||
numplts::Int
|
||||
rowcounts::AbstractVector{Int}
|
||||
end
|
||||
|
||||
|
||||
type Subplot <: PlottingObject
|
||||
type Subplot{T<:PlottingPackage, L<:SubplotLayout} <: PlottingObject{T}
|
||||
o # the underlying object
|
||||
plts::Vector{Plot} # the individual plots
|
||||
plotter::PlottingPackage
|
||||
plts::Vector{Plot{T}} # the individual plots
|
||||
backend::T
|
||||
p::Int # number of plots
|
||||
n::Int # number of series
|
||||
layout::SubplotLayout
|
||||
end
|
||||
layout::L
|
||||
# initargs::Vector{Dict}
|
||||
initargs::Dict
|
||||
initialized::Bool
|
||||
linkx::Bool
|
||||
linky::Bool
|
||||
linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
immutable Shape
|
||||
vertices::AVec
|
||||
end
|
||||
|
||||
"get an array of tuples of points on a circle with radius `r`"
|
||||
function partialcircle(start_θ, end_θ, n = 20, r=1)
|
||||
@compat(Tuple{Float64,Float64})[(r*cos(u),r*sin(u)) for u in linspace(start_θ, end_θ, n)]
|
||||
end
|
||||
|
||||
"interleave 2 vectors into each other (like a zipper's teeth)"
|
||||
function weave(x,y; ordering = Vector[x,y])
|
||||
ret = eltype(x)[]
|
||||
done = false
|
||||
while !done
|
||||
for o in ordering
|
||||
try
|
||||
push!(ret, shift!(o))
|
||||
end
|
||||
# try
|
||||
# push!(ret, shift!(y))
|
||||
# end
|
||||
end
|
||||
done = isempty(x) && isempty(y)
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
|
||||
"create a star by weaving together points from an outer and inner circle. `n` is the number of arms"
|
||||
function makestar(n; offset = -0.5, radius = 1.0)
|
||||
z1 = offset * π
|
||||
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])
|
||||
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])
|
||||
end
|
||||
|
||||
|
||||
function makecross(; offset = -0.5, radius = 1.0)
|
||||
z2 = offset * π
|
||||
z1 = z2 - π/8
|
||||
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])
|
||||
end
|
||||
|
||||
|
||||
const _shapes = @compat Dict(
|
||||
:ellipse => makeshape(20),
|
||||
:rect => makeshape(4, offset=-0.25),
|
||||
:diamond => makeshape(4),
|
||||
:utriangle => makeshape(3),
|
||||
:dtriangle => makeshape(3, offset=0.5),
|
||||
:pentagon => makeshape(5),
|
||||
:hexagon => makeshape(6),
|
||||
:heptagon => makeshape(7),
|
||||
:octagon => makeshape(8),
|
||||
:cross => makecross(offset=-0.25),
|
||||
:xcross => makecross(),
|
||||
)
|
||||
|
||||
for n in [4,5,6,7,8]
|
||||
_shapes[symbol("star$n")] = makestar(n)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
||||
immutable Font
|
||||
family::AbstractString
|
||||
pointsize::Int
|
||||
halign::Symbol
|
||||
valign::Symbol
|
||||
rotation::Float64
|
||||
color::Colorant
|
||||
end
|
||||
|
||||
"Create a Font from a list of unordered features"
|
||||
function font(args...)
|
||||
|
||||
# defaults
|
||||
family = "Helvetica"
|
||||
pointsize = 14
|
||||
halign = :hcenter
|
||||
valign = :vcenter
|
||||
rotation = 0.0
|
||||
color = colorant"black"
|
||||
|
||||
for arg in args
|
||||
T = typeof(arg)
|
||||
|
||||
if arg == :center
|
||||
halign = :hcenter
|
||||
valign = :vcenter
|
||||
elseif arg in (:hcenter, :left, :right)
|
||||
halign = arg
|
||||
elseif arg in (:vcenter, :top, :bottom)
|
||||
valign = arg
|
||||
elseif T <: Colorant
|
||||
color = arg
|
||||
elseif T <: @compat Union{Symbol,AbstractString}
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
catch
|
||||
family = string(arg)
|
||||
end
|
||||
elseif typeof(arg) <: Integer
|
||||
pointsize = arg
|
||||
elseif typeof(arg) <: Real
|
||||
rotation = convert(Float64, arg)
|
||||
else
|
||||
warn("Unused font arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
|
||||
Font(family, pointsize, halign, valign, rotation, color)
|
||||
end
|
||||
|
||||
"Wrap a string with font info"
|
||||
immutable PlotText
|
||||
str::@compat(AbstractString)
|
||||
font::Font
|
||||
end
|
||||
|
||||
function text(str, args...)
|
||||
PlotText(string(str), font(args...))
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
type OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
calcMidpoints(edges::AbstractVector) = Float64[0.5 * (edges[i] + edges[i+1]) for i in 1:length(edges)-1]
|
||||
|
||||
doc"Make histogram-like bins of data"
|
||||
"Make histogram-like bins of data"
|
||||
function binData(data, nbins)
|
||||
lo, hi = extrema(data)
|
||||
edges = collect(linspace(lo, hi, nbins+1))
|
||||
@@ -14,7 +14,7 @@ function binData(data, nbins)
|
||||
edges, midpoints, buckets, counts
|
||||
end
|
||||
|
||||
doc"""
|
||||
"""
|
||||
A hacky replacement for a histogram when the backend doesn't support histograms directly.
|
||||
Convert it into a bar chart with the appropriate x/y values.
|
||||
"""
|
||||
@@ -26,19 +26,19 @@ function histogramHack(; kw...)
|
||||
d[:x] = midpoints
|
||||
d[:y] = float(counts)
|
||||
d[:linetype] = :bar
|
||||
d[:fillto] = d[:fillto] == nothing ? 0.0 : d[:fillto]
|
||||
d[:fillrange] = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
|
||||
d
|
||||
end
|
||||
|
||||
doc"""
|
||||
"""
|
||||
A hacky replacement for a bar graph when the backend doesn't support bars directly.
|
||||
Convert it into a line chart with fillto set.
|
||||
Convert it into a line chart with fillrange set.
|
||||
"""
|
||||
function barHack(; kw...)
|
||||
d = Dict(kw)
|
||||
midpoints = d[:x]
|
||||
heights = d[:y]
|
||||
fillto = d[:fillto] == nothing ? 0.0 : d[:fillto]
|
||||
fillrange = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
|
||||
|
||||
# estimate the edges
|
||||
dists = diff(midpoints) * 0.5
|
||||
@@ -59,13 +59,307 @@ function barHack(; kw...)
|
||||
for i in 1:length(heights)
|
||||
e1, e2 = edges[i:i+1]
|
||||
append!(x, [e1, e1, e2, e2])
|
||||
append!(y, [fillto, heights[i], heights[i], fillto])
|
||||
append!(y, [fillrange, heights[i], heights[i], fillrange])
|
||||
end
|
||||
|
||||
d[:x] = x
|
||||
d[:y] = y
|
||||
d[:linetype] = :line
|
||||
d[:fillto] = fillto
|
||||
d[:linetype] = :path
|
||||
d[:fillrange] = fillrange
|
||||
d
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
A hacky replacement for a sticks graph when the backend doesn't support sticks directly.
|
||||
Convert it into a line chart that traces the sticks, and a scatter that sets markers at the points.
|
||||
"""
|
||||
function sticksHack(; kw...)
|
||||
dLine = Dict(kw)
|
||||
dScatter = copy(dLine)
|
||||
|
||||
# these are the line vertices
|
||||
x = Float64[]
|
||||
y = Float64[]
|
||||
fillrange = dLine[:fillrange] == nothing ? 0.0 : dLine[:fillrange]
|
||||
|
||||
# calculate the vertices
|
||||
yScatter = dScatter[:y]
|
||||
for (i,xi) in enumerate(dScatter[:x])
|
||||
yi = yScatter[i]
|
||||
for j in 1:3 push!(x, xi) end
|
||||
append!(y, [fillrange, yScatter[i], fillrange])
|
||||
end
|
||||
|
||||
# change the line args
|
||||
dLine[:x] = x
|
||||
dLine[:y] = y
|
||||
dLine[:linetype] = :path
|
||||
dLine[:markershape] = :none
|
||||
dLine[:fillrange] = nothing
|
||||
|
||||
# change the scatter args
|
||||
dScatter[:linetype] = :none
|
||||
|
||||
dLine, dScatter
|
||||
end
|
||||
|
||||
function regressionXY(x, y)
|
||||
# regress
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
|
||||
# make a line segment
|
||||
regx = [minimum(x), maximum(x)]
|
||||
regy = β * regx + α
|
||||
regx, regy
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
get_mod(v::AVec, idx::Int) = v[mod1(idx, length(v))]
|
||||
get_mod(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
|
||||
get_mod(v, idx::Int) = v
|
||||
|
||||
makevec(v::AVec) = v
|
||||
makevec{T}(v::T) = T[v]
|
||||
|
||||
"duplicate a single value, or pass the 2-tuple through"
|
||||
maketuple(x::Real) = (x,x)
|
||||
maketuple{T,S}(x::@compat(Tuple{T,S})) = x
|
||||
|
||||
|
||||
unzip{T,S}(v::AVec{@compat(Tuple{T,S})}) = [vi[1] for vi in v], [vi[2] for vi in v]
|
||||
|
||||
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
||||
function expandLimits!(lims, x)
|
||||
try
|
||||
e1, e2 = extrema(x)
|
||||
lims[1] = min(lims[1], e1)
|
||||
lims[2] = max(lims[2], e2)
|
||||
# catch err
|
||||
# warn(err)
|
||||
end
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
# if the type exists in a list, replace the first occurence. otherwise add it to the end
|
||||
function addOrReplace(v::AbstractVector, t::DataType, args...; kw...)
|
||||
for (i,vi) in enumerate(v)
|
||||
if isa(vi, t)
|
||||
v[i] = t(args...; kw...)
|
||||
return
|
||||
end
|
||||
end
|
||||
push!(v, t(args...; kw...))
|
||||
return
|
||||
end
|
||||
|
||||
function replaceType(vec, val)
|
||||
filter!(x -> !isa(x, typeof(val)), vec)
|
||||
push!(vec, val)
|
||||
end
|
||||
|
||||
function replaceAliases!(d::Dict, aliases::Dict)
|
||||
ks = collect(keys(d))
|
||||
# for (k,v) in d
|
||||
for k in ks
|
||||
if haskey(aliases, k)
|
||||
d[aliases[k]] = d[k]
|
||||
delete!(d, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
createSegments(z) = collect(repmat(z',2,1))[2:end]
|
||||
Base.first(c::Colorant) = c
|
||||
|
||||
|
||||
sortedkeys(d::Dict) = sort(collect(keys(d)))
|
||||
|
||||
|
||||
function fakedata(sz...)
|
||||
y = zeros(sz...)
|
||||
for r in 2:size(y,1)
|
||||
y[r,:] = 0.95 * y[r-1,:] + randn(size(y,2))'
|
||||
end
|
||||
y
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ticksType{T<:Real,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
|
||||
ticksType{T<:Real}(ticks::AVec{T}) = :ticks
|
||||
ticksType{T<:AVec,S<:AVec}(ticks::@compat(Tuple{T,S})) = :ticks_and_labels
|
||||
ticksType(ticks) = :invalid
|
||||
|
||||
limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
|
||||
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
|
||||
limsType(lims) = :invalid
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
type DebugMode
|
||||
on::Bool
|
||||
end
|
||||
const _debugMode = DebugMode(false)
|
||||
|
||||
function debugplots(on = true)
|
||||
_debugMode.on = on
|
||||
end
|
||||
|
||||
debugshow(x) = show(x)
|
||||
debugshow(x::AbstractArray) = print(summary(x))
|
||||
|
||||
function dumpdict(d::Dict, prefix = "")
|
||||
_debugMode.on || return
|
||||
println()
|
||||
if prefix != ""
|
||||
println(prefix, ":")
|
||||
end
|
||||
for k in sort(collect(keys(d)))
|
||||
@printf("%14s: ", k)
|
||||
debugshow(d[k])
|
||||
println()
|
||||
end
|
||||
println()
|
||||
end
|
||||
|
||||
|
||||
function dumpcallstack()
|
||||
error() # well... you wanted the stacktrace, didn't you?!?
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
|
||||
# push/append/clear/set the underlying plot data
|
||||
# NOTE: backends should implement the getindex and setindex! methods to get/set the x/y data objects
|
||||
|
||||
|
||||
# index versions
|
||||
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real)
|
||||
xdata, ydata = plt[i]
|
||||
plt[i] = (extendSeriesData(xdata, x), extendSeriesData(ydata, y))
|
||||
plt
|
||||
end
|
||||
function Base.push!(plt::Plot, i::Integer, y::Real)
|
||||
xdata, ydata = plt[i]
|
||||
# if !isa(xdata, UnitRange)
|
||||
# error("Expected x is a UnitRange since you're trying to push a y value only. typeof(x) = $(typeof(xdata))")
|
||||
# end
|
||||
plt[i] = (extendSeriesByOne(xdata), extendSeriesData(ydata, y))
|
||||
plt
|
||||
end
|
||||
|
||||
Base.push!(plt::Plot, y::Real) = push!(plt, 1, y)
|
||||
|
||||
# update all at once
|
||||
function Base.push!(plt::Plot, x::AVec, y::AVec)
|
||||
nx = length(x)
|
||||
ny = length(y)
|
||||
for i in 1:plt.n
|
||||
push!(plt, i, x[mod1(i,nx)], y[mod1(i,ny)])
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
function Base.push!(plt::Plot, x::Real, y::AVec)
|
||||
push!(plt, [x], y)
|
||||
end
|
||||
|
||||
function Base.push!(plt::Plot, y::AVec)
|
||||
ny = length(y)
|
||||
for i in 1:plt.n
|
||||
push!(plt, i, y[mod1(i,ny)])
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# append to index
|
||||
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec)
|
||||
@assert length(x) == length(y)
|
||||
xdata, ydata = plt[i]
|
||||
plt[i] = (extendSeriesData(xdata, x), extendSeriesData(ydata, y))
|
||||
plt
|
||||
end
|
||||
|
||||
function Base.append!(plt::Plot, i::Integer, y::AVec)
|
||||
xdata, ydata = plt[i]
|
||||
if !isa(xdata, UnitRange{Int})
|
||||
error("Expected x is a UnitRange since you're trying to push a y value only")
|
||||
end
|
||||
plt[i] = (extendSeriesByOne(xdata, length(y)), extendSeriesData(ydata, y))
|
||||
plt
|
||||
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))
|
||||
extendSeriesData{T}(v::Range{T}, z::Real) = extendSeriesData(float(collect(v)), z)
|
||||
extendSeriesData{T}(v::Range{T}, z::AVec) = extendSeriesData(float(collect(v)), z)
|
||||
extendSeriesData{T}(v::AVec{T}, z::Real) = (push!(v, convert(T, z)); v)
|
||||
extendSeriesData{T}(v::AVec{T}, z::AVec) = (append!(v, convert(Vector{T}, z)); v)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
function supportGraph(allvals, func)
|
||||
vals = reverse(sort(allvals))
|
||||
bs = sort(backends())
|
||||
x = ASCIIString[]
|
||||
y = ASCIIString[]
|
||||
for val in vals
|
||||
for b in bs
|
||||
supported = func(Plots.backendInstance(b))
|
||||
if val in supported
|
||||
push!(x, string(b))
|
||||
push!(y, string(val))
|
||||
end
|
||||
end
|
||||
end
|
||||
n = length(vals)
|
||||
|
||||
scatter(x,y,
|
||||
m=:rect,
|
||||
ms=10,
|
||||
size=(300,100+18*n),
|
||||
# xticks=(collect(1:length(bs)), bs),
|
||||
leg=false
|
||||
)
|
||||
end
|
||||
|
||||
supportGraphArgs() = supportGraph(_allArgs, supportedArgs)
|
||||
supportGraphTypes() = supportGraph(_allTypes, supportedTypes)
|
||||
supportGraphStyles() = supportGraph(_allStyles, supportedStyles)
|
||||
supportGraphMarkers() = supportGraph(_allMarkers, supportedMarkers)
|
||||
supportGraphScales() = supportGraph(_allScales, supportedScales)
|
||||
supportGraphAxes() = supportGraph(_allAxes, supportedAxes)
|
||||
|
||||
function dumpSupportGraphs()
|
||||
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
|
||||
supportGraphMarkers, supportGraphScales, supportGraphAxes)
|
||||
plt = func()
|
||||
png(IMG_DIR * "/supported/$(string(func))")
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
|
||||
# Some conversion functions
|
||||
# note: I borrowed these conversion constants from Compose.jl's Measure
|
||||
const INCH_SCALAR = 25.4
|
||||
const PX_SCALAR = 1 / 3.78
|
||||
inch2px(inches::Real) = float(inches * INCH_SCALAR / PX_SCALAR)
|
||||
px2inch(px::Real) = float(px * PX_SCALAR / INCH_SCALAR)
|
||||
inch2mm(inches::Real) = float(inches * INCH_SCALAR)
|
||||
mm2inch(mm::Real) = float(mm / INCH_SCALAR)
|
||||
px2mm(px::Real) = float(px * PX_SCALAR)
|
||||
mm2px(mm::Real) = float(px / PX_SCALAR)
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
julia 0.3
|
||||
|
||||
Colors
|
||||
Reexport
|
||||
FactCheck
|
||||
Gadfly
|
||||
Images
|
||||
ImageMagick
|
||||
PyPlot
|
||||
@@ -0,0 +1,182 @@
|
||||
|
||||
# include this first to help with crashing??
|
||||
try
|
||||
@eval using Gtk
|
||||
end
|
||||
|
||||
# don't let pyplot use a gui... it'll crash
|
||||
# note: Agg will set gui -> :none in PyPlot
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
try
|
||||
@eval import PyPlot
|
||||
end
|
||||
|
||||
# macro test_approx_eq_sigma_eps(A, B, sigma, eps)
|
||||
|
||||
include("../docs/example_generation.jl")
|
||||
|
||||
|
||||
# # make and display one plot
|
||||
# function test_examples(pkg::Symbol, idx::Int; debug = true)
|
||||
# Plots._debugMode.on = debug
|
||||
# println("Testing plot: $pkg:$idx:$(examples[idx].header)")
|
||||
# backend(pkg)
|
||||
# backend()
|
||||
# map(eval, examples[idx].exprs)
|
||||
# plt = current()
|
||||
# gui(plt)
|
||||
# plt
|
||||
# end
|
||||
|
||||
using Plots, FactCheck
|
||||
import Images, ImageMagick
|
||||
|
||||
# if !isdefined(ImageMagick, :init_deps)
|
||||
# function ImageMagick.init_deps()
|
||||
# ccall((:MagickWandGenesis,libwand), Void, ())
|
||||
# end
|
||||
# end
|
||||
|
||||
function makeImageWidget(fn)
|
||||
img = Gtk.GtkImageLeaf(fn)
|
||||
vbox = Gtk.GtkBoxLeaf(:v)
|
||||
push!(vbox, Gtk.GtkLabelLeaf(fn))
|
||||
push!(vbox, img)
|
||||
show(img)
|
||||
vbox
|
||||
end
|
||||
|
||||
function replaceReferenceImage(tmpfn, reffn)
|
||||
cmd = `cp $tmpfn $reffn`
|
||||
run(cmd)
|
||||
info("Replaced reference image with: $cmd")
|
||||
end
|
||||
|
||||
"Show a Gtk popup with both images and a confirmation whether we should replace the new image with the old one"
|
||||
function compareToReferenceImage(tmpfn, reffn)
|
||||
@eval import Gtk
|
||||
|
||||
# add the images
|
||||
imgbox = Gtk.GtkBoxLeaf(:h)
|
||||
push!(imgbox, makeImageWidget(tmpfn))
|
||||
push!(imgbox, makeImageWidget(reffn))
|
||||
|
||||
# add the buttons
|
||||
# doNothingButton = Gtk.GtkButtonLeaf("Skip")
|
||||
# replaceReferenceButton = Gtk.GtkButtonLeaf("Replace reference image")
|
||||
# btnbox = Gtk.GtkButtonBoxLeaf(:h)
|
||||
# push!(btnbox, doNothingButton)
|
||||
# push!(btnbox, replaceReferenceButton)
|
||||
|
||||
# # create the window
|
||||
# box = Gtk.GtkBoxLeaf(:v)
|
||||
# push!(box, imgbox)
|
||||
# push!(box, btnbox)
|
||||
win = Gtk.GtkWindowLeaf("Should we make this the new reference image?")
|
||||
push!(win, Gtk.GtkFrameLeaf(imgbox))
|
||||
|
||||
showall(win)
|
||||
|
||||
# now ask the question
|
||||
if Gtk.ask_dialog("Should we make this the new reference image?", "No", "Yes")
|
||||
replaceReferenceImage(tmpfn, reffn)
|
||||
end
|
||||
|
||||
destroy(win)
|
||||
|
||||
# # we'll wait on this condition
|
||||
# c = Condition()
|
||||
# Gtk.on_signal_destroy((x...) -> notify(c), win)
|
||||
|
||||
# Gtk.signal_connect(replaceReferenceButton, "clicked") do widget
|
||||
# replaceReferenceImage(tmpfn, reffn)
|
||||
# notify(c)
|
||||
# end
|
||||
|
||||
# Gtk.signal_connect(doNothingButton, "clicked") do widget
|
||||
# notify(c)
|
||||
# end
|
||||
|
||||
# # wait until a button is clicked, then close the window
|
||||
# Gtk.showall(win)
|
||||
# wait(c)
|
||||
# Gtk.destroy(win)
|
||||
end
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, sigma = [1,1], eps = 1e-2)
|
||||
|
||||
# first
|
||||
Plots._debugMode.on = debug
|
||||
info("Testing plot: $pkg:$idx:$(PlotExamples.examples[idx].header)")
|
||||
backend(pkg)
|
||||
backend()
|
||||
|
||||
# ensure consistent results
|
||||
srand(1234)
|
||||
|
||||
# run the example
|
||||
map(eval, PlotExamples.examples[idx].exprs)
|
||||
|
||||
# save the png
|
||||
tmpfn = tempname() * ".png"
|
||||
png(tmpfn)
|
||||
|
||||
# load the saved png
|
||||
tmpimg = Images.load(tmpfn)
|
||||
|
||||
# reference image location
|
||||
# refdir = joinpath(Pkg.dir("Plots"), "test", "refimg", "v$(VERSION.major).$(VERSION.minor)", string(pkg))
|
||||
refdir = joinpath(Pkg.dir("Plots"), "test", "refimg", string(pkg))
|
||||
try
|
||||
run(`mkdir -p $refdir`)
|
||||
catch err
|
||||
display(err)
|
||||
end
|
||||
reffn = joinpath(refdir, "ref$idx.png")
|
||||
|
||||
try
|
||||
|
||||
# info("Comparing $tmpfn to reference $reffn")
|
||||
|
||||
# load the reference image
|
||||
refimg = Images.load(reffn)
|
||||
|
||||
# run the comparison test... a difference will throw an error
|
||||
# NOTE: sigma is a 2-length vector with x/y values for the number of pixels
|
||||
# to blur together when comparing images
|
||||
diffpct = Images.test_approx_eq_sigma_eps(tmpimg, refimg, sigma, eps)
|
||||
|
||||
# we passed!
|
||||
info("Reference image $reffn matches. Difference: $diffpct")
|
||||
return true
|
||||
|
||||
catch err
|
||||
warn("Image did not match reference image $reffn. err: $err")
|
||||
showerror(Base.STDERR, err)
|
||||
|
||||
if isinteractive()
|
||||
|
||||
# if we're in interactive mode, open a popup and give us a chance to examine the images
|
||||
warn("Should we make this the new reference image?")
|
||||
compareToReferenceImage(tmpfn, reffn)
|
||||
return
|
||||
|
||||
else
|
||||
|
||||
# if we rejected the image, or if we're in automated tests, throw the error
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function image_comparison_tests(pkg::Symbol; skip = [], debug = false, sigma = [1,1], eps = 1e-2)
|
||||
for i in 1:length(PlotExamples.examples)
|
||||
i in skip && continue
|
||||
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) --> true
|
||||
end
|
||||
end
|
||||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 59 KiB |