Compare commits

...

176 Commits

Author SHA1 Message Date
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
Thomas Breloff e8ed79a6bb pyplot zlim fixes 2016-03-29 11:01:16 -06:00
Thomas Breloff cca730d927 atom abstractplot 2016-03-29 09:24:23 -06:00
Tom Breloff 0693cb7840 Merge pull request #176 from abelsiqueira/feat/zlim
Implement zlims!
2016-03-29 09:23:07 -06:00
Abel Soares Siqueira 3f1958cce1 Implement zlims! 2016-03-28 22:41:32 -03:00
Thomas Breloff fb708bed90 Atom fixes 2016-03-25 09:23:10 -06:00
Thomas Breloff 1636587a82 surface fix 2016-03-19 19:40:27 -06:00
Thomas Breloff 1f1ccd81bb shapes 2016-03-18 11:38:51 -04:00
Thomas Breloff 5efb0eb582 more Dict to KW renaming 2016-03-17 22:11:29 -04:00
Thomas Breloff be5f71c123 subplot groups; lots of Dict to KW replace 2016-03-17 22:02:50 -04:00
Thomas Breloff a0df6bfc12 groupby overhaul 2016-03-17 18:27:33 -04:00
Thomas Breloff 2ab0dc20d7 working on series reorg 2016-03-17 16:52:09 -04:00
Thomas Breloff 175ce3613a working on series reorg 2016-03-17 15:07:07 -04:00
Thomas Breloff eecb5c3754 started series reorg 2016-03-17 13:38:22 -04:00
Thomas Breloff b34360901f test/REQUIRE 2016-03-17 11:33:18 -04:00
Thomas Breloff 21bbd21ae9 travis 2016-03-17 10:33:39 -04:00
Thomas Breloff 0b403a4c5d changed createKWArgsList to build_series_args and moved into series_args.jl; added Cairo and Fontconfig to test/REQUIRE 2016-03-17 10:08:03 -04:00
Thomas Breloff 1d1e1beca5 pyplot surface fix 2016-03-17 08:59:43 -04:00
Tom Breloff 3d4d844e72 Merge pull request #169 from diegozea/dev
Arc and chord diagram
2016-03-17 08:39:27 -04:00
Diego Javier Zea 1f9aa72bdb clean previous commit 2016-03-17 00:18:13 -03:00
Diego Javier Zea c29f68e35d Arc and chord diagrams 2016-03-17 00:11:41 -03:00
Tom Breloff c7c10e769d Merge pull request #168 from jheinen/dev
GR.jl improvements
2016-03-16 14:55:35 -04:00
Josef Heinen 7264c01b85 Added :heatmap linetype 2016-03-16 18:27:59 +01:00
Josef Heinen 97faa7413f gr: improved subplot layout 2016-03-16 18:27:59 +01:00
Thomas Breloff 7531c48e38 shape linetype; supported in gadfly; gadfly cleanup 2016-03-16 13:12:01 -04:00
Thomas Breloff 16799d8b75 cleanup plotly 2016-03-16 11:38:20 -04:00
Thomas Breloff 901115ed10 working on plotly/interact fix 2016-03-15 13:16:14 -04:00
Tom Breloff 596ca08446 Merge pull request #162 from diegozea/dev
arcdiagram
2016-03-15 11:29:24 -04:00
Thomas Breloff 4d864d8cad working on atom integration 2016-03-14 16:58:01 -04:00
Diego Javier Zea c458a35670 arcdiagram 2016-03-13 16:27:35 -03:00
Thomas Breloff c203bfe5ad working on atom/plotly 2016-03-11 14:59:11 -05:00
Thomas Breloff 8b2a838388 fixed dataframes loading; added dataframes to tests; started atom integration 2016-03-11 13:07:25 -05:00
Thomas Breloff b08212e2f9 more layouts 2016-03-10 23:38:14 -05:00
Thomas Breloff b5f08838b3 renaming: AbstractPlot and AbstractBackend 2016-03-10 23:08:04 -05:00
Thomas Breloff 5501605a1f more cleanup; backends.jl; removed depr dir 2016-03-10 22:47:53 -05:00
Thomas Breloff 7a5197df63 started work on new FlexLayout, some reorg/cleaning 2016-03-10 22:43:02 -05:00
Tom Breloff 09d45aa64b Merge pull request #161 from dlfivefifty/dev
Update GLVisualize
2016-03-10 19:24:24 -05:00
Sheehan Olver 552810359e Merge updated glvisualize 2016-03-11 10:44:51 +11:00
Sheehan Olver a68a6885d0 updated glvisualize 2016-03-11 10:42:55 +11:00
Thomas Breloff b32589f7e7 removed plotly debugging 2016-03-10 16:20:34 -05:00
Thomas Breloff 2e6b8a067d pyplot heatmaps working; ColorGradient fix 2016-03-09 23:34:56 -05:00
Thomas Breloff 581fdfdfe0 pyplot heatmap 2016-03-09 21:55:09 -05:00
Thomas Breloff 8e4ff39738 loosen ColorGradient constructor 2016-03-09 20:31:46 -05:00
Thomas Breloff f568803af2 heatmaps, colorscales, and spy... oh my 2016-03-09 17:48:43 -05:00
Thomas Breloff 6db06857ba changed heatmap to hist2d, see #147 for details 2016-03-09 15:26:30 -05:00
Thomas Breloff fd4dd13270 added svg in pyplot; deprecate qwt; writemime backup for html 2016-03-09 13:36:21 -05:00
Thomas Breloff ded9f332f6 plotlyjs fix 2016-03-09 11:13:31 -05:00
Thomas Breloff d09ca46141 plotlyjs fix 2016-03-09 10:59:33 -05:00
Thomas Breloff b28cd7f8bd plotlyjs fix 2016-03-09 10:23:14 -05:00
Tom Breloff a4054aa500 Merge pull request #159 from jheinen/dev
Fixed some TODOs (Issue $108)
2016-03-09 09:40:06 -05:00
Josef Heinen 3332bfcee1 Fixed some TODOs (Issue $108)
- support the grid keyword
- support handling of NaN (gaps in line segments)
- support xlims/ylims
- removed gr_state macro

The GR.savestate / GR.restorestate problem (caused by exceptions) will be solved
in GR - probably by something like GR.rewind() => to unwind the attribute stack.
2016-03-09 10:34:19 +01:00
Thomas Breloff cc798a0c0a working on plotlyjs 2016-03-09 00:16:16 -05:00
Thomas Breloff fe2030b7e3 working on plotlyjs 2016-03-08 17:52:39 -05:00
Thomas Breloff ed1cce86ef ijulia, plotly, and gr display fixes 2016-03-08 15:07:51 -05:00
Thomas Breloff 9a544b0ff3 warn about Bokeh; 3D parametric plots 2016-03-08 14:18:39 -05:00
Thomas Breloff 82f7b29836 aliases 2016-03-08 13:21:54 -05:00
Thomas Breloff 076acfb242 allow one-sided limits for pyplot 2016-03-07 21:58:01 -05:00
Thomas Breloff ed336bc0ff cleaned up magic args; fixed alphas 2016-03-07 21:38:31 -05:00
Thomas Breloff 5699446d69 winston warning 2016-03-07 20:36:19 -05:00
Thomas Breloff a43d32949e better handling of 3d matrix input 2016-03-07 16:49:13 -05:00
Thomas Breloff 02a1e648bc added gr test; refactored pyplot figure generation, added overwrite_figure arg, closes #144 2016-03-07 15:17:29 -05:00
Thomas Breloff 9d3e0651e2 GR fix handling of Shape; PyPlot marker colors fix (hack), closes #145 2016-03-07 12:57:46 -05:00
Thomas Breloff cdff5a9039 gr_state macro 2016-03-07 12:17:33 -05:00
Tom Breloff ba0b5500ca Merge pull request #149 from jheinen/dev
merging GR updates
2016-03-07 11:21:09 -05:00
Josef Heinen 1439a7e289 Added support for pie charts 2016-03-07 16:36:26 +01:00
Josef Heinen 9761ede5c4 Removed support for hline, vline markers
(not yet implemented)
2016-03-07 16:36:26 +01:00
Josef Heinen e342805752 Added missing marker shapes 2016-03-07 16:36:26 +01:00
Josef Heinen d08672aa71 Improved grid plotting logic 2016-03-07 16:36:26 +01:00
Josef Heinen a3d4e05b0e Improved background color handling 2016-03-07 16:36:26 +01:00
Josef Heinen 51eeed3d50 Improved subplot layout 2016-03-07 16:36:26 +01:00
Josef Heinen 51c45456c3 Disable alpha channel when converting PNGs 2016-03-07 16:36:26 +01:00
Josef Heinen 074ba63bf5 Fill workstation background instead of viewport 2016-03-07 16:36:25 +01:00
Josef Heinen f3d552d8a0 Temporary workaround for multiple mime output 2016-03-07 16:36:25 +01:00
Josef Heinen c5fb4a9228 Added some sanity checks 2016-03-07 16:36:25 +01:00
Josef Heinen 55d7e910e4 Added workaround for multiple mime output 2016-03-07 16:36:25 +01:00
Josef Heinen ea29af8d3d Removed 'private' keyword argument :scale 2016-03-07 16:36:25 +01:00
Thomas Breloff 88b9d71bd7 fixed default PyPlot gradient 2016-03-07 09:55:05 -05:00
Thomas Breloff 165c84c246 pyplot minmax fix; convertToAnyVector fix 2016-03-04 12:46:33 -05:00
Thomas Breloff 347820867d pyplot lim fixes 2016-03-03 21:43:08 -05:00
Thomas Breloff 26d0dfbf3d Merge branch 'dev' of github.com:tbreloff/Plots.jl into dev 2016-03-03 16:06:03 -05:00
Thomas Breloff 6aa4849266 pyplot lims udpate fix 2016-03-03 16:05:27 -05:00
Thomas Breloff 335b3802b8 added FixedSizeArrays requirement 2016-03-02 22:48:27 -05:00
Thomas Breloff 73bd3fa60c curves 2016-02-22 22:53:31 -05:00
Thomas Breloff 6b61c5900c generalize curves 2016-02-22 10:39:03 -05:00
Thomas Breloff 9da4083096 directed_curve 2016-02-20 20:57:56 -05:00
Thomas Breloff 86fe244d95 revert pyplot changes 2016-02-19 21:59:43 -05:00
Thomas Breloff 732f2846de directed_curve; working on PyPlot fixes 2016-02-19 18:24:56 -05:00
Thomas Breloff 4f238caf5c added annotation options 2016-02-18 22:59:31 -05:00
Thomas Breloff c9388e9f56 bezier curves 2016-02-18 18:28:59 -05:00
Thomas Breloff 2a9fa9539f fixes to logic accepting arrays of values; additional fixes in gadfly and pyplot for this 2016-02-17 10:32:12 -05:00
Thomas Breloff b8d136588c gif macro and every/when syntax 2016-02-16 18:02:04 -05:00
Thomas Breloff 92c9e82dee fix refdir 2016-02-16 16:45:46 -05:00
Thomas Breloff 0fdb48bda3 fixes for ticks 2016-02-16 16:22:54 -05:00
Thomas Breloff e8d4fd7aac skip pyplot test 4 2016-02-12 15:41:00 -05:00
Thomas Breloff 6a4a78a26a skip pyplot test 13 2016-02-12 15:17:31 -05:00
95 changed files with 7991 additions and 5119 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.clone("Cairo"); Pkg.build("Cairo")'
- julia -e 'ENV["PYTHON"] = ""; Pkg.clone("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())'
+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
@@ -4,3 +4,4 @@ Colors
Reexport
Compat
Requires
FixedSizeArrays
-419
View File
@@ -1,419 +0,0 @@
module PlotExamples
using Plots
using Colors
using Compat
const DOCDIR = Pkg.dir("Plots") * "/docs"
const IMGDIR = Pkg.dir("Plots") * "/img"
"""
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
"""
type PlotExample
header::@compat(AbstractString)
desc::@compat(AbstractString)
exprs::Vector{Expr}
end
# the examples we'll run for each
const examples = PlotExample[
PlotExample("Lines",
"A simple line plot of the columns.",
[
:(plot(Plots.fakedata(50,5), w=3))
]),
PlotExample("Functions, adding data, and animations",
"Plot multiple functions. You can also put the function first, or use the form `plot(f, xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data: `x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with `push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the animation.",
[
:(p = plot([sin,cos], zeros(0), leg=false)),
:(anim = Animation()),
:(for x in linspace(0, 10π, 100)
push!(p, x, Float64[sin(x), cos(x)])
frame(anim)
end)
]),
PlotExample("Parametric plots",
"Plot function pair (x(u), y(u)).",
[
:(plot(sin, x->sin(2x), 0, 2π, line=4, leg=false, fill=(0,:orange)))
]),
PlotExample("Colors",
"Access predefined palettes (or build your own with the `colorscheme` method). Line/marker colors are auto-generated from the plot's palette, unless overridden. Set the `z` argument to turn on series gradients.",
[
:(y = rand(100)),
:(plot(0:10:100,rand(11,4),lab="lines",w=3, palette=:grays, fill=(0.5,:auto))),
:(scatter!(y, zcolor=abs(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs(y-0.5)+4, lab="grad"))
]),
PlotExample("Global",
"Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow you to pass a tuple or value which will be mapped to the relevant args automatically. The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`, `yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`",
[
:(y = rand(20,3)),
:(plot(y, xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2), leg=false)),
:(hline!(mean(y,1)+rand(1,3), line=(4,:dash,0.6,[:lightgreen :green :darkgreen]))),
:(vline!([5,10])),
:(title!("TITLE")),
:(yaxis!("YLABEL", :log10))
]),
PlotExample("Two-axis",
"Use the `axis` arguments.\n\nNote: Currently only supported with Qwt and PyPlot",
[
:(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT", xlabel="X", title="TITLE"))
]),
PlotExample("Arguments",
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (marker/markersize) with arguments unique to each series (colors). Special arguments `line`, `marker`, and `fill` will automatically figure out what arguments to set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with `line`.) Note that we pass a matrix of colors, and this applies the colors to each series.",
[
:(ys = Vector[rand(10), rand(20)]),
:(plot(ys, line=(:dot,4,[:black :orange]), marker=([:hex :d],12,0.8,stroke(3,:gray))))
]),
PlotExample("Build plot in pieces",
"Start with a base plot...",
[
:(plot(rand(100)/3, reg=true, fill=(0,:green)))
]),
PlotExample("",
"and add to it later.",
[
:(scatter!(rand(100), markersize=6, c=:orange))
]),
PlotExample("Heatmaps",
"",
[
:(heatmap(randn(10000),randn(10000), nbins=20))
]),
PlotExample("Line types",
"",
[
:(types = intersect(supportedTypes(), [:line, :path, :steppre, :steppost, :sticks, :scatter])'),
:(n = length(types)),
:(x = Vector[sort(rand(20)) for i in 1:n]),
:(y = rand(20,n)),
:(plot(x, y, line=(types,3), lab=map(string,types), ms=15))
]),
PlotExample("Line styles",
"",
[
:(styles = setdiff(supportedStyles(), [:auto])'),
:(plot(cumsum(randn(20,length(styles)),1), style=:auto, label=map(string,styles), w=5))
]),
PlotExample("Marker types",
"",
[
:(markers = setdiff(supportedMarkers(), [:none,:auto,Shape])'),
:(n = length(markers)),
:(x = linspace(0,10,n+2)[2:end-1]),
:(y = repmat(reverse(x)', n, 1)),
:(scatter(x, y, m=(8,:auto), lab=map(string,markers), bg=:linen))
]),
PlotExample("Bar",
"x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
[
:(bar(randn(999)))
]),
PlotExample("Histogram",
"",
[
:(histogram(randn(1000), nbins=20))
]),
PlotExample("Subplots",
"""
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
number of columns `nc`), or you can set the layout directly with `layout`.
""",
[
:(subplot(randn(100,5), layout=[1,1,3], t=[:line :hist :scatter :step :bar], nbins=10, leg=false))
]),
PlotExample("Adding to subplots",
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
[
:(subplot(Plots.fakedata(100,10), n=4, palette=[:grays :blues :heat :lightrainbow], bg=[:orange :pink :darkblue :black]))
]),
PlotExample("",
"",
[
:(subplot!(Plots.fakedata(100,10)))
]),
PlotExample("Open/High/Low/Close",
"Create an OHLC chart. Pass in a vector of OHLC objects as your `y` argument. Adjust the tick width with arg `markersize`.",
[
:(n=20),
:(hgt=rand(n)+1),
:(bot=randn(n)),
:(openpct=rand(n)),
:(closepct=rand(n)),
:(y = [OHLC(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i], closepct[i]*hgt[i]+bot[i]) for i in 1:n]),
:(ohlc(y; markersize=8))
]),
PlotExample("Annotations",
"Currently only text annotations are supported. Pass in a tuple or vector-of-tuples: (x,y,text). `annotate!(ann)` is shorthand for `plot!(; annotation=ann)`",
[
:(y = rand(10)),
:(plot(y, ann=(3,y[3],text("this is #3",:left)))),
:(annotate!([(5,y[5],text("this is #5",16,:red,:center)),
(10,y[10],text("this is #10",:right,20,"courier"))]))
]),
PlotExample("Custom Markers",
"A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and the size is expected to be rougly the area of the unit circle.",
[
:(verts = [(-1.0,1.0),(-1.28,0.6),(-0.2,-1.4),(0.2,-1.4),(1.28,0.6),(1.0,1.0),
(-1.0,1.0),(-0.2,-0.6),(0.0,-0.2),(-0.4,0.6),(1.28,0.6),(0.2,-1.4),
(-0.2,-1.4),(0.6,0.2),(-0.2,0.2),(0.0,-0.2),(0.2,0.2),(-0.2,-0.6)])
:(plot(0.1:0.2:0.9, 0.7rand(5)+0.15,
l=(3,:dash,:lightblue),
m=(Shape(verts),30,RGBA(0,0,0,0.2)),
bg=:pink, fg=:darkblue,
xlim = (0,1), ylim=(0,1), leg=false))
]),
PlotExample("Contours",
"",
[
:(x = 1:0.3:20),
:(y = x),
:(f(x,y) = sin(x)+cos(y)),
:(contour(x, y, f, fill=true))
]),
PlotExample("Pie",
"",
[
:(x = ["Nerds", "Hackers", "Scientists"]),
:(y = [0.4, 0.35, 0.25]),
:(pie(x, y, title="The Julia Community", l=0.5))
]),
PlotExample("3D",
"",
[
:(n = 100),
:(ts = linspace(0,8π,n)),
:(x = ts .* map(cos,ts)),
:(y = 0.1ts .* map(sin,ts)),
:(z = 1:n),
:(plot(x, y, z, zcolor=reverse(z), m=(10,0.8,:blues,stroke(0)), leg=false, w=5)),
:(plot!(zeros(n),zeros(n),1:n, w=10))
])
]
function createStringOfMarkDownCodeValues(arr, prefix = "")
string("`", prefix, join(sort(map(string, arr)), "`, `$prefix"), "`")
end
createStringOfMarkDownSymbols(arr) = isempty(arr) ? "" : createStringOfMarkDownCodeValues(arr, ":")
function generate_markdown(pkgname::Symbol)
# set up the backend, and don't show the plots by default
pkg = backend(pkgname)
# default(:show, false)
# mkdir if necessary
try
mkdir("$IMGDIR/$pkgname")
end
# open the markdown file
md = open("$DOCDIR/$(pkgname)_examples.md", "w")
write(md, "## Examples for backend: $pkgname\n\n")
write(md, "### Initialize\n\n```julia\nusing Plots\n$(pkgname)()\n```\n\n")
for (i,example) in enumerate(examples)
try
# we want to always produce consistent results
srand(1234)
# run the code
map(eval, example.exprs)
# # save the png
# imgname = "$(pkgname)_example_$i.png"
# NOTE: uncomment this to overwrite the images as well
if i == 2
imgname = "$(pkgname)_example_$i.gif"
gif(anim, "$IMGDIR/$pkgname/$imgname", fps=15)
else
imgname = "$(pkgname)_example_$i.png"
png("$IMGDIR/$pkgname/$imgname")
end
# write out the header, description, code block, and image link
write(md, "### $(example.header)\n\n")
write(md, "$(example.desc)\n\n")
write(md, "```julia\n$(join(map(string, example.exprs), "\n"))\n```\n\n")
write(md, "![](../img/$pkgname/$imgname)\n\n")
catch ex
# TODO: put error info into markdown?
warn("Example $pkgname:$i failed with: $ex")
end
#
end
write(md, "- Supported arguments: $(createStringOfMarkDownCodeValues(supportedArgs(pkg)))\n")
write(md, "- Supported values for axis: $(createStringOfMarkDownSymbols(supportedAxes(pkg)))\n")
write(md, "- Supported values for linetype: $(createStringOfMarkDownSymbols(supportedTypes(pkg)))\n")
write(md, "- Supported values for linestyle: $(createStringOfMarkDownSymbols(supportedStyles(pkg)))\n")
write(md, "- Supported values for marker: $(createStringOfMarkDownSymbols(supportedMarkers(pkg)))\n")
write(md, "- Is `subplot`/`subplot!` supported? $(subplotSupported(pkg) ? "Yes" : "No")\n\n")
write(md, "(Automatically generated: $(now()))")
close(md)
end
# make and display one plot
function test_examples(pkgname::Symbol, idx::Int; debug = true)
Plots._debugMode.on = debug
println("Testing plot: $pkgname:$idx:$(examples[idx].header)")
backend(pkgname)
backend()
map(eval, examples[idx].exprs)
plt = current()
gui(plt)
plt
end
# generate all plots and create a dict mapping idx --> plt
function test_examples(pkgname::Symbol; debug = false)
Plots._debugMode.on = debug
plts = Dict()
for i in 1:length(examples)
# if examples[i].header == "Subplots" && !subplotSupported()
# break
# end
try
plt = test_examples(pkgname, i, debug=debug)
plts[i] = plt
catch ex
# TODO: put error info into markdown?
warn("Example $pkgname:$i:$(examples[i].header) failed with: $ex")
end
end
plts
end
# axis # :left or :right
# color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl
# # Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
# label # string or symbol, applies to that line, may go in a legend
# width # width of a line
# linetype # :line, :step, :stepinverted, :sticks, :scatter, :none, :heatmap, :hexbin, :hist, :bar
# linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
# marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle,
# # :cross, :xcross, :star1, :star2, :hexagon
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
# markersize # size of the marker
# nbins # number of bins for heatmap/hexbin and histograms
# heatmap_c # color cutoffs for Qwt heatmaps
# fill # fill value for area plots
# title # string or symbol, title of the plot
# xlabel # string or symbol, label on the bottom (x) axis
# ylabel # string or symbol, label on the left (y) axis
# yrightlabel # string or symbol, label on the right (y) axis
# reg # true or false, add a regression line for each line
# size # (Int,Int), resize the enclosing window
# pos # (Int,Int), move the enclosing window to this position
# windowtitle # string or symbol, set the title of the enclosing windowtitle
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
@compat const _ltdesc = Dict(
:none => "No line",
:line => "Lines with sorted x-axis",
:path => "Lines",
:steppre => "Step plot (vertical then horizontal)",
:steppost => "Step plot (horizontal then vertical)",
:sticks => "Vertical lines",
:scatter => "Points, no lines",
:heatmap => "Colored regions by density",
:hexbin => "Similar to heatmap",
:hist => "Histogram (doesn't use x)",
:bar => "Bar plot (centered on x values)",
:hline => "Horizontal line (doesn't use x)",
:vline => "Vertical line (doesn't use x)",
:ohlc => "Open/High/Low/Close chart (expects y is AbstractVector{Plots.OHLC})",
:contour => "Contour lines (uses z)",
:path3d => "3D path (uses z)",
:scatter3d => "3D scatter plot (uses z)",
)
function buildReadme()
readme = readall("$DOCDIR/readme_template.md")
# build keyword arg table
table = "Keyword | Default | Type | Aliases \n---- | ---- | ---- | ----\n"
allseries = merge(Plots._seriesDefaults, @compat(Dict(:line=>nothing, :marker=>nothing, :fill=>nothing)))
allplots = merge(Plots._plotDefaults, @compat(Dict(:xaxis=>nothing, :yaxis=>nothing)))
alldefs = merge(allseries, allplots)
for k in Plots.sortedkeys(alldefs)
# for d in (Plots._seriesDefaults, Plots._plotDefaults)
# for k in Plots.sortedkeys(d)
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._keyAliases, k))
table = string(table, "`:$k` | `$(alldefs[k])` | $(haskey(allseries,k) ? "Series" : "Plot") | $aliasstr \n")
# end
end
readme = replace(readme, "[[KEYWORD_ARGS_TABLE]]", table)
# build linetypes table
table = "Type | Desc | Aliases\n---- | ---- | ----\n"
for lt in Plots._allTypes
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._typeAliases, lt))
table = string(table, "`:$lt` | $(_ltdesc[lt]) | $aliasstr \n")
end
readme = replace(readme, "[[LINETYPES_TABLE]]", table)
# build linestyles table
table = "Type | Aliases\n---- | ----\n"
for s in Plots._allStyles
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._styleAliases, s))
table = string(table, "`:$s` | $aliasstr \n")
end
readme = replace(readme, "[[LINESTYLES_TABLE]]", table)
# build markers table
table = "Type | Aliases\n---- | ----\n"
for s in Plots._allMarkers
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._markerAliases, s))
table = string(table, "`:$s` | $aliasstr \n")
end
readme = replace(readme, "[[MARKERS_TABLE]]", table)
readme_fn = Pkg.dir("Plots") * "/README.md"
f = open(readme_fn, "w")
write(f, readme)
close(f)
gadfly()
Plots.dumpSupportGraphs()
end
default(size=(500,300))
# run it!
# note: generate separately so it's easy to comment out
# @osx_only generate_markdown(:unicodeplots)
# generate_markdown(:qwt)
# generate_markdown(:gadfly)
# generate_markdown(:pyplot)
# generate_markdown(:immerse)
# generate_markdown(:winston)
end # module
-305
View File
@@ -1,305 +0,0 @@
# 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)
<!-- [![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) -->
#### Author: Thomas Breloff (@tbreloff)
Plots is a plotting API and toolset. My goals with the package are:
- **Intuitive**. Start generating complex plots without reading volumes of documentation. Commands should "just work".
- **Concise**. Less code means fewer mistakes and more efficient development/analysis.
- **Flexible**. Produce your favorite plots from your favorite package, but quicker and simpler.
- **Consistent**. Don't commit to one graphics package. Use the same code and access the strengths of all backends.
- **Lightweight**. Very few dependencies, since backends are loaded and initialized dynamically.
Use the preprocessing pipeline in Plots to fully describe your visualization before it calls the backend code. This maintains modularity and allows for efficient separation of front end code, algorithms, and backend graphics. New graphical backends can be added with minimal effort.
Check out the [summary graphs](img/supported/supported.md) for the features that each backend supports.
Please add wishlist items, bugs, or any other comments/questions to the issues list.
## Examples for each implemented backend:
- [Gadfly.jl/Immerse.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/gadfly_examples.md)
- [PyPlot.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/pyplot_examples.md)
- [UnicodePlots.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/unicodeplots_examples.md)
- [Qwt.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/qwt_examples.md)
Also check out the many [IJulia notebooks](http://nbviewer.ipython.org/github/tbreloff/ExamplePlots.jl/tree/master/examples/) with many examples.
## Installation
First, add the package
```julia
Pkg.add("Plots")
# if you want the latest features:
Pkg.checkout("Plots")
# or for the bleeding edge:
Pkg.checkout("Plots", "dev")
```
then get any plotting packages you need (obviously, you should get at least one backend).
```julia
Pkg.add("Gadfly")
Pkg.add("Immerse")
Pkg.add("PyPlot")
Pkg.add("UnicodePlots")
Pkg.add("Qwt")
Pkg.add("Bokeh")
```
## Use
Load it in. The underlying plotting backends are not imported until `backend()` is called (which happens
on your first call to `plot` or `subplot`). This means that you don't need any backends to be installed when you call `using Plots`.
Plots will try to figure out a good default backend for you automatically based on what backends are installed.
```julia
using Plots
```
Do a plot in Gadfly (inspired by [this example](http://gadflyjl.org/geom_point.html)), then save a png:
```julia
gadfly() # switch to Gadfly as a backend
dataframes() # turn on support for DataFrames inputs
# load some data
using RDatasets
iris = dataset("datasets", "iris");
# This will bring up a browser window with the plot. Add a semicolon at the end to skip display.
scatter(iris, :SepalLength, :SepalWidth, group=:Species, m=([:+ :d :s], 12), smooth=0.99, bg=:black)
# save a png (equivalent to png("gadfly1.png") and savefig("gadfly1.png"))
png("gadfly1")
```
![gadfly_plt](img/gadfly1.png)
## API
Call `backend(backend::Symbol)` or the shorthands (`gadfly()`, `qwt()`, `unicodeplots()`, etc) to set the current plotting backend.
Subsequent commands are converted into the relevant plotting commands for that package:
```julia
gadfly()
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
qwt()
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```julia
plot(args...; kw...) # creates a new plot window, and sets it to be the `current`
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
There are many ways to pass in data to the plot functions... some examples:
- Vector-like (subtypes of AbstractArray{T,1})
- Matrix-like (subtypes of AbstractArray{T,2})
- Vectors of Vectors
- Functions
- Vectors of Functions
- DataFrames with column symbols (initialize with `dataframes()`)
In general, you can pass in a `y` only, or an `x` and `y`, both of whatever type(s) you want, and Plots will slice up the data as needed.
For matrices, data is split by columns. For functions, data is mapped. For DataFrames, a Symbol/Symbols in place of x/y will map to
the relevant column(s).
Here are some example usages... remember you can always use `plot!` to update an existing plot, and that, unless specified, you will update the `current()`.
```julia
plot() # empty plot object
plot(4) # initialize with 4 empty series
plot(rand(10)) # plot 1 series... x = 1:10
plot(rand(10,5)) # plot 5 series... x = 1:10
plot(rand(10), rand(10)) # plot 1 series
plot(rand(10,5), rand(10)) # plot 5 series... y is the same for all
plot(sin, rand(10)) # y = sin(x)
plot(rand(10), sin) # same... y = sin(x)
plot([sin,cos], 0:0.1:π) # plot 2 series, sin(x) and cos(x)
plot([sin,cos], 0, π) # plot sin and cos on the range [0, π]
plot(1:10, Any[rand(10), sin]) # plot 2 series, y = rand(10) for the first, y = sin(x) for the second... x = 1:10 for both
plot(dataset("Ecdat", "Airline"), :Cost) # plot from a DataFrame (call `dataframes()` first to import DataFrames and initialize)
```
All plot methods accept a number of keyword arguments (see the tables below), which follow some rules:
- Many arguments have aliases which are replaced during preprocessing. `c` is the same as `color`, `m` is the same as `marker`, etc. You can choose how verbose you'd like to be. (see the tables below)
- There are some special arguments (`xaxis`, `yaxis`, `line`, `marker`, `fill` and the aliases `l`, `m`, `f`) which magically set many related things at once. (see the __Tip__ below)
- If the argument is a "matrix-type", then each column will map to a series, cycling through columns if there are fewer columns than series. Anything else will apply the argument value to every series.
- Many arguments accept many different types... for example the `color` (also `markercolor`, `fillcolor`, etc) argument will accept strings or symbols with a color name, or any `Colors.Colorant`, or a `ColorScheme`, or a symbol representing a `ColorGradient`, or an AbstractVector of colors/symbols/etc...
You can update certain plot settings after plot creation (not supported on all backends):
```julia
plot!(title = "New Title", xlabel = "New xlabel", ylabel = "New ylabel")
plot!(xlims = (0, 5.5), ylims = (-2.2, 6), xticks = 0:0.5:10, yticks = [0,1,5,10])
# using shorthands:
xaxis!("mylabel", :log10, :flip)
```
With `subplot`, create multiple plots at once, with flexible layout options:
```julia
y = rand(100,3)
subplot(y; n = 3) # create an automatic grid, and let it figure out the shape
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns
subplot(y; layout = [1, 2]) # explicit layout. Lists the number of plots in each row
```
__Tip__: You can call `subplot!(args...; kw...)` to add to an existing subplot.
__Tip__: Calling `subplot!` on a `Plot` object, or `plot!` on a `Subplot` object will throw an error.
Shorthands:
```julia
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
title!(s::AbstractString) = plot!(title = s)
xlabel!(s::AbstractString) = plot!(xlabel = s)
ylabel!(s::AbstractString) = plot!(ylabel = s)
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(xlims = lims)
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(ylims = lims)
xticks!{T<:Real}(v::AVec{T}) = plot!(xticks = v)
yticks!{T<:Real}(v::AVec{T}) = plot!(yticks = v)
xflip!(flip::Bool = true) = plot!(xflip = flip)
yflip!(flip::Bool = true) = plot!(yflip = flip)
xaxis!(args...) = plot!(xaxis = args)
yaxis!(args...) = plot!(yaxis = args)
annotate!(anns) = plot!(annotation = anns)
```
### Keyword arguments:
[[KEYWORD_ARGS_TABLE]]
### Plot types:
[[LINETYPES_TABLE]]
### Line styles:
[[LINESTYLES_TABLE]]
### Markers:
[[MARKERS_TABLE]]
__Tip__: With supported backends, you can pass a `Plots.Shape` object for the `marker`/`markershape` arguments. `Shape` takes a vector of 2-tuples in the constructor, defining the points of the polygon's shape in a unit-scaled coordinate space. To make a square, for example, you could do `Shape([(1,1),(1,-1),(-1,-1),(-1,1)])`
__Tip__: You can see the default value for a given argument with `default(arg::Symbol)`, and set the default value with `default(arg::Symbol, value)` or `default(; kw...)`. For example set the default window size and whether we should show a legend with `default(size=(600,400), leg=false)`.
__Tip__: There are some helper arguments you can set: `xaxis`, `yaxis`, `line`, `marker`, `fill`. These go through special preprocessing to extract values into individual arguments. The order doesn't matter, and if you pass a single value it's equivalent to wrapping it in a Tuple. Examples:
```
plot(y, xaxis = ("mylabel", :log, :flip, (-1,1))) # this sets the `xlabel`, `xscale`, `xflip`, and `xlims` arguments automatically
plot(y, line = (:bar, :blue, :dot, 10)) # this sets the `linetype`, `color`, `linestyle`, and `linewidth` arguments automatically
plot(y, marker = (:rect, :red, 10)) # this sets the `markershape`, `markercolor`, and `markersize` arguments automatically
plot(y, fill = (:green, 10)) # this sets the `fillcolor` and `fillrange` arguments automatically
# Note: `fillrange` can be:
a number (fill to horizontal line)
a vector of numbers (different for each data point)
a tuple of vectors (fill a band)
```
__Tip__: When plotting multiple lines, you can set all series to use the same value, or pass in a matrix to cycle through values. Example:
```julia
plot(rand(100,4); color = [:red RGB(0,0,1)], # (Matrix) lines 1 and 3 are red, lines 2 and 4 are blue
axis = :auto, # lines 1 and 3 are on the left axis, lines 2 and 4 are on the right
markershape = [:rect, :star] # (Vector) ALL lines are passed the vector [:rect, :star1]
width = 5) # all lines have a width of 5
```
__Tip__: Not all features are supported for each backend, but you can see what's supported by calling the functions: `supportedArgs()`, `supportedAxes()`, `supportedTypes()`, `supportedStyles()`, `supportedMarkers()`, `subplotSupported()`
__Tip__: Call `gui()` to display the plot in a window. Interactivity depends on backend. Plotting at the REPL (without semicolon) implicitly calls `gui()`.
### Animations
Animations are created in 3 steps (see example #2):
- Initialize an `Animation` object.
- Save each frame of the animation with `frame(anim)`.
- Convert the frames to an animated gif with `gif(anim, filename, fps=15)`
## TODO features:
- [x] Plot vectors/matrices/functions
- [x] Plot DataFrames
- [x] Histograms
- [x] Grouping
- [x] Annotations
- [x] Scales
- [x] Categorical Inputs (strings, etc... for hist, bar? or can split one series into multiple?)
- [x] Custom markers
- [x] Animations
- [x] Subplots
- [ ] Contours
- [ ] Boxplots
- [ ] 3D plotting
- [ ] Scenes/Drawing
- [ ] Graphs
- [ ] Interactivity (GUIs)
## TODO backends:
- [x] Gadfly.jl
- [x] Immerse.jl
- [x] PyPlot.jl
- [x] UnicodePlots.jl
- [x] Qwt.jl
- [x] Winston.jl (deprecated)
- [ ] GLPlot.jl
- [ ] Bokeh.jl
- [ ] Vega.jl
- [ ] Gaston.jl
- [ ] Plotly.jl
- [ ] GoogleCharts.jl
- [ ] PLplot.jl
- [ ] TextPlots.jl
- [ ] ASCIIPlots.jl
- [ ] Sparklines.jl
- [ ] Hinton.jl
- [ ] ImageTerm.jl
- [ ] GraphViz.jl
- [ ] TikzGraphs.jl
- [ ] GraphLayout.jl
## More information on backends (both supported and unsupported)
See the wiki at: https://github.com/JuliaPlot/juliaplot_docs/wiki
Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

-23
View File
@@ -1,23 +0,0 @@
## Supported keyword arguments
![args](Plots.supportGraphArgs.png)
## Supported plot types
![types](Plots.supportGraphTypes.png)
## Supported markers
![markers](Plots.supportGraphMarkers.png)
## Supported line styles
![styles](Plots.supportGraphStyles.png)
## Supported scales
![scales](Plots.supportGraphScales.png)
## Supported axes
![axes](Plots.supportGraphAxes.png)
-333
View File
@@ -1,333 +0,0 @@
immutable GadflyPackage <: PlottingPackage end
immutable ImmersePackage <: PlottingPackage end
immutable PyPlotPackage <: PlottingPackage end
immutable QwtPackage <: PlottingPackage end
immutable UnicodePlotsPackage <: PlottingPackage end
immutable WinstonPackage <: PlottingPackage end
immutable BokehPackage <: PlottingPackage end
immutable PlotlyPackage <: PlottingPackage end
immutable GRPackage <: PlottingPackage end
immutable GLVisualizePackage <: PlottingPackage end
immutable NoPackage <: PlottingPackage end
typealias GadflyOrImmerse @compat(Union{GadflyPackage, ImmersePackage})
export
gadfly,
immerse,
pyplot,
qwt,
unicodeplots,
bokeh,
plotly,
gr,
glvisualize
# winston
gadfly(; kw...) = (default(; kw...); backend(:gadfly))
immerse(; kw...) = (default(; kw...); backend(:immerse))
pyplot(; kw...) = (default(; kw...); backend(:pyplot))
qwt(; kw...) = (default(; kw...); backend(:qwt))
unicodeplots(; kw...) = (default(; kw...); backend(:unicodeplots))
bokeh(; kw...) = (default(; kw...); backend(:bokeh))
plotly(; kw...) = (default(; kw...); backend(:plotly))
gr(; kw...) = (default(; kw...); backend(:gr))
glvisualize(; kw...) = (default(; kw...); backend(:glvisualize))
# winston(; kw...) = (default(; kw...); backend(:winston))
backend_name(::GadflyPackage) = :gadfly
backend_name(::ImmersePackage) = :immerse
backend_name(::PyPlotPackage) = :pyplot
backend_name(::UnicodePlotsPackage) = :unicodeplots
backend_name(::QwtPackage) = :qwt
backend_name(::BokehPackage) = :bokeh
backend_name(::PlotlyPackage) = :plotly
backend_name(::GRPackage) = :gr
backend_name(::GLVisualizePackage) = :glvisualize
backend_name(::NoPackage) = :none
include("backends/supported.jl")
include("backends/qwt.jl")
include("backends/gadfly.jl")
include("backends/unicodeplots.jl")
include("backends/pyplot.jl")
include("backends/immerse.jl")
include("backends/winston.jl")
include("backends/web.jl")
include("backends/bokeh.jl")
include("backends/plotly.jl")
include("backends/gr.jl")
include("backends/glvisualize.jl")
# ---------------------------------------------------------
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
_update_plot(pkg::PlottingPackage, plt::Plot, d::Dict) = error("_update_plot($pkg, plt, d) is not implemented")
# Base.display(pkg::PlottingPackage, plt::Plot) = error("display($pkg, plt) is not implemented")
_update_plot_pos_size{P<:PlottingPackage}(plt::PlottingObject{P}, d::Dict) = nothing #error("_update_plot_pos_size(plt,d) is not implemented for $P")
subplot(pkg::PlottingPackage; kw...) = error("subplot($pkg; kw...) is not implemented")
subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
# Base.display(pkg::PlottingPackage, subplt::Subplot) = error("display($pkg, subplt) is not implemented")
# ---------------------------------------------------------
const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly, :gr]
const _initialized_backends = Set{Symbol}()
backends() = BACKENDS
function _backend_instance(sym::Symbol)
sym == :qwt && return QwtPackage()
sym == :gadfly && return GadflyPackage()
sym == :unicodeplots && return UnicodePlotsPackage()
sym == :pyplot && return PyPlotPackage()
sym == :immerse && return ImmersePackage()
sym == :winston && return WinstonPackage()
sym == :bokeh && return BokehPackage()
sym == :plotly && return PlotlyPackage()
sym == :gr && return GRPackage()
sym == :glvisualize && return GLVisualizePackage()
sym == :none && return NoPackage()
error("Unsupported backend $sym")
end
type CurrentBackend
sym::Symbol
pkg::PlottingPackage
end
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)))
end
end
backend(:plotly)
end
# ---------------------------------------------------------
"""
Returns the current plotting package name. Initializes package on first call.
"""
function backend()
global CURRENT_BACKEND
if CURRENT_BACKEND.sym == :none
pickDefaultBackend()
end
currentBackendSymbol = CURRENT_BACKEND.sym
if !(currentBackendSymbol in _initialized_backends)
# initialize
println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym)
if currentBackendSymbol == :qwt
try
@eval import Qwt
@eval export Qwt
catch err
warn("Couldn't import Qwt. Install it with: Pkg.clone(\"https://github.com/tbreloff/Qwt.jl.git\")\n (Note: also requires pyqt and pyqwt).")
rethrow(err)
end
elseif currentBackendSymbol == :gadfly
try
@eval import Gadfly, Compose
@eval export Gadfly, Compose
@eval include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
catch err
warn("Couldn't import Gadfly. Install it with: Pkg.add(\"Gadfly\").")
rethrow(err)
end
elseif currentBackendSymbol == :unicodeplots
try
@eval import UnicodePlots
@eval export UnicodePlots
catch err
warn("Couldn't import UnicodePlots. Install it with: Pkg.add(\"UnicodePlots\").")
rethrow(err)
end
elseif currentBackendSymbol == :pyplot
try
@eval import PyPlot
@eval export PyPlot
@eval const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
@eval const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path"))
@eval const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d"))
# @eval const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar"))
if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia
# # TODO: how the hell can I use PyQt4??
# "pyqt4"=>:qt_pyqt4
# PyPlot.backend[1] = "pyqt4"
# PyPlot.gui[1] = :qt_pyqt4
# PyPlot.switch_backend("Qt4Agg")
# only turn on the gui if we want it
if PyPlot.gui != :none
PyPlot.pygui(true)
end
end
catch err
warn("Couldn't import PyPlot. Install it with: Pkg.add(\"PyPlot\").")
rethrow(err)
end
elseif currentBackendSymbol == :immerse
try
@eval import Immerse, Gadfly, Compose, Gtk
@eval export Immerse, Gadfly, Compose, Gtk
@eval include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
catch err
# error("Couldn't import Immerse. Install it with: Pkg.add(\"Immerse\").\n Error: ", err)
warn("Couldn't import Immerse. Install it with: Pkg.add(\"Immerse\").")
rethrow(err)
end
elseif currentBackendSymbol == :bokeh
try
@eval import Bokeh
@eval export Bokeh
catch err
warn("Couldn't import Bokeh. Install it with: Pkg.add(\"Bokeh\").")
rethrow(err)
end
elseif currentBackendSymbol == :plotly
try
@eval begin
import JSON
JSON._print(io::IO, state::JSON.State, dt::Union{Date,DateTime}) = print(io, '"', dt, '"')
############################
# borrowed from https://github.com/spencerlyon2/Plotlyjs.jl/blob/master/src/display.jl
_js_path = joinpath(Pkg.dir("Plots"), "deps", "plotly-latest.min.js")
# if we're in IJulia call setupnotebook to load js and css
if isijulia()
# the first script is some hack I needed to do in order for the notebook
# to not complain about Plotly being undefined
display("text/html", """
<script type="text/javascript">
require=requirejs=define=undefined;
</script>
<script type="text/javascript">
$(open(readall, _js_path, "r"))
</script>
""")
# display("text/html", "<p>Plotly javascript loaded.</p>")
end
# end borrowing (thanks :)
###########################
try
include(joinpath(Pkg.dir("Plots"), "src", "backends", "plotly_blink.jl"))
catch err
warn("Error including PlotlyJS: $err\n Note: Will fall back to built-in display.")
end
end
catch err
warn("Couldn't setup Plotly")
rethrow(err)
end
elseif currentBackendSymbol == :gr
try
@eval import GR
catch err
warn("Couldn't import GR. Install it with: Pkg.add(\"GR\").")
end
elseif currentBackendSymbol == :glvisualize
try
@eval import GLVisualize
@eval export GLVisualize
catch err
warn("Couldn't setup GLVisualize")
rethrow(err)
end
elseif currentBackendSymbol == :winston
warn("Winston support is deprecated and broken. Try another backend: $BACKENDS")
try
@eval ENV["WINSTON_OUTPUT"] = "gtk"
@eval import Winston, Gtk
@eval export Winston, Gtk
catch err
warn("Couldn't import Winston. Install it with: Pkg.add(\"Winston\").")
rethrow(err)
end
else
error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS")
end
push!(_initialized_backends, currentBackendSymbol)
end
CURRENT_BACKEND.pkg
end
"""
Set the plot backend. Choose from: :qwt, :gadfly, :unicodeplots, :immerse, :pyplot
"""
function backend(pkg::PlottingPackage)
CURRENT_BACKEND.sym = backend_name(pkg)
CURRENT_BACKEND.pkg = pkg
end
function backend(modname)
# set the PlottingPackage
if modname == :qwt
CURRENT_BACKEND.pkg = QwtPackage()
elseif modname == :gadfly
CURRENT_BACKEND.pkg = GadflyPackage()
elseif modname == :unicodeplots
CURRENT_BACKEND.pkg = UnicodePlotsPackage()
elseif modname == :pyplot
CURRENT_BACKEND.pkg = PyPlotPackage()
elseif modname == :immerse
CURRENT_BACKEND.pkg = ImmersePackage()
elseif modname == :winston
CURRENT_BACKEND.pkg = WinstonPackage()
elseif modname == :bokeh
CURRENT_BACKEND.pkg = BokehPackage()
elseif modname == :plotly
CURRENT_BACKEND.pkg = PlotlyPackage()
elseif modname == :gr
CURRENT_BACKEND.pkg = GRPackage()
elseif modname == :glvisualize
CURRENT_BACKEND.pkg = GLVisualizePackage()
else
error("Unknown backend $modname. Choose from: $BACKENDS")
end
# update the symbol
CURRENT_BACKEND.sym = modname
# println("[Plots.jl] Switched to backend: ", modname)
# return the package
CURRENT_BACKEND.pkg
end
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-21
View File
@@ -1,21 +0,0 @@
function testplot_line1()
plot(rand(100,3))
end
function testplot_fn1()
plot(0:0.01:4π, [sin,cos])
end
function testplot_guides1()
plot(rand(10); title="TITLE", xlabel="XLABEL", ylabel="YLABEL", background_color=:red)
end
function testplot_points1()
plot(Vector[rand(10), rand(20)]; marker=:ellipse, markersize=8)
end
function testplot_points2()
plot(Vector[rand(10), rand(20)]; marker=:ellipse, markersize=8, markercolors=[:red,:blue])
end
+177 -147
View File
@@ -7,122 +7,153 @@ using Compat
using Reexport
@reexport using Colors
using Requires
using FixedSizeArrays
export
Plot,
Subplot,
SubplotLayout,
GridLayout,
FlexLayout,
AVec,
AMat,
AbstractPlot,
Plot,
Subplot,
SubplotLayout,
GridLayout,
RowsLayout,
FlexLayout,
AVec,
AMat,
KW,
plot,
plot!,
subplot,
subplot!,
wrap,
set_theme,
add_theme,
current,
default,
with,
scatter,
scatter!,
bar,
bar!,
histogram,
histogram!,
density,
density!,
heatmap,
heatmap!,
hexbin,
hexbin!,
sticks,
sticks!,
hline,
hline!,
vline,
vline!,
ohlc,
ohlc!,
pie,
pie!,
contour,
contour!,
surface,
surface!,
wireframe,
wireframe!,
path3d,
path3d!,
plot3d,
plot3d!,
scatter3d,
scatter3d!,
abline!,
plot,
plot!,
subplot,
subplot!,
title!,
xlabel!,
ylabel!,
xlims!,
ylims!,
xticks!,
yticks!,
annotate!,
xflip!,
yflip!,
xaxis!,
yaxis!,
current,
default,
with,
savefig,
png,
gui,
scatter,
scatter!,
bar,
bar!,
barh,
barh!,
histogram,
histogram!,
histogram2d,
histogram2d!,
density,
density!,
heatmap,
heatmap!,
hexbin,
hexbin!,
sticks,
sticks!,
hline,
hline!,
vline,
vline!,
ohlc,
ohlc!,
pie,
pie!,
contour,
contour!,
contour3d,
contour3d!,
surface,
surface!,
wireframe,
wireframe!,
path3d,
path3d!,
plot3d,
plot3d!,
scatter3d,
scatter3d!,
abline!,
boxplot,
boxplot!,
violin,
violin!,
quiver,
quiver!,
backend,
backends,
backend_name,
aliases,
dataframes,
title!,
xlabel!,
ylabel!,
xlims!,
ylims!,
zlims!,
xticks!,
yticks!,
annotate!,
xflip!,
yflip!,
xaxis!,
yaxis!,
Shape,
text,
font,
stroke,
brush,
Surface,
OHLC,
savefig,
png,
gui,
colorscheme,
ColorScheme,
ColorGradient,
ColorVector,
ColorWrapper,
ColorFunction,
ColorZFunction,
getColor,
getColorZ,
backend,
backends,
backend_name,
aliases,
dataframes,
debugplots,
Shape,
text,
font,
stroke,
brush,
Surface,
OHLC,
supportedArgs,
supportedAxes,
supportedTypes,
supportedStyles,
supportedMarkers,
subplotSupported,
colorscheme,
ColorScheme,
ColorGradient,
ColorVector,
ColorWrapper,
ColorFunction,
ColorZFunction,
getColor,
getColorZ,
Animation,
frame,
gif,
@animate,
debugplots,
# recipes
PlotRecipe,
# EllipseRecipe,
# spy,
corrplot
supportedArgs,
supportedAxes,
supportedTypes,
supportedStyles,
supportedMarkers,
subplotSupported,
Animation,
frame,
gif,
@animate,
@gif,
PlotRecipe,
spy,
arcdiagram,
chorddiagram,
translate,
translate!,
rotate,
rotate!,
center,
P2,
P3,
BezierCurve,
curve_points,
directed_curve
# ---------------------------------------------------------
@@ -130,10 +161,12 @@ include("types.jl")
include("utils.jl")
include("colors.jl")
include("components.jl")
include("plotter2.jl")
include("backends.jl")
include("args.jl")
include("plot.jl")
include("series_args.jl")
include("subplot.jl")
include("layouts.jl")
include("recipes.jl")
include("animation.jl")
include("output.jl")
@@ -145,8 +178,12 @@ 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)
histogram2d!(args...; kw...) = plot!(args...; kw..., linetype = :hist2d)
density(args...; kw...) = plot(args...; kw..., linetype = :density)
density!(args...; kw...) = plot!(args...; kw..., linetype = :density)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
@@ -161,10 +198,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)
@@ -175,20 +214,28 @@ 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::@compat(AbstractString); kw...) = plot!(; title = s, kw...)
xlabel!(s::@compat(AbstractString); kw...) = plot!(; xlabel = s, kw...)
ylabel!(s::@compat(AbstractString); kw...) = plot!(; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(lims::@compat(Tuple{T,S}); kw...) = plot!(; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(lims::@compat(Tuple{T,S}); kw...) = plot!(; ylims = lims, kw...)
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; ylims = lims, kw...)
zlims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; zlims = lims, kw...)
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin,zmax), kw...)
xticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; xticks = v, kw...)
yticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; yticks = v, kw...)
xticks!{T<:Real,S<:@compat(AbstractString)}(
xticks!{T<:Real,S<:AbstractString}(
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:@compat(AbstractString)}(
yticks!{T<:Real,S<:AbstractString}(
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; yticks = (ticks,labels), kw...)
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
annotate!{T<:Tuple}(anns::AVec{T}; kw...) = plot!(; annotation = anns, kw...)
@@ -197,18 +244,20 @@ yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
title!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(plt::Plot, lims::@compat(Tuple{T,S}); kw...) = plot!(plt; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(plt::Plot, lims::@compat(Tuple{T,S}); kw...) = plot!(plt; ylims = lims, kw...)
title!(plt::Plot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
zlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
xlims!(plt::Plot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
ylims!(plt::Plot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
zlims!(plt::Plot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
xticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
yticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
xticks!{T<:Real,S<:@compat(AbstractString)}(plt::Plot,
xticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:@compat(AbstractString)}(plt::Plot,
yticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
annotate!(plt::Plot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
annotate!{T<:Tuple}(plt::Plot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
@@ -221,38 +270,19 @@ yaxis!(plt::Plot, args...; kw...) = plot!(pl
# ---------------------------------------------------------
# try
# import DataFrames
# dataframes()
# end
# const CURRENT_BACKEND = pickDefaultBackend()
# for be in backends()
# try
# backend(be)
# backend()
# catch err
# @show err
# end
# end
const CURRENT_BACKEND = CurrentBackend(:none)
# function __init__()
# # global const CURRENT_BACKEND = pickDefaultBackend()
# # global const CURRENT_BACKEND = CurrentBackend(:none)
function __init__()
# # global CURRENT_BACKEND
# # println("[Plots.jl] Default backend: ", CURRENT_BACKEND.sym)
# 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
# # # auto init dataframes if the import statement doesn't error out
# # try
# # @eval import DataFrames
# # dataframes()
# # end
# end
setup_dataframes()
setup_atom()
end
# ---------------------------------------------------------
+68 -18
View File
@@ -9,7 +9,7 @@ function Animation()
Animation(tmpdir, ASCIIString[])
end
function frame{P<:PlottingObject}(anim::Animation, plt::P=current())
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(plt, joinpath(anim.dir, filename))
@@ -31,7 +31,7 @@ function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Inte
# high quality
speed = round(Int, 100 / fps)
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png $fn`)
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png -alpha off $fn`)
catch err
warn("Tried to create gif using convert (ImageMagick), but got error: $err\nWill try ffmpeg, but it's lower quality...)")
@@ -55,6 +55,69 @@ end
# -----------------------------------------------
function _animate(forloop::Expr, args...; callgif = false)
if forloop.head != :for
error("@animate macro expects a for-block. got: $(forloop.head)")
end
# add the call to frame to the end of each iteration
animsym = gensym("anim")
countersym = gensym("counter")
block = forloop.args[2]
# create filter
n = length(args)
filterexpr = if n == 0
# no filter... every iteration gets a frame
true
elseif args[1] == :every
# filter every `freq` frames (starting with the first frame)
@assert n == 2
freq = args[2]
@assert isa(freq, Integer) && freq > 0
:(mod1($countersym, $freq) == 1)
elseif args[1] == :when
# filter on custom expression
@assert n == 2
args[2]
else
error("Unsupported animate filter: $args")
end
push!(block.args, :(if $filterexpr; frame($animsym); end))
push!(block.args, :($countersym += 1))
# add a final call to `gif(anim)`?
retval = callgif ? :(gif($animsym)) : animsym
# full expression:
esc(quote
$animsym = Animation() # init animation object
$countersym = 1 # init iteration counter
$forloop # for loop, saving a frame after each iteration
$retval # return the animation object, or the gif
end)
end
"""
Builds an `Animation` using one frame per loop iteration, then create an animated GIF.
Example:
```
p = plot(1)
@gif for x=0:0.1:5
push!(p, 1, sin(x))
end
```
"""
macro gif(forloop::Expr, args...)
_animate(forloop, args...; callgif = true)
end
"""
Collect one frame per for-block iteration and return an `Animation` object.
@@ -65,22 +128,9 @@ Example:
anim = @animate for x=0:0.1:5
push!(p, 1, sin(x))
end
gif(anim)
```
"""
macro animate(forloop::Expr)
if forloop.head != :for
error("@animate macro expects a for-block. got: $(forloop.head)")
end
# add the call to frame to the end of each iteration
animsym = gensym("anim")
block = forloop.args[2]
push!(block.args, :(frame($animsym)))
# full expression:
esc(quote
$animsym = Animation() # init animation object
$forloop # for loop, saving a frame after each iteration
$animsym # return the animation object
end)
macro animate(forloop::Expr, args...)
_animate(forloop, args...)
end
+586 -428
View File
File diff suppressed because it is too large Load Diff
+160
View File
@@ -0,0 +1,160 @@
immutable NoBackend <: AbstractBackend end
const _backendType = Dict{Symbol, DataType}(:none => NoBackend)
const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none)
const _backends = Symbol[]
const _initialized_backends = Set{Symbol}()
backends() = _backends
backend_name() = CURRENT_BACKEND.sym
_backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
macro init_backend(s)
str = lowercase(string(s))
sym = symbol(str)
T = symbol(string(s) * "Backend")
esc(quote
immutable $T <: AbstractBackend end
export $sym
$sym(; kw...) = (default(; kw...); backend(symbol($str)))
backend_name(::$T) = symbol($str)
push!(_backends, symbol($str))
_backendType[symbol($str)] = $T
_backendSymbol[$T] = symbol($str)
include("backends/" * $str * ".jl")
end)
end
@init_backend Immerse
@init_backend Gadfly
@init_backend PyPlot
@init_backend Qwt
@init_backend UnicodePlots
@init_backend Winston
@init_backend Bokeh
@init_backend Plotly
@init_backend PlotlyJS
@init_backend GR
@init_backend GLVisualize
@init_backend PGFPlots
include("backends/web.jl")
# include("backends/supported.jl")
# ---------------------------------------------------------
plot(pkg::AbstractBackend; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::AbstractBackend, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
_update_plot(pkg::AbstractBackend, plt::Plot, d::KW) = error("_update_plot($pkg, plt, d) is not implemented")
_update_plot_pos_size{P<:AbstractBackend}(plt::AbstractPlot{P}, d::KW) = nothing
subplot(pkg::AbstractBackend; kw...) = error("subplot($pkg; kw...) is not implemented")
subplot!(pkg::AbstractBackend, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
# ---------------------------------------------------------
type CurrentBackend
sym::Symbol
pkg::AbstractBackend
end
CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
# ---------------------------------------------------------
function pickDefaultBackend()
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
# 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
# ---------------------------------------------------------
"""
Returns the current plotting package name. Initializes package on first call.
"""
function backend()
global CURRENT_BACKEND
if CURRENT_BACKEND.sym == :none
pickDefaultBackend()
end
sym = CURRENT_BACKEND.sym
if !(sym in _initialized_backends)
# initialize
println("[Plots.jl] Initializing backend: ", sym)
inst = _backend_instance(sym)
try
_initialize_backend(inst)
catch err
warn("Couldn't initialize $sym. (might need to install it?)")
rethrow(err)
end
push!(_initialized_backends, sym)
end
CURRENT_BACKEND.pkg
end
"""
Set the plot backend.
"""
function backend(pkg::AbstractBackend)
CURRENT_BACKEND.sym = backend_name(pkg)
CURRENT_BACKEND.pkg = pkg
end
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
supportedAxes() = supportedAxes(backend())
supportedTypes() = supportedTypes(backend())
supportedStyles() = supportedStyles(backend())
supportedMarkers() = supportedMarkers(backend())
supportedScales() = supportedScales(backend())
subplotSupported() = subplotSupported(backend())
stringsSupported() = stringsSupported(backend())
# ---------------------------------------------------------
+101 -39
View File
@@ -2,8 +2,78 @@
# https://github.com/bokeh/Bokeh.jl
function _initialize_backend(::BokehPackage; kw...)
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.")
import Bokeh
export Bokeh
end
@@ -17,23 +87,23 @@ end
# bokehcolor(cs::ColorScheme) = bokehcolor(getColor(cs))
const _glyphtypes = Dict(
const _glyphtypes = KW(
:ellipse => :Circle,
:rect => :Square,
:diamond => :Diamond,
:utriangle => :Triangle,
:dtriangle => :InvertedTriangle,
# :pentagon =>
# :hexagon =>
# :heptagon =>
# :octagon =>
# :pentagon =>
# :hexagon =>
# :heptagon =>
# :octagon =>
:cross => :Cross,
:xcross => :X,
:star5 => :Asterisk,
)
function bokeh_glyph_type(d::Dict)
function bokeh_glyph_type(d::KW)
lt = d[:linetype]
mt = d[:markershape]
if lt == :scatter && mt == :none
@@ -63,11 +133,7 @@ end
# ---------------------------------------------------------------------------
function _create_plot(pkg::BokehPackage; kw...)
d = Dict(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
@@ -80,20 +146,16 @@ function _create_plot(pkg::BokehPackage; kw...)
yaxis_type = d[:yscale] == :log10 ? :log : :auto
# legend = d[:legend] ? xxxx : nothing
legend = nothing
extra_args = Dict() # TODO: we'll put extra settings (xlim, etc) here
extra_args = KW() # TODO: we'll put extra settings (xlim, etc) here
bplt = Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
Plot(bplt, pkg, 0, d, Dict[])
Plot(bplt, pkg, 0, d, KW[])
end
function _add_series(::BokehPackage, plt::Plot; kw...)
d = Dict(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(
glyphtype = bokeh_glyph_type(d),
linecolor = webcolor(d[:linecolor]), # shape's stroke or line color
@@ -113,31 +175,31 @@ end
# ----------------------------------------------------------------
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{BokehPackage}, d::Dict)
function _update_plot(plt::Plot{BokehBackend}, d::KW)
end
function _update_plot_pos_size(plt::PlottingObject{BokehPackage}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{BokehBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{BokehPackage}, i::Int)
series = plt.o.datacolumns[i].data
series[:x], series[:y]
end
function Base.setindex!(plt::Plot{BokehPackage}, 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
# ----------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{BokehPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{BokehBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
@@ -145,31 +207,31 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{BokehPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{BokehBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
function _expand_limits(lims, plt::Plot{BokehPackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{BokehBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{BokehPackage}, isx::Bool)
function _remove_axis(plt::Plot{BokehBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{BokehPackage})
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
# TODO: write a png to io
warn("mime png not implemented")
end
function Base.display(::PlotsDisplay, plt::Plot{BokehPackage})
function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
Bokeh.showplot(plt.o)
end
function Base.display(::PlotsDisplay, plt::Subplot{BokehPackage})
function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
# TODO: display/show the subplot
end
+485 -482
View File
File diff suppressed because it is too large Load Diff
+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
# ---------------------------------------------------------------------------------------------
+87 -28
View File
@@ -2,7 +2,74 @@
# [WEBSITE]
function _initialize_backend(::GLVisualizePackage; kw...)
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
export GLVisualize
@@ -13,37 +80,29 @@ end
immutable GLScreenWrapper
window
render
end
function _create_plot(pkg::GLVisualizePackage; kw...)
d = Dict(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
# TODO: this should be moved to the display method?
w,r=GLVisualize.glscreen()
@async r()
o = GLScreenWrapper(w,r)
Plot(o, pkg, 0, d, Dict[])
w=GLVisualize.glscreen()
@async GLVisualize.renderloop(w)
Plot(GLScreenWrapper(w), pkg, 0, d, KW[])
end
function _add_series(::GLVisualizePackage, plt::Plot; kw...)
d = Dict(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?
x, y, z = map(Float32, d[:x]), map(Float32, d[:y]), map(Float32, d[:z].surf)
viz = GLVisualize.visualize(x*ones(y)', ones(x)*y', z, :surface)
GLVisualize.view(viz)
x,y,z=map(Float32,d[:x]), map(Float32,d[:y]), map(Float32,d[:z].surf)
GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface),plt.o.window)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{GLVisualizePackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{GLVisualizeBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
@@ -51,28 +110,28 @@ end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{GLVisualizePackage})
function _before_update_plot(plt::Plot{GLVisualizeBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{GLVisualizePackage}, d::Dict)
function _update_plot(plt::Plot{GLVisualizeBackend}, d::KW)
end
function _update_plot_pos_size(plt::PlottingObject{GLVisualizePackage}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{GLVisualizeBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{GLVisualizePackage}, 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{GLVisualizePackage}, 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
@@ -81,25 +140,25 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{GLVisualizePackage})
function _create_subplot(subplt::Subplot{GLVisualizeBackend})
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
function _expand_limits(lims, plt::Plot{GLVisualizePackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{GLVisualizeBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{GLVisualizePackage}, isx::Bool)
function _remove_axis(plt::Plot{GLVisualizeBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{GLVisualizePackage})
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
# TODO: write a png to io
end
function Base.display(::PlotsDisplay, plt::Plot{GLVisualizePackage})
function Base.display(::PlotsDisplay, plt::Plot{GLVisualizeBackend})
# TODO: display/show the plot
# NOTE: I think maybe this should be empty? We can start with the assumption that creating
@@ -107,6 +166,6 @@ function Base.display(::PlotsDisplay, plt::Plot{GLVisualizePackage})
# wouldn't actually need to do anything
end
function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizePackage})
function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizeBackend})
# TODO: display/show the subplot
end
+422 -135
View File
@@ -2,25 +2,69 @@
# https://github.com/jheinen/GR.jl
function _initialize_backend(::GRPackage; kw...)
supportedArgs(::GRBackend) = [
: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,
: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,
]
supportedAxes(::GRBackend) = _allAxes
supportedTypes(::GRBackend) = [:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :hist2d, :hexbin, :hist, :density, :bar,
:hline, :vline, :contour, :heatmap, :path3d, :scatter3d, :surface,
:wireframe, :ohlc, :pie]
supportedStyles(::GRBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GRBackend) = vcat(_allMarkers, Shape)
supportedScales(::GRBackend) = [:identity, :log10]
subplotSupported(::GRBackend) = true
# --------------------------------------------------------------------------------------
function _initialize_backend(::GRBackend; kw...)
@eval begin
import GR
export GR
end
end
const gr_linetype = Dict(
const gr_linetype = KW(
:auto => 1, :solid => 1, :dash => 2, :dot => 3, :dashdot => 4,
:dashdotdot => -1 )
const gr_markertype = Dict(
const gr_markertype = KW(
:auto => 1, :none => -1, :ellipse => -1, :rect => -7, :diamond => -13,
:utriangle => -3, :dtriangle => -5, :pentagon => -14, :hexagon => 3,
:cross => 2, :xcross => 5, :star5 => 3 )
:utriangle => -3, :dtriangle => -5, :pentagon => -21, :hexagon => -22,
:heptagon => -23, :octagon => -24, :cross => 2, :xcross => 5,
:star4 => -25, :star5 => -26, :star6 => -27, :star7 => -28, :star8 => -29,
:vline => -30, :hline => -31 )
const gr_halign = Dict(:left => 1, :hcenter => 2, :right => 3)
const gr_valign = Dict(:top => 1, :vcenter => 3, :bottom => 5)
const gr_halign = KW(:left => 1, :hcenter => 2, :right => 3)
const gr_valign = KW(:top => 1, :vcenter => 3, :bottom => 5)
const gr_font_family = Dict(
"times" => 1, "helvetica" => 5, "courier" => 9, "bookman" => 14,
@@ -40,7 +84,87 @@ function gr_getaxisind(p)
end
end
function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
function gr_setmarkershape(p)
if haskey(p, :markershape)
shape = p[:markershape]
if isa(shape, Shape)
p[:vertices] = vertices(shape)
else
GR.setmarkertype(gr_markertype[shape])
p[:vertices] = :none
end
end
end
function gr_polymarker(p, x, y)
if p[:vertices] != :none
vertices= p[:vertices]
dx = Float64[el[1] for el in vertices] * 0.01
dy = Float64[el[2] for el in vertices] * 0.01
GR.selntran(0)
GR.setfillcolorind(gr_getcolorind(p[:markercolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
for i = 1:length(x)
xn, yn = GR.wctondc(x[i], y[i])
GR.fillarea(xn + dx, yn + dy)
end
GR.selntran(1)
else
GR.polymarker(x, y)
end
end
function gr_polyline(x, y)
if NaN in x || NaN in y
i = 1
j = 1
n = length(x)
while i < n
while j < n && x[j] != Nan && y[j] != NaN
j += 1
end
if i < j
GR.polyline(x[i:j], y[i:j])
end
i = j + 1
end
else
GR.polyline(x, y)
end
end
function gr_polaraxes(rmin, rmax)
GR.savestate()
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, @sprintf("%g", rmin + i * tick))
else
GR.setlinecolorind(90)
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_display(plt::Plot{GRBackend}, clear=true, update=true,
subplot=[0, 1, 0, 1])
d = plt.plotargs
@@ -49,90 +173,144 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
mwidth, mheight, width, height = GR.inqdspsize()
w, h = d[:size]
viewport = zeros(4)
vp = float(subplot)
if w > h
ratio = float(h) / w
msize = mwidth * w / width
GR.setwsviewport(0, msize, 0, msize * ratio)
GR.setwswindow(0, 1, 0, ratio)
viewport[1] = subplot[1] + 0.1 * (subplot[2] - subplot[1])
viewport[2] = subplot[1] + 0.95 * (subplot[2] - subplot[1])
viewport[3] = ratio * (subplot[3] + 0.1 * (subplot[4] - subplot[3]))
viewport[4] = ratio * (subplot[3] + 0.95 * (subplot[4] - subplot[3]))
vp[3] *= ratio
vp[4] *= ratio
else
ratio = float(w) / h
msize = mheight * h / height
GR.setwsviewport(0, msize * ratio, 0, msize)
GR.setwswindow(0, ratio, 0, 1)
viewport[1] = ratio * (subplot[1] + 0.1 * (subplot[2] - subplot[1]))
viewport[2] = ratio * (subplot[1] + 0.95 * (subplot[2] - subplot[1]))
viewport[3] = subplot[3] + 0.1 * (subplot[4] - subplot[3])
viewport[4] = subplot[3] + 0.95 * (subplot[4] - subplot[3])
vp[1] *= ratio
vp[2] *= ratio
end
viewport[1] = vp[1] + 0.125 * (vp[2] - vp[1])
viewport[2] = vp[1] + 0.95 * (vp[2] - vp[1])
viewport[3] = vp[3] + 0.125 * (vp[4] - vp[3])
if w > h
viewport[3] += (1 - (subplot[4] - subplot[3])^2) * 0.02
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
extrema = zeros(2, 4)
num_axes = 1
cmap = false
axes_2d = true
grid_flag = get(d, :grid, true)
outside_ticks = false
for axis = 1:2
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]
elseif lt in [:hist, :density]
x, y = Base.hist(p[:y])
elseif p[:linetype] in [:heatmap, :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
cmap = true
x, y, H = Base.hist2d(E, xbins, ybins)
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]
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 lt == :heatmap
outside_ticks = true
end
x, y = p[:x], p[:y]
end
xmin = min(minimum(x), xmin)
xmax = max(maximum(x), xmax)
if p[:linetype] == :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)
if !(lt in [:pie, :polar])
xmin = min(minimum(x), xmin)
xmax = max(maximum(x), xmax)
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)
end
else
ymin = min(minimum(y), ymin)
ymax = max(maximum(y), ymax)
end
else
ymin = min(minimum(y), ymin)
ymax = max(maximum(y), ymax)
end
end
end
if d[:xlims] != :auto
xmin, xmax = d[:xlims]
end
if d[:ylims] != :auto
ymin, ymax = d[:ylims]
end
if xmax <= xmin
xmax = xmin + 1
end
if ymax <= ymin
ymax = ymin + 1
end
extrema[axis,:] = [xmin, xmax, ymin, ymax]
end
if num_axes == 2 || !axes_2d
viewport[2] -= 0.05
viewport[2] -= 0.0525
end
if cmap
viewport[2] -= 0.1
end
GR.setviewport(viewport[1], viewport[2], viewport[3], viewport[4])
scale = d[:scale]
scale = 0
d[:xscale] == :log10 && (scale |= GR.OPTION_X_LOG)
d[:yscale] == :log10 && (scale |= GR.OPTION_Y_LOG)
get(d, :xflip, false) && (scale |= GR.OPTION_FLIP_X)
get(d, :yflip, false) && (scale |= GR.OPTION_FLIP_Y)
for axis = 1:num_axes
xmin, xmax, ymin, ymax = extrema[axis,:]
if scale & GR.OPTION_X_LOG == 0
@@ -161,22 +339,23 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
end
GR.setwindow(xmin, xmax, ymin, ymax)
if axis == 1 && haskey(d, :background_color)
GR.savestate()
GR.setfillintstyle(GR.INTSTYLE_SOLID)
GR.setfillcolorind(gr_getcolorind(d[:background_color]))
GR.fillrect(xmin, xmax, ymin, ymax)
GR.restorestate()
end
GR.setscale(scale)
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.018 * diag, 0.01)
GR.setcharheight(charheight)
GR.settextcolorind(fg)
if axes_2d
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
GR.setlinewidth(1)
charheight = max(0.018 * diag, 0.01)
GR.setcharheight(charheight)
GR.setlinecolorind(fg)
ticksize = 0.0075 * diag
GR.grid(xtick, ytick, 0, 0, majorx, majory)
if outside_ticks
ticksize = -ticksize
end
if grid_flag && fg == 1
GR.grid(xtick, ytick, 0, 0, majorx, majory)
end
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)
@@ -191,63 +370,75 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
if get(d, :title, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
GR.text(0.5, min(ratio, 1), d[:title])
GR.settextcolorind(fg)
GR.text(0.5 * (viewport[1] + viewport[2]), vp[4], d[:title])
GR.restorestate()
end
if get(d, :xlabel, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM)
GR.text(0.5, 0, d[:xlabel])
GR.settextcolorind(fg)
GR.text(0.5 * (viewport[1] + viewport[2]), vp[3], d[:xlabel])
GR.restorestate()
end
if get(d, :ylabel, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
GR.setcharup(-1, 0)
GR.text(0, 0.5 * (viewport[3] + viewport[4]), d[:ylabel])
GR.settextcolorind(fg)
GR.text(vp[1], 0.5 * (viewport[3] + viewport[4]), d[:ylabel])
GR.restorestate()
end
if get(d, :yrightlabel, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
GR.setcharup(1, 0)
GR.text(1, 0.5 * (viewport[3] + viewport[4]), d[:yrightlabel])
GR.settextcolorind(fg)
GR.text(vp[2], 0.5 * (viewport[3] + viewport[4]), d[:yrightlabel])
GR.restorestate()
end
legend = false
for p in plt.seriesargs
lt = p[:linetype]
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]
if lt in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :ohlc, :polar]
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]))
end
if p[:linetype] == :path
if lt == :path
if haskey(p, :fillcolor)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
end
if p[:fillrange] != nothing
GR.fillarea([p[:x][1]; p[:x]; p[:x][length(p[:x])]], [p[:fillrange]; p[:y]; p[:fillrange]])
if length(p[:x]) > 1
if p[:fillrange] != nothing
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
GR.polyline(p[:x], p[:y])
legend = true
end
if p[:linetype] == :line
GR.polyline(p[:x], p[:y])
if lt == :line
if length(p[:x]) > 1
gr_polyline(p[:x], p[:y])
end
legend = true
elseif p[:linetype] in [:steppre, :steppost]
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
@@ -256,38 +447,44 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
end
j += 2
end
GR.polyline(x, y)
if n > 1
GR.polyline(x, y)
end
legend = true
elseif p[:linetype] == :sticks
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)
elseif lt == :scatter || (p[:markershape] != :none && axes_2d)
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
haskey(p, :markershape) && GR.setmarkertype(gr_markertype[p[:markershape]])
gr_setmarkershape(p)
if haskey(d, :markersize)
if typeof(d[:markersize]) <: Number
GR.setmarkersize(d[:markersize] / 4.0)
GR.polymarker(p[:x], p[:y])
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[:zcolor] != nothing
ci = round(Int, 1000 + p[:zcolor][i] * 255)
if isa(c, ColorGradient) && p[:marker_z] != nothing
ci = round(Int, 1000 + p[:marker_z][i] * 255)
GR.setmarkercolorind(ci)
end
GR.setmarkersize(d[:markersize][i] / 4.0)
GR.polymarker([p[:x][i]], [p[:y][i]])
GR.setmarkersize(p[:markersize][i] / 4.0)
gr_polymarker(p, [p[:x][i]], [p[:y][i]])
end
end
else
GR.polymarker(p[:x], p[:y])
if length(p[:x]) > 0
gr_polymarker(p, p[:x], p[:y])
end
end
legend = true
elseif p[:linetype] == :bar
elseif lt == :bar
y = p[:y]
for i = 1:length(y)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
@@ -297,7 +494,7 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
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]
elseif lt in [:hist, :density]
h = Base.hist(p[:y])
x, y = float(collect(h[1])), float(h[2])
for i = 2:length(y)
@@ -308,22 +505,22 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
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 [:heatmap, :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))
@@ -337,7 +534,7 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
charheight = max(0.016 * diag, 0.01)
GR.setcharheight(charheight)
GR.colormap()
elseif p[:linetype] == :contour
elseif lt == :contour
x, y, z = p[:x], p[:y], p[:z].surf
zmin, zmax = minimum(z), maximum(z)
if typeof(p[:levels]) <: Array
@@ -358,7 +555,7 @@ function gr_display(plt::Plot{GRPackage}, 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]
elseif lt in [:surface, :wireframe]
x, y, z = p[:x], p[:y], p[:z].surf
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
GR.setspace(zmin, zmax, 40, 70)
@@ -368,13 +565,13 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.018 * diag, 0.01)
ticksize = 0.01 * (viewport[2] - viewport[1])
# GR.savestate()
GR.setlinewidth(1)
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
# GR.restorestate()
if grid_flag
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
end
z = reshape(z, length(x) * length(y))
if p[:linetype] == :surface
if lt == :surface
GR.setcolormap(GR.COLORMAP_COOLWARM)
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
else
@@ -390,7 +587,19 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
viewport[3], viewport[4])
GR.colormap()
end
elseif p[:linetype] in [:path3d, :scatter3d]
elseif lt == :heatmap
x, y, z = p[:x], p[:y], p[:z].surf
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
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)
if cmap
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
viewport[3], viewport[4])
GR.colormap()
end
elseif lt in [:path3d, :scatter3d]
x, y, z = p[:x], p[:y], p[:z]
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
GR.setspace(zmin, zmax, 40, 70)
@@ -400,27 +609,29 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.018 * diag, 0.01)
ticksize = 0.01 * (viewport[2] - viewport[1])
# GR.savestate()
GR.setlinewidth(1)
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
# GR.restorestate()
if p[:linetype] == :scatter3d
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 lt == :scatter3d
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
haskey(p, :markershape) && GR.setmarkertype(gr_markertype[p[:markershape]])
gr_setmarkershape(p)
for i = 1:length(z)
px, py = GR.wc3towc(x[i], y[i], z[i])
GR.polymarker([px], [py])
gr_polymarker(p, [px], [py])
end
else
haskey(p, :linewidth) && GR.setlinewidth(p[:linewidth])
GR.polyline3d(x, y, z)
if length(x) > 0
GR.polyline3d(x, y, z)
end
end
GR.setlinewidth(1)
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
@@ -429,8 +640,71 @@ function gr_display(plt::Plot{GRPackage}, 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] in [:pie]
println("TODO: add support for linetype $(p[:linetype])")
elseif lt == :pie
GR.selntran(0)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
xmin, xmax, ymin, ymax = viewport
ymax -= 0.05 * (xmax - xmin)
xcenter = 0.5 * (xmin + xmax)
ycenter = 0.5 * (ymin + ymax)
if xmax - xmin > ymax - ymin
r = 0.5 * (ymax - ymin)
xmin, xmax = xcenter - r, xcenter + r
else
r = 0.5 * (xmax - xmin)
ymin, ymax = ycenter - r, ycenter + r
end
labels, slices = p[:x], p[:y]
numslices = length(slices)
total = sum(slices)
a1 = 0
x = zeros(3)
y = zeros(3)
for i in 1:numslices
a2 = round(Int, a1 + (slices[i] / total) * 360.0)
GR.setfillcolorind(980 + (i-1) % 20)
GR.fillarc(xmin, xmax, ymin, ymax, a1, a2)
alpha = 0.5 * (a1 + a2)
cosf = r * cos(alpha * pi / 180)
sinf = r * sin(alpha * pi / 180)
x[1] = xcenter + cosf
y[1] = ycenter + sinf
x[2] = x[1] + 0.1 * cosf
y[2] = y[1] + 0.1 * sinf
y[3] = y[2]
if 90 <= alpha < 270
x[3] = x[2] - 0.05
GR.settextalign(GR.TEXT_HALIGN_RIGHT, GR.TEXT_VALIGN_HALF)
GR.text(x[3] - 0.01, y[3], string(labels[i]))
else
x[3] = x[2] + 0.05
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
GR.text(x[3] + 0.01, y[3], string(labels[i]))
end
GR.polyline(x, y)
a1 = a2
end
GR.selntran(1)
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
@@ -453,13 +727,13 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
end
px = viewport[2] - 0.05 - w
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 + 0.03, py - 0.03 * length(plt.seriesargs))
GR.fillrect(px - 0.08, px + w + 0.02, py + dy, py - dy * length(plt.seriesargs))
GR.setlinetype(1)
GR.setlinecolorind(1)
GR.setlinewidth(1)
GR.drawrect(px - 0.08, px + w + 0.02, py + 0.03, py - 0.03 * length(plt.seriesargs))
GR.drawrect(px - 0.08, px + w + 0.02, py + dy, py - dy * length(plt.seriesargs))
haskey(d, :linewidth) && GR.setlinewidth(d[:linewidth])
i = 0
for p in plt.seriesargs
@@ -470,11 +744,11 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
end
if p[:linetype] == :scatter || p[:markershape] != :none
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
haskey(p, :markershape) && GR.setmarkertype(gr_markertype[p[:markershape]])
gr_setmarkershape(p)
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks]
GR.polymarker([px - 0.06, px - 0.02], [py, py])
gr_polymarker(p, [px - 0.06, px - 0.02], [py, py])
else
GR.polymarker([px - 0.06, px - 0.04, px - 0.02], [py, py, py])
gr_polymarker(p, [px - 0.06, px - 0.04, px - 0.02], [py, py, py])
end
end
if typeof(p[:label]) <: Array
@@ -484,8 +758,9 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
lab = p[:label]
end
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
GR.settextcolorind(1)
GR.text(px, py, lab)
py -= 0.03
py -= dy
end
GR.selntran(1)
GR.restorestate()
@@ -513,7 +788,7 @@ function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
update && GR.updatews()
end
function gr_display(subplt::Subplot{GRPackage})
function gr_display(subplt::Subplot{GRBackend})
clear = true
update = false
l = enumerate(subplt.layout)
@@ -529,19 +804,16 @@ function gr_display(subplt::Subplot{GRPackage})
end
end
function _create_plot(pkg::GRPackage; kw...)
isijulia() && GR.inline("svg")
d = Dict(kw)
Plot(nothing, pkg, 0, d, Dict[])
function _create_plot(pkg::GRBackend, d::KW)
Plot(nothing, pkg, 0, d, KW[])
end
function _add_series(::GRPackage, plt::Plot; kw...)
d = Dict(kw)
function _add_series(::GRBackend, plt::Plot, d::KW)
push!(plt.seriesargs, d)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{GRPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{GRBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
if haskey(plt.plotargs, :anns)
append!(plt.plotargs[:anns], anns)
else
@@ -551,33 +823,26 @@ end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{GRPackage})
function _before_update_plot(plt::Plot{GRBackend})
end
function _update_plot(plt::Plot{GRPackage}, d::Dict)
scale = 0
d[:xscale] == :log10 && (scale |= GR.OPTION_X_LOG)
d[:yscale] == :log10 && (scale |= GR.OPTION_Y_LOG)
get(d, :xflip, false) && (scale |= GR.OPTION_FLIP_X)
get(d, :yflip, false) && (scale |= GR.OPTION_FLIP_Y)
plt.plotargs[:scale] = scale
function _update_plot(plt::Plot{GRBackend}, d::KW)
for k in (:title, :xlabel, :ylabel)
haskey(d, k) && (plt.plotargs[k] = d[k])
end
end
function _update_plot_pos_size(plt::PlottingObject{GRPackage}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{GRBackend}, d::KW)
end
# ----------------------------------------------------------------
function Base.getindex(plt::Plot{GRPackage}, i::Int)
function getxy(plt::Plot{GRBackend}, i::Int)
d = plt.seriesargs[i]
d[:x], d[:y]
end
function Base.setindex!(plt::Plot{GRPackage}, 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
@@ -585,39 +850,61 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{GRPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{GRBackend}, isbefore::Bool)
true
end
function _expand_limits(lims, plt::Plot{GRPackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{GRBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{GRPackage}, isx::Bool)
function _remove_axis(plt::Plot{GRBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, m::MIME"image/png", plt::PlottingObject{GRPackage})
function Base.writemime(io::IO, m::MIME"image/png", plt::AbstractPlot{GRBackend})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "png"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.png"))
end
function Base.writemime(io::IO, m::MIME"image/svg+xml", plt::PlottingObject{GRPackage})
isijulia() || return
function Base.writemime(io::IO, m::MIME"image/svg+xml", plt::AbstractPlot{GRBackend})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "svg"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.svg"))
end
function Base.display(::PlotsDisplay, plt::Plot{GRPackage})
function Base.writemime(io::IO, m::MIME"text/html", plt::AbstractPlot{GRBackend})
writemime(io, MIME("image/svg+xml"), plt)
end
function Base.writemime(io::IO, m::MIME"application/pdf", plt::AbstractPlot{GRBackend})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "pdf"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.pdf"))
end
function Base.writemime(io::IO, m::MIME"application/postscript", plt::AbstractPlot{GRBackend})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "ps"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.ps"))
end
function Base.display(::PlotsDisplay, plt::Plot{GRBackend})
gr_display(plt)
end
function Base.display(::PlotsDisplay, plt::Subplot{GRPackage})
function Base.display(::PlotsDisplay, plt::Subplot{GRBackend})
gr_display(plt)
true
end
+27 -20
View File
@@ -1,7 +1,17 @@
# https://github.com/JuliaGraphics/Immerse.jl
function _initialize_backend(::ImmersePackage; kw...)
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
export Immerse, Gadfly, Compose, Gtk
@@ -9,7 +19,7 @@ function _initialize_backend(::ImmersePackage; kw...)
end
end
function createImmerseFigure(d::Dict)
function createImmerseFigure(d::KW)
w,h = d[:size]
figidx = Immerse.figure(; name = d[:windowtitle], width = w, height = h)
Immerse.Figure(figidx)
@@ -19,27 +29,24 @@ end
# create a blank Gadfly.Plot object
function _create_plot(pkg::ImmersePackage; kw...)
d = Dict(kw)
function _create_plot(pkg::ImmerseBackend, d::KW)
# create the underlying Gadfly.Plot object
gplt = createGadflyPlotObject(d)
# save both the Immerse.Figure and the Gadfly.Plot
Plot((nothing,gplt), pkg, 0, d, Dict[])
Plot((nothing,gplt), pkg, 0, d, KW[])
end
# plot one data series
function _add_series(::ImmersePackage, plt::Plot; kw...)
d = Dict(kw)
function _add_series(::ImmerseBackend, plt::Plot, d::KW)
addGadflySeries!(plt, d)
push!(plt.seriesargs, d)
plt
end
function _update_plot(plt::Plot{ImmersePackage}, d::Dict)
function _update_plot(plt::Plot{ImmerseBackend}, d::KW)
updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d)
end
@@ -48,7 +55,7 @@ end
# ----------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{ImmersePackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{ImmerseBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
end
@@ -58,12 +65,12 @@ end
# accessors for x/y data
function Base.getindex(plt::Plot{ImmersePackage}, i::Integer)
function getxy(plt::Plot{ImmerseBackend}, i::Integer)
mapping = getGadflyMappings(plt, i)[1]
mapping[:x], mapping[:y]
end
function Base.setindex!(plt::Plot{ImmersePackage}, 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
@@ -74,12 +81,12 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{ImmersePackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
return false
# isbefore && return false
end
function showSubplotObject(subplt::Subplot{ImmersePackage})
function showSubplotObject(subplt::Subplot{ImmerseBackend})
# create the Gtk window with vertical box vsep
d = getplotargs(subplt,1)
w,h = d[:size]
@@ -121,13 +128,13 @@ function showSubplotObject(subplt::Subplot{ImmersePackage})
end
function _remove_axis(plt::Plot{ImmersePackage}, isx::Bool)
function _remove_axis(plt::Plot{ImmerseBackend}, isx::Bool)
gplt = getGadflyContext(plt)
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
end
function _expand_limits(lims, plt::Plot{ImmersePackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{ImmerseBackend}, isx::Bool)
for l in getGadflyContext(plt).layers
_expand_limits(lims, l.mapping[isx ? :x : :y])
end
@@ -136,11 +143,11 @@ end
# ----------------------------------------------------------------
getGadflyContext(plt::Plot{ImmersePackage}) = plt.o[2]
getGadflyContext(subplt::Subplot{ImmersePackage}) = buildGadflySubplotContext(subplt)
getGadflyContext(plt::Plot{ImmerseBackend}) = plt.o[2]
getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
function Base.display(::PlotsDisplay, plt::Plot{ImmersePackage})
function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
fig, gplt = plt.o
if fig == nothing
@@ -154,7 +161,7 @@ function Base.display(::PlotsDisplay, plt::Plot{ImmersePackage})
end
function Base.display(::PlotsDisplay, subplt::Subplot{ImmersePackage})
function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
# if we haven't created the window yet, do it
if subplt.o == nothing
+281 -34
View File
@@ -1,7 +1,75 @@
# https://github.com/sisl/PGFPlots.jl
function _initialize_backend(::PGFPlotsPackage; kw...)
supportedArgs(::PGFPlotsBackend) = [
# :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,
: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
export PGFPlots
@@ -9,25 +77,151 @@ function _initialize_backend(::PGFPlotsPackage; 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 => "mark = none,",
:cross => "mark = +,",
:xcross => "mark = x,",
:utriangle => "mark = triangle*,",
:dtriangle => "mark = triangle*,",
:ellipse => "mark = o*,",
:rect => "mark = square*,",
:star5 => "mark = star,",
:star6 => "mark = asterisk,",
:diamond => "mark = diamond*,",
:pentagon => "mark = pentagon*,"
)
function _create_plot(pkg::PGFPlotsPackage; kw...)
d = Dict{Symbol,Any}(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, Dict[])
function _pgfplots_get_color(kwargs, symb)
c = typeof(kwargs[symb]) == Symbol ? convertColor(kwargs[symb]) : kwargs[symb].c
"{rgb,1:red,$(float(c.r));green,$(float(c.g));blue,$(float(c.b))}"
end
function _pgfplots_get_linestyle!(kwargs, plt)
ls = plt[:linestyle]
if haskey(_pgfplots_linestyles, ls)
kwargs[:style] *= _pgfplots_linestyles[ls]*","
end
kwargs[:style] *= "line width = $(plt[:linewidth]) pt"*","
end
function _add_series(::PGFPlotsPackage, plt::Plot; kw...)
d = Dict{Symbol,Any}(kw)
function _pgfplots_get_marker!(kwargs, plt)
# Control marker shape
mark = plt[:markershape]
kwargs[:style] *= _pgfplots_markers[mark]
# Control marker size
kwargs[:style] *= "mark size = $(plt[:markersize]/2),"
# Control marker colors and alphas
α = plt[:markeralpha] == nothing ? 1.0 : plt[:markeralpha]
kwargs[:style] *= "mark options = {color=$(_pgfplots_get_color(plt, :markerstrokecolor)),"
kwargs[:style] *= mark == :dtriangle ? "rotate=180," : ""
kwargs[:style] *= "fill=$(_pgfplots_get_color(plt, :markercolor)),"
kwargs[:style] *= "fill opacity = $α,"
markstrokestyle = plt[:markerstrokestyle]
if haskey(_pgfplots_linestyles, markstrokestyle)
kwargs[:style] *= _pgfplots_linestyles[markstrokestyle]
end
kwargs[:style] *= "},"
end
function _pgfplots_get_series_color!(kwargs, plt)
α = plt[:seriesalpha] == nothing ? 1.0 : plt[:seriesalpha]
kwargs[:style] *= "color=$(_pgfplots_get_color(plt, :seriescolor)),"
kwargs[:style] *= "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)),"
kwargs[:style] *= "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)),"
kwargs[:style] *= "fill opacity = $α,"
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)
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, 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, d::KW)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{PGFPlotsPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{PGFPlotsBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
# set or add to the annotation_list
if haskey(plt.plotargs, :annotation_list)
append!(plt.plotargs[:annotation_list], anns)
@@ -38,70 +232,123 @@ end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{PGFPlotsPackage})
function _before_update_plot(plt::Plot{PGFPlotsBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PGFPlotsPackage}, d::Dict)
function _update_plot(plt::Plot{PGFPlotsBackend}, d::KW)
end
function _update_plot_pos_size(plt::PlottingObject{PGFPlotsPackage}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{PGFPlotsPackage}, i::Int)
d = plt.seriesargs[i]
d[:x], d[:y]
end
function Base.setindex!(plt::Plot{PGFPlotsPackage}, 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
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PGFPlotsPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{PGFPlotsBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
true
end
function _expand_limits(lims, plt::Plot{PGFPlotsPackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{PGFPlotsBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PGFPlotsPackage}, isx::Bool)
function _remove_axis(plt::Plot{PGFPlotsBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function _pgfplots_get_axis_kwargs(d)
axisargs = KW()
for arg in (:xlabel, :ylabel, :zlabel, :title)
axisargs[arg] = d[arg]
end
axisargs[:style] = ""
axisargs[:style] *= d[:xflip] == true ? "x dir=reverse," : ""
axisargs[:style] *= d[:yflip] == true ? "y dir=reverse," : ""
if d[:xscale] in (:log, :log2, :ln, :log10)
axisargs[:xmode] = "log"
if d[:xscale] == :log2
axisargs[:style] *= "log basis x=2,"
elseif d[:xscale] in (:log, :log10)
axisargs[:style] *= "log basis x=10,"
end
end
if d[:yscale] in (:log, :log2, :ln, :log10)
axisargs[:ymode] = "log"
if d[:yscale] == :log2
axisargs[:style] *= "log basis y=2,"
elseif d[:yscale] in (:log, :log10)
axisargs[:style] *= "log basis x=10,"
end
end
if d[:zscale] in (:log, :log2, :ln, :log10)
axisargs[:zmode] = "log"
if d[:zscale] == :log2
axisargs[:style] *= "log basis z=2,"
elseif d[:zscale] in (:log, :log10)
axisargs[:style] *= "log basis x=10,"
end
end
# Control background color
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
if d[:grid] == true
axisargs[:style] *= "grid = major"
elseif d[:grid] == false
end
axisargs
end
# ----------------------------------------------------------------
################# This is the important method to implement!!! #################
function _make_pgf_plot(plt::Plot{PGFPlotsPackage})
# TODO: convert plt.plotargs and plt.seriesargs into PGFPlots calls
# TODO: return the PGFPlots object
function _make_pgf_plot(plt::Plot{PGFPlotsBackend})
os = [_pgfplots_axis(plt_series) for plt_series in plt.seriesargs]
axisargs =_pgfplots_get_axis_kwargs(plt.plotargs)
plt.o = PGFPlots.Axis([os...]; axisargs...)
end
function Base.writemime(io::IO, mime::MIME"image/png", plt::PlottingObject{PGFPlotsPackage})
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
# function Base.writemime(io::IO, ::MIME"text/html", plt::PlottingObject{PGFPlotsPackage})
# function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PGFPlotsBackend})
# end
function Base.display(::PlotsDisplay, plt::PlottingObject{PGFPlotsPackage})
function Base.display(::PlotsDisplay, plt::AbstractPlot{PGFPlotsBackend})
plt.o = _make_pgf_plot(plt)
display(plt.o)
end
# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsPackage})
# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsBackend})
# # TODO: display/show the subplot
# end
+210 -100
View File
@@ -1,59 +1,133 @@
# https://plot.ly/javascript/getting-started
function _initialize_backend(::PlotlyPackage; kw...)
supportedArgs(::PlotlyBackend) = [
: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(::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
JSON._print(io::IO, state::JSON.State, dt::Union{Date,DateTime}) = print(io, '"', dt, '"')
############################
# borrowed from https://github.com/spencerlyon2/Plotlyjs.jl/blob/master/src/display.jl
_js_path = joinpath(Pkg.dir("Plots"), "deps", "plotly-latest.min.js")
_js_path = Pkg.dir("Plots", "deps", "plotly-latest.min.js")
_js_code = open(readall, _js_path, "r")
# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2
_js_script = """
<script type='text/javascript'>
define('plotly', function(require, exports, module) {
$(_js_code)
});
require(['plotly'], function(Plotly) {
window.Plotly = Plotly;
});
</script>
"""
# if we're in IJulia call setupnotebook to load js and css
if isijulia()
# the first script is some hack I needed to do in order for the notebook
# to not complain about Plotly being undefined
display("text/html", """
<script type="text/javascript">
require=requirejs=define=undefined;
</script>
<script type="text/javascript">
$(open(readall, _js_path, "r"))
</script>
""")
# display("text/html", "<p>Plotly javascript loaded.</p>")
display("text/html", _js_script)
end
# end borrowing (thanks :)
###########################
# try
# include(joinpath(Pkg.dir("Plots"), "src", "backends", "plotly_blink.jl"))
# catch err
# warn("Error including PlotlyJS: $err\n Note: Will fall back to built-in display.")
# if isatom()
# import Atom
# Atom.@msg evaljs(_js_code)
# end
end
# TODO: other initialization
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PlotlyPackage; kw...)
d = Dict{Symbol,Any}(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, Dict[])
Plot(nothing, pkg, 0, d, KW[])
end
function _add_series(::PlotlyPackage, plt::Plot; kw...)
d = Dict{Symbol,Any}(kw)
function _add_series(::PlotlyBackend, plt::Plot, d::KW)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{PlotlyPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{PlotlyBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
# set or add to the annotation_list
if haskey(plt.plotargs, :annotation_list)
append!(plt.plotargs[:annotation_list], anns)
@@ -64,43 +138,43 @@ end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{PlotlyPackage})
function _before_update_plot(plt::Plot{PlotlyBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PlotlyPackage}, d::Dict)
function _update_plot(plt::Plot{PlotlyBackend}, d::KW)
end
function _update_plot_pos_size(plt::PlottingObject{PlotlyPackage}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{PlotlyBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{PlotlyPackage}, i::Int)
d = plt.seriesargs[i]
d[:x], d[:y]
end
function Base.setindex!(plt::Plot{PlotlyPackage}, 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
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PlotlyPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{PlotlyBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
true
end
function _expand_limits(lims, plt::Plot{PlotlyPackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{PlotlyBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PlotlyPackage}, isx::Bool)
function _remove_axis(plt::Plot{PlotlyBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
@@ -118,7 +192,7 @@ end
# _plotDefaults[:yflip] = false
function plotlyfont(font::Font, color = font.color)
Dict{Symbol,Any}(
KW(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
:color => webcolor(color),
@@ -126,7 +200,7 @@ function plotlyfont(font::Font, color = font.color)
end
function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
Dict{Symbol,Any}(
KW(
:text => val,
:xref => "x",
:x => x,
@@ -137,7 +211,7 @@ function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
end
function get_annotation_dict(x, y, ptxt::PlotText)
merge(get_annotation_dict(x, y, ptxt.str), Dict{Symbol,Any}(
merge(get_annotation_dict(x, y, ptxt.str), KW(
:font => plotlyfont(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
@@ -161,8 +235,8 @@ flipsym(isx::Bool) = symbol((isx ? "x" : "y") * "flip")
scalesym(isx::Bool) = symbol((isx ? "x" : "y") * "scale")
labelsym(isx::Bool) = symbol((isx ? "x" : "y") * "label")
function plotlyaxis(d::Dict, isx::Bool)
ax = Dict{Symbol,Any}(
function plotlyaxis(d::KW, isx::Bool)
ax = KW(
:title => d[labelsym(isx)],
:showgrid => d[:grid],
:zeroline => false,
@@ -211,10 +285,12 @@ function plotlyaxis(d::Dict, isx::Bool)
ax
end
# function get_plot_json(plt::Plot{PlotlyPackage})
# function get_plot_json(plt::Plot{PlotlyBackend})
# d = plt.plotargs
function plotly_layout(d::Dict)
d_out = Dict{Symbol,Any}()
function plotly_layout(d::KW)
d_out = KW()
d_out[:width], d_out[:height] = d[:size]
bgcolor = webcolor(d[:background_color])
fgcolor = webcolor(d[:foreground_color])
@@ -222,7 +298,7 @@ function plotly_layout(d::Dict)
# set the fields for the plot
d_out[:title] = d[:title]
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
d_out[:margin] = Dict{Symbol,Any}(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:plot_bgcolor] = bgcolor
d_out[:paper_bgcolor] = bgcolor
@@ -233,7 +309,7 @@ function plotly_layout(d::Dict)
# legend
d_out[:showlegend] = d[:legend] != :none
if d[:legend] != :none
d_out[:legend] = Dict{Symbol,Any}(
d_out[:legend] = KW(
:bgcolor => bgcolor,
:bordercolor => fgcolor,
:font => plotlyfont(d[:legendfont]),
@@ -246,20 +322,24 @@ function plotly_layout(d::Dict)
d_out[:annotations] = [get_annotation_dict(ann...) for ann in anns]
end
if get(d, :polar, false)
d_out[:direction] = "counterclockwise"
end
d_out
end
function get_plot_json(plt::Plot{PlotlyPackage})
function get_plot_json(plt::Plot{PlotlyBackend})
JSON.json(plotly_layout(plt.plotargs))
end
function plotly_colorscale(grad::ColorGradient)
[[grad.values[i], webcolor(grad.colors[i])] for i in 1:length(grad.colors)]
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) = plotly_colorscale(ColorGradient(:bluesreds))
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(default_gradient(), alpha)
const _plotly_markers = Dict{Symbol,Any}(
const _plotly_markers = KW(
:rect => "square",
:xcross => "x",
:utriangle => "triangle-up",
@@ -270,8 +350,9 @@ const _plotly_markers = Dict{Symbol,Any}(
)
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
function plotly_series(d::Dict; plot_index = nothing)
d_out = Dict{Symbol,Any}()
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])
d_out[:name] = d[:label]
@@ -301,36 +382,46 @@ function plotly_series(d::Dict; plot_index = nothing)
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
elseif lt == :heatmap
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
elseif lt in (:contour, :surface, :wireframe)
d_out[:type] = lt == :wireframe ? :surface : string(lt)
elseif lt == :heatmap
d_out[:type] = "heatmap"
d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf
# d_out[:showscale] = d[:legend]
if lt == :contour
d_out[:ncontours] = d[:levels]
d_out[:contours] = Dict{Symbol,Any}(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
end
d_out[:colorscale] = plotly_colorscale(d[lt == :contour ? :linecolor : :fillcolor])
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
elseif lt == :contour
d_out[:type] = "contour"
d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf
# d_out[:showscale] = d[:colorbar] != :none
d_out[:ncontours] = d[:levels]
d_out[:contours] = KW(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
d_out[:colorscale] = plotly_colorscale(d[:linecolor], d[:linealpha])
elseif lt in (:surface, :wireframe)
d_out[:type] = "surface"
d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
elseif lt == :pie
d_out[:type] = "pie"
@@ -350,30 +441,37 @@ function plotly_series(d::Dict; plot_index = nothing)
else
warn("Plotly: linetype $lt isn't supported.")
return Dict{Symbol,Any}()
return KW()
end
# add "marker"
if hasmarker
d_out[:marker] = Dict{Symbol,Any}(
d_out[:marker] = KW(
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
:opacity => d[:markeralpha],
:size => 2 * d[:markersize],
:color => webcolor(d[:markercolor], d[:markeralpha]),
:line => Dict{Symbol,Any}(
:line => KW(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth],
),
)
if d[:zcolor] != nothing
d_out[:marker][:color] = d[:zcolor]
d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor])
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
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[:marker_z])
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:marker_z]]
end
end
# add "line"
if hasline
d_out[:line] = Dict{Symbol,Any}(
d_out[:line] = KW(
:color => webcolor(d[:linecolor], d[:linealpha]),
:width => d[:linewidth],
:shape => if lt == :steppre
@@ -388,6 +486,12 @@ function plotly_series(d::Dict; 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)"
@@ -398,15 +502,15 @@ function plotly_series(d::Dict; plot_index = nothing)
end
# get a list of dictionaries, each representing the series params
function get_series_json(plt::Plot{PlotlyPackage})
JSON.json(map(plotly_series, plt.seriesargs))
function get_series_json(plt::Plot{PlotlyBackend})
JSON.json(map(d -> plotly_series(d, plt.plotargs), plt.seriesargs))
end
function get_series_json(subplt::Subplot{PlotlyPackage})
ds = Dict[]
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)
@@ -414,28 +518,36 @@ end
# ----------------------------------------------------------------
function html_head(plt::PlottingObject{PlotlyPackage})
function html_head(plt::AbstractPlot{PlotlyBackend})
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
end
function html_body(plt::Plot{PlotlyPackage}, style = nothing)
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
if style == nothing
w, h = plt.plotargs[:size]
style = "width:$(w)px;height:$(h)px;"
end
uuid = Base.Random.uuid4()
"""
html = """
<div id=\"$(uuid)\" style=\"$(style)\"></div>
<script>
PLOT = document.getElementById('$(uuid)');
Plotly.plot(PLOT, $(get_series_json(plt)), $(get_plot_json(plt)));
</script>
"""
# @show html
html
end
function js_body(plt::Plot{PlotlyBackend}, uuid)
js = """
PLOT = document.getElementById('$(uuid)');
Plotly.plot(PLOT, $(get_series_json(plt)), $(get_plot_json(plt)));
"""
end
function html_body(subplt::Subplot{PlotlyPackage})
function html_body(subplt::Subplot{PlotlyBackend})
w, h = subplt.plts[1].plotargs[:size]
html = ["<div style=\"width:$(w)px;height:$(h)px;\">"]
nr = nrows(subplt.layout)
@@ -462,21 +574,19 @@ end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{PlotlyPackage})
isijulia() && return
# TODO: write a png to io
println("todo: png")
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend})
writemime_png_from_html(io, plt)
end
function Base.writemime(io::IO, ::MIME"text/html", plt::PlottingObject{PlotlyPackage})
write(io, html_head(plt) * html_body(plt))
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend})
write(io, html_head(plt) * html_body(plt))
# write(io, html_body(plt))
end
function Base.display(::PlotsDisplay, plt::PlottingObject{PlotlyPackage})
function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend})
standalone_html_window(plt)
end
# function Base.display(::PlotsDisplay, plt::Subplot{PlotlyPackage})
# function Base.display(::PlotsDisplay, plt::Subplot{PlotlyBackend})
# # TODO: display/show the subplot
# end
+131 -41
View File
@@ -1,42 +1,122 @@
# https://github.com/spencerlyon2/PlotlyJS.jl
function _initialize_backend(::PlotlyJSPackage; kw...)
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
export PlotlyJS
end
for (mime, fmt) in PlotlyJS._mimeformats
@eval Base.writemime(io::IO, m::MIME{symbol($mime)}, p::Plot{PlotlyJSPackage}) =
writemime(io, m, p.o)
# 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
# override IJulia inline display
if isijulia()
IJulia.display_dict(plt::AbstractPlot{PlotlyJSBackend}) = IJulia.display_dict(plt.o)
end
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PlotlyJSPackage; kw...)
d = Dict(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(),
Base.Random.uuid4(), PlotlyJS.ElectronDisplay())
# o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(),
# Base.Random.uuid4(), PlotlyJS.ElectronDisplay())
# T = isijulia() ? PlotlyJS.JupyterPlot : PlotlyJS.ElectronPlot
# o = T(PlotlyJS.Plot())
o = PlotlyJS.plot()
Plot(o, pkg, 0, d, Dict[])
Plot(o, pkg, 0, d, KW[])
end
function _add_series(::PlotlyJSPackage, plt::Plot; kw...)
d = Dict(kw)
function _add_series(::PlotlyJSBackend, plt::Plot, d::KW)
syncplot = plt.o
# add to the data array
pdict = plotly_series(d)
pdict = plotly_series(d, plt.plotargs)
typ = pop!(pdict, :type)
gt = PlotlyJS.GenericTrace(typ; pdict...)
push!(plt.o.data, gt)
if PlotlyJS.isactive(plt.o._display)
PlotlyJS.addtraces!(plt.o, gt)
end
PlotlyJS.addtraces!(syncplot, gt)
push!(plt.seriesargs, d)
plt
@@ -46,71 +126,81 @@ end
# ---------------------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{PlotlyJSPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
# set or add to the annotation_list
if haskey(plt.plotargs, :annotation_list)
function _add_annotations{X,Y,V}(plt::Plot{PlotlyJSBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
# set or add to the annotation_list
if !haskey(plt.plotargs, :annotation_list)
plt.plotargs[:annotation_list] = Any[]
end
append!(plt.plotargs[:annotation_list], anns)
else
plt.plotargs[:annotation_list] = anns
end
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{PlotlyJSPackage})
function _before_update_plot(plt::Plot{PlotlyJSBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PlotlyJSPackage}, d::Dict)
function _update_plot(plt::Plot{PlotlyJSBackend}, d::KW)
pdict = plotly_layout(d)
plt.o.layout = PlotlyJS.Layout(pdict)
if PlotlyJS.isactive(plt.o._display)
PlotlyJS.relayout!(plt.o; pdict...)
end
syncplot = plt.o
w,h = d[:size]
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
end
function _update_plot_pos_size(plt::PlottingObject{PlotlyJSPackage}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{PlotlyJSBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{PlotlyJSPackage}, 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{PlotlyJSPackage}, 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, KW(xsym=>(d[xsym],), ysym=>(d[ysym],)))
plt
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PlotlyJSPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{PlotlyJSBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
true
end
function _expand_limits(lims, plt::Plot{PlotlyJSPackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{PlotlyJSBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PlotlyJSPackage}, isx::Bool)
function _remove_axis(plt::Plot{PlotlyJSBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSPackage})
dump(plt.o)
# 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)
end
function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSPackage})
function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSBackend})
error()
end
+954 -630
View File
File diff suppressed because it is too large Load Diff
+80 -30
View File
@@ -1,8 +1,60 @@
# https://github.com/tbreloff/Qwt.jl
function _initialize_backend(::QwtPackage; kw...)
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.")
import Qwt
export Qwt
end
@@ -10,8 +62,8 @@ end
# -------------------------------
@compat const _qwtAliases = Dict(
:nbins => :heatmap_n,
@compat const _qwtAliases = KW(
:bins => :heatmap_n,
:fillrange => :fillto,
:linewidth => :width,
:markershape => :marker,
@@ -23,7 +75,7 @@ end
:star8 => :star2,
)
function fixcolors(d::Dict)
function fixcolors(d::KW)
for (k,v) in d
if typeof(v) <: ColorScheme
d[k] = getColor(v)
@@ -37,8 +89,8 @@ function replaceQwtAliases(d, s)
end
end
function adjustQwtKeywords(plt::Plot{QwtPackage}, iscreating::Bool; kw...)
d = Dict(kw)
function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
d = KW(kw)
lt = d[:linetype]
if lt == :scatter
d[:linetype] = :none
@@ -72,21 +124,20 @@ function adjustQwtKeywords(plt::Plot{QwtPackage}, iscreating::Bool; kw...)
d[:x] = collect(d[:x])
d[:y] = collect(d[:y])
d
end
function _create_plot(pkg::QwtPackage; kw...)
d = Dict(kw)
function _create_plot(pkg::QwtBackend, d::KW)
fixcolors(d)
dumpdict(d,"\n\n!!! plot")
o = Qwt.plot(zeros(0,0); d..., show=false)
plt = Plot(o, pkg, 0, d, Dict[])
plt = Plot(o, pkg, 0, d, KW[])
plt
end
function _add_series(::QwtPackage, 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...)
@@ -97,12 +148,12 @@ end
# ----------------------------------------------------------------
function updateLimsAndTicks(plt::Plot{QwtPackage}, d::Dict, isx::Bool)
function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
lims = get(d, isx ? :xlims : :ylims, nothing)
ticks = get(d, isx ? :xticks : :yticks, nothing)
w = plt.o.widget
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
if typeof(lims) <: @compat(Union{Tuple,AVec}) && length(lims) == 2
if isx
plt.o.autoscale_x = false
@@ -137,7 +188,7 @@ function updateLimsAndTicks(plt::Plot{QwtPackage}, d::Dict, isx::Bool)
end
function _update_plot(plt::Plot{QwtPackage}, d::Dict)
function _update_plot(plt::Plot{QwtBackend}, d::KW)
haskey(d, :title) && Qwt.title(plt.o, d[:title])
haskey(d, :xlabel) && Qwt.xlabel(plt.o, d[:xlabel])
haskey(d, :ylabel) && Qwt.ylabel(plt.o, d[:ylabel])
@@ -145,7 +196,7 @@ function _update_plot(plt::Plot{QwtPackage}, d::Dict)
updateLimsAndTicks(plt, d, false)
end
function _update_plot_pos_size(plt::PlottingObject{QwtPackage}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{QwtBackend}, d::KW)
haskey(d, :size) && Qwt.resizewidget(plt.o, d[:size]...)
haskey(d, :pos) && Qwt.movewidget(plt.o, d[:pos]...)
end
@@ -154,7 +205,7 @@ end
# ----------------------------------------------------------------
# curve.setPen(Qt.QPen(Qt.QColor(color), linewidth, self.getLineStyle(linestyle)))
function addLineMarker(plt::Plot{QwtPackage}, d::Dict)
function addLineMarker(plt::Plot{QwtBackend}, d::KW)
for yi in d[:y]
marker = Qwt.QWT.QwtPlotMarker()
ishorizontal = (d[:linetype] == :hline)
@@ -188,7 +239,7 @@ function createQwtAnnotation(plt::Plot, x, y, val::@compat(AbstractString))
marker[:attach](plt.o.widget)
end
function _add_annotations{X,Y,V}(plt::Plot{QwtPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{QwtBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createQwtAnnotation(plt, ann...)
end
@@ -198,12 +249,12 @@ end
# accessors for x/y data
function Base.getindex(plt::Plot{QwtPackage}, i::Int)
function getxy(plt::Plot{QwtBackend}, i::Int)
series = plt.o.lines[i]
series.x, series.y
end
function Base.setindex!(plt::Plot{QwtPackage}, 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
@@ -212,12 +263,12 @@ end
# -------------------------------
# savepng(::QwtPackage, plt::PlottingObject, fn::@compat(AbstractString), args...) = Qwt.savepng(plt.o, fn)
# savepng(::QwtBackend, plt::AbstractPlot, fn::@compat(AbstractString), args...) = Qwt.savepng(plt.o, fn)
# -------------------------------
# create the underlying object (each backend will do this differently)
function _create_subplot(subplt::Subplot{QwtPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
isbefore && return false
i = 0
rows = Any[]
@@ -239,26 +290,26 @@ function _create_subplot(subplt::Subplot{QwtPackage}, isbefore::Bool)
true
end
function _expand_limits(lims, plt::Plot{QwtPackage}, isx::Bool)
function _expand_limits(lims, plt::Plot{QwtBackend}, isx::Bool)
for series in plt.o.lines
_expand_limits(lims, isx ? series.x : series.y)
end
end
function _remove_axis(plt::Plot{QwtPackage}, isx::Bool)
function _remove_axis(plt::Plot{QwtBackend}, isx::Bool)
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtPackage})
function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
Qwt.refresh(plt.o)
Qwt.savepng(plt.o, "/tmp/dfskjdhfkh.png")
write(io, readall("/tmp/dfskjdhfkh.png"))
end
function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtPackage})
function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
@@ -267,15 +318,14 @@ function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtPackage})
end
function Base.display(::PlotsDisplay, plt::Plot{QwtPackage})
function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
Qwt.refresh(plt.o)
Qwt.showwidget(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{QwtPackage})
function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.showwidget(subplt.o)
end
+688 -759
View File
File diff suppressed because it is too large Load Diff
+22 -24
View File
@@ -3,7 +3,7 @@
# [WEBSITE]
function _initialize_backend(::[PkgName]Package; kw...)
function _initialize_backend(::[PkgName]AbstractBackend; kw...)
@eval begin
import [PkgName]
export [PkgName]
@@ -14,22 +14,20 @@ end
# ---------------------------------------------------------------------------
function _create_plot(pkg::[PkgName]Package; kw...)
d = Dict(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, Dict[])
Plot(nothing, pkg, 0, d, KW[])
end
function _add_series(::[PkgName]Package, plt::Plot; kw...)
d = Dict(kw)
function _add_series(::[PkgName]AbstractBackend, plt::Plot, d::KW)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{[PkgName]Package}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{[PkgName]AbstractBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
@@ -37,53 +35,53 @@ end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{[PkgName]Package})
function _before_update_plot(plt::Plot{[PkgName]AbstractBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{[PkgName]Package}, d::Dict)
function _update_plot(plt::Plot{[PkgName]AbstractBackend}, d::KW)
end
function _update_plot_pos_size(plt::PlottingObject{[PkgName]Package}, d::Dict)
function _update_plot_pos_size(plt::AbstractPlot{[PkgName]AbstractBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{[PkgName]Package}, i::Int)
# TODO: return a tuple of (x, y) vectors
end
function Base.setindex!(plt::Plot{[PkgName]Package}, 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
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{[PkgName]Package}, isbefore::Bool)
function _create_subplot(subplt::Subplot{[PkgName]AbstractBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
function _expand_limits(lims, plt::Plot{[PkgName]Package}, isx::Bool)
function _expand_limits(lims, plt::Plot{[PkgName]AbstractBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{[PkgName]Package}, isx::Bool)
function _remove_axis(plt::Plot{[PkgName]AbstractBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{[PkgName]Package})
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{[PkgName]AbstractBackend})
# TODO: write a png to io
end
function Base.display(::PlotsDisplay, plt::Plot{[PkgName]Package})
function Base.display(::PlotsDisplay, plt::Plot{[PkgName]AbstractBackend})
# TODO: display/show the plot
end
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]Package})
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]AbstractBackend})
# TODO: display/show the subplot
end
+73 -13
View File
@@ -1,7 +1,69 @@
# https://github.com/Evizero/UnicodePlots.jl
function _initialize_backend(::UnicodePlotsPackage; kw...)
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
export UnicodePlots
@@ -68,7 +130,7 @@ end
# add a single series
function addUnicodeSeries!(o, d::Dict, addlegend::Bool, xlim, ylim)
function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
# get the function, or special handling for step/bar/hist
lt = d[:linetype]
@@ -111,7 +173,7 @@ function addUnicodeSeries!(o, d::Dict, addlegend::Bool, xlim, ylim)
end
function handlePlotColors(::UnicodePlotsPackage, d::Dict)
function handlePlotColors(::UnicodePlotsBackend, d::KW)
# TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
d[:color_palette] = [RGB(0,0,0)]
end
@@ -119,8 +181,8 @@ end
# -------------------------------
function _create_plot(pkg::UnicodePlotsPackage; kw...)
plt = Plot(nothing, pkg, 0, Dict(kw), Dict[])
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::UnicodePlotsPackage; kw...)
plt
end
function _add_series(::UnicodePlotsPackage, plt::Plot; kw...)
d = Dict(kw)
function _add_series(::UnicodePlotsBackend, plt::Plot, d::KW)
if d[:linetype] in (:sticks, :bar)
d = barHack(; d...)
elseif d[:linetype] == :hist
@@ -142,7 +203,7 @@ function _add_series(::UnicodePlotsPackage, plt::Plot; kw...)
end
function _update_plot(plt::Plot{UnicodePlotsPackage}, d::Dict)
function _update_plot(plt::Plot{UnicodePlotsBackend}, d::KW)
for k in (:title, :xlabel, :ylabel, :xlims, :ylims)
if haskey(d, k)
plt.plotargs[k] = d[k]
@@ -154,7 +215,7 @@ end
# -------------------------------
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
function png(plt::PlottingObject{UnicodePlotsPackage}, fn::@compat(AbstractString))
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::@compat(AbstractString))
fn = addExtension(fn, "png")
# make some whitespace and show the plot
@@ -182,22 +243,21 @@ end
# we don't do very much for subplots... just stack them vertically
function _create_subplot(subplt::Subplot{UnicodePlotsPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
isbefore && return false
true
end
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsPackage})
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsBackend})
rebuildUnicodePlot!(plt)
show(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsPackage})
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
for plt in subplt.plts
gui(plt)
end
end
+26 -4
View File
@@ -4,7 +4,7 @@
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
function standalone_html(plt::PlottingObject; title::AbstractString = get(plt.plotargs, :window_title, "Plots.jl"))
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.plotargs, :window_title, "Plots.jl"))
"""
<!DOCTYPE html>
<html>
@@ -26,13 +26,35 @@ function open_browser_window(filename::AbstractString)
warn("Unknown OS... cannot open browser window.")
end
function standalone_html_window(plt::PlottingObject; 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
+88 -27
View File
@@ -3,9 +3,73 @@
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
function _initialize_backend(::WinstonPackage; kw...)
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"
# ENV["WINSTON_OUTPUT"] = "gtk"
warn("Winston is no longer supported... many features will likely be broken.")
import Winston, Gtk
export Winston, Gtk
end
@@ -15,13 +79,13 @@ end
## dictionaries for conversion of Plots.jl names to Winston ones.
@compat const winston_linestyle = Dict(:solid=>"solid",
@compat const winston_linestyle = KW(:solid=>"solid",
:dash=>"dash",
:dot=>"dotted",
:dashdot=>"dotdashed"
)
@compat const winston_marker = Dict(:none=>".",
@compat const winston_marker = KW(:none=>".",
:rect => "square",
:ellipse=>"circle",
:diamond=>"diamond",
@@ -32,23 +96,22 @@ end
:star5 => "asterisk"
)
function _before_add_series(plt::Plot{WinstonPackage})
function _before_add_series(plt::Plot{WinstonBackend})
Winston.ghf(plt.o)
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::WinstonPackage; kw...)
d = Dict(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, Dict[])
Plot(wplt, pkg, 0, d, KW[])
end
copy_remove(d::Dict, s::Symbol) = delete!(copy(d), s)
copy_remove(d::KW, s::Symbol) = delete!(copy(d), s)
function addRegressionLineWinston(d::Dict, wplt)
function addRegressionLineWinston(d::KW, wplt)
xs, ys = regressionXY(d[:x], d[:y])
Winston.add(wplt, Winston.Curve(xs, ys, kind="dotted"))
end
@@ -63,9 +126,7 @@ function getWinstonItems(plt::Plot)
window, canvas, wplt
end
function _add_series(::WinstonPackage, plt::Plot; kw...)
d = Dict(kw)
function _add_series(::WinstonBackend, plt::Plot, d::KW)
window, canvas, wplt = getWinstonItems(plt)
# until we call it normally, do the hack
@@ -74,7 +135,7 @@ function _add_series(::WinstonPackage, plt::Plot; kw...)
end
e = Dict()
e = KW()
e[:color] = getColor(d[:linecolor])
e[:linewidth] = d[:linewidth]
e[:kind] = winston_linestyle[d[:linestyle]]
@@ -87,7 +148,7 @@ function _add_series(::WinstonPackage, plt::Plot; kw...)
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :hist2d, :hexbin, :hist, :bar
if d[:linetype] == :none
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
@@ -122,15 +183,15 @@ function _add_series(::WinstonPackage, plt::Plot; kw...)
# elseif d[:linetype] == :dots
# fn = Winston.XXX
# elseif d[:linetype] == :heatmap
# elseif d[:linetype] == :hist2d
# fn = Winston.XXX
# elseif d[:linetype] == :hexbin
# fn = Winston.XXX
elseif d[:linetype] == :hist
hst = hist(d[:y], d[:nbins])
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :nbins)...))
hst = hist(d[:y], d[:bins])
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
# elseif d[:linetype] == :bar
# # fn = Winston.XXX
@@ -157,14 +218,14 @@ end
# ----------------------------------------------------------------
@compat const _winstonNames = Dict(
@compat const _winstonNames = KW(
:xlims => :xrange,
:ylims => :yrange,
:xscale => :xlog,
:yscale => :ylog,
)
function _update_plot(plt::Plot{WinstonPackage}, d::Dict)
function _update_plot(plt::Plot{WinstonBackend}, d::KW)
window, canvas, wplt = getWinstonItems(plt)
for k in (:xlabel, :ylabel, :title, :xlims, :ylims)
if haskey(d, k)
@@ -185,11 +246,11 @@ end
# ----------------------------------------------------------------
function createWinstonAnnotationObject(plt::Plot{WinstonPackage}, x, y, val::@compat(AbstractString))
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::@compat(AbstractString))
Winston.text(x, y, val)
end
function _add_annotations{X,Y,V}(plt::Plot{WinstonPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{WinstonBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createWinstonAnnotationObject(plt, ann...)
end
@@ -198,7 +259,7 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{WinstonPackage}, isbefore::Bool)
function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
@@ -210,14 +271,14 @@ function addWinstonLegend(plt::Plot, wplt)
end
end
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{WinstonPackage})
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{WinstonBackend})
window, canvas, wplt = getWinstonItems(plt)
addWinstonLegend(plt, wplt)
writemime(io, "image/png", wplt)
end
function Base.display(::PlotsDisplay, plt::Plot{WinstonPackage})
function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
window, canvas, wplt = getWinstonItems(plt)
@@ -239,6 +300,6 @@ function Base.display(::PlotsDisplay, plt::Plot{WinstonPackage})
end
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonPackage})
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
# TODO: display/show the Subplot object
end
File diff suppressed because it is too large Load Diff
+62 -30
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 = Dict(
const _gradients = KW(
:blues => [colorant"lightblue", colorant"darkblue"],
:reds => [colorant"lightpink", colorant"darkred"],
:greens => [colorant"lightgreen", colorant"darkgreen"],
@@ -67,19 +67,26 @@ 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)
# --------------------------------------------------------------
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
immutable ColorGradient <: ColorScheme
colors::Vector{Colorant}
values::Vector{Float64}
colors::Vector
values::Vector
function ColorGradient{T<:Colorant,S<:Real}(cs::AVec{T}, vals::AVec{S} = linspace(0, 1, length(cs)); alpha = nothing)
function ColorGradient{S<:Real}(cs::AVec, vals::AVec{S} = linspace(0, 1, length(cs)); alpha = nothing)
if length(cs) == length(vals)
return new(convertColor(cs,alpha), collect(vals))
end
# # otherwise interpolate evenly between the minval and maxval
# minval, maxval = minimum(vals), maximum(vals)
# vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
@@ -103,8 +110,12 @@ function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:0; kw...)
ColorGradient(cs, vals; kw...)
end
function ColorGradient{T<:Real}(cs::AVec{Symbol}, vals::AVec{T} = linspace(0, 1, length(cs)); kw...)
ColorGradient(map(convertColor, cs), vals; kw...)
# function ColorGradient{T<:Real}(cs::AVec, vals::AVec{T} = linspace(0, 1, length(cs)); kw...)
# ColorGradient(map(convertColor, cs), vals; kw...)
# 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))]
@@ -347,39 +358,60 @@ webcolor(c, α) = webcolor(convertColor(getColor(c), α))
# ----------------------------------------------------------------------------------
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
# function getBackgroundRGBColor(c, d::Dict)
function handlePlotColors(::PlottingPackage, d::Dict)
if :background_color in supportedArgs()
bgcolor = convertColor(d[:background_color])
else
bgcolor = _plotDefaults[:background_color]
if d[:background_color] != _plotDefaults[:background_color]
warn("Cannot set background_color with backend $(backend())")
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())")
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
function getSeriesRGBColor(c, plotargs::Dict, n::Int)
function getSeriesRGBColor(c, plotargs::KW, n::Int)
if c == :auto
c = autopick(plotargs[:color_palette], n)
+203 -8
View File
@@ -1,6 +1,51 @@
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(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) = shape.x
get_ys(shape::Shape) = shape.y
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
function shape_coords(shape::Shape)
# 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 = 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)
nanappend!(x, shape.x)
nanappend!(y, shape.y)
# x = vcat(x, NaN, tmpx)
# y = vcat(y, NaN, tmpy)
end
x, y
end
"get an array of tuples of points on a circle with radius `r`"
@@ -48,12 +93,20 @@ function makecross(; offset = -0.5, radius = 1.0)
z1 = z2 - π/8
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
Shape(weave(outercircle, innercircle,
Shape(weave(outercircle, innercircle,
ordering=Vector[outercircle,innercircle,outercircle])[1:end-2])
end
const _shapes = @compat Dict(
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),
:diamond => makeshape(4),
@@ -75,6 +128,67 @@ end
# -----------------------------------------------------------------------
center(shape::Shape) = (mean(shape.x), mean(shape.y))
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
@@ -87,7 +201,7 @@ end
"Create a Font from a list of unordered features"
function font(args...)
# defaults
family = "Helvetica"
pointsize = 14
@@ -131,6 +245,7 @@ immutable PlotText
str::@compat(AbstractString)
font::Font
end
PlotText(str) = PlotText(string(str), font())
function text(str, args...)
PlotText(string(str), font(args...))
@@ -157,7 +272,8 @@ function stroke(args...; alpha = nothing)
for arg in args
T = typeof(arg)
if arg in _allStyles
# if arg in _allStyles
if allStyles(arg)
style = arg
elseif T <: Colorant
color = arg
@@ -165,7 +281,11 @@ function stroke(args...; alpha = nothing)
try
color = parse(Colorant, string(arg))
end
elseif typeof(arg) <: Real
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
alpha = arg
# elseif typeof(arg) <: Real
elseif allReals(arg)
width = arg
else
warn("Unused stroke arg: $arg ($(typeof(arg)))")
@@ -198,7 +318,11 @@ function brush(args...; alpha = nothing)
try
color = parse(Colorant, string(arg))
end
elseif typeof(arg) <: Real
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
alpha = arg
# elseif typeof(arg) <: Real
elseif allReals(arg)
size = arg
else
warn("Unused brush arg: $arg ($(typeof(arg)))")
@@ -222,8 +346,10 @@ end
# -----------------------------------------------------------------------
abstract AbstractSurface
"represents a contour or surface mesh"
immutable Surface{M<:AMat}
immutable Surface{M<:AMat} <: AbstractSurface
# x::AVec
# y::AVec
surf::M
@@ -233,6 +359,17 @@ Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for xi in x, yi in y])
Base.Array(surf::Surface) = surf.surf
for f in (:length, :size)
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
end
Base.copy(surf::Surface) = Surface(copy(surf.surf))
"For the case of representing a surface as a function of x/y... can possibly avoid allocations."
immutable SurfaceFunction <: AbstractSurface
f::Function
end
# -----------------------------------------------------------------------
type OHLC{T<:Real}
@@ -241,3 +378,61 @@ type OHLC{T<:Real}
low::T
close::T
end
# @require FixedSizeArrays begin
type BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
end
function Base.call(bc::BezierCurve, t::Real)
p = zero(P2)
n = length(bc.control_points)-1
for i in 0:n
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
end
p
end
Base.mean(x::Real, y::Real) = 0.5*(x+y)
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
# may create a loop if necessary. Assumes the view is [0,1]
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
mn = mean(p, q)
diff = q - p
minx, maxx = minimum(xview), maximum(xview)
miny, maxy = minimum(yview), maximum(yview)
diffpct = P2(diff[1] / (maxx - minx),
diff[2] / (maxy - miny))
# these points give the initial/final "rise"
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
upper_control = p + vertical_offset
lower_control = q - vertical_offset
# try to figure out when to loop around vs just connecting straight
# TODO: choose loop direction based on sign of p[1]??
# x_close_together = abs(diffpct[1]) <= 0.05
p_is_higher = diff[2] <= 0
inside_control_points = if p_is_higher
# add curve points which will create a loop
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
inside_offset = P2(0.3 * (maxx - minx), 0)
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
else
[]
end
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
end
# end
+178
View File
@@ -0,0 +1,178 @@
# -----------------------------------------------------------
# GridLayout
# -----------------------------------------------------------
"Simple grid, indices are row-major."
immutable GridLayout <: SubplotLayout
nr::Int
nc::Int
end
Base.length(layout::GridLayout) = layout.nr * layout.nc
Base.start(layout::GridLayout) = 1
Base.done(layout::GridLayout, state) = state > length(layout)
function Base.next(layout::GridLayout, state)
r = div(state-1, layout.nc) + 1
c = mod1(state, layout.nc)
(r,c), state + 1
end
nrows(layout::GridLayout) = layout.nr
ncols(layout::GridLayout) = layout.nc
ncols(layout::GridLayout, row::Int) = layout.nc
# get the plot index given row and column
Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c
# -----------------------------------------------------------
# RowsLayout
# -----------------------------------------------------------
"Number of plots per row"
immutable RowsLayout <: SubplotLayout
numplts::Int
rowcounts::AbstractVector{Int}
end
Base.length(layout::RowsLayout) = layout.numplts
Base.start(layout::RowsLayout) = 1
Base.done(layout::RowsLayout, state) = state > length(layout)
function Base.next(layout::RowsLayout, state)
r = 1
c = 0
for i = 1:state
c += 1
if c > layout.rowcounts[r]
r += 1
c = 1
end
end
(r,c), state + 1
end
nrows(layout::RowsLayout) = length(layout.rowcounts)
ncols(layout::RowsLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row])
# get the plot index given row and column
Base.getindex(layout::RowsLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c
# -----------------------------------------------------------
# FlexLayout
# -----------------------------------------------------------
"Flexible, nested layout with optional size percentages."
immutable FlexLayout <: SubplotLayout
n::Int
grid::Matrix # Nested layouts. Each position
# can be a plot index or another FlexLayout
widths::Vector{Float64}
heights::Vector{Float64}
end
typealias IntOrFlex Union{Int,FlexLayout}
Base.length(layout::FlexLayout) = layout.n
Base.start(layout::FlexLayout) = 1
Base.done(layout::FlexLayout, state) = state > length(layout)
function Base.next(layout::FlexLayout, state)
# TODO: change this method to return more info
# TODO: might consider multiple iterator types.. some backends might have an easier time row-by-row for example
error()
r = 1
c = 0
for i = 1:state
c += 1
if c > layout.rowcounts[r]
r += 1
c = 1
end
end
(r,c), state + 1
end
nrows(layout::FlexLayout) = size(layout.grid, 1)
ncols(layout::FlexLayout, row::Int) = size(layout.grid, 2)
# get the plot index given row and column
Base.getindex(layout::FlexLayout, r::Int, c::Int) = layout.grid[r,c]
# -----------------------------------------------------------
# we're taking in a nested structure of some kind... parse it out and build a FlexLayout
function subplotlayout(mat::AbstractVecOrMat; widths = nothing, heights = nothing)
n = 0
nr, nc = size(mat)
grid = Array(IntOrFlex, nr, nc)
for i=1:nr, j=1:nc
v = mat[i,j]
if isa(v, Integer)
grid[i,j] = Int(v)
n += 1
elseif isa(v, Tuple)
warn("need to handle tuples somehow... (idx, sizepct)")
grid[i,j] = nothing
elseif v == nothing
grid[i,j] = nothing
elseif isa(v, AbstractVecOrMat)
grid[i,j] = layout(v)
n += grid[i,j].n
else
error("How do we process? $v")
end
end
if widths == nothing
widths = ones(nc) ./ nc
end
if heights == nothing
heights = ones(nr) ./ nr
end
FlexLayout(n, grid, widths, heights)
end
function subplotlayout(sz::Tuple{Int,Int})
GridLayout(sz...)
end
function subplotlayout(rowcounts::AVec{Int})
RowsLayout(sum(rowcounts), rowcounts)
end
function subplotlayout(numplts::Int, nr::Int, nc::Int)
# figure out how many rows/columns we need
if nr == -1
if nc == -1
nr = round(Int, sqrt(numplts))
nc = ceil(Int, numplts / nr)
else
nr = ceil(Int, numplts / nc)
end
else
nc = ceil(Int, numplts / nr)
end
# if it's a perfect rectangle, just create a grid
if numplts == nr * nc
return GridLayout(nr, nc)
end
# create the rowcounts vector
i = 0
rowcounts = Int[]
for r in 1:nr
cnt = min(nc, numplts - i)
push!(rowcounts, cnt)
i += cnt
end
RowsLayout(numplts, rowcounts)
end
+48 -11
View File
@@ -1,8 +1,8 @@
defaultOutputFormat(plt::PlottingObject) = "png"
defaultOutputFormat(plt::AbstractPlot) = "png"
function png(plt::PlottingObject, fn::@compat(AbstractString))
function png(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "png")
io = open(fn, "w")
writemime(io, MIME("image/png"), plt)
@@ -10,7 +10,7 @@ function png(plt::PlottingObject, fn::@compat(AbstractString))
end
png(fn::@compat(AbstractString)) = png(current(), fn)
function svg(plt::PlottingObject, fn::@compat(AbstractString))
function svg(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "svg")
io = open(fn, "w")
writemime(io, MIME("image/svg+xml"), plt)
@@ -19,7 +19,7 @@ end
svg(fn::@compat(AbstractString)) = svg(current(), fn)
function pdf(plt::PlottingObject, fn::@compat(AbstractString))
function pdf(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "pdf")
io = open(fn, "w")
writemime(io, MIME("application/pdf"), plt)
@@ -28,7 +28,7 @@ end
pdf(fn::@compat(AbstractString)) = pdf(current(), fn)
function ps(plt::PlottingObject, fn::@compat(AbstractString))
function ps(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "ps")
io = open(fn, "w")
writemime(io, MIME("application/postscript"), plt)
@@ -37,7 +37,7 @@ end
ps(fn::@compat(AbstractString)) = ps(current(), fn)
function tex(plt::PlottingObject, fn::@compat(AbstractString))
function tex(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "tex")
io = open(fn, "w")
writemime(io, MIME("application/x-tex"), plt)
@@ -78,8 +78,8 @@ function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
end
end
function savefig(plt::PlottingObject, fn::@compat(AbstractString))
function savefig(plt::AbstractPlot, fn::@compat(AbstractString))
# get the extension
local ext
try
@@ -100,15 +100,52 @@ savefig(fn::@compat(AbstractString)) = savefig(current(), fn)
# savepng(args...; kw...) = savepng(current(), args...; kw...)
# savepng(plt::PlottingObject, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
# savepng(plt::AbstractPlot, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
# ---------------------------------------------------------
gui(plt::PlottingObject = current()) = display(PlotsDisplay(), plt)
gui(plt::AbstractPlot = current()) = display(PlotsDisplay(), plt)
# override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::PlottingObject) = gui(plt)
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::AbstractPlot) = gui(plt)
# a backup for html... passes to svg
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot)
writemime(io, MIME("image/svg+xml"), plt)
end
# ---------------------------------------------------------
# Atom PlotPane
# ---------------------------------------------------------
function setup_atom()
# @require Atom begin
if isatom()
# @eval import Atom, Media
@eval import Atom
# connects the render function
for T in (GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend)
Atom.Media.media(AbstractPlot{T}, Atom.Media.Plot)
end
# Atom.Media.media{T <: Union{GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend}}(Plot{T}, Atom.Media.Plot)
# Atom.displaysize(::AbstractPlot) = (535, 379)
# Atom.displaytitle(plt::AbstractPlot) = "Plots.jl (backend: $(backend(plt)))"
# this is like "display"... sends an html div with the plot to the PlotPane
function Atom.Media.render(pane::Atom.PlotPane, plt::AbstractPlot)
Atom.Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
end
# function Atom.Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend})
# html = Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
# end
end
end
+175 -432
View File
@@ -1,18 +1,18 @@
type CurrentPlot
nullableplot::Nullable{PlottingObject}
nullableplot::Nullable{AbstractPlot}
end
const CURRENT_PLOT = CurrentPlot(Nullable{PlottingObject}())
const CURRENT_PLOT = CurrentPlot(Nullable{AbstractPlot}())
isplotnull() = isnull(CURRENT_PLOT.nullableplot)
function current()
if isplotnull()
error("No current plot/subplot")
end
get(CURRENT_PLOT.nullableplot)
if isplotnull()
error("No current plot/subplot")
end
get(CURRENT_PLOT.nullableplot)
end
current(plot::PlottingObject) = (CURRENT_PLOT.nullableplot = Nullable(plot))
current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = Nullable(plot))
# ---------------------------------------------------------
@@ -32,9 +32,9 @@ convertSeriesIndex(plt::Plot, n::Int) = n
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the current
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
plot(args...; kw...) # creates a new plot window, and sets it to be the current
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
@@ -43,127 +43,184 @@ When you pass in matrices, it splits by columns. See the documentation for more
# this creates a new plot with args/kw and sets it to be the current plot
function plot(args...; kw...)
pkg = backend()
d = Dict(kw)
preprocessArgs!(d)
dumpdict(d, "After plot preprocessing")
pkg = backend()
d = KW(kw)
preprocessArgs!(d)
dumpdict(d, "After plot preprocessing")
plotargs = merge(d, getPlotArgs(pkg, d, 1))
dumpdict(plotargs, "Plot args")
plt = _create_plot(pkg; plotargs...) # create a new, blank plot
plotargs = merge(d, getPlotArgs(pkg, d, 1))
dumpdict(plotargs, "Plot args")
plt = _create_plot(pkg, plotargs) # create a new, blank plot
delete!(d, :background_color)
plot!(plt, args...; d...) # add to it
delete!(d, :background_color)
plot!(plt, args...; d...) # add to it
end
# this adds to the current plot, or creates a new plot if none are current
function plot!(args...; kw...)
local plt
try
plt = current()
catch
return plot(args...; kw...)
end
plot!(current(), args...; kw...)
end
# not allowed:
function plot!(subplt::Subplot, args...; kw...)
error("Can't call plot! on a Subplot!")
local plt
try
plt = current()
catch
return plot(args...; kw...)
end
plot!(current(), args...; kw...)
end
# this adds to a specific plot... most plot commands will flow through here
function plot!(plt::Plot, args...; kw...)
d = Dict(kw)
preprocessArgs!(d)
d = KW(kw)
preprocessArgs!(d)
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; kw...)
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; kw...)
dumpdict(d, "After plot! preprocessing")
dumpdict(d, "After plot! preprocessing")
warnOnUnsupportedArgs(plt.backend, d)
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 ? [] : [extractGroupArgs(d[:group], args...)]
# just in case the backend needs to set up the plot (make it current or something)
_before_add_series(plt)
# get the list of dictionaries, one per series
seriesArgList, xmeta, ymeta = createKWargsList(plt, groupargs..., args...; d...)
# if we were able to extract guide information from the series inputs, then update the plot
# @show xmeta, ymeta
updateDictWithMeta(d, plt.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)
# # grouping
groupby = if haskey(d, :group)
extractGroupArgs(d[:group], args...)
else
nothing
end
# remove plot args
for k in keys(_plotDefaults)
delete!(di, k)
# 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
dumpdict(di, "Series $i")
_add_series(plt, d, groupby, args...)
_add_annotations(plt, d)
_add_series(plt.backend, plt; di...)
end
_add_annotations(plt, d)
warnOnUnsupportedScales(plt.backend, d)
warnOnUnsupportedScales(plt.backend, d)
# add title, axis labels, ticks, etc
if !haskey(d, :subplot)
merge!(plt.plotargs, d)
dumpdict(plt.plotargs, "Updating plot items")
_update_plot(plt, plt.plotargs)
end
# 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
_update_plot_pos_size(plt, d)
_update_plot_pos_size(plt, d)
current(plt)
current(plt)
# NOTE: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
# # do we want to show it?
if haskey(d, :show) && d[:show]
gui()
end
# 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()
end
plt
plt
end
# handle the grouping
function _add_series(plt::Plot, d::KW, groupby::GroupBy, args...)
starting_n = plt.n
for (i, glab) in enumerate(groupby.groupLabels)
tmpd = copy(d)
tmpd[:numUncounted] = plt.n - starting_n
_add_series(plt, tmpd, nothing, args...;
idxfilter = groupby.groupIds[i],
grouplabel = string(glab))
end
end
filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
filter_data(v, idxfilter) = v
function filter_data!(d::KW, idxfilter)
for s in (:x, :y, :z)
d[s] = filter_data(get(d, s, nothing), idxfilter)
end
end
# no grouping
function _add_series(plt::Plot, d::KW, ::Void, args...;
idxfilter = nothing,
grouplabel = "")
# get the list of dictionaries, one per series
dumpdict(d, "before process_inputs")
process_inputs(plt, d, args...)
dumpdict(d, "after process_inputs")
if idxfilter != nothing
# add the group name as the label if there isn't one passed in
get!(d, :label, grouplabel)
# filter the data
filter_data!(d, idxfilter)
end
seriesArgList, xmeta, ymeta = build_series_args(plt, d) #, idxfilter)
# 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() && di[:linetype] != :pie
setTicksFromStringVector(d, di, :x, :xticks)
setTicksFromStringVector(d, di, :y, :yticks)
setTicksFromStringVector(d, di, :z, :zticks)
end
# remove plot args
for k in keys(_plotDefaults)
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)
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::Dict, di::Dict, sym::Symbol, ticksym::Symbol)
# if the x or y values are strings, set ticks to the unique values, and x/y to the indices of the ticks
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
v = di[sym]
isa(v, AbstractArray) || return
v = di[sym]
isa(v, AbstractArray) || return
T = eltype(v)
if T <: @compat(AbstractString) || (!isempty(T.types) && all(x -> x <: @compat(AbstractString), T.types))
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]]
ticks = unique(di[sym])
di[sym] = Int[findnext(ticks, v, 1) for v in di[sym]]
if !haskey(d, ticksym) || d[ticksym] == :auto
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
if !haskey(d, ticksym) || d[ticksym] == :auto
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
end
end
end
end
# --------------------------------------------------------------------
@@ -173,13 +230,13 @@ _before_add_series(plt::Plot) = nothing
# --------------------------------------------------------------------
# should we update the x/y label given the meta info during input slicing?
function updateDictWithMeta(d::Dict, plotargs::Dict, meta::Symbol, isx::Bool)
lsym = isx ? :xlabel : :ylabel
if plotargs[lsym] == default(lsym)
d[lsym] = string(meta)
end
function updateDictWithMeta(d::KW, plotargs::KW, meta::Symbol, isx::Bool)
lsym = isx ? :xlabel : :ylabel
if plotargs[lsym] == default(lsym)
d[lsym] = string(meta)
end
end
updateDictWithMeta(d::Dict, plotargs::Dict, meta, isx::Bool) = nothing
updateDictWithMeta(d::KW, plotargs::KW, meta, isx::Bool) = nothing
# --------------------------------------------------------------------
@@ -187,349 +244,35 @@ annotations(::@compat(Void)) = []
annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v
annotations{X,Y,V}(t::@compat(Tuple{X,Y,V})) = [t]
annotations(v::AVec{PlotText}) = v
annotations(v::AVec) = map(PlotText, v)
annotations(anns) = error("Expecting a tuple (or vector of tuples) for annotations: ",
"(x, y, annotation)\n got: $(typeof(anns))")
function _add_annotations(plt::Plot, d::Dict)
anns = annotations(get(d, :annotation, nothing))
if !isempty(anns)
function _add_annotations(plt::Plot, d::KW)
anns = annotations(get(d, :annotation, nothing))
if !isempty(anns)
# if we just have a list of PlotText objects, then create (x,y,text) tuples
if typeof(anns) <: AVec{PlotText}
x, y = plt[plt.n]
anns = Tuple{Float64,Float64,PlotText}[(x[i], y[i], t) for (i,t) in enumerate(anns)]
# if we just have a list of PlotText objects, then create (x,y,text) tuples
if typeof(anns) <: AVec{PlotText}
x, y = plt[plt.n]
anns = Tuple{Float64,Float64,PlotText}[(x[i], y[i], t) for (i,t) in enumerate(anns)]
end
_add_annotations(plt, anns)
end
_add_annotations(plt, anns)
end
end
# --------------------------------------------------------------------
function Base.copy(plt::Plot)
backend(plt.backend)
plt2 = plot(; plt.plotargs...)
for sargs in plt.seriesargs
sargs = filter((k,v) -> haskey(_seriesDefaults,k), sargs)
plot!(plt2; sargs...)
end
plt2
backend(plt.backend)
plt2 = plot(; plt.plotargs...)
for sargs in plt.seriesargs
sargs = filter((k,v) -> haskey(_seriesDefaults,k), sargs)
plot!(plt2; sargs...)
end
plt2
end
# --------------------------------------------------------------------
# create a new "createKWargsList" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
# Special handling for: no args, xmin/xmax, parametric, dataframes
# Then once inputs have been converted, build the series args, map functions, etc.
# This should cut down on boilerplate code and allow more focused dispatch on type
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
# missing
convertToAnyVector(v::@compat(Void); kw...) = Any[nothing], nothing
# fixed number of blank series
convertToAnyVector(n::Integer; kw...) = Any[zeros(0) for i in 1:n], nothing
# numeric vector
convertToAnyVector{T<:Real}(v::AVec{T}; kw...) = Any[v], nothing
# string vector
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}; kw...) = Any[v], nothing
# numeric matrix
convertToAnyVector{T<:Real}(v::AMat{T}; kw...) = Any[v[:,i] for i in 1:size(v,2)], nothing
# function
convertToAnyVector(f::Function; kw...) = Any[f], nothing
# surface
convertToAnyVector(s::Surface; kw...) = Any[s], nothing
# vector of OHLC
convertToAnyVector(v::AVec{OHLC}; kw...) = Any[v], nothing
# dates
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}; kw...) = Any[dts], nothing
# list of things (maybe other vectors, functions, or something else)
function convertToAnyVector(v::AVec; kw...)
if all(x -> typeof(x) <: Real, v)
# all real numbers wrap the whole vector as one item
Any[convert(Vector{Float64}, v)], nothing
else
# something else... treat each element as an item
Any[vi for vi in v], nothing
end
end
# --------------------------------------------------------------------
# in computeXandY, we take in any of the possible items, convert into proper x/y vectors, then return.
# this is also where all the "set x to 1:length(y)" happens, and also where we assert on lengths.
computeX(x::@compat(Void), y) = 1:length(y)
computeX(x, y) = copy(x)
computeY(x, y::Function) = map(y, x)
computeY(x, y) = copy(y)
function computeXandY(x, y)
if x == nothing && isa(y, Function)
error("If you want to plot the function `$y`, you need to define the x values somehow!")
end
x, y = computeX(x,y), computeY(x,y)
# @assert length(x) == length(y)
x, y
end
# --------------------------------------------------------------------
# create n=max(mx,my) series arguments. the shorter list is cycled through
# note: everything should flow through this
function createKWargsList(plt::PlottingObject, x, y; kw...)
xs, xmeta = convertToAnyVector(x; kw...)
ys, ymeta = convertToAnyVector(y; kw...)
mx = length(xs)
my = length(ys)
ret = Any[]
for i in 1:max(mx, my)
# try to set labels using ymeta
d = Dict(kw)
if !haskey(d, :label) && ymeta != nothing
if isa(ymeta, Symbol)
d[:label] = string(ymeta)
elseif isa(ymeta, AVec{Symbol})
d[:label] = string(ymeta[mod1(i,length(ymeta))])
end
end
# build the series arg dict
numUncounted = get(d, :numUncounted, 0)
n = plt.n + i + numUncounted
dumpdict(d, "before getSeriesArgs")
d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, i + numUncounted, convertSeriesIndex(plt, n), n)
dumpdict(d, "after getSeriesArgs")
d[:x], d[:y] = computeXandY(xs[mod1(i,mx)], ys[mod1(i,my)])
if haskey(d, :idxfilter)
d[:x] = d[:x][d[:idxfilter]]
d[:y] = d[:y][d[:idxfilter]]
end
# for linetype `line`, need to sort by x values
if d[:linetype] == :line
# order by x
indices = sortperm(d[:x])
d[:x] = d[:x][indices]
d[:y] = d[:y][indices]
d[:linetype] = :path
end
# map functions to vectors
if isa(d[:zcolor], Function)
d[:zcolor] = map(d[:zcolor], d[:x])
end
if isa(d[:fillrange], Function)
d[:fillrange] = map(d[:fillrange], d[:x])
end
# cleanup those fields that were used only for generating kw args
for k in (:idxfilter, :numUncounted, :dataframe)
delete!(d, k)
end
# add it to our series list
push!(ret, d)
end
ret, xmeta, ymeta
end
# handle grouping
function createKWargsList(plt::PlottingObject, groupby::GroupBy, args...; kw...)
ret = Any[]
for (i,glab) in enumerate(groupby.groupLabels)
# TODO: don't automatically overwrite labels
kwlist, xmeta, ymeta = createKWargsList(plt, args...; kw...,
idxfilter = groupby.groupIds[i],
label = string(glab),
numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
append!(ret, kwlist)
end
ret, nothing, nothing # TODO: handle passing meta through
end
# pass it off to the x/y version
function createKWargsList(plt::PlottingObject, y; kw...)
createKWargsList(plt, nothing, y; kw...)
end
# 3d line or scatter
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zvec::AVec; kw...)
d = Dict(kw)
if !(get(d, :linetype, :none) in _3dTypes)
d[:linetype] = :path3d
end
createKWargsList(plt, x, y; z=zvec, d...)
end
# contours or surfaces... function grid
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zf::Function; kw...)
# only allow sorted x/y for now
# TODO: auto sort x/y/z properly
@assert x == sort(x)
@assert y == sort(y)
surface = Float64[zf(xi, yi) for xi in x, yi in y]
createKWargsList(plt, x, y, surface; kw...) # passes it to the zmat version
end
# contours or surfaces... matrix grid
function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec, y::AVec, zmat::AMat{T}; kw...)
# only allow sorted x/y for now
# TODO: auto sort x/y/z properly
@assert x == sort(x)
@assert y == sort(y)
@assert size(zmat) == (length(x), length(y))
# surf = Surface(convert(Matrix{Float64}, zmat))
# surf = Array(Any,1,1)
# surf[1,1] = convert(Matrix{Float64}, zmat)
d = Dict(kw)
d[:z] = Surface(convert(Matrix{Float64}, zmat))
if !(get(d, :linetype, :none) in (:contour, :surface, :wireframe))
d[:linetype] = :contour
end
createKWargsList(plt, x, y; d...) #, z = surf)
end
# contours or surfaces... general x, y grid
function createKWargsList{T<:Real}(plt::PlottingObject, x::AMat{T}, y::AMat{T}, zmat::AMat{T}; kw...)
@assert size(zmat) == size(x) == size(y)
surf = Surface(convert(Matrix{Float64}, zmat))
# surf = Array(Any,1,1)
# surf[1,1] = convert(Matrix{Float64}, zmat)
d = Dict(kw)
d[:z] = Surface(convert(Matrix{Float64}, zmat))
if !(get(d, :linetype, :none) in (:contour, :surface, :wireframe))
d[:linetype] = :contour
end
createKWargsList(plt, Any[x], Any[y]; d...) #kw..., z = surf, linetype = :contour)
end
function createKWargsList(plt::PlottingObject, surf::Surface; kw...)
createKWargsList(plt, 1:size(surf.surf,1), 1:size(surf.surf,2), convert(Matrix{Float64}, surf.surf); kw...)
end
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, surf::Surface; kw...)
createKWargsList(plt, x, y, convert(Matrix{Float64}, surf.surf); kw...)
end
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs; kw...)
createKWargsList(plt, f, xmin(plt), xmax(plt); kw...)
end
# list of functions
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs, x; kw...)
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
createKWargsList(plt, x, f; kw...)
end
# special handling... xmin/xmax with function(s)
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs, xmin::Real, xmax::Real; kw...)
width = get(plt.plotargs, :size, (100,))[1]
x = collect(linspace(xmin, xmax, width)) # we don't need more than the width
createKWargsList(plt, x, f; kw...)
end
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
# special handling... xmin/xmax with parametric function(s)
createKWargsList{T<:Real}(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...)
createKWargsList{T<:Real}(plt::PlottingObject, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...)
createKWargsList(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Real, umax::Real, numPoints::Int = 1000; kw...) = createKWargsList(plt, fx, fy, linspace(umin, umax, numPoints); kw...)
# (x,y) tuples
function createKWargsList{R1<:Real,R2<:Real}(plt::PlottingObject, xy::AVec{Tuple{R1,R2}}; kw...)
createKWargsList(plt, unzip(xy)...; kw...)
end
function createKWargsList{R1<:Real,R2<:Real}(plt::PlottingObject, xy::Tuple{R1,R2}; kw...)
createKWargsList(plt, [xy[1]], [xy[2]]; kw...)
end
# special handling... no args... 1 series
function createKWargsList(plt::PlottingObject; kw...)
d = Dict(kw)
if !haskey(d, :y)
# assume we just want to create an empty plot object which can be added to later
return [], nothing, nothing
# error("Called plot/subplot without args... must set y in the keyword args. Example: plot(; y=rand(10))")
end
if haskey(d, :x)
return createKWargsList(plt, d[:x], d[:y]; kw...)
else
return createKWargsList(plt, d[:y]; kw...)
end
end
# --------------------------------------------------------------------
@require FixedSizeArrays begin
unzip{T}(x::AVec{FixedSizeArrays.Vec{2,T}}) = T[xi[1] for xi in x], T[xi[2] for xi in x]
unzip{T}(x::FixedSizeArrays.Vec{2,T}) = T[x[1]], T[x[2]]
function createKWargsList{T<:Real}(plt::PlottingObject, xy::AVec{FixedSizeArrays.Vec{2,T}}; kw...)
createKWargsList(plt, unzip(xy)...; kw...)
end
function createKWargsList{T<:Real}(plt::PlottingObject, xy::FixedSizeArrays.Vec{2,T}; kw...)
createKWargsList(plt, [xy[1]], [xy[2]]; kw...)
end
end
# --------------------------------------------------------------------
# For DataFrame support. Imports DataFrames and defines the necessary methods which support them.
@require DataFrames begin
function createKWargsList(plt::PlottingObject, df::DataFrames.AbstractDataFrame, args...; kw...)
createKWargsList(plt, args...; kw..., dataframe = df)
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(; kw...)
for (k,v) in kw
if k == :dataframe
return v
end
end
error("Missing dataframe argument in arguments!")
end
# the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
convertToAnyVector(s::Symbol; kw...) = Any[getDataFrameFromKW(;kw...)[s]], s
convertToAnyVector(v::AVec{Symbol}; kw...) = (df = getDataFrameFromKW(;kw...); Any[df[s] for s in v]), v
end
# --------------------------------------------------------------------
-121
View File
@@ -1,121 +0,0 @@
immutable NoPackage <: PlottingPackage end
const _backendType = Dict{Symbol, DataType}(:none => NoPackage)
const _backendSymbol = Dict{DataType, Symbol}(NoPackage => :none)
const _backends = Symbol[]
const _initialized_backends = Set{Symbol}()
backends() = _backends
backend_name() = CURRENT_BACKEND.sym
_backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
macro init_plotting_pkg(s)
str = lowercase(string(s))
sym = symbol(str)
T = symbol(string(s) * "Package")
esc(quote
immutable $T <: PlottingPackage end
export $sym
$sym(; kw...) = (default(; kw...); backend(symbol($str)))
backend_name(::$T) = symbol($str)
push!(_backends, symbol($str))
_backendType[symbol($str)] = $T
_backendSymbol[$T] = symbol($str)
include("backends/" * $str * ".jl")
end)
end
@init_plotting_pkg Immerse
@init_plotting_pkg Gadfly
@init_plotting_pkg PyPlot
@init_plotting_pkg Qwt
@init_plotting_pkg UnicodePlots
@init_plotting_pkg Winston
@init_plotting_pkg Bokeh
@init_plotting_pkg Plotly
@init_plotting_pkg PlotlyJS
@init_plotting_pkg GR
@init_plotting_pkg GLVisualize
@init_plotting_pkg PGFPlots
include("backends/web.jl")
include("backends/supported.jl")
# ---------------------------------------------------------
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
_update_plot(pkg::PlottingPackage, plt::Plot, d::Dict) = error("_update_plot($pkg, plt, d) is not implemented")
_update_plot_pos_size{P<:PlottingPackage}(plt::PlottingObject{P}, d::Dict) = nothing
subplot(pkg::PlottingPackage; kw...) = error("subplot($pkg; kw...) is not implemented")
subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
# ---------------------------------------------------------
type CurrentBackend
sym::Symbol
pkg::PlottingPackage
end
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)))
end
end
# the default if nothing else is installed
backend(:plotly)
end
# ---------------------------------------------------------
"""
Returns the current plotting package name. Initializes package on first call.
"""
function backend()
global CURRENT_BACKEND
if CURRENT_BACKEND.sym == :none
pickDefaultBackend()
end
sym = CURRENT_BACKEND.sym
if !(sym in _initialized_backends)
# initialize
println("[Plots.jl] Initializing backend: ", sym)
inst = _backend_instance(sym)
try
_initialize_backend(inst)
catch err
warn("Couldn't initialize $sym. (might need to install it?)")
rethrow(err)
end
push!(_initialized_backends, sym)
end
CURRENT_BACKEND.pkg
end
"""
Set the plot backend.
"""
function backend(pkg::PlottingPackage)
CURRENT_BACKEND.sym = backend_name(pkg)
CURRENT_BACKEND.pkg = pkg
end
function backend(modname::Symbol)
CURRENT_BACKEND.sym = modname
CURRENT_BACKEND.pkg = _backend_instance(modname)
end
+509 -86
View File
@@ -2,8 +2,8 @@
# TODO: there should be a distinction between an object that will manage a full plot, vs a component of a plot.
# the PlotRecipe as currently implemented is more of a "custom component"
# a recipe should fully describe the plotting command(s) and call them, likewise for updating.
# actually... maybe those should explicitly derive from PlottingObject???
# a recipe should fully describe the plotting command(s) and call them, likewise for updating.
# actually... maybe those should explicitly derive from AbstractPlot???
abstract PlotRecipe
@@ -17,10 +17,10 @@ plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)
num_series(x::AMat) = size(x,2)
num_series(x) = 1
_apply_recipe(d::Dict; kw...) = ()
_apply_recipe(d::KW; kw...) = ()
# if it's not a recipe, just do nothing and return the args
function _apply_recipe(d::Dict, args...; issubplot=false, kw...)
function _apply_recipe(d::KW, args...; issubplot=false, kw...)
if issubplot && !haskey(d, :n) && !haskey(d, :layout)
# put in a sensible default
d[:n] = maximum(map(num_series, args))
@@ -28,106 +28,343 @@ function _apply_recipe(d::Dict, args...; issubplot=false, kw...)
args
end
# # -------------------------------------------------
# function rotate(x::Real, y::Real, θ::Real; center = (0,0))
# cx = x - center[1]
# cy = y - center[2]
# xrot = cx * cos(θ) - cy * sin(θ)
# yrot = cy * cos(θ) + cx * sin(θ)
# xrot + center[1], yrot + center[2]
# end
# ---------------------------------------------------------------------------
# # -------------------------------------------------
"""
`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.
# type EllipseRecipe <: PlotRecipe
# w::Float64
# h::Float64
# x::Float64
# y::Float64
# θ::Float64
# end
# EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
Returns a Vector{KW}.
"""
apply_series_recipe(d::KW, lt) = KW[d]
# # return x,y coords of a rotated ellipse, centered at the origin
# function rotatedEllipse(w, h, x, y, θ, rotθ)
# # # coord before rotation
# xpre = w * cos(θ)
# ypre = h * sin(θ)
# ---------------------------------------------------------------------------
# Box Plot
# # rotate and translate
# r = rotate(xpre, ypre, rotθ)
# x + r[1], y + r[2]
# end
const _box_halfwidth = 0.4
# function getRecipeXY(ep::EllipseRecipe)
# x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
# top = rotate(0, ep.h, ep.θ)
# right = rotate(ep.w, 0, ep.θ)
# linex = Float64[top[1], 0, right[1]] + ep.x
# liney = Float64[top[2], 0, right[2]] + ep.y
# Any[x, linex], Any[y, liney]
# end
function apply_series_recipe(d::KW, ::Type{Val{:box}})
# dumpdict(d, "box before", true)
# TODO: add scatter series with outliers
# function getRecipeArgs(ep::EllipseRecipe)
# [(:line, (3, [:dot :solid], [:red :blue], :path))]
# end
# 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 # 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
# "Correlation scatter matrix"
# function corrplot{T<:Real,S<:Real}(mat::AMat{T}, corrmat::AMat{S} = cor(mat);
# colors = :redsblues,
# labels = nothing, kw...)
# m = size(mat,2)
# centers = Float64[mean(extrema(mat[:,i])) for i in 1:m]
function apply_series_recipe(d::KW, ::Type{Val{:violin}})
# dumpdict(d, "box before", true)
# TODO: add scatter series with outliers
# # might be a mistake?
# @assert m <= 20
# @assert size(corrmat) == (m,m)
# create a list of shapes, where each shape is a single boxplot
shapes = Shape[]
d[:linetype] = :shape
groupby = extractGroupArgs(d[:x])
# # create a subplot grid, and a gradient from -1 to 1
# p = subplot(rand(0,m^2); n=m^2, leg=false, grid=false, kw...)
# cgrad = ColorGradient(colors, [-1,1])
for (i, glabel) in enumerate(groupby.groupLabels)
# # make all the plots
# for i in 1:m
# for j in 1:m
# idx = p.layout[i,j]
# plt = p.plts[idx]
# if i==j
# # histogram on diagonal
# histogram!(plt, mat[:,i], c=:black)
# i > 1 && plot!(plt, yticks = :none)
# elseif i < j
# # annotate correlation value in upper triangle
# mi, mj = centers[i], centers[j]
# plot!(plt, [mj], [mi],
# ann = (mj, mi, text(@sprintf("Corr:\n%0.3f", corrmat[i,j]), 15)),
# yticks=:none)
# else
# # scatter plots in lower triangle; color determined by correlation
# c = RGBA(RGB(getColorZ(cgrad, corrmat[i,j])), 0.3)
# scatter!(plt, mat[:,j], mat[:,i], w=0, ms=3, c=c, smooth=true)
# end
# get the edges and widths
y = d[:y][groupby.groupIds[i]]
widths, centers = violin_coords(y)
# if labels != nothing && length(labels) >= m
# i == m && xlabel!(plt, string(labels[j]))
# j == 1 && ylabel!(plt, string(labels[i]))
# end
# 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}})
# d[:label] = ""
# d[:linetype] = :scatter
#
# # create a second series to draw the arrow shaft
# dpath = copy(d)
# error_style!(dpath)
# dpath[:markershape] = :none
#
# 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)
# paths = P2[]
# arrows = P2[]
# arrowshapes = Shape[]
# 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
# else
# error("unexpected vi type $(typeof(vi)) for quiver: $vi")
# end
# v = P2(vx, vy)
#
# nanappend!(paths, [p, p+v])
# push!(arrows, p+v)
# push!(arrowshapes, makearrowhead(compute_angle(v)))
#
# # # dist = sqrt(vx^2 + vy^2)
# # 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
# #
# # append!(pts, P2(xi, yi) .+ P2[(0,0), v-U1, v-U1+U2, v, v-U1-U2, v-U1, (NaN,NaN)])
# # # a1 = v - arrow_h * U1 + arrow_w * U2
# # # a2 = v - arrow_h * U1 - arrow_w * U2
# # # nanappend!(x, xi + [0.0, vx, a1[1], a2[1], vx])
# # # nanappend!(y, yi + [0.0, vy, a1[2], a2[2], vy])
# end
# end
# # link the axes
# subplot!(p, link = (r,c) -> (true, r!=c))
#
# # d[:x], d[:y] = Plots.unzip(pts)
# dpath[:x], dpath[:y] = Plots.unzip(paths)
# d[:x], d[:y] = Plots.unzip(arrows)
# d[:markershape] = arrowshapes
#
# KW[dpath, d]
# end
function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
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 rotate(x::Real, y::Real, θ::Real; center = (0,0))
cx = x - center[1]
cy = y - center[2]
xrot = cx * cos(θ) - cy * sin(θ)
yrot = cy * cos(θ) + cx * sin(θ)
xrot + center[1], yrot + center[2]
end
# ---------------------------------------------------------------------------
type EllipseRecipe <: PlotRecipe
w::Float64
h::Float64
x::Float64
y::Float64
θ::Float64
end
EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
# return x,y coords of a rotated ellipse, centered at the origin
function rotatedEllipse(w, h, x, y, θ, rotθ)
# # coord before rotation
xpre = w * cos(θ)
ypre = h * sin(θ)
# rotate and translate
r = rotate(xpre, ypre, rotθ)
x + r[1], y + r[2]
end
function getRecipeXY(ep::EllipseRecipe)
x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
top = rotate(0, ep.h, ep.θ)
right = rotate(ep.w, 0, ep.θ)
linex = Float64[top[1], 0, right[1]] + ep.x
liney = Float64[top[2], 0, right[2]] + ep.y
Any[x, linex], Any[y, liney]
end
function getRecipeArgs(ep::EllipseRecipe)
[(:line, (3, [:dot :solid], [:red :blue], :path))]
end
# # -------------------------------------------------
"Sparsity plot... heatmap of non-zero values of a matrix"
function spy{T<:Real}(y::AMat{T}; kw...)
I,J,V = findnz(y)
heatmap(J, I; leg=false, yflip=true, nbins=size(y), kw...)
function spy{T<:Real}(z::AMat{T}; 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"
@@ -135,4 +372,190 @@ function abline!(plt::Plot, a, b; kw...)
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
end
abline!(args...; kw...) = abline!(current(), args...; kw...)
abline!(args...; kw...) = abline!(current(), args...; kw...)
# =================================================
# Arc and chord diagrams
"Takes an adjacency matrix and returns source, destiny and weight lists"
function mat2list{T}(mat::AbstractArray{T,2})
nrow, ncol = size(mat) # rows are sources and columns are destinies
nosymmetric = !issym(mat) # plots only triu for symmetric matrices
nosparse = !issparse(mat) # doesn't plot zeros from a sparse matrix
L = length(mat)
source = Array(Int, L)
destiny = Array(Int, L)
weight = Array(T, L)
idx = 1
for i in 1:nrow, j in 1:ncol
value = mat[i, j]
if !isnan(value) && ( nosparse || value != zero(T) ) # TODO: deal with Nullable
if i < j
source[idx] = i
destiny[idx] = j
weight[idx] = value
idx += 1
elseif nosymmetric && (i > j)
source[idx] = i
destiny[idx] = j
weight[idx] = value
idx += 1
end
end
end
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))
"Plots a clockwise arc, from source to destiny, colored by weight"
function arc!(source, destiny, weight, min, max, grad)
radius = (destiny - source) / 2
arc = Plots.partialcircle(0, π, 30, radius)
x, y = Plots.unzip(arc)
plot!(x .+ radius .+ source, y, line = (curvecolor(weight, min, max, grad), 0.5, 2), legend=false)
end
"""
`arcdiagram(source, destiny, weight[, grad])`
Plots an arc diagram, form `source` to `destiny` (clockwise), using `weight` to determine the colors.
"""
function arcdiagram(source, destiny, weight; kargs...)
args = KW(kargs)
grad = pop!(args, :grad, ColorGradient([colorant"darkred", colorant"darkblue"]))
if length(source) == length(destiny) == length(weight)
vertices = unique(vcat(source, destiny))
sort!(vertices)
xmin, xmax = extrema(vertices)
plot(xlim=(xmin - 0.5, xmax + 0.5), legend=false)
wmin,wmax = extrema(weight)
for (i, j, value) in zip(source,destiny,weight)
arc!(i, j, value, wmin, wmax, grad)
end
scatter!(vertices, zeros(length(vertices)); legend=false, args...)
else
throw(ArgumentError("source, destiny and weight should have the same length"))
end
end
"""
`arcdiagram(mat[, grad])`
Plots an arc diagram from an adjacency matrix, form rows to columns (clockwise),
using the values on the matrix as weights to determine the colors.
Doesn't show edges with value zero if the input is sparse.
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),
reverse(Plots.partialcircle(θ1, θ2, 15, 0.9))))
colorlist(grad, ::Void) = :darkgray
function colorlist(grad, z)
zmin, zmax = extrema(z)
RGBA{Float64}[getColorZ(grad, (zi-zmin)/(zmax-zmin)) for zi in z]'
end
"""
`chorddiagram(source, destiny, weight[, grad, zcolor, group])`
Plots a chord diagram, form `source` to `destiny`,
using `weight` to determine the edge colors using `grad`.
`zcolor` or `group` can be used to determine the node colors.
"""
function chorddiagram(source, destiny, weight; kargs...)
args = KW(kargs)
grad = pop!(args, :grad, ColorGradient([colorant"darkred", colorant"darkblue"]))
zcolor= pop!(args, :zcolor, nothing)
group = pop!(args, :group, nothing)
if zcolor !== nothing && group !== nothing
throw(ErrorException("group and zcolor can not be used together."))
end
if length(source) == length(destiny) == length(weight)
plt = plot(xlim=(-2,2), ylim=(-2,2), legend=false, grid=false,
xticks=nothing, yticks=nothing,
xlim=(-1.2,1.2), ylim=(-1.2,1.2))
nodemin, nodemax = extrema(vcat(source, destiny))
weightmin, weightmax = extrema(weight)
A = 1.5π # Filled space
B = 0.5π # White space (empirical)
Δα = A / nodemax
Δβ = B / nodemax
δ = Δα + Δβ
for i in 1:length(source)
curve = BezierCurve(P2[ (cos((source[i ]-1)*δ + 0.5Δα), sin((source[i ]-1)*δ + 0.5Δα)), (0,0),
(cos((destiny[i]-1)*δ + 0.5Δα), sin((destiny[i]-1)*δ + 0.5Δα)) ])
plot!(curve_points(curve), line = (Plots.curvecolor(weight[i], weightmin, weightmax, grad), 1, 1))
end
if group === nothing
c = colorlist(grad, zcolor)
elseif length(group) == nodemax
idx = collect(0:(nodemax-1))
for g in group
plot!([arcshape(n*δ, n*δ + Δα) for n in idx[group .== g]]; args...)
end
return plt
else
throw(ErrorException("group should the ", nodemax, " elements."))
end
plot!([arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]; mc=c, args...)
return plt
else
throw(ArgumentError("source, destiny and weight should have the same length"))
end
end
"""
`chorddiagram(mat[, grad, zcolor, group])`
Plots a chord diagram from an adjacency matrix,
using the values on the matrix as weights to determine edge colors.
Doesn't show edges with value zero if the input is sparse.
For simmetric matrices, only the upper triangular values are used.
`zcolor` or `group` can be used to determine the node colors.
"""
chorddiagram(mat::AbstractMatrix; kargs...) = chorddiagram(mat2list(mat)...; kargs...)
+467
View File
@@ -0,0 +1,467 @@
# create a new "build_series_args" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
# Special handling for: no args, xmin/xmax, parametric, dataframes
# Then once inputs have been converted, build the series args, map functions, etc.
# This should cut down on boilerplate code and allow more focused dispatch on type
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
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
# fixed number of blank series
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
# numeric vector
convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
# string vector
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::KW) = Any[v], nothing
# numeric matrix
function convertToAnyVector{T<:Number}(v::AMat{T}, d::KW)
if all3D(d)
Any[Surface(v)]
else
Any[v[:,i] for i in 1:size(v,2)]
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
# surface
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
# vector of OHLC
convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# dates
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
# list of things (maybe other vectors, functions, or something else)
function convertToAnyVector(v::AVec, d::KW)
if all(x -> typeof(x) <: Number, v)
# all real numbers wrap the whole vector as one item
Any[convert(Vector{Float64}, v)], nothing
else
# something else... treat each element as an item
vcat(Any[convertToAnyVector(vi, d)[1] for vi in v]...), nothing
# Any[vi for vi in v], nothing
end
end
# --------------------------------------------------------------------
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
# result in that array being shared. push!, etc will add too many items to that array
compute_x(x::Void, y::Void, z) = 1:size(z,1)
compute_x(x::Void, y, z) = 1:size(y,1)
compute_x(x::Function, y, z) = map(x, y)
compute_x(x, y, z) = copy(x)
# compute_y(x::Void, y::Function, z) = error()
compute_y(x::Void, y::Void, z) = 1:size(z,2)
compute_y(x, y::Function, z) = map(y, x)
compute_y(x, y, z) = copy(y)
compute_z(x, y, z::Function) = map(z, x, y)
compute_z(x, y, z::AbstractMatrix) = Surface(z)
compute_z(x, y, z::Void) = nothing
compute_z(x, y, z) = copy(z)
@noinline function compute_xyz(x, y, z)
x = compute_x(x,y,z)
y = compute_y(x,y,z)
z = compute_z(x,y,z)
x, y, z
end
# not allowed
compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
compute_xyz(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(sym -> pop!(kw, sym, nothing), (:x, :y, :z))
if nothing == x == y == z
return [], nothing, nothing
end
xs, xmeta = convertToAnyVector(x, kw)
ys, ymeta = convertToAnyVector(y, kw)
zs, zmeta = convertToAnyVector(z, kw)
fr = pop!(kw, :fillrange, nothing)
fillranges, _ = if typeof(fr) <: Number
([fr],nothing)
else
convertToAnyVector(fr, kw)
end
mx = length(xs)
my = length(ys)
mz = length(zs)
ret = Any[]
for i in 1:max(mx, my, mz)
# try to set labels using ymeta
d = copy(kw)
if !haskey(d, :label) && ymeta != nothing
if isa(ymeta, Symbol)
d[:label] = string(ymeta)
elseif isa(ymeta, AVec{Symbol})
d[:label] = string(ymeta[mod1(i,length(ymeta))])
end
end
# build the series arg dict
numUncounted = pop!(d, :numUncounted, 0)
commandIndex = i + numUncounted
n = plt.n + i
dumpdict(d, "before getSeriesArgs")
d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n)
dumpdict(d, "after getSeriesArgs")
d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])
lt = d[:linetype]
# for linetype `line`, need to sort by x values
if lt == :line
# order by x
indices = sortperm(d[:x])
d[:x] = d[:x][indices]
d[:y] = d[:y][indices]
d[:linetype] = :path
end
# 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
# 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
# handle ribbons
if get(d, :ribbon, nothing) != nothing
rib = d[:ribbon]
d[:fillrange] = (d[:y] - rib, d[:y] + rib)
end
# handle quiver plots
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}))
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
end
# --------------------------------------------------------------------
# process_inputs
# --------------------------------------------------------------------
# These methods take a plot and the keyword arguments, and processes the input
# arguments (x/y/z, group, etc), populating the KW dict with appropriate values.
# --------------------------------------------------------------------
# 0 arguments
# --------------------------------------------------------------------
# don't do anything
function process_inputs(plt::AbstractPlot, d::KW)
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
end
# matrix... is it z or y?
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T})
if all3D(d)
n,m = size(mat)
d[:x], d[:y], d[:z] = 1:n, 1:m, mat
else
d[:y] = mat
end
end
# plotting arbitrary shapes/polygons
function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
d[:x], d[:y] = shape_coords(shape)
d[:linetype] = :shape
end
function process_inputs(plt::AbstractPlot, d::KW, shapes::AVec{Shape})
d[:x], d[:y] = shape_coords(shapes)
d[:linetype] = :shape
end
function process_inputs(plt::AbstractPlot, d::KW, shapes::AMat{Shape})
x, y = [], []
for j in 1:size(shapes, 2)
tmpx, tmpy = shape_coords(vec(shapes[:,j]))
push!(x, tmpx)
push!(y, tmpy)
end
d[:x], d[:y] = x, y
d[:linetype] = :shape
end
# function without range... use the current range of the x-axis
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs)
process_inputs(plt, d, f, xmin(plt), xmax(plt))
end
# --------------------------------------------------------------------
# 2 arguments
# --------------------------------------------------------------------
function process_inputs(plt::AbstractPlot, d::KW, x, y)
d[:x], d[:y] = x, y
end
# if functions come first, just swap the order (not to be confused with parametric functions...
# as there would be more than one function passed in)
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, x)
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
process_inputs(plt, d, x, f)
end
# --------------------------------------------------------------------
# 3 arguments
# --------------------------------------------------------------------
# no special handling... just pass them through
function process_inputs(plt::AbstractPlot, d::KW, x, y, z)
d[:x], d[:y], d[:z] = x, y, z
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
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{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{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
end
# surfaces-like... general x, y grid
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, x::AMat{T}, y::AMat{T}, zmat::AMat{T})
@assert size(zmat) == size(x) == size(y)
# d[:x], d[:y], d[:z] = Any[x], Any[y], Surface{Matrix{Float64}}(zmat)
d[:x], d[:y], d[:z] = map(Surface{Matrix{Float64}}, (x, y, zmat))
if !like_surface(get(d, :linetype, :none))
d[:linetype] = :contour
end
end
# --------------------------------------------------------------------
# Parametric functions
# --------------------------------------------------------------------
# special handling... xmin/xmax with function(s)
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, xmin::Number, xmax::Number)
width = get(plt.plotargs, :size, (100,))[1]
x = linspace(xmin, xmax, width)
process_inputs(plt, d, x, f)
end
# special handling... xmin/xmax with parametric function(s)
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, linspace(umin, umax, numPoints))
# special handling... 3D parametric function(s)
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, fz, linspace(umin, umax, numPoints))
# --------------------------------------------------------------------
# Lists of tuples and FixedSizeArrays
# --------------------------------------------------------------------
# if we get an unhandled tuple, just splat it in
function process_inputs(plt::AbstractPlot, d::KW, tup::Tuple)
process_inputs(plt, d, tup...)
end
# (x,y) tuples
function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::AVec{Tuple{R1,R2}})
process_inputs(plt, d, unzip(xy)...)
end
function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::Tuple{R1,R2})
process_inputs(plt, d, [xy[1]], [xy[2]])
end
# (x,y,z) tuples
function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{Tuple{R1,R2,R3}})
process_inputs(plt, d, unzip(xyz)...)
end
function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::Tuple{R1,R2,R3})
process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
end
# 2D FixedSizeArrays
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}})
process_inputs(plt, d, unzip(xy)...)
end
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::FixedSizeArrays.Vec{2,T})
process_inputs(plt, d, [xy[1]], [xy[2]])
end
# 3D FixedSizeArrays
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{FixedSizeArrays.Vec{3,T}})
process_inputs(plt, d, unzip(xyz)...)
end
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::FixedSizeArrays.Vec{3,T})
process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
end
# --------------------------------------------------------------------
# handle grouping
# --------------------------------------------------------------------
# function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...)
# ret = Any[]
# error("unfinished after series reorg")
# for (i,glab) in enumerate(groupby.groupLabels)
# # TODO: don't automatically overwrite labels
# kwlist, xmeta, ymeta = process_inputs(plt, d, args...,
# idxfilter = groupby.groupIds[i],
# label = string(glab),
# numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
# append!(ret, kwlist)
# end
# ret, nothing, nothing # TODO: handle passing meta through
# end
# --------------------------------------------------------------------
# For DataFrame support. Imports DataFrames and defines the necessary methods which support them.
# --------------------------------------------------------------------
function setup_dataframes()
@require DataFrames begin
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!")
# end
# end
# # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
# convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s
# convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v
end
end
+121 -170
View File
@@ -1,132 +1,18 @@
function subplotlayout(sz::@compat(Tuple{Int,Int}))
GridLayout(sz...)
end
function subplotlayout(rowcounts::AVec{Int})
FlexLayout(sum(rowcounts), rowcounts)
end
function subplotlayout(numplts::Int, nr::Int, nc::Int)
# figure out how many rows/columns we need
if nr == -1
if nc == -1
nr = round(Int, sqrt(numplts))
nc = ceil(Int, numplts / nr)
else
nr = ceil(Int, numplts / nc)
end
else
nc = ceil(Int, numplts / nr)
end
# if it's a perfect rectangle, just create a grid
if numplts == nr * nc
return GridLayout(nr, nc)
end
# create the rowcounts vector
i = 0
rowcounts = Int[]
for r in 1:nr
cnt = min(nc, numplts - i)
push!(rowcounts, cnt)
i += cnt
end
FlexLayout(numplts, rowcounts)
end
Base.length(layout::FlexLayout) = layout.numplts
Base.start(layout::FlexLayout) = 1
Base.done(layout::FlexLayout, state) = state > length(layout)
function Base.next(layout::FlexLayout, state)
r = 1
c = 0
for i = 1:state
c += 1
if c > layout.rowcounts[r]
r += 1
c = 1
end
end
(r,c), state + 1
end
nrows(layout::FlexLayout) = length(layout.rowcounts)
ncols(layout::FlexLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row])
# get the plot index given row and column
Base.getindex(layout::FlexLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c
Base.length(layout::GridLayout) = layout.nr * layout.nc
Base.start(layout::GridLayout) = 1
Base.done(layout::GridLayout, state) = state > length(layout)
function Base.next(layout::GridLayout, state)
r = div(state-1, layout.nc) + 1
c = mod1(state, layout.nc)
(r,c), state + 1
end
nrows(layout::GridLayout) = layout.nr
ncols(layout::GridLayout) = layout.nc
ncols(layout::GridLayout, row::Int) = layout.nc
# get the plot index given row and column
Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c
Base.getindex(subplt::Subplot, args...) = subplt.plts[subplt.layout[args...]]
# handle "linking" the subplot axes together
# each backend should implement the _remove_axis and _expand_limits methods
function link_axis(subplt::Subplot, isx::Bool)
# collect the list of plots and the expanded limits for those plots that should be linked on this axis
includedPlots = Any[]
# lims = [Inf, -Inf]
lims = Dict{Int,Any}() # maps column to xlim
for (i,(r,c)) in enumerate(subplt.layout)
# shouldlink will be a bool or nothing. if nothing, then use linkx/y (which is true if we get to this code)
shouldlink = subplt.linkfunc(r,c)[isx ? 1 : 2]
if shouldlink == nothing || shouldlink
plt = subplt.plts[i]
# if we don't have this
k = isx ? c : r
if (firstone = !haskey(lims, k))
lims[k] = [Inf, -Inf]
end
isinner = (isx && r < nrows(subplt.layout)) || (!isx && !firstone)
push!(includedPlots, (plt, isinner, k))
_expand_limits(lims[k], plt, isx)
end
end
# do the axis adjustments
for (plt, isinner, k) in includedPlots
if isinner
_remove_axis(plt, isx)
end
(isx ? xlims! : ylims!)(plt, lims[k]...)
end
end
# ------------------------------------------------------------
Base.string(subplt::Subplot) = "Subplot{$(subplt.backend) p=$(subplt.p) n=$(subplt.n)}"
Base.print(io::IO, subplt::Subplot) = print(io, string(subplt))
Base.show(io::IO, subplt::Subplot) = print(io, string(subplt))
function Base.copy(subplt::Subplot)
subplot(subplt.plts, subplt.layout, subplt.plotargs)
end
Base.getindex(subplt::Subplot, args...) = subplt.plts[subplt.layout[args...]]
# --------------------------------------------------------------------
getplot(subplt::Subplot, idx::Int = subplt.n) = subplt.plts[mod1(idx, subplt.p)]
getplotargs(subplt::Subplot, idx::Int) = getplot(subplt, idx).plotargs
convertSeriesIndex(subplt::Subplot, n::Int) = ceil(Int, n / subplt.p)
@@ -153,7 +39,7 @@ Create a series of plots:
"""
function subplot(args...; kw...)
validateSubplotSupported()
d = Dict(kw)
d = KW(kw)
preprocessArgs!(d)
# for plotting recipes, swap out the args and update the parameter dictionary
@@ -178,7 +64,7 @@ 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
@@ -194,23 +80,28 @@ end
# grid layout
function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...)
d = Dict(kw)
layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
d = KW(kw)
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
# explicit layout
function subplot{P,I<:Integer}(pltsPerRow::AVec{I}, plt1::Plot{P}, plts::Plot{P}...; kw...)
layout = subplotlayout(pltsPerRow)
subplot(vcat(plt1, plts...), layout, Dict(kw))
subplot(vcat(plt1, plts...), layout, KW(kw))
end
# this will be called internally
function subplot{P<:PlottingPackage}(plts::AVec{Plot{P}}, layout::SubplotLayout, d::Dict)
function subplot{P<:AbstractBackend}(plts::AVec{Plot{P}}, layout::SubplotLayout, d::KW)
validateSubplotSupported()
p = length(layout)
n = sum([plt.n for plt in plts])
subplt = Subplot(nothing, collect(plts), P(), p, n, layout, Dict(), false, false, false, (r,c) -> (nothing,nothing))
subplt = Subplot(nothing, collect(plts), P(), p, n, layout, KW(), false, false, false, (r,c) -> (nothing,nothing))
_preprocess_subplot(subplt, d)
_postprocess_subplot(subplt, d)
@@ -223,7 +114,7 @@ end
# ------------------------------------------------------------------------------------------------
function _preprocess_subplot(subplt::Subplot, d::Dict, args = ())
function _preprocess_subplot(subplt::Subplot, d::KW, args = ())
validateSubplotSupported()
preprocessArgs!(d)
@@ -257,7 +148,7 @@ function _preprocess_subplot(subplt::Subplot, d::Dict, args = ())
args
end
function _postprocess_subplot(subplt::Subplot, d::Dict)
function _postprocess_subplot(subplt::Subplot, d::KW)
# init (after plot creation)
if !subplt.initialized
subplt.initialized = _create_subplot(subplt, false)
@@ -303,50 +194,24 @@ end
function subplot!(subplt::Subplot, args...; kw...)
# validateSubplotSupported()
d = Dict(kw)
d = KW(kw)
args = _preprocess_subplot(subplt, d, args)
# create the underlying object (each backend will do this differently)
# note: we call it once before doing the individual plots, and once after
# this is because some backends need to set up the subplots and then plot,
# this is because some backends need to set up the subplots and then plot,
# and others need to do it the other way around
if !subplt.initialized
subplt.initialized = _create_subplot(subplt, true)
end
# handle grouping
group = get(d, :group, nothing)
if group == nothing
groupargs = []
groupby = if haskey(d, :group)
extractGroupArgs(d[:group], args...)
else
groupargs = [extractGroupArgs(d[:group], args...)]
delete!(d, :group)
end
kwList, xmeta, ymeta = createKWargsList(subplt, groupargs..., args...; d...)
# TODO: something useful with meta info?
for (i,di) in enumerate(kwList)
subplt.n += 1
plt = getplot(subplt)
plt.n += 1
# 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...)
nothing
end
_add_series_subplot(subplt, d, groupby, args...)
_postprocess_subplot(subplt, d)
# show it automatically?
@@ -358,23 +223,109 @@ function subplot!(subplt::Subplot, args...; kw...)
end
# not allowed:
function plot!(subplt::Subplot, args...; kw...)
error("Can't call plot! on a Subplot!")
end
function _add_series_subplot(plt::Plot, args...; kw...)
d = Dict(kw)
# 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)
_add_series(plt.backend, plt; d...)
# this is the actual call to the backend
_add_series(plt.backend, plt, d)
_add_annotations(plt, d)
warnOnUnsupportedScales(plt.backend, d)
end
# handle the grouping... add a series for each group
function _add_series_subplot(subplt::Subplot, d::KW, groupby::GroupBy, args...)
starting_n = subplt.n
for (i, glab) in enumerate(groupby.groupLabels)
tmpd = copy(d)
tmpd[:numUncounted] = subplt.n - starting_n
_add_series_subplot(subplt, tmpd, nothing, args...;
idxfilter = groupby.groupIds[i],
grouplabel = string(glab))
end
end
# process, filter, and add to the correct plot
function _add_series_subplot(subplt::Subplot, d::KW, ::Void, args...;
idxfilter = nothing,
grouplabel = "")
process_inputs(subplt, d, args...)
if idxfilter != nothing
# add the group name as the label if there isn't one passed in
get!(d, :label, grouplabel)
# filter the data
filter_data!(d, idxfilter)
end
kwList, xmeta, ymeta = build_series_args(subplt, 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
end
# --------------------------------------------------------------------
function Base.copy(subplt::Subplot)
subplot(subplt.plts, subplt.layout, subplt.plotargs)
# handle "linking" the subplot axes together
# each backend should implement the _remove_axis and _expand_limits methods
function link_axis(subplt::Subplot, isx::Bool)
# collect the list of plots and the expanded limits for those plots that should be linked on this axis
includedPlots = Any[]
# lims = [Inf, -Inf]
lims = Dict{Int,Any}() # maps column to xlim
for (i,(r,c)) in enumerate(subplt.layout)
# shouldlink will be a bool or nothing. if nothing, then use linkx/y (which is true if we get to this code)
shouldlink = subplt.linkfunc(r,c)[isx ? 1 : 2]
if shouldlink == nothing || shouldlink
plt = subplt.plts[i]
# if we don't have this
k = isx ? c : r
if (firstone = !haskey(lims, k))
lims[k] = [Inf, -Inf]
end
isinner = (isx && r < nrows(subplt.layout)) || (!isx && !firstone)
push!(includedPlots, (plt, isinner, k))
_expand_limits(lims[k], plt, isx)
end
end
# do the axis adjustments
for (plt, isinner, k) in includedPlots
if isinner
_remove_axis(plt, isx)
end
(isx ? xlims! : ylims!)(plt, lims[k]...)
end
end
+39 -33
View File
@@ -3,47 +3,53 @@ typealias AVec AbstractVector
typealias AMat AbstractMatrix
immutable PlotsDisplay <: Display end
abstract PlottingPackage
abstract PlottingObject{T<:PlottingPackage}
type Plot{T<:PlottingPackage} <: PlottingObject{T}
o # the underlying object
backend::T
n::Int # number of series
abstract AbstractBackend
abstract AbstractPlot{T<:AbstractBackend}
# store these just in case
plotargs::Dict
seriesargs::Vector{Dict} # args for each series
typealias KW Dict{Symbol,Any}
immutable InputWrapper{T}
obj::T
end
wrap{T}(obj::T) = InputWrapper{T}(obj)
Base.isempty(wrapper::InputWrapper) = false
# -----------------------------------------------------------
# Plot
# -----------------------------------------------------------
type Plot{T<:AbstractBackend} <: AbstractPlot{T}
o # the backend's plot object
backend::T # the backend type
n::Int # number of series
plotargs::KW # arguments for the whole plot
seriesargs::Vector{KW} # arguments for each series
end
# -----------------------------------------------------------
# Layout
# -----------------------------------------------------------
abstract SubplotLayout
immutable GridLayout <: SubplotLayout
nr::Int
nc::Int
end
# -----------------------------------------------------------
# Subplot
# -----------------------------------------------------------
immutable FlexLayout <: SubplotLayout
numplts::Int
rowcounts::AbstractVector{Int}
end
type Subplot{T<:PlottingPackage, L<:SubplotLayout} <: PlottingObject{T}
o # the underlying object
plts::Vector{Plot{T}} # the individual plots
backend::T
p::Int # number of plots
n::Int # number of series
layout::L
# plotargs::Vector{Dict}
plotargs::Dict
initialized::Bool
linkx::Bool
linky::Bool
linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y
type Subplot{T<:AbstractBackend, L<:SubplotLayout} <: AbstractPlot{T}
o # the underlying object
plts::Vector{Plot{T}} # the individual plots
backend::T
p::Int # number of plots
n::Int # number of series
layout::L
plotargs::KW
initialized::Bool
linkx::Bool
linky::Bool
linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y
end
# -----------------------------------------------------------------------
+262 -133
View File
@@ -19,10 +19,10 @@ A hacky replacement for a histogram when the backend doesn't support histograms
Convert it into a bar chart with the appropriate x/y values.
"""
function histogramHack(; kw...)
d = Dict(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
@@ -35,7 +35,7 @@ A hacky replacement for a bar graph when the backend doesn't support bars direct
Convert it into a line chart with fillrange set.
"""
function barHack(; kw...)
d = Dict(kw)
d = KW(kw)
midpoints = d[:x]
heights = d[:y]
fillrange = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
@@ -75,7 +75,7 @@ A hacky replacement for a sticks graph when the backend doesn't support sticks d
Convert it into a line chart that traces the sticks, and a scatter that sets markers at the points.
"""
function sticksHack(; kw...)
dLine = Dict(kw)
dLine = KW(kw)
dScatter = copy(dLine)
# these are the line vertices
@@ -114,24 +114,33 @@ function regressionXY(x, y)
regx, regy
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))]
get_mod(v, idx::Int) = v
get_mod(v, idx::Int) = v
makevec(v::AVec) = v
makevec{T}(v::T) = T[v]
"duplicate a single value, or pass the 2-tuple through"
maketuple(x::Real) = (x,x)
maketuple(x::Real) = (x,x)
maketuple{T,S}(x::@compat(Tuple{T,S})) = x
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
unzip{T,S}(v::AVec{@compat(Tuple{T,S})}) = [vi[1] for vi in v], [vi[2] for vi in v]
unzip{T,S}(xy::AVec{Tuple{T,S}}) = [x[1] for x in xy], [y[2] for y in xy]
unzip{T,S,R}(xyz::AVec{Tuple{T,S,R}}) = [x[1] for x in xyz], [y[2] for y in xyz], [z[3] for z in xyz]
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[x[1] for x in xy], T[y[2] for y in xy]
unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]]
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[x[1] for x in xyz], T[y[2] for y in xyz], T[z[3] for z in xyz]
unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
function _expand_limits(lims, x)
@@ -163,7 +172,7 @@ function replaceType(vec, val)
push!(vec, val)
end
function replaceAliases!(d::Dict, aliases::Dict)
function replaceAliases!(d::KW, aliases::KW)
ks = collect(keys(d))
for k in ks
if haskey(aliases, k)
@@ -181,6 +190,13 @@ Base.first(x::Symbol) = x
sortedkeys(d::Dict) = sort(collect(keys(d)))
"create an (n+1) list of the outsides of heatmap rectangles"
function heatmap_edges(v::AVec)
vmin, vmax = extrema(v)
extra = 0.5 * (vmax-vmin) / (length(v)-1)
vcat(vmin-extra, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra)
end
function fakedata(sz...)
y = zeros(sz...)
@@ -191,36 +207,72 @@ function fakedata(sz...)
end
isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited
isatom() = isdefined(Main, :Atom) && Main.Atom.isconnected()
istuple(::Tuple) = true
istuple(::Any) = false
istuple(::Any) = false
isvector(::AVec) = true
isvector(::Any) = false
isvector(::Any) = false
ismatrix(::AMat) = true
ismatrix(::Any) = false
ismatrix(::Any) = false
isscalar(::Real) = true
isscalar(::Any) = false
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
ticksType{T<:Real}(ticks::AVec{T}) = :ticks
ticksType{T<:AVec,S<:AVec}(ticks::@compat(Tuple{T,S})) = :ticks_and_labels
ticksType(ticks) = :invalid
ticksType{T<:Real}(ticks::AVec{T}) = :ticks
ticksType{T<:AbstractString}(ticks::AVec{T}) = :labels
ticksType{T<:AVec,S<:AVec}(ticks::@compat(Tuple{T,S})) = :ticks_and_labels
ticksType(ticks) = :invalid
limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :invalid
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}(::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
# ---------------------------------------------------------------
wraptuple(x::@compat(Tuple)) = x
wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
trueOrAllTrue(f::Function, x) = f(x)
allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
allShapes(arg) = trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg) ||
trueOrAllTrue(a -> isa(a, Shape), arg)
allAlphas(arg) = trueOrAllTrue(a -> (typeof(a) <: Real && a > 0 && a < 1) ||
(typeof(a) <: AbstractFloat && (a == zero(typeof(a)) || a == one(typeof(a)))), arg)
allReals(arg) = trueOrAllTrue(a -> typeof(a) <: Real, arg)
allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
# ---------------------------------------------------------------
# ---------------------------------------------------------------
"""
Allows temporary setting of backend and defaults for Plots. Settings apply only for the `do` block. Example:
```
@@ -231,10 +283,17 @@ end
```
"""
function with(f::Function, args...; kw...)
newdefs = KW(kw)
if :canvas in args
newdefs[:xticks] = nothing
newdefs[:yticks] = nothing
newdefs[:grid] = false
newdefs[:legend] = false
end
# dict to store old and new keyword args for anything that changes
newdefs = Dict(kw)
olddefs = Dict()
olddefs = KW()
for k in keys(newdefs)
olddefs[k] = default(k)
end
@@ -299,6 +358,7 @@ function with(f::Function, args...; kw...)
ret
end
# ---------------------------------------------------------------
# ---------------------------------------------------------------
type DebugMode
@@ -313,7 +373,7 @@ end
debugshow(x) = show(x)
debugshow(x::AbstractArray) = print(summary(x))
function dumpdict(d::Dict, prefix = "", alwaysshow = false)
function dumpdict(d::KW, prefix = "", alwaysshow = false)
_debugMode.on || alwaysshow || return
println()
if prefix != ""
@@ -333,137 +393,206 @@ 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)
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + maximum(v))
extendSeriesData{T}(v::Range{T}, z::Real) = extendSeriesData(float(collect(v)), z)
extendSeriesData{T}(v::Range{T}, z::AVec) = extendSeriesData(float(collect(v)), z)
extendSeriesData{T}(v::AVec{T}, z::Real) = (push!(v, convert(T, z)); v)
extendSeriesData{T}(v::AVec{T}, z::AVec) = (append!(v, convert(Vector{T}, z)); v)
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + maximum(v))
extendSeriesData{T}(v::Range{T}, z::Real) = extendSeriesData(float(collect(v)), z)
extendSeriesData{T}(v::Range{T}, z::AVec) = extendSeriesData(float(collect(v)), z)
extendSeriesData{T}(v::AVec{T}, z::Real) = (push!(v, convert(T, z)); v)
extendSeriesData{T}(v::AVec{T}, z::AVec) = (append!(v, convert(Vector{T}, z)); v)
# -------------------------------------------------------
# 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),
# xticks=(collect(1:length(bs)), bs),
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)
supportGraphTypes() = supportGraph(_allTypes, supportedTypes)
supportGraphStyles() = supportGraph(_allStyles, supportedStyles)
supportGraphArgs() = supportGraph(_allArgs, supportedArgs)
supportGraphTypes() = supportGraph(_allTypes, supportedTypes)
supportGraphStyles() = supportGraph(_allStyles, supportedStyles)
supportGraphMarkers() = supportGraph(_allMarkers, supportedMarkers)
supportGraphScales() = supportGraph(_allScales, supportedScales)
supportGraphAxes() = supportGraph(_allAxes, supportedAxes)
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
# note: I borrowed these conversion constants from Compose.jl's Measure
const PX_PER_INCH = 100
const DPI = PX_PER_INCH
const MM_PER_INCH = 25.4
const MM_PER_PX = MM_PER_INCH / PX_PER_INCH
inch2px(inches::Real) = float(inches * PX_PER_INCH)
px2inch(px::Real) = float(px / PX_PER_INCH)
inch2mm(inches::Real) = float(inches * MM_PER_INCH)
mm2inch(mm::Real) = float(mm / MM_PER_INCH)
px2mm(px::Real) = float(px * MM_PER_PX)
mm2px(mm::Real) = float(px / MM_PER_PX)
const PX_PER_INCH = 100
const DPI = PX_PER_INCH
const MM_PER_INCH = 25.4
const MM_PER_PX = MM_PER_INCH / PX_PER_INCH
inch2px(inches::Real) = float(inches * PX_PER_INCH)
px2inch(px::Real) = float(px / PX_PER_INCH)
inch2mm(inches::Real) = float(inches * MM_PER_INCH)
mm2inch(mm::Real) = float(mm / MM_PER_INCH)
px2mm(px::Real) = float(px * MM_PER_PX)
mm2px(mm::Real) = float(px / MM_PER_PX)
"Smallest x in plot"
@@ -472,4 +601,4 @@ xmin(plt::Plot) = minimum([minimum(d[:x]) for d in plt.seriesargs])
xmax(plt::Plot) = maximum([maximum(d[:x]) for d in plt.seriesargs])
"Extrema of x-values in plot"
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))
+6 -1
View File
@@ -4,8 +4,13 @@ Colors
Reexport
Requires
FactCheck
Cairo
Gadfly
Images
ImageMagick
PyPlot
@osx QuartzImageIO
GR
DataFrames
RDatasets
VisualRegressionTests
UnicodePlots
+17 -7
View File
@@ -2,6 +2,8 @@
using VisualRegressionTests
using ExamplePlots
import DataFrames, RDatasets
# don't let pyplot use a gui... it'll crash
# note: Agg will set gui -> :none in PyPlot
ENV["MPLBACKEND"] = "Agg"
@@ -16,12 +18,12 @@ using Plots, FactCheck
default(size=(500,300))
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
# first
# first
Plots._debugMode.on = debug
example = ExamplePlots._examples[idx]
info("Testing plot: $pkg:$idx:$(example.header)")
@@ -31,14 +33,15 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
# ensure consistent results
srand(1234)
# reference image directory setup
refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
# test function
func = (fn, idx) -> begin
map(eval, example.exprs)
png(fn)
end
# reference image directory setup
refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
try
run(`mkdir -p $refdir`)
catch err
@@ -51,9 +54,16 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
test_images(vtest, popup=popup, sigma=sigma, eps=eps)
end
function image_comparison_facts(pkg::Symbol; skip = [], debug = false, sigma = [1,1], eps = 1e-2)
function image_comparison_facts(pkg::Symbol;
skip = [], # skip these examples (int index)
only = nothing, # limit to these examples (int index)
debug = false, # print debug information?
sigma = [1,1], # number of pixels to "blur"
eps = 1e-2) # acceptable error (percent)
for i in 1:length(ExamplePlots._examples)
i in skip && continue
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
if only == nothing || i in only
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
end
end
end
+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
+54 -8
View File
@@ -4,27 +4,73 @@ 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
@fact gadfly() --> Plots.GadflyPackage()
@fact backend() --> Plots.GadflyPackage()
@fact gadfly() --> Plots.GadflyBackend()
@fact backend() --> Plots.GadflyBackend()
@fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyPackage}
@fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
@fact plot(Int[1,2,3], rand(3)) --> not(nothing)
@fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
@fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
image_comparison_facts(:gadfly, skip=[4,6,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.PyPlotPackage()
@fact backend() --> Plots.PyPlotPackage()
@fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend()
image_comparison_facts(:pyplot, skip=[10,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()
@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
+22
View File
@@ -0,0 +1,22 @@
# Pkg.clone(pwd())
# Pkg.build("Plots")
Pkg.clone("ImageMagick")
Pkg.build("ImageMagick")
Pkg.clone("GR")
Pkg.build("GR")
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)