Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65e18a8669 | |||
| 70f23e695b | |||
| 5196ce5263 | |||
| 9231da4610 | |||
| 89f8030ba1 | |||
| 534f57b227 | |||
| d778c7922d | |||
| 5109c6f72b | |||
| fe5789d747 | |||
| 00201b5f9c | |||
| 85f7acf9d5 | |||
| 44d1cdc2b2 | |||
| 90c2202f6e | |||
| d30574de24 | |||
| dec3748fe5 | |||
| e8cdff2310 | |||
| a0ca9d675f | |||
| baf410c712 | |||
| a81af2b449 | |||
| 678dde710b | |||
| 93a35d8fdc | |||
| 0878f44b09 | |||
| b89ae245ec | |||
| 536da01714 | |||
| a1b991aaeb | |||
| 7bee9a5829 | |||
| 17a90e3b00 | |||
| 43ba9b1172 | |||
| fdb01046e6 | |||
| 09421c9189 | |||
| c563204b0a | |||
| 33b3ee89f3 | |||
| 008b61c9f6 | |||
| 7546db4d42 | |||
| 925243969c | |||
| 7bce4a963e | |||
| 42d09a6d80 | |||
| b6334176e4 | |||
| 42a175bd53 | |||
| b4fb9af8e4 | |||
| ed6c9fe432 | |||
| 7e04aa438f | |||
| f1679f82c1 | |||
| 1808b81d59 | |||
| cd25c7acb2 | |||
| cf03779c5b | |||
| d3439f0d7e | |||
| 2df7f84939 | |||
| 74c76c48d5 | |||
| cdc0d22b01 | |||
| 5a520aadc0 | |||
| ec87489dd4 | |||
| e30affdfc2 | |||
| 66bd2559b9 | |||
| c8d6b1fab0 | |||
| 548c59719b | |||
| 59c1e24371 | |||
| 3c09921cbd | |||
| 00f3d450f9 | |||
| e676387c16 | |||
| 2e2e8a2230 | |||
| 8c8ede361e | |||
| 672bf05856 | |||
| 67ec31c75a | |||
| 59d13a7810 | |||
| 010a25f4cb | |||
| a0ac7b4a1e | |||
| ab7eb89466 | |||
| 649d362b85 | |||
| cb80860d6f | |||
| a8ef9e66fc | |||
| d27212e0b7 | |||
| 65650a7ae5 | |||
| 6ab5b8eb68 | |||
| c618e0577a | |||
| 0e84c91451 | |||
| 005ce11313 | |||
| db118d4f5a | |||
| c92b30a741 | |||
| d67aec6fec | |||
| ce58f25488 | |||
| e8d77a3c61 | |||
| d6d7fc1edd | |||
| 7ce7f8bfce | |||
| f0f46e5272 | |||
| f9473cc8bc | |||
| 5c7d31157b | |||
| 982a512ebd | |||
| 5dfd3d2f5d | |||
| 8d6974f67d | |||
| 8b569e3cd8 | |||
| b9d20142a2 | |||
| 6c9e818abd | |||
| d2a287d43c | |||
| d6ce2e364f | |||
| 36ca485a74 | |||
| b3230c5671 | |||
| ebf490838c | |||
| 7921cf18b6 | |||
| 0e0450ced5 | |||
| 096064ac09 | |||
| 77c2d7d846 | |||
| 40a121198d | |||
| 44079a7bf7 | |||
| 50fa013801 | |||
| d4ccf0ec62 | |||
| 04842974e7 | |||
| 6161b08f82 | |||
| b9818a4480 | |||
| e2ae22421b | |||
| b64677e14a | |||
| c910445634 | |||
| c4683a754e | |||
| 1da8fdb588 | |||
| 4bf8e342dc | |||
| 18fc50fcbf | |||
| 2fb10b7076 | |||
| 967291c2ef | |||
| 392ae871d5 | |||
| 27b3070480 | |||
| 325f59e03d | |||
| 217ffb36e6 | |||
| 4afad93ff6 | |||
| e8858cb1b7 | |||
| 52de31c9a6 | |||
| 60660a1f1a | |||
| a2ea9db90e | |||
| 236a7dadc6 | |||
| 82d2cc943d | |||
| d70f462899 | |||
| 0a636b5899 | |||
| e0b2749188 | |||
| 2fc973245d | |||
| 9bfcb64542 | |||
| 61e34b7ef8 | |||
| 3c3078875a | |||
| 5293e2c13d | |||
| fdf3b3f581 | |||
| f3f29fb54f |
+43
-8
@@ -6,18 +6,53 @@ os:
|
||||
julia:
|
||||
- 0.4
|
||||
#- nightly
|
||||
|
||||
|
||||
# # before install:
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install wkhtmltopdf; fi
|
||||
|
||||
# ref: http://askubuntu.com/a/556672 for the wkhtmltopdf apt repository info
|
||||
|
||||
sudo: required
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./test/install_wkhtmltoimage.sh ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository -y ppa:pov/wkhtmltopdf ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y wkhtmltopdf ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wkhtmltopdf -V ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wkhtmltoimage -V ; fi
|
||||
# echo 'exec xvfb-run -a -s "-screen 0 640x480x16" wkhtmltopdf "$@"' | sudo tee /usr/local/bin/wkhtmltopdf.sh >/dev/null
|
||||
# sudo chmod a+x /usr/local/bin/wkhtmltopdf.sh
|
||||
|
||||
# # borrowed from Blink.jl's travis file
|
||||
# matrix:
|
||||
# include:
|
||||
# - os: linux
|
||||
# julia: 0.4
|
||||
# env: TESTCMD="xvfb-run julia"
|
||||
# - os: osx
|
||||
# julia: 0.4
|
||||
# env: TESTCMD="julia"
|
||||
|
||||
|
||||
|
||||
notifications:
|
||||
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")'
|
||||
# - julia -e 'Pkg.clone("https://github.com/tbreloff/Images.jl.git"); Pkg.checkout("Images","tom_imgcompare");'
|
||||
# - julia -e 'Pkg.clone("Images"); Pkg.build("Images")'
|
||||
- julia test/travis_commands.jl
|
||||
# - julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
|
||||
- julia -e 'Pkg.clone("https://github.com/tbreloff/VisualRegressionTests.jl.git");'
|
||||
- julia -e 'Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");'
|
||||
# - julia -e 'Pkg.add("Cairo"); Pkg.build("Cairo")'
|
||||
- julia -e 'ENV["PYTHON"] = ""; Pkg.add("PyPlot"); Pkg.build("PyPlot")'
|
||||
- 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())'
|
||||
# - julia -e 'Pkg.clone("GR"); Pkg.build("GR")'
|
||||
# # - julia -e 'Pkg.clone("https://github.com/tbreloff/ImageMagick.jl.git"); Pkg.checkout("ImageMagick","tb_write"); Pkg.build("ImageMagick")'
|
||||
# - julia -e 'Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");'
|
||||
# # - julia -e 'Pkg.clone("https://github.com/JunoLab/Blink.jl.git"); Pkg.build("Blink"); import Blink; Blink.AtomShell.install()'
|
||||
# # - julia -e 'Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")'
|
||||
# - julia -e 'ENV["PYTHON"] = ""; Pkg.add("PyPlot"); Pkg.build("PyPlot")'
|
||||
#
|
||||
# # - $TESTCMD -e 'Pkg.test("Plots"; coverage=false)'
|
||||
# - 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())'
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
# Plots.jl NEWS
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- I (@tbreloff) am in development of totally revamped internals, in which much of the logic in building series is changed to recursively stacking recipes in a modular fashion. I'll write a blog post sometime soon detailing why this is amazing, but for now you can get a taste of it by looking at the [RecipesBase readme](https://github.com/JuliaPlots/RecipesBase.jl)
|
||||
- Version 0.6.2 will be the last full release of the "Old Plots", and the 0.6 minor will be only small bug fixes.
|
||||
- All new development should target 0.7 (recipes and subplots)!
|
||||
|
||||
## 0.7 (WIP on tb_recipes/dev and master)
|
||||
|
||||
- Revamped and simplified internals
|
||||
- [Recipes, recipes, recipes](https://github.com/JuliaPlots/RecipesBase.jl/issues/6)
|
||||
- [Layouts and Subplots](https://github.com/tbreloff/Plots.jl/issues/60)
|
||||
|
||||
## Version 0.6
|
||||
|
||||
#### 0.6.2
|
||||
|
||||
- `linewidth` fixes
|
||||
- `markershape` fix
|
||||
- converted center calc to centroid for shapes
|
||||
- new dependency on [RecipesBase](https://github.com/JuliaPlots/RecipesBase.jl)
|
||||
- REQUIRE upper limit for RecipesBase: 0.0.1
|
||||
- GR fixes/improvements (@jheinen)
|
||||
- support `zlims`, `bins`
|
||||
- allow Plots colormaps
|
||||
- other bug fixes
|
||||
- native image support
|
||||
- PGFPlots fixes/improvements (@pkofod)
|
||||
- DataFrames are handled by recipes
|
||||
- Plotly: zaxis, tick rotation, 3d axis fix
|
||||
- Improvements in handling discrete data
|
||||
- Support for image display
|
||||
- `arrow` keyword and support for adding arrows to paths
|
||||
- changed quiver recipe to use arrows
|
||||
- Bug fixes for boxplots, heatmaps, and more
|
||||
|
||||
#### 0.6.1
|
||||
|
||||
- `rotation` keyword
|
||||
- improved supported graphs
|
||||
- subplot bug fix
|
||||
|
||||
#### 0.6.0
|
||||
|
||||
- `apply_series_recipe` framework for built-in recipes
|
||||
- [boxplot/violin recipes](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/boxplot.ipynb)
|
||||
- [errorbar/ribbon recipes](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/errorbars.ipynb)
|
||||
- [quiver recipe](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/quiver.ipynb)
|
||||
- `polar` coordinates
|
||||
- better support for shapes and custom polygons (see [batman](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/batman.ipynb))
|
||||
- z-axis keywords
|
||||
- 3D indexing overhaul: `push!`, `append!` support
|
||||
- matplotlib colormap constants (`:inferno` is the new default colormap for Plots)
|
||||
- `typealias KW Dict{Symbol,Any}` used in place of splatting in many places
|
||||
- png generation for plotly backend using wkhtmltoimage
|
||||
- `normalize` and `weights` keywords
|
||||
- background/foreground subcategories for fine-tuning of looks
|
||||
- `add_theme`/`set_theme` and ggplot2 theme (see [this issue](https://github.com/tbreloff/Plots.jl/issues/201))
|
||||
- `PLOTS_DEFAULT_BACKEND` environment variable
|
||||
- `barh` linetype
|
||||
- support for non-gridded surfaces with pyplot's trisurface
|
||||
- pyplot surface zcolor
|
||||
- internal refactor of supported.jl
|
||||
- `wrap` method to bypass input processing
|
||||
- `translate`, `scale` and `rotate` methods for coordinates and shapes
|
||||
- and many more minor fixes and improvements
|
||||
|
||||
#### 0.5.4
|
||||
|
||||
- old heatmaps have been renamed to hist2d, and true heatmaps implemented (see https://github.com/tbreloff/Plots.jl/issues/147)
|
||||
- lots of reorganization and redesign of the internals
|
||||
- lots of renaming to keep to conventions: AbstractPlot, AbstractBackend, etc
|
||||
- initial redesign of layouts
|
||||
- integration with Atom PlotPane
|
||||
- arc diagram and chord diagram (thanks to @diegozea: see https://github.com/tbreloff/Plots.jl/issues/163)
|
||||
- work on GR, GLVisualize, and PGFPlots backends (thanks @jheinen @dlfivefifty @pkofod)
|
||||
- improvements to Plotly setup (thanks @spencerlyon2)
|
||||
- overhaul to series creation logic and groupby mechanic
|
||||
- replace Dict with `typealias KW Dict{Symbol,Any}` in many places, also replacing keyword arg splatting
|
||||
- new `shape` linetype for plotting polygons in plot-coordinates (see https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/batman.ipynb)
|
||||
- many other fixes
|
||||
|
||||
#### 0.5.3
|
||||
|
||||
- `@gif` macro with `every`/`when` syntax
|
||||
- bezier curves and other graph drawing helpers
|
||||
- added FixedSizeArrays dependency with relevant functionality
|
||||
- merged lots of improvements to GR (thanks @jheinen)
|
||||
- `overwrite_figure`/`reuse` arg for reusing the same figure window
|
||||
- deprecated Qwt, Winston, and Bokeh backends
|
||||
- improved handling of 3D inputs (call `z=rand(10,10); surface(z)` for example)
|
||||
- fix IJulia display issue
|
||||
- lots of progress on PlotlyJS backend
|
||||
- and many other changes and fixes...
|
||||
|
||||
#### 0.5.2
|
||||
|
||||
- Added [GR.jl](https://github.com/jheinen/GR.jl) as a backend (unfinished but functional) All credit to @jheinen
|
||||
- Set defaults within backend calls (i.e. `gadfly(legend=false)`)
|
||||
- `abline!`; also extrema allows plotting functions without giving x (i.e. `plot(cos, 0, 10); plot!(sin)`) @pkofod @joshday
|
||||
- Integration with [PlotlyJS.jl](https://github.com/spencerlyon2/PlotlyJS.jl) for using Plotly inside a Blink window @spencerlyon2
|
||||
- The Plotly backend has been split into my built-in version (`plotly()`) and @spencerlyon2's backend (`plotlyjs()`)
|
||||
- Revamped backend setup code for easily adding new backends
|
||||
- New docs (WIP) at http://plots.readthedocs.org/
|
||||
- Overhaul to `:legend` keyword (see https://github.com/tbreloff/Plots.jl/issues/135)
|
||||
- New dependency on Requires, allows auto-loading of DataFrames support
|
||||
- Support for plotting lists of Tuples and FixedSizeArrays
|
||||
- new `@animate` macro for super simple animations (see https://github.com/tbreloff/Plots.jl/issues/111#issuecomment-181515616)
|
||||
- allow Function for `:fillrange` and `zcolor` arguments (for example: `scatter(sin, 0:10, marker=15, fill=(cos,0.4), zcolor=sin)`)
|
||||
- allow vectors of PlotText without x/y coords (for example: `scatter(rand(10), m=20, ann=map(text, 1:10))`)
|
||||
- Lots and lots of fixes
|
||||
|
||||
#### 0.5.1
|
||||
|
||||
#### 0.5.0
|
||||
|
||||
- `with` function for temporary defaults
|
||||
- contours
|
||||
- basic 3D plotting
|
||||
- preliminary support for Bokeh
|
||||
- `stroke` and `brush` for more fine-tuned control over visuals
|
||||
- smarter "magic" arguments: `line`, `marker`
|
||||
@@ -1,8 +1,9 @@
|
||||
# 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://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
|
||||
<!-- [](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
|
||||
<!-- [](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
|
||||
|
||||
@@ -10,12 +11,25 @@
|
||||
|
||||
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".
|
||||
- **Powerful**. Do more with less. Complex visualizations become easy.
|
||||
- **Intuitive**. Start generating 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.
|
||||
- **Consistent**. Don't commit to one graphics package. Use the same code and access the strengths of all [backends](http://plots.readthedocs.io/en/latest/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.
|
||||
Use the [preprocessing pipeline](http://plots.readthedocs.io/en/latest/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.
|
||||
|
||||
View the [full documentation](http://plots.readthedocs.org).
|
||||
```julia
|
||||
using Plots
|
||||
pyplot(reuse=true)
|
||||
|
||||
@gif for i in linspace(0,2π,100)
|
||||
X = Y = linspace(-5,5,40)
|
||||
surface(X, Y, (x,y) -> sin(x+10sin(i))+cos(y))
|
||||
end
|
||||
```
|
||||
|
||||

|
||||
|
||||
View the [full documentation](http://plots.readthedocs.io).
|
||||
|
||||
+49
-15
@@ -8,6 +8,7 @@ using Reexport
|
||||
@reexport using Colors
|
||||
using Requires
|
||||
using FixedSizeArrays
|
||||
@reexport using RecipesBase
|
||||
|
||||
export
|
||||
AbstractPlot,
|
||||
@@ -19,6 +20,11 @@ export
|
||||
FlexLayout,
|
||||
AVec,
|
||||
AMat,
|
||||
KW,
|
||||
|
||||
wrap,
|
||||
set_theme,
|
||||
add_theme,
|
||||
|
||||
plot,
|
||||
plot!,
|
||||
@@ -33,6 +39,8 @@ export
|
||||
scatter!,
|
||||
bar,
|
||||
bar!,
|
||||
barh,
|
||||
barh!,
|
||||
histogram,
|
||||
histogram!,
|
||||
histogram2d,
|
||||
@@ -55,6 +63,8 @@ export
|
||||
pie!,
|
||||
contour,
|
||||
contour!,
|
||||
contour3d,
|
||||
contour3d!,
|
||||
surface,
|
||||
surface!,
|
||||
wireframe,
|
||||
@@ -66,6 +76,12 @@ export
|
||||
scatter3d,
|
||||
scatter3d!,
|
||||
abline!,
|
||||
boxplot,
|
||||
boxplot!,
|
||||
violin,
|
||||
violin!,
|
||||
quiver,
|
||||
quiver!,
|
||||
|
||||
title!,
|
||||
xlabel!,
|
||||
@@ -98,6 +114,7 @@ export
|
||||
brush,
|
||||
Surface,
|
||||
OHLC,
|
||||
arrow,
|
||||
|
||||
colorscheme,
|
||||
ColorScheme,
|
||||
@@ -124,14 +141,25 @@ export
|
||||
@animate,
|
||||
@gif,
|
||||
|
||||
# recipes
|
||||
PlotRecipe,
|
||||
# EllipseRecipe,
|
||||
spy,
|
||||
arcdiagram,
|
||||
chorddiagram
|
||||
# corrplot
|
||||
chorddiagram,
|
||||
|
||||
# @kw,
|
||||
@recipe,
|
||||
# @plotrecipe,
|
||||
|
||||
translate,
|
||||
translate!,
|
||||
rotate,
|
||||
rotate!,
|
||||
center,
|
||||
P2,
|
||||
P3,
|
||||
BezierCurve,
|
||||
curve_points,
|
||||
directed_curve
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -141,6 +169,7 @@ include("colors.jl")
|
||||
include("components.jl")
|
||||
include("backends.jl")
|
||||
include("args.jl")
|
||||
include("themes.jl")
|
||||
include("plot.jl")
|
||||
include("series_args.jl")
|
||||
include("subplot.jl")
|
||||
@@ -156,6 +185,8 @@ 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)
|
||||
barh(args...; kw...) = plot(args...; kw..., linetype = :barh, orientation = :h)
|
||||
barh!(args...; kw...) = plot!(args...; kw..., linetype = :barh, orientation = :h)
|
||||
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
|
||||
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
|
||||
histogram2d(args...; kw...) = plot(args...; kw..., linetype = :hist2d)
|
||||
@@ -174,10 +205,12 @@ 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)
|
||||
pie(args...; kw...) = plot(args...; kw..., linetype = :pie)
|
||||
pie!(args...; kw...) = plot!(args...; kw..., linetype = :pie)
|
||||
pie(args...; kw...) = plot(args...; kw..., linetype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
pie!(args...; kw...) = plot!(args...; kw..., linetype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
contour(args...; kw...) = plot(args...; kw..., linetype = :contour)
|
||||
contour!(args...; kw...) = plot!(args...; kw..., linetype = :contour)
|
||||
contour3d(args...; kw...) = plot(args...; kw..., linetype = :contour3d)
|
||||
contour3d!(args...; kw...) = plot!(args...; kw..., linetype = :contour3d)
|
||||
surface(args...; kw...) = plot(args...; kw..., linetype = :surface)
|
||||
surface!(args...; kw...) = plot!(args...; kw..., linetype = :surface)
|
||||
wireframe(args...; kw...) = plot(args...; kw..., linetype = :wireframe)
|
||||
@@ -188,6 +221,12 @@ plot3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
|
||||
plot3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
|
||||
scatter3d(args...; kw...) = plot(args...; kw..., linetype = :scatter3d)
|
||||
scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d)
|
||||
boxplot(args...; kw...) = plot(args...; kw..., linetype = :box)
|
||||
boxplot!(args...; kw...) = plot!(args...; kw..., linetype = :box)
|
||||
violin(args...; kw...) = plot(args...; kw..., linetype = :violin)
|
||||
violin!(args...; kw...) = plot!(args...; kw..., linetype = :violin)
|
||||
quiver(args...; kw...) = plot(args...; kw..., linetype = :quiver)
|
||||
quiver!(args...; kw...) = plot!(args...; kw..., linetype = :quiver)
|
||||
|
||||
|
||||
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
|
||||
@@ -240,16 +279,11 @@ yaxis!(plt::Plot, args...; kw...) = plot!(pl
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
|
||||
function __init__()
|
||||
|
||||
# override IJulia inline display
|
||||
if isijulia()
|
||||
@eval import IJulia
|
||||
IJulia.display_dict(plt::AbstractPlot) = Dict{ASCIIString, ByteString}("text/html" => sprint(writemime, "text/html", plt))
|
||||
end
|
||||
|
||||
setup_dataframes()
|
||||
setup_atom()
|
||||
setup_ijulia()
|
||||
setup_dataframes()
|
||||
setup_atom()
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
+516
-430
File diff suppressed because it is too large
Load Diff
+48
-7
@@ -40,7 +40,7 @@ end
|
||||
@init_backend PGFPlots
|
||||
|
||||
include("backends/web.jl")
|
||||
include("backends/supported.jl")
|
||||
# include("backends/supported.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -64,14 +64,33 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function pickDefaultBackend()
|
||||
for pkgstr in ("PyPlot", "Immerse", "Qwt", "Gadfly", "GR", "UnicodePlots", "Bokeh", "GLVisualize")
|
||||
if Pkg.installed(pkgstr) != nothing
|
||||
return backend(symbol(lowercase(pkgstr)))
|
||||
env_default = get(ENV, "PLOTS_DEFAULT_BACKEND", "")
|
||||
if env_default != ""
|
||||
try
|
||||
Pkg.installed(env_default) # this will error if not installed
|
||||
sym = symbol(lowercase(env_default))
|
||||
if haskey(_backendType, sym)
|
||||
return backend(sym)
|
||||
else
|
||||
warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t",
|
||||
join(sort(_backends), "\n\t"))
|
||||
end
|
||||
catch
|
||||
warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not installed.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# the default if nothing else is installed
|
||||
backend(:plotly)
|
||||
# the ordering/inclusion of this package list is my semi-arbitrary guess at
|
||||
# which one someone will want to use if they have the package installed...accounting for
|
||||
# features, speed, and robustness
|
||||
for pkgstr in ("PyPlot", "GR", "PlotlyJS", "Immerse", "Gadfly", "UnicodePlots")
|
||||
if Pkg.installed(pkgstr) != nothing
|
||||
return backend(symbol(lowercase(pkgstr)))
|
||||
end
|
||||
end
|
||||
|
||||
# the default if nothing else is installed
|
||||
backend(:plotly)
|
||||
end
|
||||
|
||||
|
||||
@@ -119,3 +138,25 @@ function backend(modname::Symbol)
|
||||
CURRENT_BACKEND.sym = modname
|
||||
CURRENT_BACKEND.pkg = _backend_instance(modname)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
supportedAxes(::AbstractBackend) = [:left]
|
||||
supportedTypes(::AbstractBackend) = []
|
||||
supportedStyles(::AbstractBackend) = [:solid]
|
||||
supportedMarkers(::AbstractBackend) = [:none]
|
||||
supportedScales(::AbstractBackend) = [:identity]
|
||||
subplotSupported(::AbstractBackend) = false
|
||||
stringsSupported(::AbstractBackend) = false
|
||||
nativeImagesSupported(::AbstractBackend) = false
|
||||
|
||||
supportedAxes() = supportedAxes(backend())
|
||||
supportedTypes() = supportedTypes(backend())
|
||||
supportedStyles() = supportedStyles(backend())
|
||||
supportedMarkers() = supportedMarkers(backend())
|
||||
supportedScales() = supportedScales(backend())
|
||||
subplotSupported() = subplotSupported(backend())
|
||||
stringsSupported() = stringsSupported(backend())
|
||||
nativeImagesSupported() = nativeImagesSupported(backend())
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
+81
-20
@@ -2,6 +2,75 @@
|
||||
# https://github.com/bokeh/Bokeh.jl
|
||||
|
||||
|
||||
supportedArgs(::BokehBackend) = [
|
||||
# :annotation,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
:linecolor,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
# :fillcolor,
|
||||
# :fillalpha,
|
||||
# :foreground_color,
|
||||
:group,
|
||||
# :label,
|
||||
# :layout,
|
||||
# :legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
# :linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
# :markeralpha,
|
||||
# :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :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,
|
||||
# :levels,
|
||||
]
|
||||
supportedAxes(::BokehBackend) = [:auto, :left]
|
||||
supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
supportedStyles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::BokehBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::BokehBackend) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
subplotSupported(::BokehBackend) = false
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
function _initialize_backend(::BokehBackend; kw...)
|
||||
@eval begin
|
||||
warn("Bokeh is no longer supported... many features will likely be broken.")
|
||||
@@ -64,11 +133,7 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::BokehBackend; kw...)
|
||||
d = KW(kw)
|
||||
|
||||
# dumpdict(d, "plot", true)
|
||||
|
||||
function _create_plot(pkg::BokehBackend, d::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
|
||||
|
||||
@@ -88,11 +153,7 @@ function _create_plot(pkg::BokehBackend; kw...)
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::BokehBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
|
||||
# dumpdict(d, "plot!", true)
|
||||
|
||||
function _add_series(::BokehBackend, plt::Plot, d::KW)
|
||||
bdata = Dict{Symbol, Vector}(:x => collect(d[:x]), :y => collect(d[:y]))
|
||||
|
||||
glyph = Bokeh.Bokehjs.Glyph(
|
||||
@@ -124,16 +185,16 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{BokehBackend}, i::Int)
|
||||
series = plt.o.datacolumns[i].data
|
||||
series[:x], series[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{BokehBackend}, xy::Tuple, i::Integer)
|
||||
series = plt.o.datacolumns[i].data
|
||||
series[:x], series[:y] = xy
|
||||
plt
|
||||
end
|
||||
# function getxy(plt::Plot{BokehBackend}, i::Int)
|
||||
# series = plt.o.datacolumns[i].data
|
||||
# series[:x], series[:y]
|
||||
# end
|
||||
#
|
||||
# function setxy!(plt::Plot{BokehBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# series = plt.o.datacolumns[i].data
|
||||
# series[:x], series[:y] = xy
|
||||
# plt
|
||||
# end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
+105
-44
@@ -2,6 +2,43 @@
|
||||
# https://github.com/dcjones/Gadfly.jl
|
||||
|
||||
|
||||
supportedArgs(::GadflyBackend) = [
|
||||
:annotation,
|
||||
:background_color, :foreground_color, :color_palette,
|
||||
:group, :label, :linetype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :n, :nc, :nr, :layout, :smooth,
|
||||
:title, :windowtitle, :show, :size,
|
||||
:x, :xlabel, :xlims, :xticks, :xscale, :xflip,
|
||||
:y, :ylabel, :ylims, :yticks, :yscale, :yflip,
|
||||
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
|
||||
:z,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:xerror, :yerror,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
]
|
||||
supportedAxes(::GadflyBackend) = [:auto, :left]
|
||||
supportedTypes(::GadflyBackend) = [
|
||||
:none, :line, :path, :steppre, :steppost, :sticks,
|
||||
:scatter, :hist2d, :hexbin, :hist,
|
||||
:bar, :box, :violin, :quiver,
|
||||
:hline, :vline, :contour, :shape
|
||||
]
|
||||
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
|
||||
supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
subplotSupported(::GadflyBackend) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::GadflyBackend; kw...)
|
||||
@eval begin
|
||||
import Gadfly, Compose
|
||||
@@ -33,13 +70,15 @@ end
|
||||
|
||||
function getLineGeom(d::KW)
|
||||
lt = d[:linetype]
|
||||
xbins, ybins = maketuple(d[:nbins])
|
||||
xbins, ybins = maketuple(d[:bins])
|
||||
if lt == :hexb
|
||||
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
|
||||
elseif lt == :hist2d
|
||||
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
|
||||
elseif lt == :hist
|
||||
Gadfly.Geom.histogram(bincount = xbins)
|
||||
Gadfly.Geom.histogram(bincount = xbins,
|
||||
orientation = isvertical(d) ? :vertical : :horizontal,
|
||||
position = d[:bar_position] == :stack ? :stack : :dodge)
|
||||
elseif lt == :path
|
||||
Gadfly.Geom.path
|
||||
elseif lt in (:bar, :sticks)
|
||||
@@ -77,12 +116,13 @@ function get_extra_theme_args(d::KW, k::Symbol)
|
||||
end
|
||||
|
||||
function getGadflyLineTheme(d::KW)
|
||||
lt = d[:linetype]
|
||||
lc = convertColor(getColor(d[:linecolor]), d[:linealpha])
|
||||
fc = convertColor(getColor(d[:fillcolor]), d[:fillalpha])
|
||||
|
||||
Gadfly.Theme(;
|
||||
default_color = lc,
|
||||
line_width = (d[:linetype] == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
|
||||
default_color = (lt in (:hist,:hist2d,:hexbin,:bar,:sticks) ? fc : lc),
|
||||
line_width = (lt == :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
|
||||
@@ -132,10 +172,16 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
get_shape(sym::Symbol) = _shapes[sym]
|
||||
get_shape(shape::Shape) = shape
|
||||
|
||||
# extract the underlying ShapeGeometry object(s)
|
||||
getMarkerGeom(shape::Shape) = gadflyshape(shape)
|
||||
getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
||||
getMarkerGeom(shapes::AVec) = map(getMarkerGeom, shapes)
|
||||
getMarkerGeom(shapes::AVec) = gadflyshape(map(get_shape, shapes))
|
||||
getMarkerGeom(other) = gadflyshape(get_shape(other))
|
||||
|
||||
# getMarkerGeom(shape::Shape) = gadflyshape(shape)
|
||||
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
||||
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
|
||||
function getMarkerGeom(d::KW)
|
||||
if d[:linetype] == :shape
|
||||
Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
@@ -172,7 +218,7 @@ end
|
||||
function addGadflyContColorScale(plt::Plot{GadflyBackend}, c)
|
||||
plt.plotargs[:colorbar] == :none && return
|
||||
if !isa(c, ColorGradient)
|
||||
c = colorscheme(:bluesreds)
|
||||
c = default_gradient()
|
||||
end
|
||||
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(c, p))))
|
||||
end
|
||||
@@ -182,7 +228,7 @@ function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, plotargs::KW, geoms.
|
||||
kwargs = KW()
|
||||
|
||||
# handle continuous color scales for the markers
|
||||
zcolor = d[:zcolor]
|
||||
zcolor = d[:marker_z]
|
||||
if zcolor != nothing && typeof(zcolor) <: AVec
|
||||
kwargs[:color] = zcolor
|
||||
addGadflyContColorScale(plt, d[:markercolor])
|
||||
@@ -254,8 +300,8 @@ function addGadflySeries!(plt::Plot, d::KW)
|
||||
lt = d[:linetype]
|
||||
if lt == :ohlc
|
||||
error("Haven't re-implemented after refactoring")
|
||||
elseif lt in (:hist2d, :hexbin) && (isa(d[:linecolor], ColorGradient) || isa(d[:linecolor], ColorFunction))
|
||||
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:linecolor], p))))
|
||||
elseif lt in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
|
||||
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
|
||||
elseif lt == :scatter && d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
@@ -315,17 +361,25 @@ function addGadflyTicksGuide(gplt, ticks, isx::Bool)
|
||||
replaceType(gplt.guides, gtype(ticks = collect(ticks)))
|
||||
|
||||
# set the ticks and the labels
|
||||
# Note: this is pretty convoluted, but I think it works. We set the ticks using Gadfly.Guide,
|
||||
# and then set the label function (wraps a dict lookup) through a continuous Gadfly.Scale.
|
||||
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)
|
||||
# # 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 = collect(ticks[1]), labels = labelfunc))
|
||||
|
||||
filterGadflyScale(gplt, isx)
|
||||
gfunc = isx ? Gadfly.Scale.x_discrete : Gadfly.Scale.y_discrete
|
||||
gfunc = isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous
|
||||
labelmap = Dict(zip(ticks...))
|
||||
labelfunc = val -> labelmap[val]
|
||||
push!(gplt.scales, gfunc(levels = collect(ticks[1]), labels = labelfunc))
|
||||
push!(gplt.scales, gfunc(labels = labelfunc))
|
||||
|
||||
else
|
||||
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
|
||||
@@ -341,18 +395,18 @@ function getGadflyScaleFunction(d::KW, isx::Bool)
|
||||
hasScaleKey = haskey(d, scalekey)
|
||||
if hasScaleKey
|
||||
scale = d[scalekey]
|
||||
scale == :ln && 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
|
||||
scale == :ln && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey, log
|
||||
scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey, log2
|
||||
scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey, log10
|
||||
scale == :asinh && return isx ? Gadfly.Scale.x_asinh : Gadfly.Scale.y_asinh, hasScaleKey, asinh
|
||||
scale == :sqrt && return isx ? Gadfly.Scale.x_sqrt : Gadfly.Scale.y_sqrt, hasScaleKey, sqrt
|
||||
end
|
||||
isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous, hasScaleKey
|
||||
isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous, hasScaleKey, identity
|
||||
end
|
||||
|
||||
|
||||
function addGadflyLimitsScale(gplt, d::KW, isx::Bool)
|
||||
gfunc, hasScaleKey = getGadflyScaleFunction(d, isx)
|
||||
gfunc, hasScaleKey, func = getGadflyScaleFunction(d, isx)
|
||||
|
||||
# do we want to add min/max limits for the axis?
|
||||
limsym = isx ? :xlims : :ylims
|
||||
@@ -377,18 +431,18 @@ function addGadflyLimitsScale(gplt, d::KW, isx::Bool)
|
||||
push!(gplt.scales, gfunc(; limargs...))
|
||||
end
|
||||
|
||||
lims
|
||||
lims, func
|
||||
end
|
||||
|
||||
function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims)
|
||||
function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims, xfunc, yfunc)
|
||||
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),
|
||||
xmin = xlims == nothing ? gplt.coord.xmin : xfunc(minimum(xlims)),
|
||||
xmax = xlims == nothing ? gplt.coord.xmax : xfunc(maximum(xlims)),
|
||||
ymin = ylims == nothing ? gplt.coord.ymin : yfunc(minimum(ylims)),
|
||||
ymax = ylims == nothing ? gplt.coord.ymax : yfunc(maximum(ylims)),
|
||||
xflip = get(d, :xflip, gplt.coord.xflip),
|
||||
yflip = get(d, :yflip, gplt.coord.yflip),
|
||||
fixed = gplt.coord.fixed,
|
||||
@@ -418,8 +472,8 @@ function updateGadflyGuides(plt::Plot, d::KW)
|
||||
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)
|
||||
xlims, xfunc = addGadflyLimitsScale(gplt, d, true)
|
||||
ylims, yfunc = addGadflyLimitsScale(gplt, d, false)
|
||||
|
||||
ticks = get(d, :xticks, :auto)
|
||||
if ticks == :none
|
||||
@@ -434,12 +488,22 @@ function updateGadflyGuides(plt::Plot, d::KW)
|
||||
addGadflyTicksGuide(gplt, ticks, false)
|
||||
end
|
||||
|
||||
updateGadflyAxisFlips(gplt, d, xlims, ylims)
|
||||
updateGadflyAxisFlips(gplt, d, xlims, ylims, xfunc, yfunc)
|
||||
end
|
||||
|
||||
function updateGadflyPlotTheme(plt::Plot, d::KW)
|
||||
kwargs = KW()
|
||||
|
||||
# colors
|
||||
insidecolor, gridcolor, textcolor, guidecolor, legendcolor =
|
||||
map(s -> getColor(d[s]), (
|
||||
:background_color_inside,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_text,
|
||||
:foreground_color_guide,
|
||||
:foreground_color_legend
|
||||
))
|
||||
|
||||
# # hide the legend?
|
||||
leg = d[d[:legend] == :none ? :colorbar : :legend]
|
||||
if leg != :best
|
||||
@@ -447,25 +511,24 @@ function updateGadflyPlotTheme(plt::Plot, d::KW)
|
||||
end
|
||||
|
||||
if !get(d, :grid, true)
|
||||
kwargs[:grid_color] = getColor(d[:background_color])
|
||||
kwargs[:grid_color] = gridcolor
|
||||
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,
|
||||
background_color = insidecolor,
|
||||
minor_label_color = textcolor,
|
||||
minor_label_font = tfont.family,
|
||||
minor_label_font_size = tfont.pointsize * Gadfly.pt,
|
||||
major_label_color = fg,
|
||||
major_label_color = guidecolor,
|
||||
major_label_font = gfont.family,
|
||||
major_label_font_size = gfont.pointsize * Gadfly.pt,
|
||||
key_title_color = fg,
|
||||
key_title_color = guidecolor,
|
||||
key_title_font = gfont.family,
|
||||
key_title_font_size = gfont.pointsize * Gadfly.pt,
|
||||
key_label_color = fg,
|
||||
key_label_color = legendcolor,
|
||||
key_label_font = lfont.family,
|
||||
key_label_font_size = lfont.pointsize * Gadfly.pt,
|
||||
plot_padding = 1 * Gadfly.mm,
|
||||
@@ -507,22 +570,20 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
function _create_plot(pkg::GadflyBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::GadflyBackend, d::KW)
|
||||
gplt = createGadflyPlotObject(d)
|
||||
Plot(gplt, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
# plot one data series
|
||||
function _add_series(::GadflyBackend, plt::Plot; kw...)
|
||||
function _add_series(::GadflyBackend, plt::Plot, d::KW)
|
||||
# first clear out the temporary layer
|
||||
gplt = getGadflyContext(plt)
|
||||
if gplt.layers[1].geom.tag == :remove
|
||||
gplt.layers = gplt.layers[2:end]
|
||||
end
|
||||
|
||||
d = KW(kw)
|
||||
addGadflySeries!(plt, d)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
@@ -546,12 +607,12 @@ function getGadflyMappings(plt::Plot, i::Integer)
|
||||
mappings = [l.mapping for l in plt.seriesargs[i][:gadflylayers]]
|
||||
end
|
||||
|
||||
function Base.getindex(plt::Plot{GadflyBackend}, i::Integer)
|
||||
function getxy(plt::Plot{GadflyBackend}, i::Integer)
|
||||
mapping = getGadflyMappings(plt, i)[1]
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{GadflyBackend}, xy::Tuple, i::Integer)
|
||||
function setxy!{X,Y}(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
|
||||
|
||||
# Geometry which displays arbitrary shapes at given (x, y) positions.
|
||||
# note: vertices is a list of shapes
|
||||
immutable ShapeGeometry <: Gadfly.GeometryElement
|
||||
vertices::AbstractVector{@compat(Tuple{Float64,Float64})}
|
||||
vertices::AbstractVector #{Tuple{Float64,Float64}}
|
||||
tag::Symbol
|
||||
|
||||
function ShapeGeometry(shape; tag::Symbol=Gadfly.Geom.empty_tag)
|
||||
@@ -66,24 +67,27 @@ function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aes
|
||||
end
|
||||
|
||||
function gadflyshape(sv::Shape)
|
||||
ShapeGeometry(sv.vertices)
|
||||
ShapeGeometry(Any[vertices(sv)])
|
||||
end
|
||||
|
||||
function gadflyshape(sv::AVec{Shape})
|
||||
ShapeGeometry(Any[vertices(s) for s in sv])
|
||||
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})
|
||||
T = 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]
|
||||
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in get_mod(geom.vertices, i)]
|
||||
end
|
||||
Gadfly.polygon(polys, geom.tag)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -2,6 +2,73 @@
|
||||
|
||||
# [WEBSITE]
|
||||
|
||||
supportedArgs(::GLVisualizeBackend) = [
|
||||
# :annotation,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
# :fillcolor,
|
||||
# :fillalpha,
|
||||
# :foreground_color,
|
||||
# :group,
|
||||
# :label,
|
||||
# :layout,
|
||||
# :legend,
|
||||
# :linecolor,
|
||||
# :linestyle,
|
||||
:linetype
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linewidth,
|
||||
# :linealpha,
|
||||
# :markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# :markeralpha,
|
||||
# :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :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
|
||||
# :levels,
|
||||
]
|
||||
supportedAxes(::GLVisualizeBackend) = [:auto, :left]
|
||||
supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
supportedStyles(::GLVisualizeBackend) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::GLVisualizeBackend) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::GLVisualizeBackend) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
|
||||
subplotSupported(::GLVisualizeBackend) = false
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _initialize_backend(::GLVisualizeBackend; kw...)
|
||||
@eval begin
|
||||
import GLVisualize
|
||||
@@ -15,8 +82,7 @@ immutable GLScreenWrapper
|
||||
window
|
||||
end
|
||||
|
||||
function _create_plot(pkg::GLVisualizeBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::GLVisualizeBackend, d::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
|
||||
|
||||
@@ -27,8 +93,7 @@ function _create_plot(pkg::GLVisualizeBackend; kw...)
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::GLVisualizeBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::GLVisualizeBackend, plt::Plot, d::KW)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
# TODO: this should be moved to the display method?
|
||||
@@ -59,14 +124,14 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{GLVisualizeBackend}, i::Int)
|
||||
function getxy(plt::Plot{GLVisualizeBackend}, i::Int)
|
||||
# TODO:
|
||||
# series = plt.o.lines[i]
|
||||
# series.x, series.y
|
||||
nothing, nothing
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{GLVisualizeBackend}, xy::Tuple, i::Integer)
|
||||
function setxy!{X,Y}(plt::Plot{GLVisualizeBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# TODO:
|
||||
# series = plt.o.lines[i]
|
||||
# series.x, series.y = xy
|
||||
|
||||
+295
-130
@@ -1,6 +1,58 @@
|
||||
|
||||
# https://github.com/jheinen/GR.jl
|
||||
|
||||
|
||||
supportedArgs(::GRBackend) = [
|
||||
:annotation,
|
||||
:background_color, :foreground_color, :color_palette,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:group,
|
||||
:label,
|
||||
:linetype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
:n, :nc, :nr, :layout,
|
||||
:smooth,
|
||||
:title, :windowtitle, :show, :size,
|
||||
:x, :xlabel, :xlims, :xticks, :xscale, :xflip,
|
||||
:y, :ylabel, :ylims, :yticks, :yscale, :yflip,
|
||||
:axis, :yrightlabel,
|
||||
:z, :zlabel, :zlims, :zticks, :zscale, :zflip,
|
||||
:z,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:xerror, :yerror,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:aspect_ratio
|
||||
]
|
||||
supportedAxes(::GRBackend) = _allAxes
|
||||
supportedTypes(::GRBackend) = [
|
||||
:none, :line, :path, :steppre, :steppost,
|
||||
:scatter, :hist2d, :hexbin, :hist, :density,
|
||||
:bar, :sticks,
|
||||
:hline, :vline, :heatmap, :pie, :image, :ohlc,
|
||||
:contour, :path3d, :scatter3d, :surface, :wireframe
|
||||
]
|
||||
supportedStyles(::GRBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::GRBackend) = vcat(_allMarkers, Shape)
|
||||
supportedScales(::GRBackend) = [:identity, :log10]
|
||||
subplotSupported(::GRBackend) = true
|
||||
nativeImagesSupported(::GRBackend) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _initialize_backend(::GRBackend; kw...)
|
||||
@eval begin
|
||||
import GR
|
||||
@@ -41,10 +93,10 @@ function gr_getaxisind(p)
|
||||
end
|
||||
|
||||
function gr_setmarkershape(p)
|
||||
if haskey(p, :markershape)
|
||||
if p[:markershape] != :none
|
||||
shape = p[:markershape]
|
||||
if isa(shape, Shape)
|
||||
p[:vertices] = shape.vertices
|
||||
p[:vertices] = vertices(shape)
|
||||
else
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
p[:vertices] = :none
|
||||
@@ -89,6 +141,56 @@ function gr_polyline(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
function gr_polaraxes(rmin, rmax)
|
||||
GR.savestate()
|
||||
GR.setlinetype(GR.LINETYPE_SOLID)
|
||||
GR.setlinecolorind(88)
|
||||
tick = 0.5 * GR.tick(rmin, rmax)
|
||||
n = round(Int, (rmax - rmin) / tick + 0.5)
|
||||
for i in 0:n
|
||||
r = float(i) / n
|
||||
if i % 2 == 0
|
||||
GR.setlinecolorind(88)
|
||||
if i > 0
|
||||
GR.drawarc(-r, r, -r, r, 0, 359)
|
||||
end
|
||||
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
|
||||
x, y = GR.wctondc(0.05, r)
|
||||
GR.text(x, y, string(signif(rmin + i * tick, 12)))
|
||||
else
|
||||
GR.setlinecolorind(90)
|
||||
GR.drawarc(-r, r, -r, r, 0, 359)
|
||||
end
|
||||
end
|
||||
for alpha in 0:45:315
|
||||
a = alpha + 90
|
||||
sinf = sin(a * pi / 180)
|
||||
cosf = cos(a * pi / 180)
|
||||
GR.polyline([sinf, 0], [cosf, 0])
|
||||
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_HALF)
|
||||
x, y = GR.wctondc(1.1 * sinf, 1.1 * cosf)
|
||||
GR.textext(x, y, string(alpha, "^o"))
|
||||
end
|
||||
GR.restorestate()
|
||||
end
|
||||
|
||||
function gr_getzlims(d, zmin, zmax, adjust)
|
||||
if d[:zlims] != :auto
|
||||
zlims = d[:zlims]
|
||||
if zlims[1] != NaN
|
||||
zmin = zlims[1]
|
||||
end
|
||||
if zlims[2] != NaN
|
||||
zmax = zlims[2]
|
||||
end
|
||||
adjust = false
|
||||
end
|
||||
if adjust
|
||||
zmin, zmax = GR.adjustrange(zmin, zmax)
|
||||
end
|
||||
zmin, zmax
|
||||
end
|
||||
|
||||
function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
subplot=[0, 1, 0, 1])
|
||||
d = plt.plotargs
|
||||
@@ -122,23 +224,20 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
end
|
||||
viewport[4] = vp[3] + 0.95 * (vp[4] - vp[3])
|
||||
|
||||
if haskey(d, :background_color)
|
||||
GR.savestate()
|
||||
GR.selntran(0)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
GR.setfillcolorind(gr_getcolorind(d[:background_color]))
|
||||
GR.fillrect(vp[1], vp[2], vp[3], vp[4])
|
||||
GR.selntran(1)
|
||||
GR.restorestate()
|
||||
c = getColor(d[:background_color])
|
||||
if 0.21 * c.r + 0.72 * c.g + 0.07 * c.b < 0.9
|
||||
fg = convert(Int, GR.inqcolorfromrgb(1-c.r, 1-c.g, 1-c.b))
|
||||
else
|
||||
fg = 1
|
||||
end
|
||||
else
|
||||
fg = 1
|
||||
end
|
||||
bg = gr_getcolorind(d[:background_color]) # TODO: background for all subplots?
|
||||
fg = gr_getcolorind(d[:foreground_color])
|
||||
|
||||
GR.savestate()
|
||||
GR.selntran(0)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
GR.setfillcolorind(gr_getcolorind(d[:background_color_outside]))
|
||||
GR.fillrect(vp[1], vp[2], vp[3], vp[4])
|
||||
c = getColor(d[:background_color_inside])
|
||||
dark_bg = 0.21 * c.r + 0.72 * c.g + 0.07 * c.b < 0.9
|
||||
GR.setfillcolorind(gr_getcolorind(d[:background_color_inside]))
|
||||
GR.fillrect(viewport[1], viewport[2], viewport[3], viewport[4])
|
||||
GR.selntran(1)
|
||||
GR.restorestate()
|
||||
|
||||
extrema = zeros(2, 4)
|
||||
num_axes = 1
|
||||
@@ -151,47 +250,51 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
xmin = ymin = typemax(Float64)
|
||||
xmax = ymax = typemin(Float64)
|
||||
for p in plt.seriesargs
|
||||
lt = p[:linetype]
|
||||
if get(d, :polar, false)
|
||||
lt = :polar
|
||||
end
|
||||
if axis == gr_getaxisind(p)
|
||||
if axis == 2
|
||||
num_axes = 2
|
||||
end
|
||||
if p[:linetype] == :bar
|
||||
if lt == :bar
|
||||
x, y = 1:length(p[:y]), p[:y]
|
||||
elseif p[:linetype] == :ohlc
|
||||
elseif lt == :ohlc
|
||||
x, y = 1:size(p[:y], 1), p[:y]
|
||||
elseif p[:linetype] in [:hist, :density]
|
||||
x, y = Base.hist(p[:y])
|
||||
elseif p[:linetype] in [:hist2d, :hexbin]
|
||||
elseif lt in [:hist, :density]
|
||||
x, y = Base.hist(p[:y], p[:bins])
|
||||
elseif lt in [:hist2d, :hexbin]
|
||||
E = zeros(length(p[:x]),2)
|
||||
E[:,1] = p[:x]
|
||||
E[:,2] = p[:y]
|
||||
if isa(p[:nbins], Tuple)
|
||||
xbins, ybins = p[:nbins]
|
||||
if isa(p[:bins], Tuple)
|
||||
xbins, ybins = p[:bins]
|
||||
else
|
||||
xbins = ybins = p[:nbins]
|
||||
xbins = ybins = p[:bins]
|
||||
end
|
||||
cmap = true
|
||||
x, y, H = Base.hist2d(E, xbins, ybins)
|
||||
elseif p[:linetype] == :pie
|
||||
elseif lt in [:pie, :polar]
|
||||
axes_2d = false
|
||||
xmin, xmax, ymin, ymax = 0, 1, 0, 1
|
||||
x, y = p[:x], p[:y]
|
||||
else
|
||||
if p[:linetype] in [:contour, :surface, :heatmap]
|
||||
if lt in [:contour, :surface, :heatmap]
|
||||
cmap = true
|
||||
end
|
||||
if p[:linetype] in [:surface, :wireframe, :path3d, :scatter3d]
|
||||
if lt in [:surface, :wireframe, :path3d, :scatter3d]
|
||||
axes_2d = false
|
||||
end
|
||||
if p[:linetype] == :heatmap
|
||||
if lt == :heatmap
|
||||
outside_ticks = true
|
||||
end
|
||||
x, y = p[:x], p[:y]
|
||||
end
|
||||
if p[:linetype] != :pie
|
||||
if !(lt in [:pie, :polar])
|
||||
xmin = min(minimum(x), xmin)
|
||||
xmax = max(maximum(x), xmax)
|
||||
if p[:linetype] == :ohlc
|
||||
if lt == :ohlc
|
||||
for val in y
|
||||
ymin = min(val.open, val.high, val.low, val.close, ymin)
|
||||
ymax = max(val.open, val.high, val.low, val.close, ymax)
|
||||
@@ -200,6 +303,14 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
ymin = min(minimum(y), ymin)
|
||||
ymax = max(maximum(y), ymax)
|
||||
end
|
||||
if p[:xerror] != nothing || p[:yerror] != nothing
|
||||
dx = xmax - xmin
|
||||
xmin -= 0.02 * dx
|
||||
xmax += 0.02 * dx
|
||||
dy = ymax - ymin
|
||||
ymin -= 0.02 * dy
|
||||
ymax += 0.02 * dy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -259,6 +370,7 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
yorg = (ymax, ymin)
|
||||
end
|
||||
|
||||
extrema[axis,:] = [xmin, xmax, ymin, ymax]
|
||||
GR.setwindow(xmin, xmax, ymin, ymax)
|
||||
GR.setscale(scale)
|
||||
|
||||
@@ -269,14 +381,19 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
|
||||
if axes_2d
|
||||
GR.setlinewidth(1)
|
||||
GR.setlinecolorind(fg)
|
||||
GR.setlinecolorind(gr_getcolorind(d[:foreground_color_grid]))
|
||||
ticksize = 0.0075 * diag
|
||||
if outside_ticks
|
||||
ticksize = -ticksize
|
||||
end
|
||||
if grid_flag && fg == 1
|
||||
GR.grid(xtick, ytick, 0, 0, majorx, majory)
|
||||
if grid_flag
|
||||
if dark_bg
|
||||
GR.grid(xtick * majorx, ytick * majory, 0, 0, 1, 1)
|
||||
else
|
||||
GR.grid(xtick, ytick, 0, 0, majorx, majory)
|
||||
end
|
||||
end
|
||||
GR.setlinecolorind(gr_getcolorind(d[:foreground_color_axis]))
|
||||
if num_axes == 1
|
||||
GR.axes(xtick, ytick, xorg[1], yorg[1], majorx, majory, ticksize)
|
||||
GR.axes(xtick, ytick, xorg[2], yorg[2], -majorx, -majory, -ticksize)
|
||||
@@ -319,43 +436,58 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
GR.restorestate()
|
||||
end
|
||||
|
||||
legend = false
|
||||
GR.setcolormap(1000 + GR.COLORMAP_COOLWARM)
|
||||
|
||||
for p in plt.seriesargs
|
||||
legend = falses(length(plt.seriesargs))
|
||||
|
||||
for (ind, p) in enumerate(plt.seriesargs)
|
||||
lt = p[:linetype]
|
||||
if lt in (:hist2d, :hexbin, :contour, :surface, :wireframe, :heatmap)
|
||||
if isa(p[:fillcolor], ColorGradient)
|
||||
grad = p[:fillcolor]
|
||||
else
|
||||
grad = default_gradient()
|
||||
end
|
||||
cs = [getColorZ(grad, z) for z in linspace(0, 1, 256)]
|
||||
for (i, c) in enumerate(cs)
|
||||
GR.setcolorrep(999+i, red(c), green(c), blue(c))
|
||||
end
|
||||
end
|
||||
if get(d, :polar, false)
|
||||
lt = :polar
|
||||
end
|
||||
GR.savestate()
|
||||
xmin, xmax, ymin, ymax = extrema[gr_getaxisind(p),:]
|
||||
GR.setwindow(xmin, xmax, ymin, ymax)
|
||||
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :ohlc]
|
||||
haskey(p, :linestyle) && GR.setlinetype(gr_linetype[p[:linestyle]])
|
||||
haskey(p, :linewidth) && GR.setlinewidth(p[:linewidth])
|
||||
haskey(p, :linecolor) && GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
|
||||
if lt in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :ohlc, :polar]
|
||||
GR.setlinetype(gr_linetype[p[:linestyle]])
|
||||
GR.setlinewidth(p[:linewidth])
|
||||
GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
|
||||
end
|
||||
if p[:linetype] == :path
|
||||
if haskey(p, :fillcolor)
|
||||
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
end
|
||||
if lt == :path
|
||||
if length(p[:x]) > 1
|
||||
if p[:fillrange] != nothing
|
||||
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
GR.fillarea([p[:x][1]; p[:x]; p[:x][length(p[:x])]], [p[:fillrange]; p[:y]; p[:fillrange]])
|
||||
end
|
||||
GR.polyline(p[:x], p[:y])
|
||||
end
|
||||
legend = true
|
||||
legend[ind] = true
|
||||
end
|
||||
if p[:linetype] == :line
|
||||
if lt == :line
|
||||
if length(p[:x]) > 1
|
||||
gr_polyline(p[:x], p[:y])
|
||||
end
|
||||
legend = true
|
||||
elseif p[:linetype] in [:steppre, :steppost]
|
||||
legend[ind] = true
|
||||
elseif lt in [:steppre, :steppost]
|
||||
n = length(p[:x])
|
||||
x = zeros(2*n + 1)
|
||||
y = zeros(2*n + 1)
|
||||
x[1], y[1] = p[:x][1], p[:y][1]
|
||||
j = 2
|
||||
for i = 2:n
|
||||
if p[:linetype] == :steppre
|
||||
if lt == :steppre
|
||||
x[j], x[j+1] = p[:x][i-1], p[:x][i]
|
||||
y[j], y[j+1] = p[:y][i], p[:y][i]
|
||||
else
|
||||
@@ -367,100 +499,94 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
if n > 1
|
||||
GR.polyline(x, y)
|
||||
end
|
||||
legend = true
|
||||
elseif p[:linetype] == :sticks
|
||||
legend[ind] = true
|
||||
elseif lt == :sticks
|
||||
x, y = p[:x], p[:y]
|
||||
for i = 1:length(y)
|
||||
GR.polyline([x[i], x[i]], [ymin, y[i]])
|
||||
end
|
||||
legend = true
|
||||
elseif p[:linetype] == :scatter || (p[:markershape] != :none && axes_2d)
|
||||
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
|
||||
legend[ind] = true
|
||||
elseif lt == :scatter || (p[:markershape] != :none && axes_2d)
|
||||
GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
|
||||
gr_setmarkershape(p)
|
||||
if haskey(d, :markersize)
|
||||
if typeof(d[:markersize]) <: Number
|
||||
GR.setmarkersize(d[:markersize] / 4.0)
|
||||
if length(p[:x]) > 0
|
||||
gr_polymarker(p, p[:x], p[:y])
|
||||
end
|
||||
else
|
||||
c = p[:markercolor]
|
||||
GR.setcolormap(-GR.COLORMAP_GLOWING)
|
||||
for i = 1:length(p[:x])
|
||||
if isa(c, ColorGradient) && p[:zcolor] != nothing
|
||||
ci = round(Int, 1000 + p[:zcolor][i] * 255)
|
||||
GR.setmarkercolorind(ci)
|
||||
end
|
||||
GR.setmarkersize(d[:markersize][i] / 4.0)
|
||||
gr_polymarker(p, [p[:x][i]], [p[:y][i]])
|
||||
end
|
||||
end
|
||||
else
|
||||
if typeof(p[:markersize]) <: Number
|
||||
GR.setmarkersize(p[:markersize] / 4.0)
|
||||
if length(p[:x]) > 0
|
||||
gr_polymarker(p, p[:x], p[:y])
|
||||
end
|
||||
else
|
||||
c = p[:markercolor]
|
||||
GR.setcolormap(-GR.COLORMAP_GLOWING)
|
||||
for i = 1:length(p[:x])
|
||||
if isa(c, ColorGradient) && p[:marker_z] != nothing
|
||||
ci = round(Int, 1000 + p[:marker_z][i] * 255)
|
||||
GR.setmarkercolorind(ci)
|
||||
end
|
||||
GR.setmarkersize(p[:markersize][i] / 4.0)
|
||||
gr_polymarker(p, [p[:x][i]], [p[:y][i]])
|
||||
end
|
||||
end
|
||||
legend = true
|
||||
elseif p[:linetype] == :bar
|
||||
legend[ind] = true
|
||||
elseif lt == :bar
|
||||
y = p[:y]
|
||||
for i = 1:length(y)
|
||||
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
GR.fillrect(i-0.4, i+0.4, max(0, ymin), y[i])
|
||||
GR.setfillcolorind(1)
|
||||
GR.setfillcolorind(fg)
|
||||
GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
|
||||
GR.fillrect(i-0.4, i+0.4, max(0, ymin), y[i])
|
||||
end
|
||||
elseif p[:linetype] in [:hist, :density]
|
||||
h = Base.hist(p[:y])
|
||||
elseif lt in [:hist, :density]
|
||||
h = Base.hist(p[:y], p[:bins])
|
||||
x, y = float(collect(h[1])), float(h[2])
|
||||
for i = 2:length(y)
|
||||
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
GR.fillrect(x[i-1], x[i], ymin, y[i])
|
||||
GR.setfillcolorind(1)
|
||||
GR.setfillcolorind(fg)
|
||||
GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
|
||||
GR.fillrect(x[i-1], x[i], ymin, y[i])
|
||||
end
|
||||
elseif p[:linetype] in [:hline, :vline]
|
||||
elseif lt in [:hline, :vline]
|
||||
for xy in p[:y]
|
||||
if p[:linetype] == :hline
|
||||
if lt == :hline
|
||||
GR.polyline([xmin, xmax], [xy, xy])
|
||||
else
|
||||
GR.polyline([xy, xy], [ymin, ymax])
|
||||
end
|
||||
end
|
||||
elseif p[:linetype] in [:hist2d, :hexbin]
|
||||
elseif lt in [:hist2d, :hexbin]
|
||||
E = zeros(length(p[:x]),2)
|
||||
E[:,1] = p[:x]
|
||||
E[:,2] = p[:y]
|
||||
if isa(p[:nbins], Tuple)
|
||||
xbins, ybins = p[:nbins]
|
||||
if isa(p[:bins], Tuple)
|
||||
xbins, ybins = p[:bins]
|
||||
else
|
||||
xbins = ybins = p[:nbins]
|
||||
xbins = ybins = p[:bins]
|
||||
end
|
||||
x, y, H = Base.hist2d(E, xbins, ybins)
|
||||
counts = round(Int32, 1000 + 255 * H / maximum(H))
|
||||
n, m = size(counts)
|
||||
GR.setcolormap(GR.COLORMAP_COOLWARM)
|
||||
GR.cellarray(xmin, xmax, ymin, ymax, n, m, counts)
|
||||
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
|
||||
viewport[3], viewport[4])
|
||||
GR.setspace(0, maximum(counts), 0, 90)
|
||||
zmin, zmax = gr_getzlims(d, 0, maximum(counts), false)
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
|
||||
charheight = max(0.016 * diag, 0.01)
|
||||
GR.setcharheight(charheight)
|
||||
GR.colormap()
|
||||
elseif p[:linetype] == :contour
|
||||
x, y, z = p[:x], p[:y], p[:z].surf
|
||||
zmin, zmax = minimum(z), maximum(z)
|
||||
GR.setviewport(viewport[1], viewport[2], viewport[3], viewport[4])
|
||||
elseif lt == :contour
|
||||
x, y, z = p[:x], p[:y], transpose_z(p, p[:z].surf, false)
|
||||
zmin, zmax = gr_getzlims(d, minimum(z), maximum(z), false)
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
if typeof(p[:levels]) <: Array
|
||||
h = p[:levels]
|
||||
else
|
||||
h = linspace(zmin, zmax, p[:levels])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
GR.setcolormap(GR.COLORMAP_COOLWARM)
|
||||
GR.contour(x, y, h, reshape(z, length(x) * length(y)), 1000)
|
||||
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
|
||||
viewport[3], viewport[4])
|
||||
@@ -472,9 +598,10 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
charheight = max(0.016 * diag, 0.01)
|
||||
GR.setcharheight(charheight)
|
||||
GR.axes(0, ztick, xmax, zmin, 0, 1, 0.005)
|
||||
elseif p[:linetype] in [:surface, :wireframe]
|
||||
x, y, z = p[:x], p[:y], p[:z].surf
|
||||
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
|
||||
GR.setviewport(viewport[1], viewport[2], viewport[3], viewport[4])
|
||||
elseif lt in [:surface, :wireframe]
|
||||
x, y, z = p[:x], p[:y], transpose_z(p, p[:z].surf, false)
|
||||
zmin, zmax = gr_getzlims(d, minimum(z), maximum(z), true)
|
||||
GR.setspace(zmin, zmax, 40, 70)
|
||||
xtick = GR.tick(xmin, xmax) / 2
|
||||
ytick = GR.tick(ymin, ymax) / 2
|
||||
@@ -488,8 +615,7 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
|
||||
end
|
||||
z = reshape(z, length(x) * length(y))
|
||||
if p[:linetype] == :surface
|
||||
GR.setcolormap(GR.COLORMAP_COOLWARM)
|
||||
if lt == :surface
|
||||
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
|
||||
else
|
||||
GR.setfillcolorind(0)
|
||||
@@ -504,21 +630,21 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
viewport[3], viewport[4])
|
||||
GR.colormap()
|
||||
end
|
||||
elseif p[:linetype] == :heatmap
|
||||
x, y, z = p[:x], p[:y], p[:z].surf
|
||||
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
|
||||
elseif lt == :heatmap
|
||||
x, y, z = p[:x], p[:y], transpose_z(p, p[:z].surf, false)
|
||||
zmin, zmax = gr_getzlims(d, minimum(z), maximum(z), true)
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
GR.setcolormap(GR.COLORMAP_COOLWARM)
|
||||
z = reshape(z, length(x) * length(y))
|
||||
GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
|
||||
GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
|
||||
if cmap
|
||||
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
|
||||
viewport[3], viewport[4])
|
||||
GR.colormap()
|
||||
GR.setviewport(viewport[1], viewport[2], viewport[3], viewport[4])
|
||||
end
|
||||
elseif p[:linetype] in [:path3d, :scatter3d]
|
||||
elseif lt in [:path3d, :scatter3d]
|
||||
x, y, z = p[:x], p[:y], p[:z]
|
||||
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
|
||||
zmin, zmax = gr_getzlims(d, minimum(z), maximum(z), true)
|
||||
GR.setspace(zmin, zmax, 40, 70)
|
||||
xtick = GR.tick(xmin, xmax) / 2
|
||||
ytick = GR.tick(ymin, ymax) / 2
|
||||
@@ -527,20 +653,20 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
charheight = max(0.018 * diag, 0.01)
|
||||
ticksize = 0.01 * (viewport[2] - viewport[1])
|
||||
GR.setlinewidth(1)
|
||||
if grid_flag && p[:linetype] == :path3d
|
||||
if grid_flag && lt == :path3d
|
||||
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
|
||||
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
|
||||
end
|
||||
if p[:linetype] == :scatter3d
|
||||
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
|
||||
if lt == :scatter3d
|
||||
GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
|
||||
gr_setmarkershape(p)
|
||||
for i = 1:length(z)
|
||||
px, py = GR.wc3towc(x[i], y[i], z[i])
|
||||
gr_polymarker(p, [px], [py])
|
||||
end
|
||||
else
|
||||
haskey(p, :linewidth) && GR.setlinewidth(p[:linewidth])
|
||||
if length(x) > 0
|
||||
GR.setlinewidth(p[:linewidth])
|
||||
GR.polyline3d(x, y, z)
|
||||
end
|
||||
end
|
||||
@@ -548,7 +674,7 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
GR.setcharheight(charheight)
|
||||
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
|
||||
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
|
||||
elseif p[:linetype] == :ohlc
|
||||
elseif lt == :ohlc
|
||||
y = p[:y]
|
||||
n = size(y, 1)
|
||||
ticksize = 0.5 * (xmax - xmin) / n
|
||||
@@ -557,7 +683,7 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
GR.polyline([i, i], [y[i].low, y[i].high])
|
||||
GR.polyline([i, i+ticksize], [y[i].close, y[i].close])
|
||||
end
|
||||
elseif p[:linetype] == :pie
|
||||
elseif lt == :pie
|
||||
GR.selntran(0)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
xmin, xmax, ymin, ymax = viewport
|
||||
@@ -602,17 +728,55 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
a1 = a2
|
||||
end
|
||||
GR.selntran(1)
|
||||
elseif lt == :image
|
||||
img = p[:z].surf
|
||||
w, h = size(img)
|
||||
if eltype(img) <: Colors.AbstractGray
|
||||
grey = round(UInt8, float(img) * 255)
|
||||
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
|
||||
else
|
||||
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
|
||||
round(Int, blue(c) * 255) << 16 +
|
||||
round(Int, green(c) * 255) << 8 +
|
||||
round(Int, red(c) * 255) ), img)
|
||||
end
|
||||
GR.drawimage(xmin, xmax, ymin, ymax, w, h, rgba)
|
||||
elseif lt == :polar
|
||||
xmin, xmax, ymin, ymax = viewport
|
||||
ymax -= 0.05 * (xmax - xmin)
|
||||
xcenter = 0.5 * (xmin + xmax)
|
||||
ycenter = 0.5 * (ymin + ymax)
|
||||
r = 0.5 * min(xmax - xmin, ymax - ymin)
|
||||
GR.setviewport(xcenter -r, xcenter + r, ycenter - r, ycenter + r)
|
||||
GR.setwindow(-1, 1, -1, 1)
|
||||
rmin, rmax = GR.adjustrange(minimum(r), maximum(r))
|
||||
gr_polaraxes(rmin, rmax)
|
||||
phi, r = p[:x], p[:y]
|
||||
r = 0.5 * (r - rmin) / (rmax - rmin)
|
||||
n = length(r)
|
||||
x = zeros(n)
|
||||
y = zeros(n)
|
||||
for i in 1:n
|
||||
x[i] = r[i] * cos(phi[i])
|
||||
y[i] = r[i] * sin(phi[i])
|
||||
end
|
||||
GR.polyline(x, y)
|
||||
end
|
||||
GR.restorestate()
|
||||
end
|
||||
|
||||
if d[:legend] != :none && legend
|
||||
if d[:legend] != :none && any(legend) == true
|
||||
GR.savestate()
|
||||
GR.selntran(0)
|
||||
GR.setscale(0)
|
||||
w = 0
|
||||
i = 0
|
||||
for p in plt.seriesargs
|
||||
n = 0
|
||||
for (ind, p) in enumerate(plt.seriesargs)
|
||||
if !legend[ind] || p[:label] == ""
|
||||
continue
|
||||
end
|
||||
n += 1
|
||||
if typeof(p[:label]) <: Array
|
||||
i += 1
|
||||
lab = p[:label][i]
|
||||
@@ -626,21 +790,24 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
py = viewport[4] - 0.06
|
||||
dy = 0.03 * sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
GR.setfillcolorind(0)
|
||||
GR.fillrect(px - 0.08, px + w + 0.02, py + dy, py - dy * length(plt.seriesargs))
|
||||
GR.setfillcolorind(gr_getcolorind(d[:background_color_legend]))
|
||||
GR.fillrect(px - 0.08, px + w + 0.02, py + dy, py - dy * n)
|
||||
GR.setlinetype(1)
|
||||
GR.setlinewidth(1)
|
||||
GR.drawrect(px - 0.08, px + w + 0.02, py + dy, py - dy * length(plt.seriesargs))
|
||||
haskey(d, :linewidth) && GR.setlinewidth(d[:linewidth])
|
||||
GR.drawrect(px - 0.08, px + w + 0.02, py + dy, py - dy * n)
|
||||
i = 0
|
||||
for p in plt.seriesargs
|
||||
for (ind, p) in enumerate(plt.seriesargs)
|
||||
if !legend[ind] || p[:label] == ""
|
||||
continue
|
||||
end
|
||||
GR.setlinewidth(p[:linewidth])
|
||||
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks]
|
||||
haskey(p, :linecolor) && GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
|
||||
haskey(p, :linestyle) && GR.setlinetype(gr_linetype[p[:linestyle]])
|
||||
GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
|
||||
GR.setlinetype(gr_linetype[p[:linestyle]])
|
||||
GR.polyline([px - 0.07, px - 0.01], [py, py])
|
||||
end
|
||||
if p[:linetype] == :scatter || p[:markershape] != :none
|
||||
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
|
||||
GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
|
||||
gr_setmarkershape(p)
|
||||
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks]
|
||||
gr_polymarker(p, [px - 0.06, px - 0.02], [py, py])
|
||||
@@ -655,7 +822,7 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
|
||||
lab = p[:label]
|
||||
end
|
||||
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
|
||||
GR.settextcolorind(1)
|
||||
GR.settextcolorind(gr_getcolorind(d[:foreground_color_legend]))
|
||||
GR.text(px, py, lab)
|
||||
py -= dy
|
||||
end
|
||||
@@ -701,13 +868,11 @@ function gr_display(subplt::Subplot{GRBackend})
|
||||
end
|
||||
end
|
||||
|
||||
function _create_plot(pkg::GRBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::GRBackend, d::KW)
|
||||
Plot(nothing, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
function _add_series(::GRBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::GRBackend, plt::Plot, d::KW)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
@@ -736,12 +901,12 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.getindex(plt::Plot{GRBackend}, i::Int)
|
||||
function getxy(plt::Plot{GRBackend}, i::Int)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{GRBackend}, xy::Tuple, i::Integer)
|
||||
function setxy!{X,Y}(plt::Plot{GRBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y] = xy
|
||||
plt
|
||||
|
||||
+14
-7
@@ -1,6 +1,16 @@
|
||||
|
||||
# https://github.com/JuliaGraphics/Immerse.jl
|
||||
|
||||
supportedArgs(::ImmerseBackend) = supportedArgs(GadflyBackend())
|
||||
supportedAxes(::ImmerseBackend) = supportedAxes(GadflyBackend())
|
||||
supportedTypes(::ImmerseBackend) = supportedTypes(GadflyBackend())
|
||||
supportedStyles(::ImmerseBackend) = supportedStyles(GadflyBackend())
|
||||
supportedMarkers(::ImmerseBackend) = supportedMarkers(GadflyBackend())
|
||||
supportedScales(::ImmerseBackend) = supportedScales(GadflyBackend())
|
||||
subplotSupported(::ImmerseBackend) = true
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::ImmerseBackend; kw...)
|
||||
@eval begin
|
||||
import Immerse, Gadfly, Compose, Gtk
|
||||
@@ -19,9 +29,7 @@ end
|
||||
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
function _create_plot(pkg::ImmerseBackend; kw...)
|
||||
d = KW(kw)
|
||||
|
||||
function _create_plot(pkg::ImmerseBackend, d::KW)
|
||||
# create the underlying Gadfly.Plot object
|
||||
gplt = createGadflyPlotObject(d)
|
||||
|
||||
@@ -31,8 +39,7 @@ end
|
||||
|
||||
|
||||
# plot one data series
|
||||
function _add_series(::ImmerseBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::ImmerseBackend, plt::Plot, d::KW)
|
||||
addGadflySeries!(plt, d)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
@@ -58,12 +65,12 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{ImmerseBackend}, i::Integer)
|
||||
function getxy(plt::Plot{ImmerseBackend}, i::Integer)
|
||||
mapping = getGadflyMappings(plt, i)[1]
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{ImmerseBackend}, xy::Tuple, i::Integer)
|
||||
function setxy!{X,Y}(plt::Plot{ImmerseBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
|
||||
+299
-18
@@ -1,6 +1,75 @@
|
||||
|
||||
# https://github.com/sisl/PGFPlots.jl
|
||||
|
||||
supportedArgs(::PGFPlotsBackend) = [
|
||||
# :annotation,
|
||||
:aspect_ratio,
|
||||
# :axis,
|
||||
:background_color,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
# :foreground_color,
|
||||
# :group,
|
||||
:label,
|
||||
# :layout,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokestyle,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
# :smooth,
|
||||
# :show,
|
||||
:size,
|
||||
:title,
|
||||
# :windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
:xflip,
|
||||
:yflip,
|
||||
:z,
|
||||
:zscale,
|
||||
# :tickfont,
|
||||
# :guidefont,
|
||||
# :legendfont,
|
||||
:grid,
|
||||
# :surface
|
||||
# :levels,
|
||||
]
|
||||
supportedAxes(::PGFPlotsBackend) = [:auto, :left]
|
||||
supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :line, :steppre, :stepmid, :steppost, :hist, :bar, :hist2d, :sticks, :ysticks, :xsticks, :contour] # :hexbin, :hline, :vline,]
|
||||
supportedStyles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::PGFPlotsBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::PGFPlotsBackend) = [:identity, :log, :ln, :log2, :log10] # :asinh, :sqrt]
|
||||
subplotSupported(::PGFPlotsBackend) = false
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _initialize_backend(::PGFPlotsBackend; kw...)
|
||||
@eval begin
|
||||
import PGFPlots
|
||||
@@ -9,19 +78,167 @@ function _initialize_backend(::PGFPlotsBackend; kw...)
|
||||
end
|
||||
# TODO: other initialization
|
||||
end
|
||||
const _pgfplots_linestyles = KW(
|
||||
:solid => "solid",
|
||||
:dash => "dashed",
|
||||
:dot => "dotted",
|
||||
:dashdot => "dashdotted",
|
||||
:dashdotdot => "dashdotdotted"
|
||||
)
|
||||
|
||||
const _pgfplots_markers = KW(
|
||||
:none => "none",
|
||||
:cross => "+",
|
||||
:xcross => "x",
|
||||
:utriangle => "triangle*",
|
||||
:dtriangle => "triangle*",
|
||||
:ellipse => "o*",
|
||||
:rect => "square*",
|
||||
:star5 => "star",
|
||||
:star6 => "asterisk",
|
||||
:diamond => "diamond*",
|
||||
:pentagon => "pentagon*"
|
||||
)
|
||||
|
||||
const _pgfplots_legend_pos = KW(
|
||||
:bottomleft => "south west",
|
||||
:bottomright => "south east",
|
||||
:topright => "north east",
|
||||
:topleft => "north west"
|
||||
)
|
||||
|
||||
function _pgfplots_get_color(kwargs, symb)
|
||||
c = typeof(kwargs[symb]) == Symbol ? convertColor(kwargs[symb]) : kwargs[symb].c
|
||||
# We need to convert to decimals here because pgfplot will error
|
||||
# for colors in engineering notation
|
||||
r_str = @sprintf("%.8f", float(c.r))
|
||||
g_str = @sprintf("%.8f", float(c.g))
|
||||
b_str = @sprintf("%.8f", float(c.b))
|
||||
"{rgb,1:red,$(r_str);green,$(g_str);blue,$(b_str)}"
|
||||
end
|
||||
|
||||
function _pgfplots_get_linestyle!(kwargs, plt)
|
||||
ls = plt[:linestyle]
|
||||
if haskey(_pgfplots_linestyles, ls)
|
||||
push!(kwargs[:style], _pgfplots_linestyles[ls])
|
||||
end
|
||||
|
||||
push!(kwargs[:style], "line width = $(plt[:linewidth]) pt")
|
||||
end
|
||||
|
||||
|
||||
function _pgfplots_get_marker!(kwargs, plt)
|
||||
# Control marker shape, size, colors, alphas, and stroke width
|
||||
mark = plt[:markershape]
|
||||
α = plt[:markeralpha] == nothing ? 1.0 : plt[:markeralpha]
|
||||
push!(kwargs[:style], "mark = " * _pgfplots_markers[mark],
|
||||
"mark size = $(plt[:markersize]/2)",
|
||||
"mark options = {color=$(_pgfplots_get_color(plt, :markerstrokecolor))",
|
||||
"fill=$(_pgfplots_get_color(plt, :markercolor))",
|
||||
"fill opacity = $α",
|
||||
"line width=$(plt[:markerstrokewidth])")
|
||||
|
||||
# Rotate the marker if :dtriangle was chosen
|
||||
mark == :dtriangle && push!(kwargs[:style], "rotate=180")
|
||||
|
||||
# Apply marker stroke style if it is a valid PGFPlots stroke style
|
||||
if haskey(_pgfplots_linestyles, plt[:markerstrokestyle])
|
||||
push!(kwargs[:style], _pgfplots_linestyles[plt[:markerstrokestyle]])
|
||||
end
|
||||
|
||||
# End the open mark options bracker
|
||||
push!(kwargs[:style], "}")
|
||||
end
|
||||
|
||||
function _pgfplots_get_series_color!(kwargs, plt)
|
||||
α = plt[:seriesalpha] == nothing ? 1.0 : plt[:seriesalpha]
|
||||
push!(kwargs[:style], "color=$(_pgfplots_get_color(plt, :seriescolor))",
|
||||
"draw opacity = $α")
|
||||
end
|
||||
|
||||
function _pgfplots_get_line_color!(kwargs, plt)
|
||||
α = plt[:linealpha] == nothing ? 1.0 : plt[:linealpha]
|
||||
kwargs[:style] *= ", color=$(_pgfplots_get_color(plt, :linecolor))" *
|
||||
", draw opacity = $α"
|
||||
end
|
||||
|
||||
function _pgfplots_get_fill_color!(kwargs, plt)
|
||||
α = plt[:fillalpha] == nothing ? 1.0 : plt[:fillalpha]
|
||||
kwargs[:style] *= ", fill=$(_pgfplots_get_color(plt, :fillcolor))" *
|
||||
", fill opacity = $α"
|
||||
end
|
||||
|
||||
function _pgfplots_get_label!(kwargs, plt)
|
||||
if plt[:label] != nothing && plt[:legend] != :none
|
||||
kwargs[:legendentry] = plt[:label]
|
||||
end
|
||||
end
|
||||
|
||||
function _pgfplots_get_plot_kwargs(plt)
|
||||
kwargs = KW()
|
||||
kwargs[:style] = []
|
||||
_pgfplots_get_linestyle!(kwargs, plt)
|
||||
_pgfplots_get_marker!(kwargs, plt)
|
||||
_pgfplots_get_series_color!(kwargs, plt)
|
||||
_pgfplots_get_label!(kwargs, plt)
|
||||
kwargs[:style] = join(kwargs[:style], ',')
|
||||
kwargs
|
||||
end
|
||||
|
||||
function _pgfplots_axis(plt_series)
|
||||
line_type = plt_series[:linetype]
|
||||
plt_kwargs = _pgfplots_get_plot_kwargs(plt_series)
|
||||
if line_type == :path
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :path3d
|
||||
PGFPlots.Linear3(plt_series[:x], plt_series[:y], plt_series[:z]; plt_kwargs...)
|
||||
elseif line_type == :scatter
|
||||
PGFPlots.Scatter(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :steppre
|
||||
plt_kwargs[:style] *= ", const plot mark right"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :stepmid
|
||||
plt_kwargs[:style] *= ", const plot mark mid"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :steppost
|
||||
plt_kwargs[:style] *= ", const plot"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :hist
|
||||
#TODO patch this in PGFPlots.jl instead; the problem is that PGFPlots will
|
||||
# save _all_ data points in the figure which can be quite heavy
|
||||
plt_hist = hist(plt_series[:y])
|
||||
plt_kwargs[:style] *= ", ybar interval"
|
||||
_pgfplots_get_line_color!(plt_kwargs, plt_series)
|
||||
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
|
||||
PGFPlots.Linear(plt_hist[1][1:end-1]+plt_hist[1].step/2, plt_hist[2]; plt_kwargs...)
|
||||
elseif line_type == :hist2d
|
||||
PGFPlots.Histogram2(plt_series[:x], plt_series[:y])
|
||||
elseif line_type == :bar
|
||||
plt_kwargs[:style] *= ", ybar"
|
||||
_pgfplots_get_line_color!(plt_kwargs, plt_series)
|
||||
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :sticks || line_type == :ysticks
|
||||
plt_kwargs[:style] *= ", ycomb"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :xsticks
|
||||
plt_kwargs[:style] *= ", xcomb"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :contour
|
||||
PGFPlots.Contour(plt_series[:z].surf, plt_series[:x], plt_series[:y])
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::PGFPlotsBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::PGFPlotsBackend, d::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(nothing, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::PGFPlotsBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::PGFPlotsBackend, plt::Plot, d::KW)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
@@ -52,16 +269,16 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{PGFPlotsBackend}, i::Int)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{PGFPlotsBackend}, xy::Tuple, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y] = xy
|
||||
plt
|
||||
end
|
||||
# function getxy(plt::Plot{PGFPlotsBackend}, i::Int)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y]
|
||||
# end
|
||||
#
|
||||
# function setxy!{X,Y}(plt::Plot{PGFPlotsBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y] = xy
|
||||
# plt
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -80,16 +297,80 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _pgfplots_get_axis_kwargs(d)
|
||||
axisargs = KW()
|
||||
for arg in (:xlabel, :ylabel, :zlabel, :title)
|
||||
axisargs[arg] = d[arg]
|
||||
end
|
||||
axisargs[:style] = []
|
||||
d[:xflip] == true && push!(axisargs[:style], "x dir=reverse")
|
||||
d[:yflip] == true && push!(axisargs[:style], "y dir=reverse")
|
||||
if d[:xscale] in (:log, :log2, :ln, :log10)
|
||||
axisargs[:xmode] = "log"
|
||||
if d[:xscale] == :log2
|
||||
push!(axisargs[:style], "log basis x=2")
|
||||
elseif d[:xscale] in (:log, :log10)
|
||||
push!(axisargs[:style], "log basis x=10")
|
||||
end
|
||||
end
|
||||
if d[:yscale] in (:log, :log2, :ln, :log10)
|
||||
axisargs[:ymode] = "log"
|
||||
if d[:yscale] == :log2
|
||||
push!(axisargs[:style], "log basis y=2")
|
||||
elseif d[:yscale] in (:log, :log10)
|
||||
push!(axisargs[:style], "log basis x=10")
|
||||
end
|
||||
end
|
||||
if d[:zscale] in (:log, :log2, :ln, :log10)
|
||||
axisargs[:zmode] = "log"
|
||||
if d[:zscale] == :log2
|
||||
push!(axisargs[:style], "log basis z=2")
|
||||
elseif d[:zscale] in (:log, :log10)
|
||||
push!(axisargs[:style], "log basis x=10")
|
||||
end
|
||||
end
|
||||
|
||||
# Control background color
|
||||
push!(axisargs[:style], "axis background/.style={fill=$(_pgfplots_get_color(d, :background_color))}")
|
||||
# Control x/y-limits
|
||||
if d[:xlims] !== :auto
|
||||
axisargs[:xmin] = d[:xlims][1]
|
||||
axisargs[:xmax] = d[:xlims][2]
|
||||
end
|
||||
if d[:ylims] !== :auto
|
||||
axisargs[:ymin] = d[:ylims][1]
|
||||
axisargs[:ymax] = d[:ylims][2]
|
||||
end
|
||||
|
||||
d[:grid] == true && push!(axisargs[:style], "grid = major")
|
||||
|
||||
if d[:aspect_ratio] == :equal || d[:aspect_ratio] == 1
|
||||
axisargs[:axisEqual] = "true"
|
||||
end
|
||||
|
||||
if ((d[:legend] != :none) || (d[:legend] != :best)) && (d[:legend] in keys(_pgfplots_legend_pos))
|
||||
axisargs[:legendPos] = _pgfplots_legend_pos[d[:legend]]
|
||||
end
|
||||
axisargs[:style] = join(axisargs[:style], ',')
|
||||
axisargs
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
################# This is the important method to implement!!! #################
|
||||
function _make_pgf_plot(plt::Plot{PGFPlotsBackend})
|
||||
# TODO: convert plt.plotargs and plt.seriesargs into PGFPlots calls
|
||||
# TODO: return the PGFPlots object
|
||||
os = Any[]
|
||||
# We need to send the :legend KW to the axis
|
||||
for plt_series in plt.seriesargs
|
||||
plt_series[:legend] = plt.plotargs[:legend]
|
||||
push!(os, _pgfplots_axis(plt_series))
|
||||
end
|
||||
axisargs =_pgfplots_get_axis_kwargs(plt.plotargs)
|
||||
w, h = map(px2mm, plt.plotargs[:size])
|
||||
plt.o = PGFPlots.Axis([os...]; width = "$w mm", height = "$h mm", axisargs...)
|
||||
end
|
||||
|
||||
function Base.writemime(io::IO, mime::MIME"image/png", plt::AbstractPlot{PGFPlotsBackend})
|
||||
function Base.writemime(io::IO, mime::MIME"image/svg+xml", plt::AbstractPlot{PGFPlotsBackend})
|
||||
plt.o = _make_pgf_plot(plt)
|
||||
writemime(io, mime, plt.o)
|
||||
end
|
||||
|
||||
+177
-79
@@ -1,6 +1,53 @@
|
||||
|
||||
# https://plot.ly/javascript/getting-started
|
||||
|
||||
supportedArgs(::PlotlyBackend) = [
|
||||
:annotation,
|
||||
:background_color, :foreground_color, :color_palette,
|
||||
# :background_color_legend, :background_color_inside, :background_color_outside,
|
||||
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
:group,
|
||||
:label,
|
||||
:linetype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
:n, :nc, :nr, :layout,
|
||||
# :smooth,
|
||||
:title, :windowtitle, :show, :size,
|
||||
:x, :xlabel, :xlims, :xticks, :xscale, :xflip, :xrotation,
|
||||
:y, :ylabel, :ylims, :yticks, :yscale, :yflip, :yrotation,
|
||||
:z, :zlabel, :zlims, :zticks, :zscale, :zflip, :zrotation,
|
||||
:z,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:xerror, :yerror,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
# :overwrite_figure,
|
||||
:polar,
|
||||
# :normalize, :weights, :contours, :aspect_ratio
|
||||
]
|
||||
|
||||
supportedAxes(::PlotlyBackend) = [:auto, :left]
|
||||
supportedTypes(::PlotlyBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
|
||||
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
|
||||
supportedStyles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supportedMarkers(::PlotlyBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
|
||||
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::PlotlyBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
subplotSupported(::PlotlyBackend) = true
|
||||
stringsSupported(::PlotlyBackend) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::PlotlyBackend; kw...)
|
||||
@eval begin
|
||||
import JSON
|
||||
@@ -37,16 +84,14 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::PlotlyBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::PlotlyBackend, d::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(nothing, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::PlotlyBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::PlotlyBackend, plt::Plot, d::KW)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
@@ -77,16 +122,16 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{PlotlyBackend}, i::Int)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y]
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{PlotlyBackend}, xy::Tuple, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y] = xy
|
||||
plt
|
||||
end
|
||||
# function getxy(plt::Plot{PlotlyBackend}, i::Int)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y]
|
||||
# end
|
||||
#
|
||||
# function setxy!{X,Y}(plt::Plot{PlotlyBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y] = xy
|
||||
# plt
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -105,17 +150,6 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# TODO:
|
||||
# _plotDefaults[:yrightlabel] = ""
|
||||
# _plotDefaults[:xlims] = :auto
|
||||
# _plotDefaults[:ylims] = :auto
|
||||
# _plotDefaults[:xticks] = :auto
|
||||
# _plotDefaults[:yticks] = :auto
|
||||
# _plotDefaults[:xscale] = :identity
|
||||
# _plotDefaults[:yscale] = :identity
|
||||
# _plotDefaults[:xflip] = false
|
||||
# _plotDefaults[:yflip] = false
|
||||
|
||||
function plotlyfont(font::Font, color = font.color)
|
||||
KW(
|
||||
:family => font.family,
|
||||
@@ -144,6 +178,29 @@ function get_annotation_dict(x, y, ptxt::PlotText)
|
||||
))
|
||||
end
|
||||
|
||||
# function get_annotation_dict_for_arrow(d::KW, xyprev::Tuple, xy::Tuple, a::Arrow)
|
||||
# xdiff = xyprev[1] - xy[1]
|
||||
# ydiff = xyprev[2] - xy[2]
|
||||
# dist = sqrt(xdiff^2 + ydiff^2)
|
||||
# KW(
|
||||
# :showarrow => true,
|
||||
# :x => xy[1],
|
||||
# :y => xy[2],
|
||||
# # :ax => xyprev[1] - xy[1],
|
||||
# # :ay => xy[2] - xyprev[2],
|
||||
# # :ax => 0,
|
||||
# # :ay => -40,
|
||||
# :ax => 10xdiff / dist,
|
||||
# :ay => -10ydiff / dist,
|
||||
# :arrowcolor => webcolor(d[:linecolor], d[:linealpha]),
|
||||
# :xref => "x",
|
||||
# :yref => "y",
|
||||
# :arrowsize => 10a.headwidth,
|
||||
# # :arrowwidth => a.headlength,
|
||||
# :arrowwidth => 0.1,
|
||||
# )
|
||||
# end
|
||||
|
||||
function plotlyscale(scale::Symbol)
|
||||
if scale == :log10
|
||||
"log"
|
||||
@@ -154,37 +211,43 @@ end
|
||||
|
||||
use_axis_field(ticks) = !(ticks in (nothing, :none))
|
||||
|
||||
tickssym(isx::Bool) = symbol((isx ? "x" : "y") * "ticks")
|
||||
limssym(isx::Bool) = symbol((isx ? "x" : "y") * "lims")
|
||||
flipsym(isx::Bool) = symbol((isx ? "x" : "y") * "flip")
|
||||
scalesym(isx::Bool) = symbol((isx ? "x" : "y") * "scale")
|
||||
labelsym(isx::Bool) = symbol((isx ? "x" : "y") * "label")
|
||||
tickssym(letter) = symbol(letter * "ticks")
|
||||
limssym(letter) = symbol(letter * "lims")
|
||||
flipsym(letter) = symbol(letter * "flip")
|
||||
scalesym(letter) = symbol(letter * "scale")
|
||||
labelsym(letter) = symbol(letter * "label")
|
||||
rotationsym(letter) = symbol(letter * "rotation")
|
||||
|
||||
function plotlyaxis(d::KW, isx::Bool)
|
||||
function plotlyaxis(d::KW, letter)
|
||||
ax = KW(
|
||||
:title => d[labelsym(isx)],
|
||||
:title => d[labelsym(letter)],
|
||||
:showgrid => d[:grid],
|
||||
:zeroline => false,
|
||||
)
|
||||
|
||||
fgcolor = webcolor(d[:foreground_color])
|
||||
tsym = tickssym(isx)
|
||||
tsym = tickssym(letter)
|
||||
|
||||
rot = d[rotationsym(letter)]
|
||||
if rot != 0
|
||||
ax[:tickangle] = rot
|
||||
end
|
||||
|
||||
if use_axis_field(d[tsym])
|
||||
ax[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
|
||||
ax[:type] = plotlyscale(d[scalesym(isx)])
|
||||
ax[:type] = plotlyscale(d[scalesym(letter)])
|
||||
ax[:tickfont] = plotlyfont(d[:tickfont], fgcolor)
|
||||
ax[:tickcolor] = fgcolor
|
||||
ax[:linecolor] = fgcolor
|
||||
|
||||
# xlims
|
||||
lims = d[limssym(isx)]
|
||||
lims = d[limssym(letter)]
|
||||
if lims != :auto && limsType(lims) == :limits
|
||||
ax[:range] = lims
|
||||
end
|
||||
|
||||
# xflip
|
||||
if d[flipsym(isx)]
|
||||
if d[flipsym(letter)]
|
||||
ax[:autorange] = "reversed"
|
||||
end
|
||||
|
||||
@@ -212,51 +275,79 @@ end
|
||||
|
||||
# function get_plot_json(plt::Plot{PlotlyBackend})
|
||||
# d = plt.plotargs
|
||||
function plotly_layout(d::KW)
|
||||
d_out = KW()
|
||||
function plotly_layout(d::KW, seriesargs::AVec{KW})
|
||||
d_out = KW()
|
||||
|
||||
bgcolor = webcolor(d[:background_color])
|
||||
fgcolor = webcolor(d[:foreground_color])
|
||||
d_out[:width], d_out[:height] = d[:size]
|
||||
|
||||
# set the fields for the plot
|
||||
d_out[:title] = d[:title]
|
||||
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
|
||||
d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
|
||||
d_out[:plot_bgcolor] = bgcolor
|
||||
d_out[:paper_bgcolor] = bgcolor
|
||||
bgcolor = webcolor(d[:background_color])
|
||||
fgcolor = webcolor(d[:foreground_color])
|
||||
|
||||
# TODO: x/y axis tick values/labels
|
||||
d_out[:xaxis] = plotlyaxis(d, true)
|
||||
d_out[:yaxis] = plotlyaxis(d, false)
|
||||
# set the fields for the plot
|
||||
d_out[:title] = d[:title]
|
||||
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
|
||||
d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
|
||||
d_out[:plot_bgcolor] = bgcolor
|
||||
d_out[:paper_bgcolor] = bgcolor
|
||||
|
||||
# legend
|
||||
d_out[:showlegend] = d[:legend] != :none
|
||||
if d[:legend] != :none
|
||||
d_out[:legend] = KW(
|
||||
:bgcolor => bgcolor,
|
||||
:bordercolor => fgcolor,
|
||||
:font => plotlyfont(d[:legendfont]),
|
||||
)
|
||||
end
|
||||
# TODO: x/y axis tick values/labels
|
||||
if any(is3d, seriesargs)
|
||||
d_out[:scene] = KW(
|
||||
:xaxis => plotlyaxis(d, "x"),
|
||||
:yaxis => plotlyaxis(d, "y"),
|
||||
:xzxis => plotlyaxis(d, "z"),
|
||||
)
|
||||
else
|
||||
d_out[:xaxis] = plotlyaxis(d, "x")
|
||||
d_out[:yaxis] = plotlyaxis(d, "y")
|
||||
end
|
||||
|
||||
# annotations
|
||||
anns = get(d, :annotation_list, [])
|
||||
if !isempty(anns)
|
||||
d_out[:annotations] = [get_annotation_dict(ann...) for ann in anns]
|
||||
end
|
||||
# legend
|
||||
d_out[:showlegend] = d[:legend] != :none
|
||||
if d[:legend] != :none
|
||||
d_out[:legend] = KW(
|
||||
:bgcolor => bgcolor,
|
||||
:bordercolor => fgcolor,
|
||||
:font => plotlyfont(d[:legendfont]),
|
||||
)
|
||||
end
|
||||
|
||||
d_out
|
||||
# annotations
|
||||
anns = get(d, :annotation_list, [])
|
||||
d_out[:annotations] = if isempty(anns)
|
||||
KW[]
|
||||
else
|
||||
KW[get_annotation_dict(ann...) for ann in anns]
|
||||
end
|
||||
|
||||
# # arrows
|
||||
# for sargs in seriesargs
|
||||
# a = sargs[:arrow]
|
||||
# if sargs[:linetype] in (:path, :line) && typeof(a) <: Arrow
|
||||
# add_arrows(sargs[:x], sargs[:y]) do xyprev, xy
|
||||
# push!(d_out[:annotations], get_annotation_dict_for_arrow(sargs, xyprev, xy, a))
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# dumpdict(d_out,"",true)
|
||||
# @show d_out[:annotations]
|
||||
|
||||
if get(d, :polar, false)
|
||||
d_out[:direction] = "counterclockwise"
|
||||
end
|
||||
|
||||
d_out
|
||||
end
|
||||
|
||||
function get_plot_json(plt::Plot{PlotlyBackend})
|
||||
JSON.json(plotly_layout(plt.plotargs))
|
||||
JSON.json(plotly_layout(plt.plotargs, plt.seriesargs))
|
||||
end
|
||||
|
||||
|
||||
function plotly_colorscale(grad::ColorGradient, alpha = nothing)
|
||||
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
|
||||
end
|
||||
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(ColorGradient(:bluesreds), alpha)
|
||||
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(default_gradient(), alpha)
|
||||
|
||||
const _plotly_markers = KW(
|
||||
:rect => "square",
|
||||
@@ -269,7 +360,8 @@ const _plotly_markers = KW(
|
||||
)
|
||||
|
||||
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
|
||||
function plotly_series(d::KW; plot_index = nothing)
|
||||
function plotly_series(d::KW, plotargs::KW; plot_index = nothing)
|
||||
# dumpdict(d,"series",true)
|
||||
d_out = KW()
|
||||
|
||||
x, y = collect(d[:x]), collect(d[:y])
|
||||
@@ -303,19 +395,19 @@ function plotly_series(d::KW; plot_index = nothing)
|
||||
elseif lt == :hist2d
|
||||
d_out[:type] = "histogram2d"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
if isa(d[:nbins], Tuple)
|
||||
xbins, ybins = d[:nbins]
|
||||
if isa(d[:bins], Tuple)
|
||||
xbins, ybins = d[:bins]
|
||||
else
|
||||
xbins = ybins = d[:nbins]
|
||||
xbins = ybins = d[:bins]
|
||||
end
|
||||
d_out[:nbinsx] = xbins
|
||||
d_out[:nbinsy] = ybins
|
||||
|
||||
elseif lt in (:hist, :density)
|
||||
d_out[:type] = "histogram"
|
||||
isvert = d[:orientation] in (:vertical, :v, :vert)
|
||||
isvert = isvertical(d)
|
||||
d_out[isvert ? :x : :y] = y
|
||||
d_out[isvert ? :nbinsx : :nbinsy] = d[:nbins]
|
||||
d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
|
||||
if lt == :density
|
||||
d_out[:histnorm] = "probability density"
|
||||
end
|
||||
@@ -376,13 +468,13 @@ function plotly_series(d::KW; plot_index = nothing)
|
||||
)
|
||||
|
||||
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
|
||||
if d[:zcolor] != nothing
|
||||
# d_out[:marker][:color] = d[:zcolor]
|
||||
if d[:marker_z] != nothing
|
||||
# d_out[:marker][:color] = d[:marker_z]
|
||||
# d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor], d[:markeralpha])
|
||||
# d_out[:showscale] = true
|
||||
grad = ColorGradient(d[:markercolor], alpha=d[:markeralpha])
|
||||
zmin, zmax = extrema(d[:zcolor])
|
||||
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:zcolor]]
|
||||
zmin, zmax = extrema(d[:marker_z])
|
||||
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:marker_z]]
|
||||
end
|
||||
|
||||
end
|
||||
@@ -404,6 +496,12 @@ function plotly_series(d::KW; plot_index = nothing)
|
||||
)
|
||||
end
|
||||
|
||||
# convert polar plots x/y to theta/radius
|
||||
if get(plotargs, :polar, false)
|
||||
d_out[:t] = rad2deg(pop!(d_out, :x))
|
||||
d_out[:r] = pop!(d_out, :y)
|
||||
end
|
||||
|
||||
# # for subplots, we need to add the xaxis/yaxis fields
|
||||
# if plot_index != nothing
|
||||
# d_out[:xaxis] = "x$(plot_index)"
|
||||
@@ -415,14 +513,14 @@ end
|
||||
|
||||
# get a list of dictionaries, each representing the series params
|
||||
function get_series_json(plt::Plot{PlotlyBackend})
|
||||
JSON.json(map(plotly_series, plt.seriesargs))
|
||||
JSON.json(map(d -> plotly_series(d, plt.plotargs), plt.seriesargs))
|
||||
end
|
||||
|
||||
function get_series_json(subplt::Subplot{PlotlyBackend})
|
||||
ds = KW[]
|
||||
for (i,plt) in enumerate(subplt.plts)
|
||||
for d in plt.seriesargs
|
||||
push!(ds, plotly_series(d, plot_index = i))
|
||||
push!(ds, plotly_series(d, plt.plotargs, plot_index = i))
|
||||
end
|
||||
end
|
||||
JSON.json(ds)
|
||||
@@ -487,7 +585,7 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend})
|
||||
warn("todo: png")
|
||||
writemime_png_from_html(io, plt)
|
||||
end
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend})
|
||||
|
||||
+99
-22
@@ -1,6 +1,82 @@
|
||||
|
||||
# https://github.com/spencerlyon2/PlotlyJS.jl
|
||||
|
||||
supportedArgs(::PlotlyJSBackend) = [
|
||||
:annotation,
|
||||
# :axis,
|
||||
:background_color,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:foreground_color,
|
||||
:group,
|
||||
:label,
|
||||
:layout,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokestyle,
|
||||
:n,
|
||||
:bins,
|
||||
:nc,
|
||||
:nr,
|
||||
# :pos,
|
||||
# :smooth,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
:yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
:xflip,
|
||||
:yflip,
|
||||
:z,
|
||||
:marker_z,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:levels,
|
||||
:xerror,
|
||||
:yerror,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
:polar,
|
||||
]
|
||||
supportedAxes(::PlotlyJSBackend) = [:auto, :left]
|
||||
supportedTypes(::PlotlyJSBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
|
||||
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
|
||||
supportedStyles(::PlotlyJSBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supportedMarkers(::PlotlyJSBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
|
||||
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::PlotlyJSBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
subplotSupported(::PlotlyJSBackend) = true
|
||||
stringsSupported(::PlotlyJSBackend) = true
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::PlotlyJSBackend; kw...)
|
||||
@eval begin
|
||||
import PlotlyJS
|
||||
@@ -8,6 +84,7 @@ function _initialize_backend(::PlotlyJSBackend; kw...)
|
||||
end
|
||||
|
||||
for (mime, fmt) in PlotlyJS._mimeformats
|
||||
# mime == "image/png" && continue # don't use plotlyjs's writemime for png
|
||||
@eval Base.writemime(io::IO, m::MIME{symbol($mime)}, p::Plot{PlotlyJSBackend}) = writemime(io, m, p.o)
|
||||
end
|
||||
|
||||
@@ -19,8 +96,7 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::PlotlyJSBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::PlotlyJSBackend, d::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
|
||||
# o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(),
|
||||
@@ -33,18 +109,14 @@ function _create_plot(pkg::PlotlyJSBackend; kw...)
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::PlotlyJSBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::PlotlyJSBackend, plt::Plot, d::KW)
|
||||
syncplot = plt.o
|
||||
|
||||
# dumpdict(d, "addseries", true)
|
||||
|
||||
# add to the data array
|
||||
pdict = plotly_series(d)
|
||||
pdict = plotly_series(d, plt.plotargs)
|
||||
typ = pop!(pdict, :type)
|
||||
gt = PlotlyJS.GenericTrace(typ; pdict...)
|
||||
PlotlyJS.addtraces!(syncplot, gt)
|
||||
# PlotlyJS.addtraces!(syncplot.plot, gt)
|
||||
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
@@ -69,12 +141,10 @@ end
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot(plt::Plot{PlotlyJSBackend}, d::KW)
|
||||
pdict = plotly_layout(d)
|
||||
# dumpdict(pdict, "pdict updateplot", true)
|
||||
pdict = plotly_layout(plt.plotargs, plt.seriesargs)
|
||||
syncplot = plt.o
|
||||
w,h = d[:size]
|
||||
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
|
||||
# PlotlyJS.relayout!(syncplot.plot, pdict, width = w, height = h)
|
||||
end
|
||||
|
||||
|
||||
@@ -85,17 +155,19 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{PlotlyJSBackend}, i::Int)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y]
|
||||
end
|
||||
# function getxy(plt::Plot{PlotlyJSBackend}, i::Int)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y]
|
||||
# end
|
||||
|
||||
function Base.setindex!(plt::Plot{PlotlyJSBackend}, xy::Tuple, i::Integer)
|
||||
function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y] = xy
|
||||
ispolar = get(plt.plotargs, :polar, false)
|
||||
xsym = ispolar ? :t : :x
|
||||
ysym = ispolar ? :r : :y
|
||||
d[xsym], d[ysym] = xy
|
||||
# TODO: this is likely ineffecient... we should make a call that ONLY changes the plot data
|
||||
# PlotlyJS.restyle!(plt.o, i, plotly_series(d))
|
||||
PlotlyJS.restyle!(plt.o, i, KW(:x=>(d[:x],), :y=>(d[:y],)))
|
||||
PlotlyJS.restyle!(plt.o, i, KW(xsym=>(d[xsym],), ysym=>(d[ysym],)))
|
||||
plt
|
||||
end
|
||||
|
||||
@@ -116,9 +188,14 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.writemime(io::IO, m::MIME"text/html", plt::AbstractPlot{PlotlyJSBackend})
|
||||
Base.writemime(io, m, plt.o)
|
||||
end
|
||||
# function Base.writemime(io::IO, m::MIME"text/html", plt::AbstractPlot{PlotlyJSBackend})
|
||||
# Base.writemime(io, m, plt.o)
|
||||
# end
|
||||
|
||||
# function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyJSBackend})
|
||||
# println("here!")
|
||||
# writemime_png_from_html(io, plt)
|
||||
# end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend})
|
||||
display(plt.o)
|
||||
|
||||
+954
-702
File diff suppressed because it is too large
Load Diff
+57
-7
@@ -1,6 +1,57 @@
|
||||
|
||||
# https://github.com/tbreloff/Qwt.jl
|
||||
|
||||
|
||||
supportedArgs(::QwtBackend) = [
|
||||
:annotation,
|
||||
:axis,
|
||||
:background_color,
|
||||
:linecolor,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:foreground_color,
|
||||
:group,
|
||||
:label,
|
||||
:layout,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:n,
|
||||
:bins,
|
||||
:nc,
|
||||
:nr,
|
||||
:pos,
|
||||
:smooth,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
:yrightlabel,
|
||||
:yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
]
|
||||
supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
|
||||
supportedMarkers(::QwtBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
|
||||
supportedScales(::QwtBackend) = [:identity, :log10]
|
||||
subplotSupported(::QwtBackend) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::QwtBackend; kw...)
|
||||
@eval begin
|
||||
warn("Qwt is no longer supported... many features will likely be broken.")
|
||||
@@ -12,7 +63,7 @@ end
|
||||
# -------------------------------
|
||||
|
||||
@compat const _qwtAliases = KW(
|
||||
:nbins => :heatmap_n,
|
||||
:bins => :heatmap_n,
|
||||
:fillrange => :fillto,
|
||||
:linewidth => :width,
|
||||
:markershape => :marker,
|
||||
@@ -77,8 +128,7 @@ function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
|
||||
d
|
||||
end
|
||||
|
||||
function _create_plot(pkg::QwtBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::QwtBackend, d::KW)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot")
|
||||
o = Qwt.plot(zeros(0,0); d..., show=false)
|
||||
@@ -86,8 +136,8 @@ function _create_plot(pkg::QwtBackend; kw...)
|
||||
plt
|
||||
end
|
||||
|
||||
function _add_series(::QwtBackend, plt::Plot; kw...)
|
||||
d = adjustQwtKeywords(plt, false; kw...)
|
||||
function _add_series(::QwtBackend, plt::Plot, d::KW)
|
||||
d = adjustQwtKeywords(plt, false; d...)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot!")
|
||||
Qwt.oplot(plt.o; d...)
|
||||
@@ -199,12 +249,12 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{QwtBackend}, i::Int)
|
||||
function getxy(plt::Plot{QwtBackend}, i::Int)
|
||||
series = plt.o.lines[i]
|
||||
series.x, series.y
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{QwtBackend}, xy::Tuple, i::Integer)
|
||||
function setxy!{X,Y}(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
series = plt.o.lines[i]
|
||||
series.x, series.y = xy
|
||||
plt
|
||||
|
||||
+688
-763
File diff suppressed because it is too large
Load Diff
+10
-12
@@ -14,16 +14,14 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::[PkgName]AbstractBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::[PkgName]AbstractBackend, d::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(nothing, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::[PkgName]AbstractBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::[PkgName]AbstractBackend, plt::Plot, d::KW)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
@@ -51,14 +49,14 @@ end
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function Base.getindex(plt::Plot{[PkgName]AbstractBackend}, i::Int)
|
||||
# TODO: return a tuple of (x, y) vectors
|
||||
end
|
||||
|
||||
function Base.setindex!(plt::Plot{[PkgName]AbstractBackend}, xy::Tuple, i::Integer)
|
||||
# TODO: set the plot data from the (x,y) tuple
|
||||
plt
|
||||
end
|
||||
# function getxy(plt::Plot{[PkgName]AbstractBackend}, i::Int)
|
||||
# # TODO: return a tuple of (x, y) vectors
|
||||
# end
|
||||
#
|
||||
# function setxy!{X,Y}(plt::Plot{[PkgName]AbstractBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# # TODO: set the plot data from the (x,y) tuple
|
||||
# plt
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,6 +1,68 @@
|
||||
|
||||
# https://github.com/Evizero/UnicodePlots.jl
|
||||
|
||||
supportedArgs(::UnicodePlotsBackend) = [
|
||||
# :annotation,
|
||||
# :args,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
# :linecolor,
|
||||
# :fill,
|
||||
# :foreground_color,
|
||||
:group,
|
||||
# :heatmap_c,
|
||||
# :kwargs,
|
||||
:label,
|
||||
# :layout,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
# :linewidth,
|
||||
:markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# :n,
|
||||
:bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
# :reg,
|
||||
# :ribbon,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
]
|
||||
supportedAxes(::UnicodePlotsBackend) = [:auto, :left]
|
||||
supportedTypes(::UnicodePlotsBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
|
||||
supportedStyles(::UnicodePlotsBackend) = [:auto, :solid]
|
||||
supportedMarkers(::UnicodePlotsBackend) = [:none, :auto, :ellipse]
|
||||
supportedScales(::UnicodePlotsBackend) = [:identity]
|
||||
subplotSupported(::UnicodePlotsBackend) = true
|
||||
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::UnicodePlotsBackend; kw...)
|
||||
@eval begin
|
||||
import UnicodePlots
|
||||
@@ -119,8 +181,8 @@ end
|
||||
# -------------------------------
|
||||
|
||||
|
||||
function _create_plot(pkg::UnicodePlotsBackend; kw...)
|
||||
plt = Plot(nothing, pkg, 0, KW(kw), KW[])
|
||||
function _create_plot(pkg::UnicodePlotsBackend, d::KW)
|
||||
plt = Plot(nothing, pkg, 0, d, KW[])
|
||||
|
||||
# do we want to give a new default size?
|
||||
if !haskey(plt.plotargs, :size) || plt.plotargs[:size] == _plotDefaults[:size]
|
||||
@@ -130,8 +192,7 @@ function _create_plot(pkg::UnicodePlotsBackend; kw...)
|
||||
plt
|
||||
end
|
||||
|
||||
function _add_series(::UnicodePlotsBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
function _add_series(::UnicodePlotsBackend, plt::Plot, d::KW)
|
||||
if d[:linetype] in (:sticks, :bar)
|
||||
d = barHack(; d...)
|
||||
elseif d[:linetype] == :hist
|
||||
|
||||
+25
-3
@@ -26,13 +26,35 @@ function open_browser_window(filename::AbstractString)
|
||||
warn("Unknown OS... cannot open browser window.")
|
||||
end
|
||||
|
||||
function standalone_html_window(plt::AbstractPlot; kw...)
|
||||
html = standalone_html(plt; kw...)
|
||||
# println(html)
|
||||
function write_temp_html(plt::AbstractPlot)
|
||||
html = standalone_html(plt; title = plt.plotargs[:title])
|
||||
filename = string(tempname(), ".html")
|
||||
output = open(filename, "w")
|
||||
write(output, html)
|
||||
close(output)
|
||||
filename
|
||||
end
|
||||
|
||||
function standalone_html_window(plt::AbstractPlot)
|
||||
filename = write_temp_html(plt)
|
||||
open_browser_window(filename)
|
||||
end
|
||||
|
||||
# uses wkhtmltopdf/wkhtmltoimage: http://wkhtmltopdf.org/downloads.html
|
||||
function html_to_png(html_fn, png_fn, w, h)
|
||||
run(`wkhtmltoimage -f png -q --width $w --height $h --disable-smart-width $html_fn $png_fn`)
|
||||
end
|
||||
|
||||
function writemime_png_from_html(io::IO, plt::AbstractPlot)
|
||||
# write html to a temporary file
|
||||
html_fn = write_temp_html(plt)
|
||||
|
||||
# convert that html file to a temporary png file using wkhtmltoimage
|
||||
png_fn = tempname() * ".png"
|
||||
w, h = plt.plotargs[:size]
|
||||
html_to_png(html_fn, png_fn, w, h)
|
||||
|
||||
# now read that file data into io
|
||||
pngdata = readall(png_fn)
|
||||
write(io, pngdata)
|
||||
end
|
||||
|
||||
+67
-7
@@ -3,6 +3,69 @@
|
||||
|
||||
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
|
||||
|
||||
supportedArgs(::WinstonBackend) = [
|
||||
:annotation,
|
||||
# :args,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
:linecolor,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
# :foreground_color,
|
||||
:group,
|
||||
# :heatmap_c,
|
||||
# :kwargs,
|
||||
:label,
|
||||
# :layout,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
# :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# :n,
|
||||
:bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
:smooth,
|
||||
# :ribbon,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
]
|
||||
supportedAxes(::WinstonBackend) = [:auto, :left]
|
||||
supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
|
||||
supportedStyles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supportedMarkers(::WinstonBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
||||
supportedScales(::WinstonBackend) = [:identity, :log10]
|
||||
subplotSupported(::WinstonBackend) = false
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _initialize_backend(::WinstonBackend; kw...)
|
||||
@eval begin
|
||||
# ENV["WINSTON_OUTPUT"] = "gtk"
|
||||
@@ -40,8 +103,7 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _create_plot(pkg::WinstonBackend; kw...)
|
||||
d = KW(kw)
|
||||
function _create_plot(pkg::WinstonBackend, d::KW)
|
||||
wplt = Winston.FramedPlot(title = d[:title], xlabel = d[:xlabel], ylabel = d[:ylabel])
|
||||
|
||||
Plot(wplt, pkg, 0, d, KW[])
|
||||
@@ -64,9 +126,7 @@ function getWinstonItems(plt::Plot)
|
||||
window, canvas, wplt
|
||||
end
|
||||
|
||||
function _add_series(::WinstonBackend, plt::Plot; kw...)
|
||||
d = KW(kw)
|
||||
|
||||
function _add_series(::WinstonBackend, plt::Plot, d::KW)
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
|
||||
# until we call it normally, do the hack
|
||||
@@ -130,8 +190,8 @@ function _add_series(::WinstonBackend, plt::Plot; kw...)
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:linetype] == :hist
|
||||
hst = hist(d[:y], d[:nbins])
|
||||
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :nbins)...))
|
||||
hst = hist(d[:y], d[:bins])
|
||||
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
|
||||
|
||||
# elseif d[:linetype] == :bar
|
||||
# # fn = Winston.XXX
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+52
-23
@@ -52,7 +52,7 @@ const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", color
|
||||
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 = KW(
|
||||
const _gradients = KW(
|
||||
:blues => [colorant"lightblue", colorant"darkblue"],
|
||||
:reds => [colorant"lightpink", colorant"darkred"],
|
||||
:greens => [colorant"lightgreen", colorant"darkgreen"],
|
||||
@@ -67,6 +67,13 @@ const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcya
|
||||
:lighttest => map(c -> lighten(c, 0.3), _testColors),
|
||||
)
|
||||
|
||||
function register_gradient_colors{C<:Colorant}(name::Symbol, colors::AVec{C})
|
||||
_gradients[name] = colors
|
||||
end
|
||||
|
||||
include("color_gradients.jl")
|
||||
|
||||
default_gradient() = ColorGradient(:inferno)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
@@ -107,9 +114,9 @@ end
|
||||
# ColorGradient(map(convertColor, cs), vals; kw...)
|
||||
# end
|
||||
|
||||
# function ColorGradient(grad::ColorGradient; alpha = nothing)
|
||||
# ColorGradient(convertColor(grad.colors, alpha), grad.values)
|
||||
# end
|
||||
function ColorGradient(grad::ColorGradient; alpha = nothing)
|
||||
ColorGradient(convertColor(grad.colors, alpha), grad.values)
|
||||
end
|
||||
|
||||
getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]
|
||||
|
||||
@@ -354,31 +361,53 @@ webcolor(c, α) = webcolor(convertColor(getColor(c), α))
|
||||
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
|
||||
|
||||
function handlePlotColors(::AbstractBackend, d::KW)
|
||||
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())")
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100)
|
||||
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
|
||||
# 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
|
||||
|
||||
# bg/fg color
|
||||
d[:background_color] = colorscheme(bgcolor)
|
||||
d[:foreground_color] = colorscheme(fgcolor)
|
||||
|
||||
# update sub-background colors
|
||||
for bgtype in ("legend", "inside", "outside")
|
||||
bgsym = symbol("background_color_" * bgtype)
|
||||
if d[bgsym] == :match
|
||||
d[bgsym] = d[:background_color]
|
||||
elseif d[bgsym] == nothing
|
||||
d[bgsym] = colorscheme(RGBA(0,0,0,0))
|
||||
end
|
||||
end
|
||||
|
||||
# update sub-foreground colors
|
||||
for fgtype in ("legend", "grid", "axis", "text", "border", "guide")
|
||||
fgsym = symbol("foreground_color_" * fgtype)
|
||||
if d[fgsym] == :match
|
||||
d[fgsym] = d[:foreground_color]
|
||||
elseif d[fgsym] == nothing
|
||||
d[fgsym] = colorscheme(RGBA(0,0,0,0))
|
||||
end
|
||||
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
|
||||
|
||||
+166
-24
@@ -1,47 +1,49 @@
|
||||
|
||||
export
|
||||
P2,
|
||||
P3,
|
||||
BezierCurve,
|
||||
curve_points,
|
||||
directed_curve
|
||||
|
||||
typealias P2 FixedSizeArrays.Vec{2,Float64}
|
||||
typealias P3 FixedSizeArrays.Vec{3,Float64}
|
||||
|
||||
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
|
||||
nanpush!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); append!(a, b))
|
||||
compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : angle)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
|
||||
immutable Shape
|
||||
vertices::AVec
|
||||
# vertices::AVec
|
||||
x::AVec
|
||||
y::AVec
|
||||
end
|
||||
|
||||
Shape(x, y) = Shape(collect(zip(x, y)))
|
||||
# Shape(x, y) = Shape(collect(zip(x, y)))
|
||||
Shape(verts::AVec) = Shape(unzip(verts)...)
|
||||
|
||||
get_xs(shape::Shape) = Float64[v[1] for v in shape.vertices]
|
||||
get_ys(shape::Shape) = Float64[v[2] for v in shape.vertices]
|
||||
# get_xs(shape::Shape) = Float64[v[1] for v in shape.vertices]
|
||||
# get_ys(shape::Shape) = Float64[v[2] for v in shape.vertices]
|
||||
get_xs(shape::Shape) = shape.x
|
||||
get_ys(shape::Shape) = shape.y
|
||||
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
|
||||
|
||||
function scale(shape::Shape, x, y=x)
|
||||
sx, sy = shape_coords(shape)
|
||||
Shape(sx .* x, sy .* y)
|
||||
end
|
||||
|
||||
function translate(shape::Shape, x, y=x)
|
||||
sx, sy = shape_coords(shape)
|
||||
Shape(sx .+ x, sy .+ y)
|
||||
end
|
||||
|
||||
function shape_coords(shape::Shape)
|
||||
unzip(shape.vertices)
|
||||
# unzip(shape.vertices)
|
||||
shape.x, shape.y
|
||||
end
|
||||
|
||||
function shape_coords(shapes::AVec{Shape})
|
||||
length(shapes) == 0 && return zeros(0), zeros(0)
|
||||
xs = map(get_xs, shapes)
|
||||
ys = map(get_ys, shapes)
|
||||
x, y = unzip(shapes[1].vertices)
|
||||
# x, y = shapes[1].x, shapes[1].y #unzip(shapes[1].vertices)
|
||||
x, y = map(copy, shape_coords(shapes[1]))
|
||||
for shape in shapes[2:end]
|
||||
tmpx, tmpy = unzip(shape.vertices)
|
||||
x = vcat(x, NaN, tmpx)
|
||||
y = vcat(y, NaN, tmpy)
|
||||
# tmpx, tmpy = unzip(shape.vertices)
|
||||
nanappend!(x, shape.x)
|
||||
nanappend!(y, shape.y)
|
||||
# x = vcat(x, NaN, tmpx)
|
||||
# y = vcat(y, NaN, tmpy)
|
||||
end
|
||||
x, y
|
||||
end
|
||||
@@ -96,6 +98,14 @@ function makecross(; offset = -0.5, radius = 1.0)
|
||||
end
|
||||
|
||||
|
||||
from_polar(angle, dist) = P2(dist*cos(angle), dist*sin(angle))
|
||||
|
||||
function makearrowhead(angle; h = 2.0, w = 0.4)
|
||||
tip = from_polar(angle, h)
|
||||
Shape(P2[(0,0), from_polar(angle - 0.5π, w) - tip,
|
||||
from_polar(angle + 0.5π, w) - tip, (0,0)])
|
||||
end
|
||||
|
||||
const _shapes = KW(
|
||||
:ellipse => makeshape(20),
|
||||
:rect => makeshape(4, offset=-0.25),
|
||||
@@ -118,6 +128,86 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# center(shape::Shape) = (mean(shape.x), mean(shape.y))
|
||||
|
||||
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
|
||||
function center(shape::Shape)
|
||||
x, y = shape_coords(shape)
|
||||
n = length(x)
|
||||
A, Cx, Cy = 0.0, 0.0, 0.0
|
||||
for i=1:n
|
||||
ip1 = i==n ? 1 : i+1
|
||||
A += x[i] * y[ip1] - x[ip1] * y[i]
|
||||
end
|
||||
A *= 0.5
|
||||
for i=1:n
|
||||
ip1 = i==n ? 1 : i+1
|
||||
m = (x[i] * y[ip1] - x[ip1] * y[i])
|
||||
Cx += (x[i] + x[ip1]) * m
|
||||
Cy += (y[i] + y[ip1]) * m
|
||||
end
|
||||
Cx / 6A, Cy / 6A
|
||||
end
|
||||
|
||||
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = shape_coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(sx)
|
||||
sx[i] = (sx[i] - cx) * x + cx
|
||||
sy[i] = (sy[i] - cy) * y + cy
|
||||
end
|
||||
shape
|
||||
end
|
||||
|
||||
function Base.scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
shapecopy = deepcopy(shape)
|
||||
scale!(shape, x, y, c)
|
||||
end
|
||||
|
||||
function translate!(shape::Shape, x::Real, y::Real = x)
|
||||
sx, sy = shape_coords(shape)
|
||||
for i=1:length(sx)
|
||||
sx[i] += x
|
||||
sy[i] += y
|
||||
end
|
||||
shape
|
||||
end
|
||||
|
||||
function translate(shape::Shape, x::Real, y::Real = x)
|
||||
shapecopy = deepcopy(shape)
|
||||
translate!(shape, x, y)
|
||||
end
|
||||
|
||||
function rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
|
||||
(x - centerx) * cos(Θ) - (y - centery) * sin(Θ) + centerx
|
||||
end
|
||||
|
||||
function rotate_y(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
|
||||
(y - centery) * cos(Θ) + (x - centerx) * sin(Θ) + centery
|
||||
end
|
||||
|
||||
function rotate(x::Real, y::Real, θ::Real, c = center(shape))
|
||||
cx, cy = c
|
||||
rotate_x(x, y, Θ, cx, cy), rotate_y(x, y, Θ, cx, cy)
|
||||
end
|
||||
|
||||
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
||||
x, y = shape_coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(x)
|
||||
x[i] = rotate_x(x[i], y[i], Θ, cx, cy)
|
||||
y[i] = rotate_y(x[i], y[i], Θ, cx, cy)
|
||||
end
|
||||
shape
|
||||
end
|
||||
|
||||
function rotate(shape::Shape, Θ::Real, c = center(shape))
|
||||
shapecopy = deepcopy(shape)
|
||||
rotate!(shapecopy, Θ, c)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
||||
immutable Font
|
||||
family::AbstractString
|
||||
@@ -308,6 +398,58 @@ type OHLC{T<:Real}
|
||||
close::T
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# style is :open or :closed (for now)
|
||||
immutable Arrow
|
||||
style::Symbol
|
||||
headlength::Float64
|
||||
headwidth::Float64
|
||||
end
|
||||
|
||||
function arrow(args...)
|
||||
style = :simple
|
||||
headlength = 0.3
|
||||
headwidth = 0.3
|
||||
setlength = false
|
||||
for arg in args
|
||||
T = typeof(arg)
|
||||
if T == Symbol
|
||||
style = arg
|
||||
elseif T <: Number
|
||||
# first we apply to both, but if there's more, then only change width after the first number
|
||||
headwidth = Float64(arg)
|
||||
if !setlength
|
||||
headlength = headwidth
|
||||
end
|
||||
setlength = true
|
||||
elseif T <: Tuple && length(arg) == 2
|
||||
headlength, headwidth = Float64(arg[1]), Float64(arg[2])
|
||||
else
|
||||
warn("Skipped arrow arg $arg")
|
||||
end
|
||||
end
|
||||
Arrow(style, headlength, headwidth)
|
||||
end
|
||||
|
||||
|
||||
# allow for do-block notation which gets called on every valid start/end pair which
|
||||
# we need to draw an arrow
|
||||
function add_arrows(func::Function, x::AVec, y::AVec)
|
||||
for i=2:length(x)
|
||||
xyprev = (x[i-1], y[i-1])
|
||||
xy = (x[i], y[i])
|
||||
if ok(xyprev) && ok(xy)
|
||||
if i==length(x) || !ok(x[i+1], y[i+1])
|
||||
# add the arrow from xyprev to xy
|
||||
func(xyprev, xy)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# @require FixedSizeArrays begin
|
||||
|
||||
|
||||
+28
-7
@@ -99,12 +99,6 @@ end
|
||||
savefig(fn::@compat(AbstractString)) = savefig(current(), fn)
|
||||
|
||||
|
||||
# savepng(args...; kw...) = savepng(current(), args...; kw...)
|
||||
# savepng(plt::AbstractPlot, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
gui(plt::AbstractPlot = current()) = display(PlotsDisplay(), plt)
|
||||
@@ -115,9 +109,36 @@ Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::AbstractPlot) = g
|
||||
|
||||
# a backup for html... passes to svg
|
||||
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot)
|
||||
writemime(io, MIME("image/svg+xml"), plt)
|
||||
writemime(io, MIME("image/svg+xml"), plt)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# IJulia
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _ijulia_output = ASCIIString["text/html"]
|
||||
|
||||
function setup_ijulia()
|
||||
# override IJulia inline display
|
||||
if isijulia()
|
||||
@eval begin
|
||||
import IJulia
|
||||
export set_ijulia_output
|
||||
function set_ijulia_output(mimestr::ASCIIString)
|
||||
# info("Setting IJulia output format to $mimestr")
|
||||
global _ijulia_output
|
||||
_ijulia_output[1] = mimestr
|
||||
end
|
||||
function IJulia.display_dict(plt::AbstractPlot)
|
||||
global _ijulia_output
|
||||
Dict{ASCIIString, ByteString}(_ijulia_output[1] => sprint(writemime, _ijulia_output[1], plt))
|
||||
end
|
||||
end
|
||||
|
||||
# IJulia.display_dict(plt::AbstractPlot) = Dict{ASCIIString, ByteString}("text/html" => sprint(writemime, "text/html", plt))
|
||||
set_ijulia_output("text/html")
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Atom PlotPane
|
||||
|
||||
+83
-63
@@ -50,10 +50,10 @@ function plot(args...; kw...)
|
||||
|
||||
plotargs = merge(d, getPlotArgs(pkg, d, 1))
|
||||
dumpdict(plotargs, "Plot args")
|
||||
plt = _create_plot(pkg; plotargs...) # create a new, blank plot
|
||||
plt = _create_plot(pkg, plotargs) # create a new, blank plot
|
||||
|
||||
delete!(d, :background_color)
|
||||
plot!(plt, args...; d...) # add to it
|
||||
plot!(plt, args...; kw...) # add to it
|
||||
end
|
||||
|
||||
|
||||
@@ -72,68 +72,37 @@ end
|
||||
# this adds to a specific plot... most plot commands will flow through here
|
||||
function plot!(plt::Plot, args...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
userkw = preprocessArgs!(d)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
args = _apply_recipe(d, args...; kw...)
|
||||
args = RecipesBase.apply_recipe(d, userkw, args...)
|
||||
_add_markershape(d)
|
||||
|
||||
dumpdict(d, "After plot! preprocessing")
|
||||
# @show groupargs map(typeof, args)
|
||||
|
||||
warnOnUnsupportedArgs(plt.backend, d)
|
||||
|
||||
# just in case the backend needs to set up the plot (make it current or something)
|
||||
_before_add_series(plt)
|
||||
|
||||
# # grouping
|
||||
# groupargs = get(d, :group, nothing) == nothing ? [nothing] : [extractGroupArgs(d[:group], args...)]
|
||||
# # @show groupargs
|
||||
|
||||
# TODO: get the GroupBy object (or nothing), and loop through the groups, doing the following section many times
|
||||
# dumpdict(d, "before", true)
|
||||
groupby = if haskey(d, :group)
|
||||
extractGroupArgs(d[:group], args...)
|
||||
else
|
||||
nothing
|
||||
end
|
||||
# dumpdict(d, "after", true)
|
||||
# @show groupby map(typeof, args)
|
||||
|
||||
# merge plot args
|
||||
if !haskey(d, :subplot)
|
||||
for k in keys(_plotDefaults)
|
||||
if haskey(d, k)
|
||||
plt.plotargs[k] = d[k]
|
||||
end
|
||||
end
|
||||
# merge!(plt.plotargs, d)
|
||||
handlePlotColors(plt.backend, plt.plotargs)
|
||||
end
|
||||
|
||||
_add_series(plt, d, groupby, args...)
|
||||
|
||||
#
|
||||
# # get the list of dictionaries, one per series
|
||||
# @show groupargs map(typeof, args)
|
||||
# dumpdict(d, "before process_inputs")
|
||||
# process_inputs(plt, d, groupargs..., args...)
|
||||
# dumpdict(d, "after process_inputs", true)
|
||||
# seriesArgList, xmeta, ymeta = build_series_args(plt, d)
|
||||
# # seriesArgList, xmeta, ymeta = build_series_args(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.plotargs, xmeta, true)
|
||||
# updateDictWithMeta(d, plt.plotargs, ymeta, false)
|
||||
#
|
||||
# # now we can plot the series
|
||||
# for (i,di) in enumerate(seriesArgList)
|
||||
# plt.n += 1
|
||||
#
|
||||
# if !stringsSupported()
|
||||
# setTicksFromStringVector(d, di, :x, :xticks)
|
||||
# setTicksFromStringVector(d, di, :y, :yticks)
|
||||
# end
|
||||
#
|
||||
# # remove plot args
|
||||
# for k in keys(_plotDefaults)
|
||||
# delete!(di, k)
|
||||
# end
|
||||
#
|
||||
# dumpdict(di, "Series $i")
|
||||
#
|
||||
# _add_series(plt.backend, plt; di...)
|
||||
# end
|
||||
|
||||
_add_annotations(plt, d)
|
||||
|
||||
warnOnUnsupportedScales(plt.backend, d)
|
||||
@@ -142,6 +111,7 @@ function plot!(plt::Plot, args...; kw...)
|
||||
# add title, axis labels, ticks, etc
|
||||
if !haskey(d, :subplot)
|
||||
merge!(plt.plotargs, d)
|
||||
# handlePlotColors(plt.backend, plt.plotargs)
|
||||
dumpdict(plt.plotargs, "Updating plot items")
|
||||
_update_plot(plt, plt.plotargs)
|
||||
end
|
||||
@@ -150,7 +120,7 @@ function plot!(plt::Plot, args...; kw...)
|
||||
|
||||
current(plt)
|
||||
|
||||
# NOTE: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
|
||||
# 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]
|
||||
gui()
|
||||
@@ -180,6 +150,13 @@ function filter_data!(d::KW, idxfilter)
|
||||
end
|
||||
end
|
||||
|
||||
function _replace_linewidth(d::KW)
|
||||
# get a good default linewidth... 0 for surface and heatmaps
|
||||
if get(d, :linewidth, :auto) == :auto
|
||||
d[:linewidth] = (get(d, :linetype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
|
||||
end
|
||||
end
|
||||
|
||||
# no grouping
|
||||
function _add_series(plt::Plot, d::KW, ::Void, args...;
|
||||
idxfilter = nothing,
|
||||
@@ -196,6 +173,7 @@ function _add_series(plt::Plot, d::KW, ::Void, args...;
|
||||
# filter the data
|
||||
filter_data!(d, idxfilter)
|
||||
end
|
||||
# dumpdict(d,"",true)
|
||||
|
||||
seriesArgList, xmeta, ymeta = build_series_args(plt, d) #, idxfilter)
|
||||
# seriesArgList, xmeta, ymeta = build_series_args(plt, groupargs..., args...; d...)
|
||||
@@ -209,9 +187,10 @@ function _add_series(plt::Plot, d::KW, ::Void, args...;
|
||||
for (i,di) in enumerate(seriesArgList)
|
||||
plt.n += 1
|
||||
|
||||
if !stringsSupported()
|
||||
setTicksFromStringVector(d, di, :x, :xticks)
|
||||
setTicksFromStringVector(d, di, :y, :yticks)
|
||||
if !stringsSupported() && di[:linetype] != :pie
|
||||
setTicksFromStringVector(plt, d, di, "x")
|
||||
setTicksFromStringVector(plt, d, di, "y")
|
||||
setTicksFromStringVector(plt, d, di, "z")
|
||||
end
|
||||
|
||||
# remove plot args
|
||||
@@ -219,29 +198,70 @@ function _add_series(plt::Plot, d::KW, ::Void, args...;
|
||||
delete!(di, k)
|
||||
end
|
||||
|
||||
# merge in plotarg_overrides
|
||||
plotarg_overrides = pop!(di, :plotarg_overrides, nothing)
|
||||
if plotarg_overrides != nothing
|
||||
merge!(plt.plotargs, plotarg_overrides)
|
||||
end
|
||||
# dumpdict(plt.plotargs, "pargs", true)
|
||||
|
||||
dumpdict(di, "Series $i")
|
||||
|
||||
_add_series(plt.backend, plt; di...)
|
||||
_replace_linewidth(di)
|
||||
|
||||
_add_series(plt.backend, plt, di)
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# 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::KW, di::KW, 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
|
||||
function get_indices(orig, labels)
|
||||
Int[findnext(labels, l, 1) for l in orig]
|
||||
end
|
||||
|
||||
function setTicksFromStringVector(plt::Plot, d::KW, di::KW, letter)
|
||||
sym = symbol(letter)
|
||||
ticksym = symbol(letter * "ticks")
|
||||
pargs = plt.plotargs
|
||||
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]]
|
||||
# do we really want to do this?
|
||||
typeof(v) <: AbstractArray || return
|
||||
isempty(v) && return
|
||||
trueOrAllTrue(_ -> typeof(_) <: AbstractString, v) || return
|
||||
|
||||
if !haskey(d, ticksym) || d[ticksym] == :auto
|
||||
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
|
||||
# compute the ticks and labels
|
||||
ticks, labels = if ticksType(pargs[ticksym]) == :ticks_and_labels
|
||||
# extend the existing ticks and labels. only add to labels if they're new!
|
||||
ticks, labels = pargs[ticksym]
|
||||
newlabels = filter(_ -> !(_ in labels), unique(v))
|
||||
newticks = if isempty(ticks)
|
||||
collect(1:length(newlabels))
|
||||
else
|
||||
maximum(ticks) + collect(1:length(newlabels))
|
||||
end
|
||||
ticks = vcat(ticks, newticks)
|
||||
labels = vcat(labels, newlabels)
|
||||
ticks, labels
|
||||
else
|
||||
# create new ticks and labels
|
||||
newlabels = unique(v)
|
||||
collect(1:length(newlabels)), newlabels
|
||||
end
|
||||
|
||||
d[ticksym] = ticks, labels
|
||||
plt.plotargs[ticksym] = ticks, labels
|
||||
|
||||
# add an origsym field so that later on we can re-compute the x vector if ticks change
|
||||
origsym = symbol(letter * "orig")
|
||||
di[origsym] = v
|
||||
di[sym] = get_indices(v, labels)
|
||||
|
||||
# loop through existing plt.seriesargs and adjust indices if there is an origsym key
|
||||
for sargs in plt.seriesargs
|
||||
if haskey(sargs, origsym)
|
||||
# TODO: might need to call the setindex function instead to trigger a plot update for some backends??
|
||||
sargs[sym] = get_indices(sargs[origsym], labels)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+399
-10
@@ -17,18 +17,405 @@ plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)
|
||||
num_series(x::AMat) = size(x,2)
|
||||
num_series(x) = 1
|
||||
|
||||
_apply_recipe(d::KW; kw...) = ()
|
||||
# _apply_recipe(d::KW, kw::KW) = ()
|
||||
|
||||
# if it's not a recipe, just do nothing and return the args
|
||||
function _apply_recipe(d::KW, args...; issubplot=false, kw...)
|
||||
if issubplot && !haskey(d, :n) && !haskey(d, :layout)
|
||||
function RecipesBase.apply_recipe(d::KW, kw::KW, args...; issubplot=false)
|
||||
if issubplot && !isempty(args) && !haskey(d, :n) && !haskey(d, :layout)
|
||||
# put in a sensible default
|
||||
d[:n] = maximum(map(num_series, args))
|
||||
end
|
||||
args
|
||||
end
|
||||
|
||||
# -------------------------------------------------
|
||||
# macro kw(k, v)
|
||||
# esc(:(get!(d, $k, $v)))
|
||||
# end
|
||||
#
|
||||
# function _is_arrow_tuple(expr::Expr)
|
||||
# expr.head == :tuple &&
|
||||
# isa(expr.args[1], Expr) &&
|
||||
# expr.args[1].head == :(-->)
|
||||
# end
|
||||
#
|
||||
# function _equals_symbol(arg::Symbol, sym::Symbol)
|
||||
# arg == sym
|
||||
# end
|
||||
# function _equals_symbol(arg::Expr, sym::Symbol)
|
||||
# arg.head == :quote && arg.args[1] == sym
|
||||
# end
|
||||
#
|
||||
# # TODO: when this is moved out of Plots, also move the replacement of key aliases to just after the _apply_recipe calls
|
||||
# function replace_recipe_arrows!(expr::Expr)
|
||||
# for (i,e) in enumerate(expr.args)
|
||||
# if isa(e,Expr)
|
||||
#
|
||||
# # process trailing flags, like:
|
||||
# # a --> b, :quiet, :force
|
||||
# quiet, require, force = false, false, false
|
||||
# if _is_arrow_tuple(e)
|
||||
# for flag in e.args
|
||||
# if _equals_symbol(flag, :quiet)
|
||||
# quiet = true
|
||||
# elseif _equals_symbol(flag, :require)
|
||||
# require = true
|
||||
# elseif _equals_symbol(flag, :force)
|
||||
# force = true
|
||||
# end
|
||||
# end
|
||||
# e = e.args[1]
|
||||
# end
|
||||
#
|
||||
# # we are going to recursively swap out `a --> b, flags...` commands
|
||||
# if e.head == :(-->)
|
||||
# k, v = e.args
|
||||
# keyexpr = :(get(Plots._keyAliases, $k, $k))
|
||||
#
|
||||
# set_expr = if force
|
||||
# # forced override user settings
|
||||
# :(d[$keyexpr] = $v)
|
||||
# else
|
||||
# # if the user has set this keyword, use theirs
|
||||
# :(get!(d, $keyexpr, $v))
|
||||
# end
|
||||
#
|
||||
# expr.args[i] = if quiet
|
||||
# # quietly ignore keywords which are not supported
|
||||
# :($keyexpr in supportedArgs() ? $set_expr : nothing)
|
||||
# elseif require
|
||||
# # error when not supported by the backend
|
||||
# :($keyexpr in supportedArgs() ? $set_expr : error("In recipe: required keyword ", $k, " is not supported by backend $(backend_name())"))
|
||||
# else
|
||||
# set_expr
|
||||
# end
|
||||
#
|
||||
# # @show quiet, force, expr.args[i]
|
||||
#
|
||||
# elseif e.head != :call
|
||||
# # we want to recursively replace the arrows, but not inside function calls
|
||||
# # as this might include things like Dict(1=>2)
|
||||
# replace_recipe_arrows!(e)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# macro recipe(funcexpr::Expr)
|
||||
# lhs, body = funcexpr.args
|
||||
#
|
||||
# if !(funcexpr.head in (:(=), :function))
|
||||
# error("Must wrap a valid function call!")
|
||||
# end
|
||||
# if !(isa(lhs, Expr) && lhs.head == :call)
|
||||
# error("Expected `lhs = ...` with lhs as a call Expr... got: $lhs")
|
||||
# end
|
||||
#
|
||||
# # for parametric definitions, take the "curly" expression and add the func
|
||||
# front = lhs.args[1]
|
||||
# func = :(Plots._apply_recipe)
|
||||
# if isa(front, Expr) && front.head == :curly
|
||||
# front.args[1] = func
|
||||
# func = front
|
||||
# end
|
||||
#
|
||||
# # get the arg list, stripping out any keyword parameters into a
|
||||
# # bunch of get!(kw, key, value) lines
|
||||
# args = lhs.args[2:end]
|
||||
# kw_body = Expr(:block)
|
||||
# if isa(args[1], Expr) && args[1].head == :parameters
|
||||
# for kwpair in args[1].args
|
||||
# k, v = kwpair.args
|
||||
# push!(kw_body.args, :(get!(kw, $(QuoteNode(k)), $v)))
|
||||
# end
|
||||
# args = args[2:end]
|
||||
# end
|
||||
#
|
||||
# # replace all the key => value lines with argument setting logic
|
||||
# replace_recipe_arrows!(body)
|
||||
#
|
||||
# # now build a function definition for _apply_recipe, wrapping the return value in a tuple if needed
|
||||
# esc(quote
|
||||
# function $func(d::KW, kw::KW, $(args...); issubplot=false)
|
||||
# $kw_body
|
||||
# ret = $body
|
||||
# if typeof(ret) <: Tuple
|
||||
# ret
|
||||
# else
|
||||
# (ret,)
|
||||
# end
|
||||
# end
|
||||
# end)
|
||||
# end
|
||||
#
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
`apply_series_recipe` should take a processed series KW dict and break it up
|
||||
into component parts. For example, a box plot is made up of `shape` for the
|
||||
boxes, `path` for the lines, and `scatter` for the outliers.
|
||||
|
||||
Returns a Vector{KW}.
|
||||
"""
|
||||
apply_series_recipe(d::KW, lt) = KW[d]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Box Plot
|
||||
|
||||
const _box_halfwidth = 0.4
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
||||
# dumpdict(d, "box before", true)
|
||||
# TODO: add scatter series with outliers
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
d[:linetype] = :shape
|
||||
groupby = extractGroupArgs(d[:x])
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
|
||||
# filter y values, then compute quantiles
|
||||
q1,q2,q3,q4,q5 = quantile(d[:y][groupby.groupIds[i]], linspace(0,1,5))
|
||||
|
||||
# make the shape
|
||||
l, m, r = i - _box_halfwidth, i, i + _box_halfwidth
|
||||
xcoords = [
|
||||
m, l, r, m, m, NaN, # lower T
|
||||
l, l, r, r, l, NaN, # lower box
|
||||
l, l, r, r, l, NaN, # upper box
|
||||
m, l, r, m, m, NaN, # upper T
|
||||
]
|
||||
ycoords = [
|
||||
q1, q1, q1, q1, q2, NaN, # lower T
|
||||
q2, q3, q3, q2, q2, NaN, # lower box
|
||||
q4, q3, q3, q4, q4, NaN, # upper box
|
||||
q5, q5, q5, q5, q4, NaN, # upper T
|
||||
]
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
end
|
||||
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
|
||||
KW[d]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Violin Plot
|
||||
|
||||
# if the user has KernelDensity installed, use this for violin plots.
|
||||
# otherwise, just use a histogram
|
||||
try
|
||||
Pkg.installed("KernelDensity")
|
||||
import KernelDensity
|
||||
|
||||
# warn("using KD for violin")
|
||||
@eval function violin_coords(y)
|
||||
kd = KernelDensity.kde(y, npoints = 30)
|
||||
kd.density, kd.x
|
||||
end
|
||||
catch
|
||||
# warn("using hist for violin")
|
||||
@eval function violin_coords(y)
|
||||
edges, widths = hist(y, 20)
|
||||
centers = 0.5 * (edges[1:end-1] + edges[2:end])
|
||||
ymin, ymax = extrema(y)
|
||||
vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
# dumpdict(d, "box before", true)
|
||||
# TODO: add scatter series with outliers
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
d[:linetype] = :shape
|
||||
groupby = extractGroupArgs(d[:x])
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
|
||||
# get the edges and widths
|
||||
y = d[:y][groupby.groupIds[i]]
|
||||
widths, centers = violin_coords(y)
|
||||
|
||||
# normalize
|
||||
widths = _box_halfwidth * widths / maximum(widths)
|
||||
|
||||
# make the violin
|
||||
xcoords = vcat(widths, -reverse(widths)) + i
|
||||
ycoords = vcat(centers, reverse(centers))
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
end
|
||||
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
|
||||
KW[d]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Error Bars
|
||||
|
||||
function error_style!(d::KW)
|
||||
d[:linetype] = :path
|
||||
d[:linecolor] = d[:markerstrokecolor]
|
||||
d[:linewidth] = d[:markerstrokewidth]
|
||||
d[:label] = ""
|
||||
end
|
||||
|
||||
# if we're passed a tuple of vectors, convert to a vector of tuples
|
||||
function error_zipit(ebar)
|
||||
if istuple(ebar)
|
||||
collect(zip(ebar...))
|
||||
else
|
||||
ebar
|
||||
end
|
||||
end
|
||||
|
||||
function error_coords(xorig, yorig, ebar)
|
||||
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
|
||||
x, y = zeros(0), zeros(0)
|
||||
|
||||
# for each point, create a line segment from the bottom to the top of the errorbar
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
xi = get_mod(xorig, i)
|
||||
yi = get_mod(yorig, i)
|
||||
ebi = get_mod(ebar, i)
|
||||
nanappend!(x, [xi, xi])
|
||||
e1, e2 = if istuple(ebi)
|
||||
first(ebi), last(ebi)
|
||||
elseif isscalar(ebi)
|
||||
ebi, ebi
|
||||
else
|
||||
error("unexpected ebi type $(typeof(ebi)) for errorbar: $ebi")
|
||||
end
|
||||
nanappend!(y, [yi - e1, yi + e2])
|
||||
end
|
||||
x, y
|
||||
end
|
||||
|
||||
# we will create a series of path segments, where each point represents one
|
||||
# side of an errorbar
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:yerror}})
|
||||
error_style!(d)
|
||||
d[:markershape] = :hline
|
||||
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
|
||||
KW[d]
|
||||
end
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:xerror}})
|
||||
error_style!(d)
|
||||
d[:markershape] = :vline
|
||||
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
|
||||
KW[d]
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# quiver
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(d::KW)
|
||||
d[:label] = ""
|
||||
d[:linetype] = :path
|
||||
if !isa(d[:arrow], Arrow)
|
||||
d[:arrow] = arrow()
|
||||
end
|
||||
|
||||
velocity = error_zipit(d[:quiver])
|
||||
xorig, yorig = d[:x], d[:y]
|
||||
|
||||
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
x, y = zeros(0), zeros(0)
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
# get the starting position
|
||||
xi = get_mod(xorig, i)
|
||||
yi = get_mod(yorig, i)
|
||||
|
||||
# get the velocity
|
||||
vi = get_mod(velocity, i)
|
||||
vx, vy = if istuple(vi)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
end
|
||||
|
||||
# add the points
|
||||
nanappend!(x, [xi, xi+vx, NaN])
|
||||
nanappend!(y, [yi, yi+vy, NaN])
|
||||
end
|
||||
|
||||
d[:x], d[:y] = x, y
|
||||
KW[d]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(d::KW)
|
||||
d[:label] = ""
|
||||
d[:linetype] = :shape
|
||||
|
||||
velocity = error_zipit(d[:quiver])
|
||||
xorig, yorig = d[:x], d[:y]
|
||||
|
||||
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
pts = P2[]
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
|
||||
# get the starting position
|
||||
xi = get_mod(xorig, i)
|
||||
yi = get_mod(yorig, i)
|
||||
p = P2(xi, yi)
|
||||
|
||||
# get the velocity
|
||||
vi = get_mod(velocity, i)
|
||||
vx, vy = if istuple(vi)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
end
|
||||
v = P2(vx, vy)
|
||||
|
||||
dist = norm(v)
|
||||
arrow_h = 0.1dist # height of arrowhead
|
||||
arrow_w = 0.5arrow_h # halfwidth of arrowhead
|
||||
U1 = v ./ dist # vector of arrowhead height
|
||||
U2 = P2(-U1[2], U1[1]) # vector of arrowhead halfwidth
|
||||
U1 *= arrow_h
|
||||
U2 *= arrow_w
|
||||
|
||||
ppv = p+v
|
||||
nanappend!(pts, P2[p, ppv-U1, ppv-U1+U2, ppv, ppv-U1-U2, ppv-U1])
|
||||
end
|
||||
|
||||
d[:x], d[:y] = Plots.unzip(pts[2:end])
|
||||
KW[d]
|
||||
end
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
if :arrow in supportedArgs()
|
||||
quiver_using_arrows(d)
|
||||
else
|
||||
quiver_using_hack(d)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function rotate(x::Real, y::Real, θ::Real; center = (0,0))
|
||||
cx = x - center[1]
|
||||
@@ -38,7 +425,7 @@ function rotate(x::Real, y::Real, θ::Real; center = (0,0))
|
||||
xrot + center[1], yrot + center[2]
|
||||
end
|
||||
|
||||
# -------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
type EllipseRecipe <: PlotRecipe
|
||||
w::Float64
|
||||
@@ -78,9 +465,11 @@ end
|
||||
|
||||
"Sparsity plot... heatmap of non-zero values of a matrix"
|
||||
function spy{T<:Real}(z::AMat{T}; kw...)
|
||||
# I,J,V = findnz(z)
|
||||
# heatmap(J, I; leg=false, yflip=true, kw...)
|
||||
heatmap(map(zi->float(zi!=0), z); leg=false, yflip=true, kw...)
|
||||
mat = map(zi->float(zi!=0), z)'
|
||||
xn, yn = size(mat)
|
||||
heatmap(mat; leg=false, yflip=true, aspect_ratio=:equal,
|
||||
xlim=(0.5, xn+0.5), ylim=(0.5, yn+0.5),
|
||||
kw...)
|
||||
end
|
||||
|
||||
"Adds a+bx... straight line over the current plot"
|
||||
@@ -129,7 +518,7 @@ function mat2list{T}(mat::AbstractArray{T,2})
|
||||
resize!(source, idx-1), resize!(destiny, idx-1), resize!(weight, idx-1)
|
||||
end
|
||||
|
||||
# -------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
# Arc Diagram
|
||||
|
||||
curvecolor(value, min, max, grad) = getColorZ(grad, (value-min)/(max-min))
|
||||
@@ -185,7 +574,7 @@ For simmetric matrices, only the upper triangular values are used.
|
||||
"""
|
||||
arcdiagram{T}(mat::AbstractArray{T,2}; kargs...) = arcdiagram(mat2list(mat)...; kargs...)
|
||||
|
||||
# -------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
# Chord diagram
|
||||
|
||||
arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 15, 1.1),
|
||||
|
||||
+169
-66
@@ -7,7 +7,7 @@
|
||||
|
||||
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(lt -> lt in (:contour, :heatmap, :surface, :wireframe), get(d, :linetype, :none))
|
||||
all3D(d::KW) = trueOrAllTrue(lt -> lt in (:contour, :heatmap, :surface, :wireframe, :contour3d), get(d, :linetype, :none))
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
|
||||
@@ -30,6 +30,14 @@ function convertToAnyVector{T<:Number}(v::AMat{T}, d::KW)
|
||||
end, nothing
|
||||
end
|
||||
|
||||
# other matrix... vector of columns
|
||||
function convertToAnyVector(m::AMat, d::KW)
|
||||
Any[begin
|
||||
v = vec(m[:,i])
|
||||
length(v) == 1 ? v[1] : v
|
||||
end for i=1:size(m,2)], nothing
|
||||
end
|
||||
|
||||
# function
|
||||
convertToAnyVector(f::Function, d::KW) = Any[f], nothing
|
||||
|
||||
@@ -92,7 +100,7 @@ compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
# create n=max(mx,my) series arguments. the shorter list is cycled through
|
||||
# note: everything should flow through this
|
||||
function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
x, y, z = map(a -> pop!(kw, a, nothing), (:x, :y, :z))
|
||||
x, y, z = map(sym -> pop!(kw, sym, nothing), (:x, :y, :z))
|
||||
if nothing == x == y == z
|
||||
return [], nothing, nothing
|
||||
end
|
||||
@@ -101,17 +109,12 @@ function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
ys, ymeta = convertToAnyVector(y, kw)
|
||||
zs, zmeta = convertToAnyVector(z, kw)
|
||||
|
||||
# if idxfilter != nothing
|
||||
# xs = filter_data(xs, idxfilter)
|
||||
# ys = filter_data(ys, idxfilter)
|
||||
# zs = filter_data(zs, idxfilter)
|
||||
# # # filter the data
|
||||
# # for sym in (:x, :y, :z)
|
||||
# # @show "before" sym, d[sym], idxfilter
|
||||
# # d[sym] = filter_data(get(d, sym, nothing), idxfilter)
|
||||
# # @show "after" sym, d[sym], idxfilter
|
||||
# # end
|
||||
# end
|
||||
fr = pop!(kw, :fillrange, nothing)
|
||||
fillranges, _ = if typeof(fr) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(fr, kw)
|
||||
end
|
||||
|
||||
mx = length(xs)
|
||||
my = length(ys)
|
||||
@@ -135,30 +138,11 @@ function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
n = plt.n + i
|
||||
|
||||
dumpdict(d, "before getSeriesArgs")
|
||||
# @show numUncounted i n commandIndex convertSeriesIndex(plt, n)
|
||||
d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n)
|
||||
dumpdict(d, "after getSeriesArgs")
|
||||
|
||||
# @show map(typeof, (xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]))
|
||||
d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])
|
||||
# @show map(typeof, (d[:x], d[:y], d[:z]))
|
||||
|
||||
|
||||
# # NOTE: this should be handled by the time it gets here
|
||||
lt = d[:linetype]
|
||||
# if isa(d[:y], Surface)
|
||||
# if lt in (:contour, :heatmap, :surface, :wireframe)
|
||||
# z = d[:y]
|
||||
# d[:y] = 1:size(z,2)
|
||||
# d[:z] = z
|
||||
# end
|
||||
# end
|
||||
|
||||
# if haskey(d, :idxfilter)
|
||||
# idxfilter = pop!(d, :idxfilter)
|
||||
# d[:x] = d[:x][idxfilter]
|
||||
# d[:y] = d[:y][idxfilter]
|
||||
# end
|
||||
|
||||
# for linetype `line`, need to sort by x values
|
||||
if lt == :line
|
||||
@@ -169,22 +153,60 @@ function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
d[:linetype] = :path
|
||||
end
|
||||
|
||||
# map functions to vectors
|
||||
if isa(d[:zcolor], Function)
|
||||
d[:zcolor] = map(d[:zcolor], d[:x])
|
||||
# special handling for missing x in box plot... all the same category
|
||||
if lt == :box && xs[mod1(i,mx)] == nothing
|
||||
d[:x] = ones(Int, length(d[:y]))
|
||||
end
|
||||
|
||||
# map functions to vectors
|
||||
if isa(d[:marker_z], Function)
|
||||
d[:marker_z] = map(d[:marker_z], d[:x])
|
||||
end
|
||||
|
||||
# @show fillranges
|
||||
d[:fillrange] = fillranges[mod1(i,length(fillranges))]
|
||||
if isa(d[:fillrange], Function)
|
||||
d[:fillrange] = map(d[:fillrange], d[:x])
|
||||
end
|
||||
|
||||
# cleanup those fields that were used only for generating kw args
|
||||
# delete!(d, :dataframe)
|
||||
# for k in (:idxfilter, :numUncounted, :dataframe)
|
||||
# delete!(d, k)
|
||||
# end
|
||||
# handle error bars
|
||||
for esym in (:xerror, :yerror)
|
||||
if get(d, esym, nothing) != nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
append!(ret, apply_series_recipe(copy(d), Val{esym}))
|
||||
end
|
||||
end
|
||||
|
||||
# add it to our series list
|
||||
push!(ret, d)
|
||||
# handle ribbons
|
||||
if get(d, :ribbon, nothing) != nothing
|
||||
rib = d[:ribbon]
|
||||
d[:fillrange] = (d[:y] - rib, d[:y] + rib)
|
||||
end
|
||||
|
||||
# handle quiver plots
|
||||
# either a series of velocity vectors are passed in (`:quiver` keyword),
|
||||
# or we just add arrows to the path
|
||||
|
||||
# if lt == :quiver
|
||||
# d[:linetype] = lt = :path
|
||||
# d[:linewidth] = 0
|
||||
# end
|
||||
if get(d, :quiver, nothing) != nothing
|
||||
append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
|
||||
elseif lt == :quiver
|
||||
d[:linetype] = lt = :path
|
||||
d[:arrow] = arrow()
|
||||
end
|
||||
|
||||
# now that we've processed a given series... optionally split into
|
||||
# multiple dicts through a recipe (for example, a box plot is split into component
|
||||
# parts... polygons, lines, and scatters)
|
||||
# note: we pass in a Val type (i.e. Val{:box}) so that we can dispatch on the linetype
|
||||
kwlist = apply_series_recipe(d, Val{lt})
|
||||
append!(ret, kwlist)
|
||||
|
||||
# # add it to our series list
|
||||
# push!(ret, d)
|
||||
end
|
||||
|
||||
ret, xmeta, ymeta
|
||||
@@ -210,6 +232,11 @@ end
|
||||
# 1 argument
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
function process_inputs(plt::AbstractPlot, d::KW, n::Integer)
|
||||
# d[:x], d[:y], d[:z] = zeros(0), zeros(0), zeros(0)
|
||||
d[:x] = d[:y] = d[:z] = n
|
||||
end
|
||||
|
||||
# no special handling... assume x and z are nothing
|
||||
function process_inputs(plt::AbstractPlot, d::KW, y)
|
||||
d[:y] = y
|
||||
@@ -225,6 +252,32 @@ function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
end
|
||||
end
|
||||
|
||||
# images - grays
|
||||
function process_inputs{T<:Gray}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
d[:linetype] = :image
|
||||
n,m = size(mat)
|
||||
d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
|
||||
# handle images... when not supported natively, do a hack to use heatmap machinery
|
||||
if !nativeImagesSupported()
|
||||
d[:linetype] = :heatmap
|
||||
d[:yflip] = true
|
||||
d[:z] = Surface(convert(Matrix{Float64}, mat.surf))
|
||||
d[:fillcolor] = ColorGradient([:black, :white])
|
||||
end
|
||||
end
|
||||
|
||||
# images - colors
|
||||
function process_inputs{T<:Colorant}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
d[:linetype] = :image
|
||||
n,m = size(mat)
|
||||
d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
|
||||
# handle images... when not supported natively, do a hack to use heatmap machinery
|
||||
if !nativeImagesSupported()
|
||||
d[:yflip] = true
|
||||
imageHack(d)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# plotting arbitrary shapes/polygons
|
||||
function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
|
||||
@@ -279,29 +332,36 @@ end
|
||||
# 3d line or scatter
|
||||
function process_inputs(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zvec::AVec)
|
||||
# default to path3d if we haven't set a 3d linetype
|
||||
if !(get(d, :linetype, :none) in _3dTypes)
|
||||
lt = get(d, :linetype, :none)
|
||||
if lt == :scatter
|
||||
d[:linetype] = :scatter3d
|
||||
elseif !(lt in _3dTypes)
|
||||
d[:linetype] = :path3d
|
||||
end
|
||||
d[:x], d[:y], d[:z] = x, y, zvec
|
||||
end
|
||||
|
||||
# surface-like... function
|
||||
function process_inputs(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zf::Function)
|
||||
x, y = sort(x), sort(y)
|
||||
function process_inputs{TX,TY}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zf::Function)
|
||||
x = TX <: Number ? sort(x) : x
|
||||
y = TY <: Number ? sort(y) : y
|
||||
# x, y = sort(x), sort(y)
|
||||
d[:z] = Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
d[:x], d[:y] = x, y
|
||||
end
|
||||
|
||||
# surface-like... matrix grid
|
||||
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zmat::AMat{T})
|
||||
@assert size(zmat) == (length(x), length(y))
|
||||
if !issorted(x) || !issorted(y)
|
||||
x_idx = sortperm(x)
|
||||
y_idx = sortperm(y)
|
||||
x, y = x[x_idx], y[y_idx]
|
||||
zmat = zmat[x_idx, y_idx]
|
||||
end
|
||||
d[:x], d[:y], d[:z] = x, y, Surface{Matrix{Float64}}(zmat)
|
||||
function process_inputs{TX,TY,TZ}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zmat::AMat{TZ})
|
||||
# @assert size(zmat) == (length(x), length(y))
|
||||
# if TX <: Number && !issorted(x)
|
||||
# idx = sortperm(x)
|
||||
# x, zmat = x[idx], zmat[idx, :]
|
||||
# end
|
||||
# if TY <: Number && !issorted(y)
|
||||
# idx = sortperm(y)
|
||||
# y, zmat = y[idx], zmat[:, idx]
|
||||
# end
|
||||
d[:x], d[:y], d[:z] = x, y, Surface{Matrix{TZ}}(zmat)
|
||||
if !like_surface(get(d, :linetype, :none))
|
||||
d[:linetype] = :contour
|
||||
end
|
||||
@@ -405,24 +465,67 @@ end
|
||||
|
||||
function setup_dataframes()
|
||||
@require DataFrames begin
|
||||
# @eval begin
|
||||
# import DataFrames
|
||||
|
||||
get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
|
||||
get_data(df::DataFrames.AbstractDataFrame, arg) = arg
|
||||
DFS = Union{Symbol, AbstractArray{Symbol}}
|
||||
|
||||
function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
|
||||
# d[:dataframe] = df
|
||||
process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
|
||||
end
|
||||
|
||||
# expecting the column name of a dataframe that was passed in... anything else should error
|
||||
function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
||||
if haskey(df, s)
|
||||
return extractGroupArgs(df[s])
|
||||
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS)
|
||||
if isa(dfs, Symbol)
|
||||
get!(d, symbol(letter * "label"), string(dfs))
|
||||
collect(df[dfs])
|
||||
else
|
||||
error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
|
||||
get!(d, :label, reshape(dfs, 1, length(dfs)))
|
||||
Any[collect(df[s]) for s in dfs]
|
||||
end
|
||||
end
|
||||
|
||||
function handle_group(df::DataFrames.AbstractDataFrame, d::KW)
|
||||
if haskey(d, :group)
|
||||
g = d[:group]
|
||||
if isa(g, Symbol)
|
||||
d[:group] = collect(df[g])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@recipe function plot(df::DataFrames.AbstractDataFrame, sy::DFS)
|
||||
handle_group(df, d)
|
||||
handle_dfs(df, d, "y", sy)
|
||||
end
|
||||
|
||||
@recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS)
|
||||
handle_group(df, d)
|
||||
x = handle_dfs(df, d, "x", sx)
|
||||
y = handle_dfs(df, d, "y", sy)
|
||||
x, y
|
||||
end
|
||||
|
||||
@recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS)
|
||||
handle_group(df, d)
|
||||
x = handle_dfs(df, d, "x", sx)
|
||||
y = handle_dfs(df, d, "y", sy)
|
||||
z = handle_dfs(df, d, "z", sz)
|
||||
x, y, z
|
||||
end
|
||||
|
||||
# get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
|
||||
# get_data(df::DataFrames.AbstractDataFrame, arg) = arg
|
||||
#
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
|
||||
# # d[:dataframe] = df
|
||||
# process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
|
||||
# end
|
||||
#
|
||||
# # expecting the column name of a dataframe that was passed in... anything else should error
|
||||
# function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, 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
|
||||
|
||||
# function getDataFrameFromKW(d::KW)
|
||||
# get(d, :dataframe) do
|
||||
# error("Missing dataframe argument!")
|
||||
|
||||
+20
-49
@@ -43,7 +43,8 @@ function subplot(args...; kw...)
|
||||
preprocessArgs!(d)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
args = _apply_recipe(d, args...; kw..., issubplot=true)
|
||||
args = RecipesBase.apply_recipe(d, KW(kw), args...; issubplot=true)
|
||||
_add_markershape(d)
|
||||
|
||||
# figure out the layout
|
||||
layoutarg = get(d, :layout, nothing)
|
||||
@@ -64,12 +65,12 @@ function subplot(args...; kw...)
|
||||
di = getPlotArgs(pkg, d, i)
|
||||
di[:subplot] = true
|
||||
dumpdict(di, "Plot args (subplot $i)")
|
||||
push!(plts, _create_plot(pkg; di...))
|
||||
push!(plts, _create_plot(pkg, di))
|
||||
end
|
||||
|
||||
# create the object and do the plotting
|
||||
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout, d, false, false, false, (r,c) -> (nothing,nothing))
|
||||
subplot!(subplt, args...; d...)
|
||||
subplot!(subplt, args...; kw...)
|
||||
|
||||
subplt
|
||||
end
|
||||
@@ -81,7 +82,12 @@ end
|
||||
# grid layout
|
||||
function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...)
|
||||
d = KW(kw)
|
||||
layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
|
||||
layout = if haskey(d, :layout)
|
||||
subplotlayout(d[:layout])
|
||||
else
|
||||
subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
|
||||
end
|
||||
# layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
|
||||
subplot(vcat(plt1, plts...), layout, d)
|
||||
end
|
||||
|
||||
@@ -111,10 +117,11 @@ end
|
||||
|
||||
function _preprocess_subplot(subplt::Subplot, d::KW, args = ())
|
||||
validateSubplotSupported()
|
||||
preprocessArgs!(d)
|
||||
userkw = preprocessArgs!(d)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
args = _apply_recipe(d, args...; d..., issubplot=true)
|
||||
args = RecipesBase.apply_recipe(d, userkw, args...; issubplot=true)
|
||||
_add_markershape(d)
|
||||
|
||||
dumpdict(d, "After subplot! preprocessing")
|
||||
|
||||
@@ -200,53 +207,13 @@ function subplot!(subplt::Subplot, args...; kw...)
|
||||
subplt.initialized = _create_subplot(subplt, true)
|
||||
end
|
||||
|
||||
# # handle grouping
|
||||
# group = get(d, :group, nothing)
|
||||
# if group == nothing
|
||||
# groupargs = []
|
||||
# else
|
||||
# groupargs = [extractGroupArgs(d[:group], args...)]
|
||||
# delete!(d, :group)
|
||||
# end
|
||||
|
||||
groupby = if haskey(d, :group)
|
||||
extractGroupArgs(d[:group], args...)
|
||||
else
|
||||
nothing
|
||||
end
|
||||
# dumpdict(d, "after", true)
|
||||
# @show groupby map(typeof, args)
|
||||
|
||||
_add_series_subplot(subplt, d, groupby, args...)
|
||||
|
||||
# process_inputs(subplt, d, groupargs..., args...)
|
||||
#
|
||||
# # TODO: filter the data
|
||||
#
|
||||
# kwList, xmeta, ymeta = build_series_args(subplt, d)
|
||||
# # kwList, xmeta, ymeta = build_series_args(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
|
||||
#
|
||||
# # 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.plotargs, "plt.plotargs before plotting")
|
||||
#
|
||||
# _add_series_subplot(plt; di...)
|
||||
# end
|
||||
|
||||
_postprocess_subplot(subplt, d)
|
||||
|
||||
# show it automatically?
|
||||
@@ -265,11 +232,14 @@ end
|
||||
|
||||
# given a fully processed KW, add the series to the Plot
|
||||
function _add_series_subplot(plt::Plot, d::KW)
|
||||
setTicksFromStringVector(d, d, :x, :xticks)
|
||||
setTicksFromStringVector(d, d, :y, :yticks)
|
||||
# setTicksFromStringVector(d, d, :x, :xticks)
|
||||
# setTicksFromStringVector(d, d, :y, :yticks)
|
||||
setTicksFromStringVector(plt, d, d, "x")
|
||||
setTicksFromStringVector(plt, d, d, "y")
|
||||
setTicksFromStringVector(plt, d, d, "z")
|
||||
|
||||
# this is the actual call to the backend
|
||||
_add_series(plt.backend, plt; d...)
|
||||
_add_series(plt.backend, plt, d)
|
||||
|
||||
_add_annotations(plt, d)
|
||||
warnOnUnsupportedScales(plt.backend, d)
|
||||
@@ -321,6 +291,7 @@ function _add_series_subplot(subplt::Subplot, d::KW, ::Void, args...;
|
||||
dumpdict(di, "subplot! kwList $i")
|
||||
dumpdict(plt.plotargs, "plt.plotargs before plotting")
|
||||
|
||||
_replace_linewidth(di)
|
||||
_add_series_subplot(plt, di)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
const _invisible = RGBA(0,0,0,0)
|
||||
|
||||
const _themes = KW(
|
||||
:default => KW(
|
||||
:bg => :white,
|
||||
:bglegend => :match,
|
||||
:bginside => :match,
|
||||
:bgoutside => :match,
|
||||
:fg => :auto,
|
||||
:fglegend => :match,
|
||||
:fggrid => :match,
|
||||
:fgaxis => :match,
|
||||
:fgtext => :match,
|
||||
:fgborder => :match,
|
||||
:fgguide => :match,
|
||||
)
|
||||
)
|
||||
|
||||
function add_theme(sym::Symbol, theme::KW)
|
||||
_themes[sym] = theme
|
||||
end
|
||||
|
||||
# add a new theme, using an existing theme as the base
|
||||
function add_theme(sym::Symbol;
|
||||
base = :default, # start with this theme
|
||||
bg = _themes[base][:bg],
|
||||
bglegend = _themes[base][:bglegend],
|
||||
bginside = _themes[base][:bginside],
|
||||
bgoutside = _themes[base][:bgoutside],
|
||||
fg = _themes[base][:fg],
|
||||
fglegend = _themes[base][:fglegend],
|
||||
fggrid = _themes[base][:fggrid],
|
||||
fgaxis = _themes[base][:fgaxis],
|
||||
fgtext = _themes[base][:fgtext],
|
||||
fgborder = _themes[base][:fgborder],
|
||||
fgguide = _themes[base][:fgguide],
|
||||
kw...)
|
||||
_themes[sym] = merge(KW(
|
||||
:bg => bg,
|
||||
:bglegend => bglegend,
|
||||
:bginside => bginside,
|
||||
:bgoutside => bgoutside,
|
||||
:fg => fg,
|
||||
:fglegend => fglegend,
|
||||
:fggrid => fggrid,
|
||||
:fgaxis => fgaxis,
|
||||
:fgtext => fgtext,
|
||||
:fgborder => fgborder,
|
||||
:fgguide => fgguide,
|
||||
), KW(kw))
|
||||
end
|
||||
|
||||
add_theme(:ggplot2,
|
||||
bglegend = _invisible,
|
||||
bginside = :lightgray,
|
||||
fg = :white,
|
||||
fglegend = _invisible,
|
||||
fgtext = :gray,
|
||||
fgguide = :black
|
||||
)
|
||||
|
||||
function set_theme(sym::Symbol)
|
||||
default(; _themes[sym]...)
|
||||
end
|
||||
@@ -8,6 +8,14 @@ abstract AbstractBackend
|
||||
abstract AbstractPlot{T<:AbstractBackend}
|
||||
|
||||
typealias KW Dict{Symbol,Any}
|
||||
|
||||
immutable InputWrapper{T}
|
||||
obj::T
|
||||
end
|
||||
|
||||
wrap{T}(obj::T) = InputWrapper{T}(obj)
|
||||
Base.isempty(wrapper::InputWrapper) = false
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Plot
|
||||
# -----------------------------------------------------------
|
||||
|
||||
+227
-88
@@ -22,7 +22,7 @@ function histogramHack(; kw...)
|
||||
d = KW(kw)
|
||||
|
||||
# we assume that the y kwarg is set with the data to be binned, and nbins is also defined
|
||||
edges, midpoints, buckets, counts = binData(d[:y], d[:nbins])
|
||||
edges, midpoints, buckets, counts = binData(d[:y], d[:bins])
|
||||
d[:x] = midpoints
|
||||
d[:y] = float(counts)
|
||||
d[:linetype] = :bar
|
||||
@@ -114,10 +114,33 @@ function regressionXY(x, y)
|
||||
regx, regy
|
||||
end
|
||||
|
||||
function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
|
||||
@show T, size(z)
|
||||
n, m = size(z)
|
||||
# idx = 0
|
||||
colors = ColorGradient(vec(z))
|
||||
newz = reshape(linspace(0, 1, n*m), n, m)
|
||||
newz, colors
|
||||
# newz = zeros(n, m)
|
||||
# for i=1:n, j=1:m
|
||||
# push!(colors, T(z[i,j]...))
|
||||
# newz[i,j] = idx / (n*m-1)
|
||||
# idx += 1
|
||||
# end
|
||||
# newz, ColorGradient(colors)
|
||||
end
|
||||
|
||||
function imageHack(d::KW)
|
||||
:heatmap in supportedTypes() || error("Neither :image or :heatmap are supported!")
|
||||
d[:linetype] = :heatmap
|
||||
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
|
||||
end
|
||||
# ---------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
nop() = nothing
|
||||
notimpl() = error("This has not been implemented yet")
|
||||
|
||||
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))]
|
||||
@@ -170,13 +193,20 @@ function replaceType(vec, val)
|
||||
push!(vec, val)
|
||||
end
|
||||
|
||||
function replaceAlias!(d::KW, k::Symbol, aliases::KW)
|
||||
if haskey(aliases, k)
|
||||
d[aliases[k]] = pop!(d, k)
|
||||
end
|
||||
end
|
||||
|
||||
function replaceAliases!(d::KW, aliases::KW)
|
||||
ks = collect(keys(d))
|
||||
for k in ks
|
||||
if haskey(aliases, k)
|
||||
d[aliases[k]] = d[k]
|
||||
delete!(d, k)
|
||||
end
|
||||
replaceAlias!(d, k, aliases)
|
||||
# if haskey(aliases, k)
|
||||
# d[aliases[k]] = d[k]
|
||||
# delete!(d, k)
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -217,6 +247,7 @@ isscalar(::Real) = true
|
||||
isscalar(::Any) = false
|
||||
|
||||
|
||||
isvertical(d::KW) = get(d, :orientation, :vertical) in (:vertical, :v, :vert)
|
||||
|
||||
|
||||
# ticksType{T<:Real,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
|
||||
@@ -229,12 +260,44 @@ limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
|
||||
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
|
||||
limsType(lims) = :invalid
|
||||
|
||||
axis_symbol(letter, postfix) = symbol(letter * postfix)
|
||||
axis_symbols(letter, postfix...) = map(s -> axis_symbol(letter, s), postfix)
|
||||
|
||||
Base.convert{T<:Real}(::Type{Vector{T}}, rng::Range{T}) = T[x for x in rng]
|
||||
Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]
|
||||
|
||||
Base.merge(a::AbstractVector, b::AbstractVector) = sort(unique(vcat(a,b)))
|
||||
|
||||
nanpush!(a::AbstractVector, b) = (push!(a, NaN); push!(a, b))
|
||||
nanappend!(a::AbstractVector, b) = (push!(a, NaN); append!(a, b))
|
||||
|
||||
# given an array of discrete values, turn it into an array of indices of the unique values
|
||||
# returns the array of indices (znew) and a vector of unique values (vals)
|
||||
function indices_and_unique_values(z::AbstractArray)
|
||||
vals = sort(unique(z))
|
||||
vmap = Dict([(v,i) for (i,v) in enumerate(vals)])
|
||||
newz = map(zi -> vmap[zi], z)
|
||||
newz, vals
|
||||
end
|
||||
|
||||
# this is a helper function to determine whether we need to transpose a surface matrix.
|
||||
# it depends on whether the backend matches rows to x (transpose_on_match == true) or vice versa
|
||||
# for example: PyPlot sends rows to y, so transpose_on_match should be true
|
||||
function transpose_z(d::KW, z, transpose_on_match::Bool = true)
|
||||
if d[:match_dimensions] == transpose_on_match
|
||||
z'
|
||||
else
|
||||
z
|
||||
end
|
||||
end
|
||||
|
||||
function ok(x::Number, y::Number, z::Number = 0)
|
||||
isfinite(x) && isfinite(y) && isfinite(z)
|
||||
end
|
||||
ok(tup::Tuple) = ok(tup...)
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
wraptuple(x::@compat(Tuple)) = x
|
||||
@@ -253,6 +316,7 @@ allReals(arg) = trueOrAllTrue(a -> typeof(a) <: Real, arg)
|
||||
allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
|
||||
"""
|
||||
@@ -340,6 +404,7 @@ function with(f::Function, args...; kw...)
|
||||
ret
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
type DebugMode
|
||||
@@ -374,70 +439,7 @@ function dumpcallstack()
|
||||
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)
|
||||
@@ -448,24 +450,160 @@ 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)
|
||||
|
||||
|
||||
# -------------------------------------------------------
|
||||
# NOTE: backends should implement the following methods to get/set the x/y/z data objects
|
||||
|
||||
tovec(v::AbstractVector) = v
|
||||
tovec(v::Void) = zeros(0)
|
||||
|
||||
function getxy(plt::Plot, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
tovec(d[:x]), tovec(d[:y])
|
||||
end
|
||||
function getxyz(plt::Plot, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
tovec(d[:x]), tovec(d[:y]), tovec(d[:z])
|
||||
end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y] = xy
|
||||
end
|
||||
function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y], d[:z] = xyz
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------------------------------
|
||||
# indexing notation
|
||||
|
||||
Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
|
||||
Base.setindex!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer) = setxy!(plt, xy, i)
|
||||
Base.setindex!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) = setxyz!(plt, xyz, i)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# push/append for one series
|
||||
|
||||
# push value to first series
|
||||
Base.push!(plt::Plot, y::Real) = push!(plt, 1, y)
|
||||
Base.push!(plt::Plot, x::Real, y::Real) = push!(plt, 1, x, y)
|
||||
Base.push!(plt::Plot, x::Real, y::Real, z::Real) = push!(plt, 1, x, y, z)
|
||||
|
||||
# y only
|
||||
function Base.push!(plt::Plot, i::Integer, y::Real)
|
||||
xdata, ydata = getxy(plt, i)
|
||||
setxy!(plt, (extendSeriesByOne(xdata), extendSeriesData(ydata, y)), i)
|
||||
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
|
||||
|
||||
# x and y
|
||||
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real)
|
||||
xdata, ydata = getxy(plt, i)
|
||||
setxy!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y)), i)
|
||||
plt
|
||||
end
|
||||
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec)
|
||||
@assert length(x) == length(y)
|
||||
xdata, ydata = getxy(plt, i)
|
||||
setxy!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y)), i)
|
||||
plt
|
||||
end
|
||||
|
||||
# x, y, and z
|
||||
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real, z::Real)
|
||||
# @show i, x, y, z
|
||||
xdata, ydata, zdata = getxyz(plt, i)
|
||||
# @show xdata, ydata, zdata
|
||||
setxyz!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y), extendSeriesData(zdata, z)), i)
|
||||
plt
|
||||
end
|
||||
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec, z::AVec)
|
||||
@assert length(x) == length(y) == length(z)
|
||||
xdata, ydata, zdata = getxyz(plt, i)
|
||||
setxyz!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y), extendSeriesData(zdata, z)), i)
|
||||
plt
|
||||
end
|
||||
|
||||
# tuples
|
||||
Base.push!{X,Y}(plt::Plot, xy::Tuple{X,Y}) = push!(plt, 1, xy...)
|
||||
Base.push!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}) = push!(plt, 1, xyz...)
|
||||
Base.push!{X,Y}(plt::Plot, i::Integer, xy::Tuple{X,Y}) = push!(plt, i, xy...)
|
||||
Base.push!{X,Y,Z}(plt::Plot, i::Integer, xyz::Tuple{X,Y,Z}) = push!(plt, i, xyz...)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# push/append for all series
|
||||
|
||||
# push y[i] to the ith series
|
||||
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
|
||||
|
||||
# push y[i] to the ith series
|
||||
# same x for each series
|
||||
function Base.push!(plt::Plot, x::Real, y::AVec)
|
||||
push!(plt, [x], y)
|
||||
end
|
||||
|
||||
# push (x[i], y[i]) to the ith series
|
||||
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
|
||||
|
||||
# push (x[i], y[i], z[i]) to the ith series
|
||||
function Base.push!(plt::Plot, x::AVec, y::AVec, z::AVec)
|
||||
nx = length(x)
|
||||
ny = length(y)
|
||||
nz = length(z)
|
||||
for i in 1:plt.n
|
||||
push!(plt, i, x[mod1(i,nx)], y[mod1(i,ny)], z[mod1(i,nz)])
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# ---------------------------------------------------------------
|
||||
# graphs detailing the features that each backend supports
|
||||
|
||||
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._backend_instance(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), leg=false)
|
||||
vals = reverse(sort(allvals))
|
||||
bs = sort(backends())
|
||||
x, y = map(string, bs), map(string, vals)
|
||||
nx, ny = map(length, (x,y))
|
||||
z = zeros(nx, ny)
|
||||
for i=1:nx, j=1:ny
|
||||
supported = func(Plots._backend_instance(bs[i]))
|
||||
z[i,j] = float(vals[j] in supported) * (0.4i/nx+0.6)
|
||||
end
|
||||
heatmap(x, y, z,
|
||||
color = ColorGradient([:white, :darkblue]),
|
||||
line = (1, :black),
|
||||
leg = false,
|
||||
size = (50nx+50, 35ny+100),
|
||||
xlim = (0.5, nx+0.5),
|
||||
ylim = (0.5, ny+0.5),
|
||||
xrotation = 60,
|
||||
aspect_ratio = :equal)
|
||||
end
|
||||
|
||||
supportGraphArgs() = supportGraph(_allArgs, supportedArgs)
|
||||
@@ -476,14 +614,15 @@ supportGraphScales() = supportGraph(_allScales, supportedScales)
|
||||
supportGraphAxes() = supportGraph(_allAxes, supportedAxes)
|
||||
|
||||
function dumpSupportGraphs()
|
||||
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
|
||||
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
|
||||
supportGraphMarkers, supportGraphScales, supportGraphAxes)
|
||||
plt = func()
|
||||
png(joinpath(Pkg.dir("ExamplePlots"), "docs", "examples", "img", "supported", "$(string(func))"))
|
||||
end
|
||||
plt = func()
|
||||
png(Pkg.dir("ExamplePlots", "docs", "examples", "img", "supported", "$(string(func))"))
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
|
||||
# Some conversion functions
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
julia 0.4
|
||||
|
||||
RecipesBase 0.0.1 0.0.2
|
||||
Colors
|
||||
Reexport
|
||||
Requires
|
||||
@@ -7,9 +8,10 @@ FactCheck
|
||||
Cairo
|
||||
Gadfly
|
||||
Images
|
||||
ImageMagick
|
||||
PyPlot
|
||||
@osx QuartzImageIO
|
||||
GR
|
||||
DataFrames
|
||||
RDatasets
|
||||
VisualRegressionTests
|
||||
UnicodePlots
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
# sudo add-apt-repository -y ppa:pov/wkhtmltopdf
|
||||
sudo apt-get -qq update
|
||||
# sudo apt-get install -y wkhtmltopdf
|
||||
|
||||
sudo apt-get install -y xfonts-75dpi
|
||||
wget http://download.gna.org/wkhtmltopdf/0.12/0.12.2/wkhtmltox-0.12.2_linux-trusty-amd64.deb
|
||||
sudo dpkg -i wkhtmltox-0.12.2_linux-trusty-amd64.deb
|
||||
wkhtmltoimage http://www.google.com test.png
|
||||
ls
|
||||
|
||||
wkhtmltopdf -V
|
||||
wkhtmltoimage -V
|
||||
|
||||
# echo 'exec xvfb-run -a -s "-screen 0 640x480x16" wkhtmltoimage "$@"' | sudo tee /usr/local/bin/wkhtmltoimage.sh >/dev/null
|
||||
# sudo chmod a+x /usr/local/bin/wkhtmltoimage.sh
|
||||
|
||||
# export DISPLAY=:99.0
|
||||
# sh -e /etc/init.d/xvfb start
|
||||
# sleep 3 # give xvfb some time to start
|
||||
+43
-4
@@ -4,7 +4,7 @@ include("imgcomp.jl")
|
||||
|
||||
# don't actually show the plots
|
||||
srand(1234)
|
||||
default(show=false)
|
||||
default(show=false, reuse=true)
|
||||
img_eps = 5e-2
|
||||
|
||||
facts("Gadfly") do
|
||||
@@ -16,22 +16,61 @@ facts("Gadfly") do
|
||||
@fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
|
||||
@fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
|
||||
|
||||
image_comparison_facts(:gadfly, skip=[4,6,19,23,24], eps=img_eps)
|
||||
image_comparison_facts(:gadfly, skip=[4,6,19,23,24,27], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("PyPlot") do
|
||||
@fact pyplot() --> Plots.PyPlotBackend()
|
||||
@fact backend() --> Plots.PyPlotBackend()
|
||||
|
||||
image_comparison_facts(:pyplot, skip=[4,10,13,19,21,23], eps=img_eps)
|
||||
image_comparison_facts(:pyplot, skip=[19], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("GR") do
|
||||
@fact gr() --> Plots.GRBackend()
|
||||
@fact backend() --> Plots.GRBackend()
|
||||
|
||||
# image_comparison_facts(:gr, only=[1], eps=img_eps)
|
||||
@linux_only image_comparison_facts(:gr, skip=[24], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("Plotly") do
|
||||
@fact plotly() --> Plots.PlotlyBackend()
|
||||
@fact backend() --> Plots.PlotlyBackend()
|
||||
|
||||
# # until png generation is reliable on OSX, just test on linux
|
||||
# @linux_only image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
|
||||
end
|
||||
|
||||
|
||||
# facts("Immerse") do
|
||||
# @fact immerse() --> Plots.ImmerseBackend()
|
||||
# @fact backend() --> Plots.ImmerseBackend()
|
||||
#
|
||||
# # as long as we can plot anything without error, it should be the same as Gadfly
|
||||
# image_comparison_facts(:immerse, only=[1], eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
# facts("PlotlyJS") do
|
||||
# @fact plotlyjs() --> Plots.PlotlyJSBackend()
|
||||
# @fact backend() --> Plots.PlotlyJSBackend()
|
||||
#
|
||||
# # as long as we can plot anything without error, it should be the same as Plotly
|
||||
# image_comparison_facts(:plotlyjs, only=[1], eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
facts("UnicodePlots") do
|
||||
@fact unicodeplots() --> Plots.UnicodePlotsBackend()
|
||||
@fact backend() --> Plots.UnicodePlotsBackend()
|
||||
|
||||
# lets just make sure it runs without error
|
||||
@fact isa(plot(rand(10)), Plot) --> true
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
FactCheck.exitstatus()
|
||||
end # module
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Pkg.clone(pwd())
|
||||
# Pkg.build("Plots")
|
||||
|
||||
Pkg.clone("ImageMagick")
|
||||
Pkg.build("ImageMagick")
|
||||
|
||||
Pkg.clone("GR")
|
||||
Pkg.build("GR")
|
||||
|
||||
# Pkg.clone("https://github.com/JuliaPlots/RecipesBase.jl.git")
|
||||
Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
|
||||
|
||||
# Pkg.clone("https://github.com/JunoLab/Blink.jl.git")
|
||||
# Pkg.build("Blink")
|
||||
# import Blink
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")
|
||||
|
||||
ENV["PYTHON"] = ""
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.build("PyPlot")
|
||||
|
||||
Pkg.test("Plots"; coverage=false)
|
||||
Reference in New Issue
Block a user