Compare commits

..

139 Commits

Author SHA1 Message Date
Thomas Breloff 65e18a8669 upper bound on RecipesBase in REQUIRE; added NEWS.md; closes #259 2016-05-19 12:36:34 -04:00
Tom Breloff 70f23e695b Merge pull request #252 from pkofod/join
Replace string cats with join() in pgfplots (and more).
2016-05-17 09:09:04 -04:00
Patrick Kofod Mogensen 5196ce5263 Add PGFPlots support for the size kw. 2016-05-12 17:32:54 +02:00
Patrick Kofod Mogensen 9231da4610 Clean up wall of pushes in _pgfplots_get_marker, and lines starting with kwarg. 2016-05-12 17:32:54 +02:00
Patrick Kofod Mogensen 89f8030ba1 Add markerstrokewidth to pgfplots. 2016-05-12 17:32:54 +02:00
Patrick Kofod Mogensen 534f57b227 Replace string cats with join() in pgfplots. 2016-05-12 17:32:54 +02:00
Tom Breloff d778c7922d Merge pull request #255 from jheinen/dev
Fixed some bugs in gr.jl
2016-05-12 10:12:29 -04:00
Josef Heinen 5109c6f72b Fixed some bugs in gr.jl
- Turn off interpolation for heatmaps
- Fixed bug plotting scatter after heatmap (scatter drew to the colorbar
  viewport)
- Don't draw superfluous legends
2016-05-12 15:29:43 +02:00
Tom Breloff fe5789d747 Merge pull request #251 from jheinen/dev
Added (native) image support
2016-05-11 10:41:39 -04:00
Josef Heinen 00201b5f9c Added (native) image support 2016-05-11 16:18:03 +02:00
Thomas Breloff 85f7acf9d5 add PyPlot.ioff to init; closes #229 2016-05-11 09:45:44 -04:00
Thomas Breloff 44d1cdc2b2 pgfplots aspect_ratio 2016-05-11 09:18:40 -04:00
Tom Breloff 90c2202f6e Merge pull request #250 from KristofferC/kc/fix_pgfplots_color
Fix bug in some linecolors in PGFPlots
2016-05-11 09:11:13 -04:00
Kristoffer Carlsson d30574de24 workaround a bug in engineering notation for colors in pgfplot 2016-05-11 15:01:17 +02:00
Tom Breloff dec3748fe5 Merge pull request #249 from KristofferC/kc/pgfplots_label
add support for legend and label in pgfplots
2016-05-11 08:51:38 -04:00
Kristoffer Carlsson e8cdff2310 add support for legend and label in pgfplots
also adds some extra kwargs for legend positions
2016-05-11 13:59:32 +02:00
Thomas Breloff a0ca9d675f add_arrows function; plotly cleanup and arrows attempt 2016-05-10 17:22:47 -04:00
Thomas Breloff baf410c712 remove pyplot dash_capstyle option 2016-05-10 14:34:28 -04:00
Thomas Breloff a81af2b449 arrow conveniences 2016-05-10 13:57:38 -04:00
Thomas Breloff 678dde710b arrows with updated quiver recipe; only pyplot so far 2016-05-10 13:40:25 -04:00
Tom Breloff 93a35d8fdc Merge pull request #244 from jheinen/dev
Improved scaling of plots with error bars
2016-05-10 12:21:18 -04:00
Josef Heinen 0878f44b09 Improved scaling of plots with error bars 2016-05-10 18:14:08 +02:00
Thomas Breloff b89ae245ec removed heatmap assert 2016-05-09 22:44:22 -04:00
Thomas Breloff 536da01714 boxplot nan fix; add match_dimensions to pyplot supportedargs 2016-05-09 22:13:53 -04:00
Thomas Breloff a1b991aaeb image fixes and support in pyplot 2016-05-09 15:53:33 -04:00
Thomas Breloff 7bee9a5829 setTicksFromStringVector fixes; travis 2016-05-09 14:36:09 -04:00
Thomas Breloff 17a90e3b00 revamped setTicksFromStringVector; dataframes label fix 2016-05-09 14:00:07 -04:00
Tom Breloff 43ba9b1172 Merge pull request #239 from jheinen/dev
Fix axes offset bug (#221)
2016-05-09 11:28:04 -04:00
Thomas Breloff fdb01046e6 plotly zaxis, tick rotation, 3d axis fix; dataframes recipe fixes 2016-05-09 11:19:20 -04:00
Josef Heinen 09421c9189 Fix axes offset bug (#221) 2016-05-09 17:10:20 +02:00
Thomas Breloff c563204b0a added RecipesBase dep 2016-05-09 09:01:05 -04:00
Thomas Breloff 33b3ee89f3 replaced dataframes with recipes 2016-05-09 08:31:50 -04:00
Tom Breloff 008b61c9f6 Merge pull request #236 from jheinen/dev
Fixed colormap problem
2016-05-09 06:12:55 -04:00
Josef Heinen 7546db4d42 Fixed colormap problem 2016-05-09 11:44:37 +02:00
Thomas Breloff 925243969c attempted colormap fix for gr 2016-05-08 11:02:20 -04:00
Tom Breloff 7bce4a963e Merge pull request #233 from jheinen/dev
gr: support for colormaps
2016-05-08 10:30:40 -04:00
Josef Heinen 42d09a6d80 Working on colormaps 2016-05-08 14:06:11 +02:00
Josef Heinen b6334176e4 Added support for :nbins keyword 2016-05-07 13:24:11 +02:00
Josef Heinen 42a175bd53 Added support for :zlims keyword 2016-05-07 12:45:09 +02:00
Josef Heinen b4fb9af8e4 Fixed a problem with Julia v0.3 2016-05-07 07:47:13 +02:00
Thomas Breloff ed6c9fe432 add is_key_supported definition 2016-05-06 15:17:56 -04:00
Thomas Breloff 7e04aa438f removed RecipesBase from REQUIRE 2016-05-06 15:09:53 -04:00
Thomas Breloff f1679f82c1 remove recipe macro; add dependency on RecipesBase 2016-05-06 14:55:42 -04:00
Thomas Breloff 1808b81d59 recipe fix for empty args in subplot 2016-05-06 11:51:23 -04:00
Thomas Breloff cd25c7acb2 markershape fix; added require option for recipes 2016-05-06 11:39:17 -04:00
Thomas Breloff cf03779c5b handle user keywords properly for recipes 2016-05-06 10:50:54 -04:00
Thomas Breloff d3439f0d7e recipe macro handle curly 2016-05-05 16:58:04 -04:00
Thomas Breloff 2df7f84939 recipe macro 2016-05-05 16:18:56 -04:00
Thomas Breloff 74c76c48d5 working on plotrecipe 2016-05-05 12:22:31 -04:00
Thomas Breloff cdc0d22b01 changed recipe to plotrecipe and improved it 2016-05-05 11:51:50 -04:00
Thomas Breloff 5a520aadc0 removed some debugging 2016-05-05 10:35:04 -04:00
Thomas Breloff ec87489dd4 working on image type; working on ijulia display_dict 2016-05-05 00:13:45 -04:00
Thomas Breloff e30affdfc2 made center calc centroid 2016-05-04 21:36:15 -04:00
Thomas Breloff 66bd2559b9 recipe macro; markershape fix; moved/fixed themes 2016-05-04 16:09:09 -04:00
Thomas Breloff c8d6b1fab0 linewidth fix; kw macro 2016-05-04 14:24:15 -04:00
Thomas Breloff 548c59719b linewidth default 2016-05-04 13:38:08 -04:00
Thomas Breloff 59c1e24371 default_kw and force_kw macros for recipes 2016-05-04 13:24:44 -04:00
Thomas Breloff 3c09921cbd x/y/z ticklabel rotation; new supported graphs using heatmap 2016-05-03 17:14:52 -04:00
Thomas Breloff 00f3d450f9 fix for integer arg in process_inputs 2016-05-03 14:43:34 -04:00
Thomas Breloff e676387c16 shapes change verts to x/y; translate/scale/rotate/center; x/y scale alias fix 2016-05-02 16:37:10 -04:00
Thomas Breloff 2e2e8a2230 move plotargs merge and handlePlotColors before series 2016-05-02 12:16:41 -04:00
Thomas Breloff 8c8ede361e removed plotly tests 2016-04-29 17:00:56 -04:00
Thomas Breloff 672bf05856 travis 2016-04-29 14:03:50 -04:00
Thomas Breloff 67ec31c75a travis 2016-04-29 13:52:22 -04:00
Thomas Breloff 59d13a7810 travis; readme 2016-04-29 13:41:28 -04:00
Thomas Breloff 010a25f4cb travis; readme 2016-04-29 13:29:58 -04:00
Thomas Breloff a0ac7b4a1e travis; readme 2016-04-29 13:02:11 -04:00
Thomas Breloff ab7eb89466 travis; gitter badge 2016-04-29 12:44:05 -04:00
Thomas Breloff 649d362b85 travis install_wkhtmltoimage.sh 2016-04-29 12:20:18 -04:00
Thomas Breloff cb80860d6f travis install_wkhtmltoimage.sh 2016-04-29 12:15:42 -04:00
Thomas Breloff a8ef9e66fc travis install_wkhtmltoimage.sh 2016-04-29 12:05:38 -04:00
Thomas Breloff d27212e0b7 travis 2016-04-29 11:30:47 -04:00
Thomas Breloff 65650a7ae5 travis 2016-04-29 11:26:53 -04:00
Thomas Breloff 6ab5b8eb68 travis 2016-04-29 10:59:10 -04:00
Thomas Breloff c618e0577a travis and tests 2016-04-29 10:49:11 -04:00
Thomas Breloff 0e84c91451 split out supported.jl, added tests for more backends; closes #206 2016-04-29 10:13:41 -04:00
Thomas Breloff 005ce11313 InputWrapper and pyplot surface zcolor 2016-04-28 22:03:52 -04:00
Tom Breloff db118d4f5a Merge pull request #208 from pkofod/newpgf
Further changes to PGFPlots backend.
2016-04-28 15:13:58 -04:00
Patrick Kofod Mogensen c92b30a741 Further changes to PGFPlots backend. 2016-04-28 20:52:56 +02:00
Thomas Breloff d67aec6fec gr fixes and tests; fillrange convertToAnyVector 2016-04-28 12:51:08 -04:00
Tom Breloff ce58f25488 Merge pull request #209 from jheinen/dev
GR - Added polar plots
2016-04-28 11:09:56 -04:00
Josef Heinen e8d77a3c61 Added polar plots 2016-04-28 16:25:32 +02:00
Thomas Breloff d6d7fc1edd gr fix zcolor to marker_z 2016-04-27 12:10:33 -04:00
Thomas Breloff 7ce7f8bfce gr tests 2016-04-27 09:20:19 -04:00
Tom Breloff f0f46e5272 Merge pull request #166 from pkofod/pkm/pgfplots
PGFPlots.jl support
2016-04-27 09:17:45 -04:00
Patrick Kofod Mogensen f9473cc8bc Initial functionality added to PGFPlots backend. 2016-04-27 10:05:48 +02:00
Thomas Breloff 5c7d31157b spy fixes; closes #196 2016-04-26 17:02:16 -04:00
Thomas Breloff 982a512ebd pyplot support for pie charts 2016-04-26 16:01:50 -04:00
Thomas Breloff 5dfd3d2f5d added aspect_ratio keyword and implemented in pyplot 2016-04-26 15:35:26 -04:00
Thomas Breloff 8d6974f67d contours keyword for surface and wireframe plots 2016-04-26 15:09:18 -04:00
Thomas Breloff 8b569e3cd8 contour3d 2016-04-26 14:35:51 -04:00
Thomas Breloff b9d20142a2 pyplot trisurface support 2016-04-26 14:20:07 -04:00
Thomas Breloff 6c9e818abd discrete colorbar for pyplot 2016-04-26 13:19:22 -04:00
Thomas Breloff d2a287d43c gr tests linux_only 2016-04-26 11:59:08 -04:00
Thomas Breloff d6ce2e364f fixes and cleanup: colorbars, fills, polar 2016-04-26 11:18:48 -04:00
Thomas Breloff 36ca485a74 added fg_guide coloring; allow markers for bar/sticks and sticks fixes; legend text color 2016-04-25 16:57:16 -04:00
Thomas Breloff b3230c5671 pyplot shapes 2016-04-25 15:14:54 -04:00
Thomas Breloff ebf490838c pyplot surface, wireframe, heatmap; sorting fixes; default linewidth is :auto 2016-04-25 15:01:24 -04:00
Thomas Breloff 7921cf18b6 pyplot contours 2016-04-25 12:47:35 -04:00
Thomas Breloff 0e0450ced5 gadfly color fixes; pyplot fill_between fix; pyplot hline/vline 2016-04-25 12:24:03 -04:00
Thomas Breloff 096064ac09 subplot layout fix; closes #202 2016-04-25 12:00:43 -04:00
Thomas Breloff 77c2d7d846 subcategories for background/foreground colors, implemented in pyplot; add_theme/set_theme and ggplot2; add_aliases function; nbins renamed bins 2016-04-25 11:37:14 -04:00
Thomas Breloff 40a121198d added weights kw; pyplot hist2d and hexbin 2016-04-24 23:33:51 -04:00
Thomas Breloff 44079a7bf7 added normalize kw, and added to pyplot 2016-04-24 22:47:32 -04:00
Thomas Breloff 50fa013801 working on pyplot refactor; barh added 2016-04-23 09:32:57 -04:00
Thomas Breloff d4ccf0ec62 PLOTS_DEFAULT_BACKEND env variable; closes #200 2016-04-23 09:04:02 -04:00
Thomas Breloff 04842974e7 working on pyplot refactor 2016-04-22 16:46:02 -04:00
Thomas Breloff 6161b08f82 working on pyplot refactor; path/scatter in 2d/3d done 2016-04-22 12:53:40 -04:00
Thomas Breloff b9818a4480 working on seriescolor and pyplot reorgs 2016-04-22 10:44:12 -04:00
Thomas Breloff e2ae22421b working on seriescolor and pyplot reorgs 2016-04-22 10:09:55 -04:00
Thomas Breloff b64677e14a changed kw splatting to passing in KW: _add_series and _create_plot; removed plotly tests 2016-04-22 08:56:54 -04:00
Thomas Breloff c910445634 quiet flag for wkhtmltoimage 2016-04-21 15:10:25 -04:00
Thomas Breloff c4683a754e plotly png generation using wkhtmltoimage 2016-04-20 17:34:55 -04:00
Thomas Breloff 1da8fdb588 plotlyjs travis 2016-04-20 15:35:23 -04:00
Thomas Breloff 4bf8e342dc plotlyjs travis; plotly/plotlyjs polar 2016-04-20 14:44:27 -04:00
Thomas Breloff 18fc50fcbf plotlyjs travis 2016-04-20 12:33:32 -04:00
Thomas Breloff 2fb10b7076 plotlyjs travis 2016-04-20 11:59:17 -04:00
Thomas Breloff 967291c2ef export KW; plotlyjs travis 2016-04-20 11:27:02 -04:00
Thomas Breloff 392ae871d5 plotlyjs travis 2016-04-20 10:57:07 -04:00
Thomas Breloff 27b3070480 Merge remote-tracking branch 'origin/master' into dev 2016-04-20 10:20:55 -04:00
Tom Breloff 325f59e03d Merge pull request #190 from mfalt/master
Better handling of xlims in Gadfly
2016-04-20 10:17:29 -04:00
Thomas Breloff 217ffb36e6 plotlyjs tests and fixes 2016-04-20 10:10:07 -04:00
Mattias Fält 4afad93ff6 Missing changes 2016-04-20 15:51:33 +02:00
Thomas Breloff e8858cb1b7 removed sort in ticks 2016-04-20 09:12:36 -04:00
Mattias Fält 52de31c9a6 Proper implementation of gadfly min/max in Cartesian 2016-04-20 14:37:34 +02:00
Mattias Fält 60660a1f1a Fixed Gadfly axis scale inversion 2016-04-20 10:48:13 +02:00
Thomas Breloff a2ea9db90e added bar_position; fixed orientation handling; pyplot cleanup 2016-04-18 15:12:13 -04:00
Thomas Breloff 236a7dadc6 added matplotlib colormaps; default changed to inferno; pyplot subplot fix 2016-04-13 13:49:03 -04:00
Thomas Breloff 82d2cc943d working on get/set xy/xyz indexing overhaul 2016-04-11 18:19:06 -04:00
Thomas Breloff d70f462899 z axis args; pyplot z axis; major pyplot cleanup; other cleanup/fixes 2016-04-11 16:16:47 -04:00
Thomas Breloff 0a636b5899 polar coords for pyplot 2016-04-11 06:46:28 -04:00
Thomas Breloff e0b2749188 added support for shape linetype in pyplot 2016-04-08 17:10:15 -04:00
Thomas Breloff 2fc973245d added quiver plot/arg; fixed gadfly shapes to allow vector of shapes 2016-04-08 16:05:20 -04:00
Thomas Breloff 9bfcb64542 asymmetric errorbars 2016-04-08 12:06:12 -04:00
Thomas Breloff 61e34b7ef8 ribbons; changed errorbar to yerror and added xerror 2016-04-08 11:52:02 -04:00
Thomas Breloff 3c3078875a errorbars 2016-04-08 10:35:17 -04:00
Thomas Breloff 5293e2c13d sort string ticks 2016-04-07 17:27:12 -04:00
Thomas Breloff fdf3b3f581 added violin plot 2016-04-07 17:05:31 -04:00
Thomas Breloff f3f29fb54f apply_series_recipe framework and boxplot; fix Gadfly scales for tick labels 2016-04-07 15:56:09 -04:00
38 changed files with 6192 additions and 2630 deletions
+43 -8
View File
@@ -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())'
+124
View File
@@ -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`
+20 -6
View File
@@ -1,8 +1,9 @@
# Plots
[![Build Status](https://travis-ci.org/tbreloff/Plots.jl.svg?branch=master)](https://travis-ci.org/tbreloff/Plots.jl)
[![Plots](http://pkg.julialang.org/badges/Plots_0.3.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.3)
[![Plots](http://pkg.julialang.org/badges/Plots_0.4.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.4)
[![Join the chat at https://gitter.im/tbreloff/Plots.jl](https://badges.gitter.im/tbreloff/Plots.jl.svg)](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.3.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
<!-- [![Plots](http://pkg.julialang.org/badges/Plots_0.4.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
<!-- [![Coverage Status](https://coveralls.io/repos/tbreloff/Plots.jl/badge.svg?branch=master)](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
<!-- [![codecov.io](http://codecov.io/github/tbreloff/Plots.jl/coverage.svg?branch=master)](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
@@ -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
```
![waves](http://plots.readthedocs.io/en/latest/examples/img/waves.gif)
View the [full documentation](http://plots.readthedocs.io).
+1
View File
@@ -1,5 +1,6 @@
julia 0.4
RecipesBase 0.0.1 0.0.2
Colors
Reexport
Compat
+49 -15
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+48 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9 -5
View File
@@ -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
# ---------------------------------------------------------------------------------------------
+71 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+57 -7
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+10 -12
View File
@@ -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
# ----------------------------------------------------------------
+65 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+65
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+22
View File
@@ -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
View File
@@ -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
+23
View File
@@ -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)