Compare commits

..

669 Commits

Author SHA1 Message Date
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
Thomas Breloff 6c6769ca98 handle vector of PlotText as annotations 2016-02-10 13:09:22 -05:00
Thomas Breloff 3e49af9093 allow Function for fillrange and zcolor; fix fill kw 2016-02-09 19:22:05 -05:00
Thomas Breloff 5f8ecfa3fa deprecated plotter.jl 2016-02-08 14:24:25 -05:00
Thomas Breloff aca03d2609 replaced dataframes() with require block 2016-02-08 14:23:02 -05:00
Thomas Breloff 5db9493d2b added dependency on Requires; added support for plotting tuples/FixedSizeArrays; added animate macro; legend ignores empty labels in gadfly/pyplot 2016-02-08 13:42:23 -05:00
Thomas Breloff ac3c041d6d split plotly into 2 different backends: plotly and plotlyjs 2016-02-05 15:44:27 -05:00
Thomas Breloff a319c0c94a legend and colorbar overhaul 2016-02-05 15:10:12 -05:00
Thomas Breloff f74d619748 pgfplots supported 2016-02-04 16:05:21 -05:00
Thomas Breloff eef1981f20 travis 2016-02-04 14:20:34 -05:00
Thomas Breloff ade9d86696 deprecated a bunch of images and docs-related stuff; cleaned up travis, imgcomp, and runtests 2016-02-04 14:00:19 -05:00
Thomas Breloff 935c1cfeab readme 2016-02-04 13:01:06 -05:00
Thomas Breloff 5706770a11 readme 2016-02-04 13:00:12 -05:00
Thomas Breloff 2643417c0b readme 2016-02-04 12:59:40 -05:00
Thomas Breloff d388036601 major overhaul to backend definition and initialization; added pgfplots template; some new reference images 2016-02-04 12:29:56 -05:00
Tom Breloff 96df75cc05 Merge pull request #132 from spencerlyon2/dev
fix change in checking if plotly_blink window is active
2016-02-03 14:32:32 -05:00
Spencer Lyon c62d8d6753 fix change in checking if plotly_blink window is active 2016-02-03 14:21:43 -05:00
Tom Breloff 1562c96d4c Merge pull request #130 from joshday/dev
Add plot!(f::Function) method
2016-02-03 13:30:47 -05:00
Josh Day 94c87d436f Update plot.jl 2016-02-03 12:47:48 -05:00
Tom Breloff b9c8ffb5cf Merge pull request #127 from spencerlyon2/sl/plotly-blink
Sl/plotly blink
2016-02-03 10:08:57 -05:00
Spencer Lyon a37a23bcfc PlotlyJS blink backend working 2016-02-03 09:58:04 -05:00
Spencer Lyon 863ad22b48 added writemime methods for plotly_blink 2016-02-03 09:58:04 -05:00
Tom Breloff bce865ff9f Merge pull request #126 from pkofod/dev
Add abline! which creates a straight line according to the formula b+…
2016-02-03 08:55:46 -05:00
Patrick Kofod Mogensen 1963fe208e Added an extrema(plt) which return the tuple (xmin, xmax), and changed abline! to use extrema. 2016-02-03 10:39:32 +01:00
Patrick Kofod Mogensen 5c4526725b Remove trailing spaces. 2016-02-03 10:32:58 +01:00
Patrick Kofod Mogensen 3f42e9c5bd Add abline! which creates a straight line according to the formula b+a*x, and export abline!
Also add utility functions xmin(plt::Plot) and xmax(plt::Plot).
2016-02-02 23:16:02 +01:00
Thomas Breloff b1c9d0a0af gadfly hist in legend 2016-02-02 10:49:33 -05:00
Thomas Breloff adcbfb4b9e Merge branch 'master' into dev 2016-02-01 13:45:55 -05:00
Thomas Breloff b3ebccab36 gradient fixes 2016-01-30 10:03:19 -05:00
Thomas Breloff d5797054fb ColorGradient fixes; allow default keywords in backend calls 2016-01-29 17:21:25 -05:00
Tom Breloff aab0ec22b9 Merge pull request #120 from Evizero/master
fix unicodeplots compat
2016-01-28 20:25:12 -05:00
Christof Stocker f7babcc1d2 fix unicodeplots compat 2016-01-29 02:06:22 +01:00
Tom Breloff d077307721 Merge pull request #117 from jheinen/dev
Improved GR framework support
2016-01-28 14:04:55 -05:00
Josef Heinen 72e8e10bd6 Improved graphics state handling 2016-01-28 18:26:56 +01:00
Josef Heinen 5ea9e8a0d7 Added support for OHLC charts 2016-01-28 13:44:40 +01:00
Josef Heinen 87b050fad2 Added path3d and scatter3d functions 2016-01-27 16:26:57 +01:00
Josef Heinen 0c8cd74e28 Improved surface output 2016-01-26 20:49:10 +01:00
Josef Heinen 445d970784 Added color bars and surface plots 2016-01-26 17:33:13 +01:00
Josef Heinen 125a1eb9fc Added support for subplots and further linetypes:
- heatmaps
- contour plots
2016-01-25 17:43:13 +01:00
Josef Heinen a2a578d04d Improved representation of legends 2016-01-24 10:47:32 +01:00
Josef Heinen 11e5c0aa9d Use SVG output for inline graphics (IJulia) 2016-01-22 13:00:30 +01:00
Josef Heinen 14b856b0cf Added support for two y-axis scales 2016-01-18 17:25:59 +01:00
Josef Heinen 2e8dd73822 Added more line plots 2016-01-16 15:20:16 +01:00
Josef Heinen da9fdb356c Added support for bar plots 2016-01-16 13:53:22 +01:00
Josef Heinen 054a99ca38 Added horizontal/vertical linetype 2016-01-16 13:23:23 +01:00
Josef Heinen baf33609af Allow combination of lines and markers 2016-01-14 15:50:48 +01:00
Josef Heinen b4eb0b6b41 Added histogram output 2016-01-12 16:33:07 +01:00
Josef Heinen 3e9a47a683 Changed default colormap 2016-01-11 18:16:31 +01:00
Josef Heinen d71c3ecd86 Added support for multiple marker colors / sizes 2016-01-10 19:06:47 +01:00
Josef Heinen e5a864a760 Implemented filled backgrounds 2016-01-09 14:05:02 +01:00
Josef Heinen 81b59f9bc9 Implemented annotations 2016-01-08 17:08:34 +01:00
Josef Heinen 2c2a36ce34 Use Plots predefined colors 2016-01-08 11:56:20 +01:00
Josef Heinen cb0020c629 Fixed problem with uninitialized strings 2016-01-07 14:28:08 +01:00
Josef Heinen 003d39c8cd Added support for inline graphics (IJulia) 2016-01-07 11:37:04 +01:00
Josef Heinen ca866183f1 Print TODO messages for missing features 2016-01-06 10:11:30 +01:00
Thomas Breloff b6e627369b backend priority 2016-01-03 09:53:33 -05:00
Thomas Breloff da96cfc158 Merge remote-tracking branch 'origin/master' into dev 2016-01-03 09:40:05 -05:00
Tom Breloff 0abbd53e07 Merge pull request #107 from jheinen/dev
Initial support for GR framework (GR.jl)
2016-01-03 09:35:40 -05:00
Tom Breloff 6fc0f858c7 Merge pull request #106 from rcnlee/master
Minor changes - animations and AbstractDataFrame
2016-01-03 09:35:24 -05:00
Josef Heinen b3613b68cd Moved GR calls into display method 2016-01-03 11:54:24 +01:00
Josef Heinen 2b6619b3c4 Avoid global 'fig' object 2016-01-03 10:32:47 +01:00
Josef Heinen a33161aa9c Reset default backend to PyPlot
GR should NOT be the default backend until it is finished :-)
2016-01-02 12:15:07 +01:00
Ritchie Lee 5c3c5b999f Support for SubDataFrame by switching to AbstractDataFrame 2016-01-02 02:06:51 -08:00
Josef Heinen 7acca18313 remove superfluous compat macros 2016-01-02 10:40:45 +01:00
Ritchie Lee 7b71d9a448 Small API change to animations
Animation constructor minor fix
2016-01-02 01:36:36 -08:00
Josef Heinen 64ffa3e70f Merge remote-tracking branch 'upstream/master' 2016-01-01 19:53:34 +01:00
Josef Heinen fd88972380 Added support for legends 2016-01-01 19:33:35 +01:00
Tom Breloff dd7b09552a Merge pull request #101 from tbreloff/dev
merging dev into master
2015-12-30 12:58:01 -05:00
Thomas Breloff 0b23aa9e7e relax tests 2015-12-30 12:36:18 -05:00
Thomas Breloff 0caa31bd6b fixes 2015-12-30 12:02:43 -05:00
Thomas Breloff cafac135a0 PyPlot fixes; test fixes 2015-12-30 11:24:44 -05:00
Thomas Breloff 6ac312bd49 removed nlevels keyword, now an alias to levels; cleanup 2015-12-30 10:13:53 -05:00
Tom Breloff 489877d6bf Merge pull request #99 from dlfivefifty/pull-request/c09545de
Add support for levels keyword in PyPlot
2015-12-30 09:38:27 -05:00
Josef Heinen b0b2e695e1 Added support for logarithmic / flipped axes 2015-12-30 11:52:19 +01:00
Josef Heinen c3808c0c5b gr.jl: implemented line/scatter plots 2015-12-29 20:15:10 +01:00
Sheehan Olver c09545de64 Add support for levels keyword in PyPlot 2015-12-29 15:32:49 +11:00
Josef Heinen 7566b0159c Start experimenting with GR.jl backend 2015-12-25 20:11:05 +01:00
Thomas Breloff ed7cc9b89e pyplot surface/wireframe fix for matrix x/y; fix in with, when backend unset 2015-12-23 22:15:13 -05:00
Thomas Breloff 55501e68fb added tex output; fixes for surface inputs; commented out plotly_blink 2015-12-23 13:03:25 -05:00
Thomas Breloff eabe670ae8 working on plotlyjs integration 2015-12-18 17:33:57 -05:00
Thomas Breloff 9fffef6f53 cleanup 2015-12-18 13:19:22 -05:00
Thomas Breloff 368429b757 working on glvisualize 2015-12-18 10:14:15 -05:00
Tom Breloff 49f774eb0d Merge pull request #92 from dlfivefifty/dev
Add initial support for GLVisualize
2015-12-18 09:29:58 -05:00
Sheehan Olver 0487d7a058 merge glvisualize 2015-12-18 15:32:35 +11:00
Sheehan Olver 5943e77563 Fix backend_name 2015-12-18 15:30:07 +11:00
Sheehan Olver b10f0fb945 Started supporting GLVisualize 2015-12-18 15:30:07 +11:00
Sheehan Olver c299891fb0 Merge pull request #1 from tbreloff/dev
Dev
2015-12-18 15:27:35 +11:00
Thomas Breloff fe0f22dfc0 changed :log to map to :log10, switched :log to :ln; better support for plotly axis attributes, ticks, scale, etc; fix plotly bg/fg colors 2015-12-17 14:59:50 -05:00
Thomas Breloff 002603c388 added wireframe linetype; support for surface and wireframe in pyplot 2015-12-17 11:55:26 -05:00
Thomas Breloff 30ef8456c3 added xlims/ylims support in plotly 2015-12-17 00:14:38 -05:00
Thomas Breloff bee1ce401a moved annotation arg from series to plot; added annotation support for plotly 2015-12-16 23:22:46 -05:00
Thomas Breloff e18c4a370a added vline/hline marker shapes; plotly set/get, ticks 2015-12-16 11:14:06 -05:00
Thomas Breloff 2a60a929f1 recipe fix; plotly fixes 2015-12-15 22:53:57 -05:00
Thomas Breloff 939295ab04 _apply_recipe fix 2015-12-15 17:31:11 -05:00
Tom Breloff f6a0ef6a61 Merge pull request #90 from pkofod/patch-1
Added :line as to supportedTypes
2015-12-15 09:28:45 -05:00
Patrick Kofod Mogensen b3bce314e9 Added :line as to supportedTypes 2015-12-15 14:26:17 +01:00
Thomas Breloff e28b85d8b6 plotly support for ijulia and subplots 2015-12-08 12:01:26 -05:00
Thomas Breloff a0af2fc997 fix. closes #84 2015-12-07 23:14:10 -05:00
Tom Breloff 7f7f5c6f12 Merge pull request #85 from stevengj/patch-1
don't test for 0.4-
2015-11-28 20:13:06 -05:00
Steven G. Johnson d21fbfc9be don't test for 0.4-
Testing version > 0.4- is wrong because it incorrectly implies that all pre-releases of 0.4 work.  Either test for the specific 0.4 build you need, or test for `VERSION > v"0.4"` (i.e., the release version).

However, since you REQUIRE julia 0.4 anyway, you can omit this test entirely.
2015-11-25 17:00:57 -05:00
Thomas Breloff 366dc1cca9 travis 2015-11-24 17:43:18 -05:00
Thomas Breloff e9e3913bb7 switch tests to use ExamplePlots and VisualRegressionTests 2015-11-24 17:08:15 -05:00
Thomas Breloff 5491f6eff7 issubplot in _apply_recipe 2015-11-24 13:56:20 -05:00
Thomas Breloff 32e36ba324 added 3d example 2015-11-24 13:05:07 -05:00
Thomas Breloff 9d4c456461 plotly 3d path/scatter, surface 2015-11-24 12:39:54 -05:00
Thomas Breloff cdc7920dff added _apply_recipe 2015-11-24 10:45:49 -05:00
Thomas Breloff cdb4901d91 plotly fill, nbins, density; ref img 2015-11-23 23:05:24 -05:00
Thomas Breloff c7f3520ea8 plotly linestyle/markershape and others 2015-11-23 22:20:04 -05:00
Thomas Breloff f0e728c316 working on plotly; added contour and pie examples 2015-11-23 17:46:30 -05:00
Thomas Breloff 4b1f127eaf surface arg is now z; added orientation arg; working on plotly 2015-11-22 22:34:30 -05:00
Thomas Breloff 1db2d8d8d8 removed tests 2015-11-22 21:13:33 -05:00
Thomas Breloff 87bb45d01b added Surface, removed x/y length assert; added Surface createKWargsList; closes #82 2015-11-22 11:21:07 -05:00
Thomas Breloff 6beadcece8 fixes for contours in pyplot and gadfly 2015-11-21 13:59:26 -05:00
Thomas Breloff fc6f0f3a86 fixes in supported 2015-11-21 10:10:09 -05:00
Thomas Breloff ef7370f56d added density type 2015-11-21 09:26:35 -05:00
Thomas Breloff 52ba7cdbff working on plotly 2015-11-20 23:53:19 -05:00
Thomas Breloff b0c9dd44e9 working on plotly 2015-11-20 23:24:38 -05:00
Thomas Breloff 92ec093ed9 working on plotly 2015-11-20 18:36:50 -05:00
Thomas Breloff 6d2a1aafd2 working on plotly/web 2015-11-20 16:25:50 -05:00
Thomas Breloff f3a7734584 ref imgs 2015-11-20 14:28:26 -05:00
Thomas Breloff 9cf92f37f5 preparing for MLPlots; removed corrplot 2015-11-20 14:05:06 -05:00
Thomas Breloff 34831d42a4 subplot palette fix, added copy methods for Plot and Subplot 2015-11-20 12:55:45 -05:00
Thomas Breloff 607d5d7841 fixed empty plot() in gadfly backend 2015-11-20 10:17:31 -05:00
Thomas Breloff 75adfd2e9d new init logic; fix pyplot subplot bug 2015-11-20 09:26:43 -05:00
Thomas Breloff e92b5b2d35 fixed gadfly scales bug and new ref img; closes #74 2015-11-20 08:58:45 -05:00
Thomas Breloff a8c40a5493 started work on plotly 2015-11-18 21:26:16 -05:00
Thomas Breloff be5c3fb55c img_eps 2015-11-17 16:20:24 -05:00
Thomas Breloff e6e4a63022 added QuartzImageIO to test REQUIRE 2015-11-17 16:19:05 -05:00
Thomas Breloff 1cdc8928ba various fixes for pyplot and gadfly; new ref imgs 2015-11-17 15:59:45 -05:00
Thomas Breloff ad111a0163 added mpl version log 2015-11-17 11:48:10 -05:00
Thomas Breloff 7091a4899c imgcomp cleanup; pyplot dpi fix; new ref imgs 2015-11-17 11:19:12 -05:00
Thomas Breloff 3bd9e70fa9 moved PyPlot to first priority 2015-11-16 16:06:48 -05:00
Thomas Breloff 54bd2c620e gadfly ref imgs 2015-11-16 15:50:02 -05:00
Thomas Breloff 121640b1b3 fixed gadfly hline/vline constructors 2015-11-16 15:37:55 -05:00
Thomas Breloff 55e388513c fixes for gadfly and unicodeplots 2015-11-12 17:30:55 -05:00
Thomas Breloff b153dcbd63 reference images; gadfly markersize fix 2015-11-11 21:47:53 -05:00
Thomas Breloff 242cbe5ecb z/zcolor 2015-11-11 17:46:44 -05:00
Thomas Breloff f1cca13796 fix pyplot alpha and added stroke support 2015-11-11 16:27:21 -05:00
Thomas Breloff 918ba72107 readme 2015-11-10 16:41:26 -05:00
Thomas Breloff e596f27666 fixed numeric array without numeric type; updated readme; closes #64 2015-11-10 16:38:47 -05:00
Thomas Breloff a40d0000c5 fixed pyplot 2015-11-09 09:19:00 -05:00
Thomas Breloff fb82da74a1 gadfly layer order; fixes #63 2015-11-07 21:58:04 -05:00
Thomas Breloff b0991a0624 working on args 2015-11-06 15:58:15 -05:00
Thomas Breloff 6236acea02 working on markerstroke, name changes, and arg improvements 2015-11-06 15:49:27 -05:00
Thomas Breloff 391c7e8570 name changes; require to 0.4 2015-11-06 11:46:24 -05:00
Thomas Breloff 58041cc64e working on big renaming 2015-11-05 16:39:04 -05:00
Thomas Breloff 8c7d3a6e92 working on stroke/fill 2015-11-05 11:52:22 -05:00
Thomas Breloff ae1982369c working on bokeh 2015-11-03 15:48:04 -05:00
Thomas Breloff 5f85ac01f7 working on bokeh backend 2015-11-03 13:45:23 -05:00
Thomas Breloff cca263fb2b with method fix 2015-10-30 12:47:48 -04:00
Thomas Breloff 809ed02b85 gadfly/immerse downgrade fix 2015-10-30 10:03:10 -04:00
Thomas Breloff 9eab1083ab working on with 2015-10-29 17:18:14 -04:00
Thomas Breloff 60523bef80 working on 3d plotting 2015-10-29 15:44:07 -04:00
Thomas Breloff 46b959ff3c color reorg 2015-10-29 14:31:36 -04:00
Thomas Breloff 42c3827ded 0.3 fix 2015-10-29 13:26:40 -04:00
Thomas Breloff 26e4b4efda back to 0.3; working on fixes 2015-10-29 12:57:07 -04:00
Thomas Breloff 2169ab3bb8 ref imgs 2015-10-29 12:50:25 -04:00
Thomas Breloff 85dad8db88 removed gitattributes 2015-10-29 11:25:08 -04:00
Thomas Breloff 8120c34064 added smooth and opacity args for pyplot; fixes in regressionXY, replaceAliases 2015-10-28 12:14:58 -04:00
Thomas Breloff 1f1d1f70a1 fix for group arg 2015-10-28 01:37:52 -04:00
Thomas Breloff 373d868a17 gadfly color fixes; pyplot grid support 2015-10-28 01:12:20 -04:00
Thomas Breloff 834ba9bc61 fix imgcmp 2015-10-27 23:37:58 -04:00
Thomas Breloff 3ffec5aed9 fixed colors from subplot changes; fix imgcmp; new refimgs 2015-10-27 23:03:30 -04:00
Thomas Breloff ef20c1d684 subplot initargs fix 2015-10-27 22:08:47 -04:00
Thomas Breloff 697a59568c fixes 2015-10-27 20:08:33 -04:00
Thomas Breloff e58fb935b0 several fixes, plus qwt pos/size 2015-10-27 19:25:27 -04:00
Thomas Breloff 044d23f8a5 working on fixing problems with subplots and contours 2015-10-27 18:03:37 -04:00
Thomas Breloff 1604d867c3 cleared out examples markdown 2015-10-27 13:43:49 -04:00
Thomas Breloff 011db481c3 cleaned out examples and img directories 2015-10-27 13:42:29 -04:00
Thomas Breloff c404cdc38a qwt subplot pos/size 2015-10-27 13:38:16 -04:00
Thomas Breloff 20689af7dd alpha kw for colors; pyplot fix, debugshow 2015-10-27 12:40:38 -04:00
Thomas Breloff 9ea0585d71 support for contours in gadfly/immerse/pyplot 2015-10-26 18:07:22 -04:00
Thomas Breloff 614c5f694b readme 2015-10-26 15:58:26 -04:00
Thomas Breloff fa36aae068 readme 2015-10-26 15:52:44 -04:00
Thomas Breloff 3e85232310 readme 2015-10-26 15:46:34 -04:00
Thomas Breloff 894928d7ca fixes, examples, and readme; also deprecated winston 2015-10-26 14:03:06 -04:00
Thomas Breloff afb1486f86 tests and readme 2015-10-26 13:18:22 -04:00
Thomas Breloff 2799bd18e1 moved refimg dirs 2015-10-26 12:44:16 -04:00
Thomas Breloff 43c2a885b9 travis and 0.3 fixes 2015-10-26 12:25:09 -04:00
Thomas Breloff be7feb0eaa travis and readme 2015-10-26 11:43:30 -04:00
Thomas Breloff e3d653a8d9 tests 2015-10-26 11:38:33 -04:00
Thomas Breloff b9f9d8160d working on tests 2015-10-26 11:28:49 -04:00
Thomas Breloff 141b5c604d pyplot test fixes; pyplot ref imgs; travis fix 2015-10-26 10:52:29 -04:00
Thomas Breloff 8a0fc00435 imgcmp and travis 2015-10-26 09:59:26 -04:00
Thomas Breloff a3a8cb9368 nnet 2015-10-26 01:40:12 -04:00
Thomas Breloff 96c66b33a2 nnet 2015-10-26 00:11:43 -04:00
Thomas Breloff 62fad3724f pyplot push and nnet 2015-10-25 17:08:42 -04:00
Thomas Breloff ca2b6c87ef enabled lfs 2015-10-25 13:53:09 -04:00
Thomas Breloff 20348645a3 pyplot fixes 2015-10-25 00:44:18 -04:00
Thomas Breloff a9b39fad64 pyplot gui fixes and background/foreground fixes with writemime 2015-10-25 00:34:20 -04:00
Thomas Breloff 1c556ade57 more subplot fixes 2015-10-24 22:29:27 -04:00
Thomas Breloff e08957f5ec cleanup and fixes for subplot logic 2015-10-24 20:59:42 -04:00
Thomas Breloff e76ded69e6 some fixes for subplots and imgcmp 2015-10-24 16:11:55 -04:00
Thomas Breloff 7e740861d8 palette fixes and working on imgcmp 2015-10-24 15:09:13 -04:00
Thomas Breloff f28e65a26c meetup 2015-10-24 00:00:26 -04:00
Thomas Breloff a29f0c820b nnet 2015-10-23 17:05:35 -04:00
Thomas Breloff b2bc30cfed qwt fix 2015-10-23 15:58:42 -04:00
Thomas Breloff 6ce235bb68 immerse gui fix 2015-10-23 13:53:13 -04:00
Thomas Breloff 479ab989df immerse subplot gui fix; pyplot colorbars; subplot creation fix 2015-10-23 13:26:34 -04:00
Thomas Breloff 8d490b9e52 meetup nnet 2015-10-22 16:53:39 -04:00
Thomas Breloff 949f11a8c9 meetup 2015-10-22 15:10:15 -04:00
Thomas Breloff d4896e1978 improved color palette generation; removed some predefined gradients 2015-10-22 12:16:00 -04:00
Thomas Breloff c4020080b3 nnet 2015-10-22 00:40:41 -04:00
Thomas Breloff 5a68003d16 fixed empty plots 2015-10-21 21:30:05 -04:00
Thomas Breloff 9fcc3cdc24 pre/post processing subplots 2015-10-21 13:52:58 -04:00
Thomas Breloff ee80ce5ae6 subplots and colors 2015-10-21 12:27:13 -04:00
Thomas Breloff 456c4019c4 misc 2015-10-21 01:34:50 -04:00
Thomas Breloff f65627a505 travis; wine 2015-10-21 00:44:48 -04:00
Thomas Breloff 38d1fc15fe travis; wine 2015-10-21 00:29:53 -04:00
Thomas Breloff 5eead5b0fa travis; wine 2015-10-21 00:21:55 -04:00
Thomas Breloff 4223bd57f1 travis; wine 2015-10-21 00:15:11 -04:00
Thomas Breloff 5bfa653858 travis; wine 2015-10-20 23:57:18 -04:00
Thomas Breloff 5342764caf travis 2015-10-20 23:23:38 -04:00
Thomas Breloff 04a44a82c9 tests 2015-10-20 23:07:47 -04:00
Thomas Breloff c28820f39c colors and tests 2015-10-20 17:11:21 -04:00
Thomas Breloff 546955a78b working on colors and tests 2015-10-20 14:49:01 -04:00
Thomas Breloff d1e9cc5800 palettes notebook 2015-10-20 12:55:36 -04:00
Thomas Breloff 4ef2fb9933 palettes notebook 2015-10-20 12:23:45 -04:00
Thomas Breloff d7bc8adf21 palettes notebook 2015-10-20 12:15:13 -04:00
Thomas Breloff b5f44efc69 palettes notebook 2015-10-20 11:56:06 -04:00
Thomas Breloff 5e8abea222 palettes notebook 2015-10-20 11:53:02 -04:00
Thomas Breloff f291454ccb palettes notebook 2015-10-20 10:40:49 -04:00
Thomas Breloff bac6637b46 messing with colors 2015-10-20 08:24:58 -04:00
Thomas Breloff 866ac9fb72 working on test framework 2015-10-19 17:53:17 -04:00
Thomas Breloff 8208f6947f pdf/ps/svg outputs 2015-10-19 14:11:22 -04:00
Thomas Breloff dcd50a7c62 started work on imgcomp tests 2015-10-19 00:31:43 -04:00
Thomas Breloff 781aa86568 started work on imgcomp tests 2015-10-19 00:24:04 -04:00
Thomas Breloff 3facc116c8 gadfly examples 2015-10-18 00:56:38 -04:00
Thomas Breloff 9178c3b087 winston examples 2015-10-18 00:50:30 -04:00
Thomas Breloff 5c6c85a22f qwt fixes and examples 2015-10-18 00:41:04 -04:00
Thomas Breloff b8adb4d229 unicodeplots examples 2015-10-18 00:08:42 -04:00
Thomas Breloff 093118da8b unicodeplots examples 2015-10-17 23:52:07 -04:00
Thomas Breloff 0aa760119f examples 2015-10-17 23:43:52 -04:00
Thomas Breloff 60b3a1583d examples 2015-10-17 23:31:50 -04:00
Thomas Breloff 593c47b938 examples 2015-10-17 23:29:03 -04:00
Thomas Breloff 0f6bb7ce6f examples 2015-10-17 23:10:45 -04:00
Thomas Breloff 048c2541e6 readme 2015-10-17 23:07:35 -04:00
Thomas Breloff f3e7d1a43b examples 2015-10-17 22:43:36 -04:00
Thomas Breloff 5df24d6683 custom markers example; pyplot markers 2015-10-17 22:16:06 -04:00
Thomas Breloff 73779898b7 pyplot fonts and fixes; examples 2015-10-17 16:49:45 -04:00
Thomas Breloff 7200b6adc6 pyplot set/get data; examples 2015-10-17 16:17:18 -04:00
Thomas Breloff d48f15ab4d examples 2015-10-17 15:22:00 -04:00
Thomas Breloff 2747671935 readme 2015-10-17 14:07:53 -04:00
Thomas Breloff d86f24dd35 pyplot fixes 2015-10-17 01:26:58 -04:00
Thomas Breloff 60edccade5 readme 2015-10-16 17:46:20 -04:00
Thomas Breloff 540460cae9 readme 2015-10-16 17:37:30 -04:00
Thomas Breloff df44d86f46 examples and readme 2015-10-16 17:25:54 -04:00
Thomas Breloff c287b446c9 fixes and examples 2015-10-16 17:21:28 -04:00
Thomas Breloff 1db36c05a8 animations 2015-10-16 16:36:40 -04:00
Thomas Breloff 2c884ab5ab animations 2015-10-16 12:03:40 -04:00
Thomas Breloff cd0dfc956d gadfly cleanup 2015-10-16 11:03:34 -04:00
Thomas Breloff 03392a5f03 gadfly cleanup 2015-10-16 10:58:44 -04:00
Thomas Breloff a7da999bd7 Font and tick/guide/legend fonts 2015-10-16 10:52:36 -04:00
Thomas Breloff 47f21b1652 fixes; changed reg to smooth, added loess smoothing in gadfly 2015-10-16 00:47:46 -04:00
Thomas Breloff 7c91d10c79 several fixes after gadfly reorg 2015-10-16 00:21:37 -04:00
Thomas Breloff fac2f5494b gadfly overhaul 2015-10-15 23:11:17 -04:00
Thomas Breloff bfcdb7e840 example 2015-10-15 17:30:44 -04:00
Thomas Breloff 21d55db3b4 slides 2015-10-15 17:19:57 -04:00
Thomas Breloff 18e49888fe jpg 2015-10-15 16:51:48 -04:00
Thomas Breloff 7ba2f61361 0.3 fix 2015-10-15 16:50:57 -04:00
Thomas Breloff 407df158ef PlotText/text for annotations; fixes 2015-10-15 14:46:29 -04:00
Thomas Breloff eab46ba9c4 fixes 2015-10-15 01:34:06 -04:00
Thomas Breloff 39318e5e61 working on shapes 2015-10-14 20:53:44 -04:00
Thomas Breloff ac1ce03fe5 working on shapes overhaul 2015-10-14 20:46:17 -04:00
Thomas Breloff 4fa697ec75 big shapes overhaul; gadfly ShapeGeometry; some reorg and cleanup... still working on it 2015-10-14 18:14:44 -04:00
Thomas Breloff 027961bffa various fixes 2015-10-14 12:11:20 -04:00
Thomas Breloff 7dfcbabe0c wine 2015-10-13 17:35:10 -04:00
Thomas Breloff dfc7658426 misc 2015-10-13 16:51:19 -04:00
Thomas Breloff 04c6726b76 pyplot cleanup 2015-10-13 15:56:14 -04:00
Thomas Breloff 8fdbbe4c16 pyplot subplots 2015-10-13 15:30:25 -04:00
Thomas Breloff 09fceab75b pyplot subplots 2015-10-13 15:02:27 -04:00
Thomas Breloff 8df4c27285 pyplot, corrplot, and nba 2015-10-13 01:16:11 -04:00
Thomas Breloff e38820b74a working on pyplot 2015-10-12 18:16:55 -04:00
Thomas Breloff c052e731f8 fix links to keep separate lims per row/column 2015-10-12 16:42:34 -04:00
Thomas Breloff 3636860262 subplot linkfunc, fixes, cleanup 2015-10-12 14:31:27 -04:00
Thomas Breloff ce7421bef4 immerse fix 2015-10-12 10:42:29 -04:00
Thomas Breloff 23b51c4c3e working on pyplot subplots 2015-10-10 00:57:48 -04:00
Thomas Breloff 48d802fc40 qwt subplots 2015-10-10 00:03:23 -04:00
Thomas Breloff 51e8f3ff6e working on subplots 2015-10-09 23:21:45 -04:00
Thomas Breloff 7917397bf3 working on corrplot and recipes 2015-10-09 17:08:17 -04:00
Thomas Breloff e0c0052b63 working on corrplot and recipes 2015-10-09 17:08:06 -04:00
Thomas Breloff 15a4b50ce4 working on corrplot and recipes 2015-10-09 17:03:54 -04:00
Thomas Breloff a51a8abf27 working on subplots and recipes 2015-10-09 16:00:24 -04:00
Thomas Breloff 45747e3635 working on recipes 2015-10-09 14:35:00 -04:00
Thomas Breloff 64b8e15918 working on subplots and recipes 2015-10-09 13:12:40 -04:00
Thomas Breloff f4b716b255 working on linked subplots 2015-10-08 17:44:00 -04:00
Thomas Breloff 1115ff83bb working on contours 2015-10-08 14:18:49 -04:00
Thomas Breloff c24550c756 0.3 fix gadfly shapes 2015-10-08 14:01:30 -04:00
Thomas Breloff b5cdfe30ea readme and examples 2015-10-08 13:33:14 -04:00
Thomas Breloff a10e594da1 tests 2015-10-08 12:31:33 -04:00
Thomas Breloff 63aee387f0 badges and test fix 2015-10-08 12:05:21 -04:00
Thomas Breloff ab9de1e199 added 0.3 and nightly to travis; force Winston to use gtk 2015-10-08 11:48:54 -04:00
Thomas Breloff e60c463328 0.3 support; require Compat; pyplot scatter colormaps 2015-10-08 11:30:20 -04:00
Thomas Breloff 537d74196b working on contours 2015-10-08 07:49:26 -04:00
Thomas Breloff a8bbbe8582 working on contours 2015-10-08 00:49:30 -04:00
Thomas Breloff 29471a7aac readme 2015-10-07 18:18:44 -04:00
Thomas Breloff 894a4a5567 winston fixes/examples and updated readme 2015-10-07 18:09:32 -04:00
Thomas Breloff f7cd5276f0 fixed qwt and examples 2015-10-07 17:42:08 -04:00
Thomas Breloff 95b8c71083 examples 2015-10-07 17:04:19 -04:00
Thomas Breloff 52c82e6fe2 examples 2015-10-07 16:54:42 -04:00
Thomas Breloff cef1abd6aa readme 2015-10-07 16:29:32 -04:00
Thomas Breloff 91da261128 working on colors/args and examples 2015-10-07 16:04:30 -04:00
Thomas Breloff 1c70346a61 bringing pyplot up to speed 2015-10-07 13:45:38 -04:00
Thomas Breloff 2de017086d working on colors and args 2015-10-07 12:50:07 -04:00
Thomas Breloff d6253c72d4 working on colors, args, and axis flip 2015-10-06 18:19:44 -04:00
Thomas Breloff e5007b6c87 working on colors, args, and axis flip 2015-10-06 17:51:54 -04:00
Thomas Breloff bbc18549b3 working on args 2015-10-06 11:37:45 -05:00
Thomas Breloff b6855b6fba working on args 2015-10-06 10:07:18 -05:00
Thomas Breloff 70db97d576 working on args 2015-10-06 09:49:33 -05:00
Thomas Breloff f632f2f39e working on args 2015-10-06 09:04:57 -05:00
Thomas Breloff b23f968d57 working on axis/line/marker/fill overhaul 2015-10-06 08:45:53 -05:00
Thomas Breloff ea88877a11 working on color schemes 2015-10-05 17:06:52 -05:00
Thomas Breloff 1de3decede working on color schemes 2015-10-05 16:28:43 -05:00
Thomas Breloff 78045fd2ab working on spy and nbins tuple 2015-10-04 21:39:32 -05:00
Thomas Breloff 7c8898158b working on gadfly continuous colors 2015-10-04 16:52:29 -05:00
Thomas Breloff 3fc5f87388 added better error for Winston output_surface 2015-10-04 09:41:42 -05:00
Thomas Breloff 623ca1effc winston log10 scale 2015-10-03 17:18:58 -05:00
Thomas Breloff da05e7fe5e qwt log10 scale 2015-10-03 17:03:13 -05:00
Thomas Breloff f3734d4c64 added pyplot scales 2015-10-03 09:46:02 -05:00
Thomas Breloff 9ab458e109 switched gadfly heatmap to Geom.histogram2d 2015-10-01 12:55:05 -05:00
Thomas Breloff d4c72e6f78 gadfly ribbon 2015-09-30 10:36:00 -04:00
Thomas Breloff 1f58ef5140 don't error on savefig without an ext, use default; closes #32 2015-09-30 09:55:07 -04:00
Thomas Breloff 69d574c587 added scales globally and to gadfly/immerse 2015-09-30 09:18:16 -04:00
Thomas Breloff a027fdded7 autoload dataframes 2015-09-29 14:40:11 -04:00
Thomas Breloff 443df0501d supported graphs 2015-09-29 14:27:18 -04:00
Thomas Breloff d68fe5f80d supported readme 2015-09-29 14:14:13 -04:00
Thomas Breloff ebf868bae3 supported readme 2015-09-29 14:13:46 -04:00
Thomas Breloff 8a9b3286e9 supportedGraphs utils and pngs; removed args/kwargs from defaults 2015-09-29 14:05:24 -04:00
Thomas Breloff a0455561b6 unicode hline/vline; supportGraph utils 2015-09-29 13:46:31 -04:00
Thomas Breloff 518ccf5a74 pyplot hline/vline 2015-09-29 12:20:58 -04:00
Thomas Breloff 54f09c58cb unicodeplots lims 2015-09-29 12:00:48 -04:00
Thomas Breloff 029ad1045a qwt lims/ticks 2015-09-29 11:50:02 -04:00
Thomas Breloff 2f55fb4d84 winston lims 2015-09-29 10:40:46 -04:00
Thomas Breloff add724eee9 working on gadfly ticks 2015-09-29 09:50:53 -04:00
Thomas Breloff 07324ae892 working on gadfly ticks 2015-09-29 09:49:58 -04:00
Thomas Breloff ffc05e971f working on ticks 2015-09-28 23:49:03 -04:00
Thomas Breloff 30f42fd1d4 added lims/ticks for pyplot 2015-09-28 22:49:46 -04:00
Thomas Breloff f284857438 added qwt hline/vline; working on setTicksFromStringVector 2015-09-28 17:56:32 -04:00
Thomas Breloff 06ab514472 qwt annotations and examples 2015-09-28 17:11:43 -04:00
Thomas Breloff 85b65acbe0 readme 2015-09-28 16:48:32 -04:00
Thomas Breloff 4d1d9de22f gitignore 2015-09-28 16:43:04 -04:00
Thomas Breloff a68ec9b191 savefig and png 2015-09-28 16:42:02 -04:00
Thomas Breloff e386636d66 readme 2015-09-28 16:01:48 -04:00
Thomas Breloff cc3311b8eb examples 2015-09-28 15:49:46 -04:00
Thomas Breloff d3d1181769 examples and readme 2015-09-28 15:44:50 -04:00
Thomas Breloff 001abf684c examples 2015-09-28 15:35:45 -04:00
Thomas Breloff 92a65296b5 redid supportedArgs 2015-09-28 15:21:45 -04:00
Thomas Breloff 3a7887d003 working on massive naming overhaul and fixes 2015-09-28 14:58:56 -04:00
Thomas Breloff 3334c4a87a winston annotations 2015-09-28 12:52:44 -04:00
Thomas Breloff 228ce768e0 pyplot annotations and ioff fix 2015-09-28 12:09:52 -04:00
Thomas Breloff eafc1a784c working on annotations 2015-09-28 11:09:45 -04:00
Thomas Breloff fd3a4d0a0e working on annotations 2015-09-28 10:46:53 -04:00
Thomas Breloff 77d679b63b working on handling string input data 2015-09-27 23:46:42 -04:00
Thomas Breloff 730d764c6e gadfly color group 2015-09-25 17:03:57 -04:00
Thomas Breloff 6f1ef82e89 gadfly color groups for line segments 2015-09-25 16:47:17 -04:00
Thomas Breloff d4da910f19 call Gadfly.get_stroke_vector; added getindex/setindex to qwt 2015-09-25 13:33:06 -04:00
Thomas Breloff a0a0773bd2 fix push warning 2015-09-25 09:42:16 -04:00
Thomas Breloff 9ce8d138d5 working on data updating and notebooks 2015-09-25 01:00:22 -04:00
Thomas Breloff 0041b034e0 working on various things 2015-09-24 16:47:30 -04:00
Thomas Breloff d781c437de started real time updates; fixes #29 (pyplot ijulia display) 2015-09-24 13:01:31 -04:00
Thomas Breloff 2fe21b8547 fixes, gadfly star shape, readme example 2015-09-24 00:35:18 -04:00
Thomas Breloff dbc90c9417 working on groups and iris notebook 2015-09-23 23:34:54 -04:00
Thomas Breloff 431d60a58a working on groups and iris notebook 2015-09-23 23:15:15 -04:00
Thomas Breloff fcfcc9a94a working on groups and iris notebook 2015-09-23 17:13:20 -04:00
Thomas Breloff 870fb4e2df working on groups and iris notebook 2015-09-23 17:12:26 -04:00
Thomas Breloff e3612b973f working on groups and iris notebook 2015-09-23 16:48:49 -04:00
Thomas Breloff 75f4ca928f working on auto-labels from dataframes meta info, and started group arg 2015-09-23 15:34:42 -04:00
Thomas Breloff 22fd25b30f readme 2015-09-23 13:36:51 -04:00
Thomas Breloff 610ddb09ff readme and cleanup 2015-09-23 13:33:12 -04:00
Thomas Breloff 33602aca11 fixed tests 2015-09-23 13:03:37 -04:00
Thomas Breloff a207856e99 lims/ticks for gadfly/immerse 2015-09-23 12:45:24 -04:00
Thomas Breloff 80e8b5b5e4 fixes 2015-09-23 11:35:08 -04:00
Thomas Breloff 273996aa91 working on ticks/lims 2015-09-23 00:14:32 -04:00
Thomas Breloff 646b88c5be working on ticks 2015-09-22 23:30:20 -04:00
Thomas Breloff 88cfd82a25 unicodeplots fixes 2015-09-22 17:19:37 -04:00
Thomas Breloff 2f8436be87 winston examples 2015-09-22 17:04:51 -04:00
Thomas Breloff a6237cdda3 qwt examples 2015-09-22 17:02:19 -04:00
Thomas Breloff eb93a767fc pyplot fixes and examples 2015-09-22 16:57:23 -04:00
Thomas Breloff 41852412ca gadfly/immerse fixes and examples 2015-09-22 16:46:54 -04:00
Thomas Breloff 224b2b7323 various fixes; readme 2015-09-22 16:30:40 -04:00
Thomas Breloff ec939f9f9a more display fun 2015-09-22 14:56:37 -04:00
Thomas Breloff eced15b712 working on display/writemime overhaul 2015-09-22 12:54:55 -04:00
Thomas Breloff f8fb473981 reexport Colors; move default backend choice into __init__; fixed pickDefaultBackend 2015-09-22 08:10:30 -04:00
Thomas Breloff f2bcb77cfb trying fgcolor gadfly 2015-09-22 00:04:35 -04:00
Thomas Breloff dc5da28176 initial dataframes support 2015-09-21 23:36:07 -04:00
Thomas Breloff be43e64743 foreground_color fixes and added support in pyplot 2015-09-21 23:04:37 -04:00
Thomas Breloff 454630dace qwt fixes 2015-09-21 22:14:59 -04:00
Thomas Breloff c9df244333 qwt fixes 2015-09-21 17:38:27 -04:00
Thomas Breloff 2649d97d69 readme 2015-09-21 17:31:36 -04:00
Thomas Breloff bcb9ac5501 readme 2015-09-21 17:29:51 -04:00
Thomas Breloff 76d1e26f7d readme 2015-09-21 17:04:20 -04:00
Thomas Breloff ba13ab9926 readme 2015-09-21 16:59:15 -04:00
Thomas Breloff c43b34b67f auto-generated readme 2015-09-21 16:49:45 -04:00
Thomas Breloff 3739c2881b fixed linetype aliases; for qwt as well 2015-09-21 16:18:58 -04:00
Thomas Breloff bc13b81351 working on readme_template; added path linetype and sorting logic for line 2015-09-21 16:03:59 -04:00
Thomas Breloff 86a56e487a working on aliases 2015-09-21 14:38:03 -04:00
Thomas Breloff 031058cd0c cleanup and renaming constants 2015-09-21 12:15:40 -04:00
Thomas Breloff 85040d6104 fix creating empty plot with plot() 2015-09-21 12:05:17 -04:00
Thomas Breloff efbf74b44c working on auto generated color palette 2015-09-21 11:45:37 -04:00
Thomas Breloff 774fc42e8e replaced creation of kw args 2015-09-20 23:48:45 -04:00
Thomas Breloff 692cfe4f52 added srand calls to tests and examples 2015-09-20 22:59:25 -04:00
Thomas Breloff 8b34d4417b gadfly display size and fix: check for line_style 2015-09-20 21:12:56 -04:00
Thomas Breloff c20a7b3c69 added fillto for gadfly/immerse 2015-09-19 17:23:18 -04:00
Thomas Breloff ff6e83c55e working on new plot logic 2015-09-19 14:39:25 -04:00
Thomas Breloff 6aa56fd30f fixed subplot bug 2015-09-19 00:38:50 -04:00
Thomas Breloff f5a6f129dd foreground_color 2015-09-19 00:05:24 -04:00
Thomas Breloff 5405c1d240 working on colors 2015-09-18 17:20:35 -04:00
Thomas Breloff 455f85e230 working on colors, args 2015-09-18 16:53:43 -04:00
Thomas Breloff fa899a67a7 added support for gadfly linestyles... depends on gadfly PR 695 2015-09-18 12:26:18 -04:00
Thomas Breloff 56cf25eebf gadfly shapes 2015-09-18 01:55:16 -04:00
Thomas Breloff 1a490551e6 working on arg aliases and reorg 2015-09-18 01:48:00 -04:00
Thomas Breloff 4514802e35 replacing String with AbstractString 2015-09-17 21:32:27 -04:00
Thomas Breloff a9062b0611 examples 2015-09-17 17:11:25 -04:00
Thomas Breloff eb6b30aab1 markers, fixes, and rerun examples 2015-09-17 17:05:08 -04:00
Thomas Breloff 922abd7ad3 examples 2015-09-17 16:21:13 -04:00
Thomas Breloff c4596dda75 more shape fun... completed gadfly's set 2015-09-17 16:18:48 -04:00
Thomas Breloff 168b77326b fixing markers 2015-09-17 15:44:01 -04:00
Thomas Breloff f674bba861 added gadfly shapes utriangle, dtriangle, and xcross 2015-09-17 15:28:54 -04:00
Thomas Breloff 6b2e65e738 ohlc example 2015-09-17 14:49:37 -04:00
Thomas Breloff 51021c799d accept vectors of tuples; implement ohlc for gadfly with an example 2015-09-17 14:44:29 -04:00
Thomas Breloff 01cb036976 added fillto support for winston 2015-09-17 11:26:26 -04:00
Thomas Breloff ea7867429f added hline/vline support in gadfly 2015-09-17 10:14:13 -04:00
Thomas Breloff c5300cd7be notes 2015-09-17 08:32:32 -04:00
Thomas Breloff ea34c7d226 working on winston and examples 2015-09-16 17:17:05 -04:00
Thomas Breloff 537e04989f working on winston 2015-09-16 16:34:40 -04:00
Thomas Breloff d447c5dc25 moved exports into backend files 2015-09-16 15:11:44 -04:00
Thomas Breloff 73944fc9bf first draft winston 2015-09-16 15:08:50 -04:00
Thomas Breloff 01e994e0f1 regenerate markdown 2015-09-16 14:32:36 -04:00
Thomas Breloff af3f3f436c improving markdown 2015-09-16 14:24:58 -04:00
Thomas Breloff 56e90a266a examples 2015-09-16 14:16:58 -04:00
Thomas Breloff 1fc59ed522 fix ALL_ARGS; unicodeplots examples 2015-09-16 14:01:53 -04:00
Thomas Breloff 3a3b1a6c17 examples 2015-09-16 13:40:23 -04:00
Thomas Breloff 242ce807df examples 2015-09-16 13:32:43 -04:00
Thomas Breloff 734967152c fixed suppported typo 2015-09-16 13:29:51 -04:00
Thomas Breloff 8023e15bb9 examples 2015-09-16 13:17:06 -04:00
Thomas Breloff ceb58a6fe1 supported functions; renamed a few things package to backend 2015-09-16 13:03:37 -04:00
Thomas Breloff ae48bd1abb working on big set of fixes/reorg; better example code, supportedArgs and subplotSupported, change dots to scatter and handle better 2015-09-16 12:07:08 -04:00
Thomas Breloff d27fe2fbad examples 2015-09-16 01:47:17 -04:00
Thomas Breloff 970686efaf supported functions and gadfly shapes 2015-09-16 01:42:47 -04:00
Thomas Breloff 8df46ec0e9 gadfly_shapes.jl 2015-09-16 00:25:51 -04:00
Thomas Breloff 864057a6ed unicode examples 2015-09-16 00:12:14 -04:00
Thomas Breloff 92ca4cb1d1 examples 2015-09-16 00:01:38 -04:00
Thomas Breloff aabf811fe7 working on Immerse subplot support 2015-09-15 23:27:00 -04:00
Thomas Breloff 9b0a645a05 working on immerse subplot window 2015-09-15 17:54:40 -04:00
Thomas Breloff 24b5e7ff09 pushtomaster 2015-09-15 17:11:36 -04:00
Thomas Breloff 48969ecee3 added sticks and sticksHack for Gadfly 2015-09-15 16:49:56 -04:00
Thomas Breloff 6ae0912e0e readme 2015-09-15 13:24:14 -04:00
Thomas Breloff e8062d544d readme 2015-09-15 13:18:49 -04:00
Thomas Breloff 07332aa4ac readme 2015-09-15 13:16:19 -04:00
Thomas Breloff 1e2b3559f2 gadfly marker fixes and examples 2015-09-15 13:08:04 -04:00
Thomas Breloff 8bfaa14edb added backend template 2015-09-15 10:03:56 -04:00
Thomas Breloff d4e87d3fe8 working on gadfly and immerse 2015-09-14 23:44:28 -04:00
Thomas Breloff 460348c0b6 working on gadfly and immerse 2015-09-14 23:28:47 -04:00
Thomas Breloff 264e8c735e working on gadfly markers 2015-09-14 17:27:37 -04:00
Thomas Breloff c4b04e2327 immerse final fixes and examples 2015-09-14 15:30:09 -04:00
Thomas Breloff dc1389bd1a working on immerse 2015-09-14 15:24:18 -04:00
Thomas Breloff 51e38c80d8 working on immerse 2015-09-14 15:08:25 -04:00
Thomas Breloff a33f5fabd1 working on immerse 2015-09-14 14:41:13 -04:00
Thomas Breloff e6c8344970 working on immerse 2015-09-14 13:44:58 -04:00
Thomas Breloff 2207ae6617 working on immerse 2015-09-14 12:35:24 -04:00
Thomas Breloff cdc6707ad3 pyplot bg color 2015-09-14 10:46:33 -04:00
Thomas Breloff 448fdaecbd readme 2015-09-14 00:45:55 -04:00
Thomas Breloff 4aa79aed6f readme 2015-09-14 00:45:00 -04:00
Thomas Breloff c8fcc373f8 examples and readme 2015-09-14 00:28:08 -04:00
Thomas Breloff 1410141b46 examples 2015-09-14 00:16:24 -04:00
Thomas Breloff 76efabb5f3 heatmaps, fixes 2015-09-14 00:15:51 -04:00
Thomas Breloff 36effc6dc4 working on pyplot 2015-09-13 22:20:15 -04:00
Thomas Breloff 8d5412a38f working on pyplot 2015-09-13 21:34:28 -04:00
Thomas Breloff 465b5d20b4 working on pyplot 2015-09-13 18:39:56 -04:00
Thomas Breloff 60a22b1897 working on pyplot 2015-09-13 17:02:03 -04:00
Thomas Breloff 09a685e977 gitignore 2015-09-13 16:36:32 -04:00
Thomas Breloff 31da90865c added args and kwargs to PLOT_DEFAULTS 2015-09-13 14:06:33 -04:00
Thomas Breloff 9ea98bcb56 readme 2015-09-12 22:54:16 -04:00
Thomas Breloff 4f9804d24e readme 2015-09-12 22:48:07 -04:00
Thomas Breloff 0a88518188 readme 2015-09-12 22:38:56 -04:00
Thomas Breloff ccae6373be readme 2015-09-12 22:31:05 -04:00
Thomas Breloff 31e5d07a45 readme 2015-09-12 22:24:44 -04:00
Thomas Breloff 056fee4b28 added parametric plot with example 2015-09-12 22:02:17 -04:00
Thomas Breloff 5a47864d73 readme 2015-09-12 21:46:30 -04:00
Thomas Breloff 1c7da432f1 readme 2015-09-12 21:40:25 -04:00
Thomas Breloff 3d3ceacaab pickDefaultBackend with warning; readme improvements 2015-09-12 21:33:38 -04:00
Thomas Breloff 35df39a44b readme and shorthands for switching plotters 2015-09-12 21:12:18 -04:00
Thomas Breloff c23d6d62e7 examples, fixes, and reorg img dir 2015-09-12 20:47:09 -04:00
Thomas Breloff 243d4a7ac3 working on unicodeplots savepng and examples 2015-09-12 20:28:40 -04:00
Thomas Breloff 50254d06d7 working on unicodeplots savepng and examples 2015-09-12 20:23:36 -04:00
Thomas Breloff 475b46f516 working on unicodeplots 2015-09-12 16:49:02 -04:00
Thomas Breloff f4573f7e87 added unicodeplots to examples 2015-09-12 16:37:34 -04:00
Thomas Breloff 7fb1a4ec0f better error when importing a package that is not installed; xmin/xmax functions example 2015-09-12 16:10:42 -04:00
Thomas Breloff 4299aa0ff8 more ways to plot Funtions 2015-09-12 15:59:45 -04:00
Thomas Breloff 4eb7e3810e unicodeplots 2015-09-12 14:58:15 -04:00
Thomas Breloff 3e7e7e9ac0 working on unicodeplots 2015-09-12 00:57:58 -04:00
Thomas Breloff 9e1058ec0a working on unicodeplots 2015-09-12 00:54:41 -04:00
Thomas Breloff 426a9eb6d3 working on unicodeplots 2015-09-12 00:31:48 -04:00
Thomas Breloff f322b9ac1c working on unicodeplots; added args to Plot; some reorg 2015-09-12 00:24:35 -04:00
Thomas Breloff e13fec08ca readme 2015-09-11 22:12:33 -04:00
80 changed files with 12521 additions and 1566 deletions
+3
View File
@@ -1,3 +1,6 @@
*.jl.cov
*.jl.*.cov
*.jl.mem
.DS_Store
examples/.ipynb_checkpoints/*
examples/meetup/.ipynb_checkpoints/*
+47 -2
View File
@@ -5,9 +5,54 @@ os:
- osx
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: false
email: true
# uncomment the following lines to override the default test script
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots"); Pkg.add("Gadfly"); Pkg.test("Plots"; coverage=true)'
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots")'
- julia test/travis_commands.jl
# - julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
# - 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())'
+23 -212
View File
@@ -1,224 +1,35 @@
# Plots
[![Build Status](https://travis-ci.org/tbreloff/Plots.jl.svg?branch=master)](https://travis-ci.org/tbreloff/Plots.jl)
[![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) -->
Plotting interface and wrapper for several plotting packages.
#### Author: Thomas Breloff (@tbreloff)
Please add wishlist items, bugs, or any other comments/questions to the issues list.
Plots is a plotting API and toolset. My goals with the package are:
## Examples
- **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](http://plots.readthedocs.io/en/latest/backends/).
- **Lightweight**. Very few dependencies, since backends are loaded and initialized dynamically.
- [Qwt.jl](docs/qwt_examples.md)
- [Gadfly.jl](docs/gadfly_examples.md)
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.
## Installation
First, clone the package, and get any plotting packages you need:
```
Pkg.clone("https://github.com/tbreloff/Plots.jl.git")
Pkg.add("Gadfly") # [optional]
Pkg.clone("https://github.com/tbreloff/Qwt.jl.git") # [optional] requires pyqt and pyqwt
```
## Use
Load it in. The underlying plotting backends are not imported until `plotter()` is called (which happens
on your first call to `plot`). This means that you don't need any backends to be installed when you call `using Plots`.
For now, the default backend is Gadfly.
```
```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
```
Do a plot in Gadfly, then save a png:
```
plot(rand(10,2); marker = :rect)
savepng(Plots.IMG_DIR * "gadfly1.png")
```
which saves:
![gadfly_plt](img/gadfly1.png)
Do a plot in Qwt, then save a png:
```
plotter!(:qwt) # switches the backend to Qwt
plot(rand(10,2); marker = :rect)
savepng(Plots.IMG_DIR * "qwt1.png")
```
which saves:
![qwt_plt](img/qwt1.png)
## plot and plotter! interface (WIP)
The main plot command. Call `plotter!(:module)` to set the current plotting backend.
Commands are converted into the relevant plotting commands for that package:
```
plotter!(:gadfly)
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
plotter!(:qwt)
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the `currentPlot`
plot!(args...; kw...) # adds to the `currentPlot`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
Here are some various args to supply, and the implicit mapping (AVec == AbstractVector and AMat == AbstractMatrix):
```
plot(y::AVec; kw...) # one line... x = 1:length(y)
plot(x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
plot(y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
plot(x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
plot(x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
plot(x::AVec, f::Function; kw...) # one line, y = f(x)
plot(x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
plot(x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
plot(y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
plot(x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
plot(x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
plot(n::Integer; kw...) # n lines, all empty (for updating plots)
# TODO: how do we handle NA values in dataframes?
plot(df::DataFrame; kw...) # one line per DataFrame column, labels == names(df)
plot(df::DataFrame, columns; kw...) # one line per column, but on a subset of column names
```
[TODO] You can swap out `plot` for `subplot`. Each line will go into a separate plot. Use the layout keyword:
```
y = rand(100,3)
subplot(y; layout=(2,2), kw...) # creates 3 lines going into 3 separate plots, laid out on a 2x2 grid (last row is filled with plot #3)
subplot(y; layout=(1,3), kw...) # again 3 plots, all in the same row
subplot(y; layout=[1,[2,3]]) # pass a nested Array to fully specify the layout. here the first plot will take up the first row,
# and the others will share the second row
```
Shorthands:
```
scatter(args...; kw...) = plot(args...; kw..., linetype = :none, marker = :hexagon)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :none, marker = :hexagon)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
```
Some keyword arguments you can set:
```
axis # :left or :right
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
label # string or symbol, applies to that line, may go in a legend
width # width of a line
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
markercolor # same choices as `color`, or :match will set the color to be the same as `color`
markersize # size of the marker
nbins # number of bins for heatmap/hexbin and histograms
heatmap_c # color cutoffs for Qwt heatmaps
fillto # fillto value for area plots
title # string or symbol, title of the plot
xlabel # string or symbol, label on the bottom (x) axis
ylabel # string or symbol, label on the left (y) axis
yrightlabel # string or symbol, label on the right (y) axis
reg # true or false, add a regression line for each line
size # (Int,Int), resize the enclosing window
pos # (Int,Int), move the enclosing window to this position
windowtitle # string or symbol, set the title of the enclosing windowtitle
screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
show # true or false, show the plot (in case you don't want the window to pop up right away)
```
If you don't include a keyword argument, these are the defaults:
```
axis = :left
color = :auto
label = automatically generated (y1, y2, ...., or y1 (R), y2 (R) for the right axis)
width = 1
linetype = :line
linestype = :solid
marker = :none
markercolor = :match
markersize = 3
nbins = 100
heatmap_c = (0.15, 0.5)
fillto = nothing
title = ""
xlabel = ""
ylabel = ""
yrightlabel = ""
reg = false
size = (800,600)
pos = (0,0)
windowtitle = ""
screen = 1
show = true
```
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
(yes I know it's not gramatically correct, but it's easy to use and implement)
```
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
```
# TODO
- [x] Plot vectors/matrices/functions
- [ ] Plot DataFrames
- [ ] Subplots
- [ ] Histograms
- [ ] 3D plotting
- [ ] Scenes/Drawing
- [ ] Graphs
- [ ] Interactivity (GUIs)
- [x] Gadfly.jl
- [ ] PyPlot.jl
- [ ] Winston.jl
- [ ] Gaston.jl
- [ ] GLPlot.jl
- [x] Qwt.jl
- [ ] Bokeh.jl
- [ ] Plotly.jl
- [ ] GoogleCharts.jl
- [ ] Vega.jl
- [ ] PLplot.jl
- [ ] TextPlots.jl
- [ ] ASCIIPlots.jl
- [ ] Sparklines.jl
- [ ] UnicodePlots.jl
- [ ] Hinton.jl
- [ ] ImageTerm.jl
- [ ] GraphViz.jl
- [ ] TikzGraphs.jl
- [ ] GraphLayout.jl
# Backends
See the wiki at: https://github.com/JuliaPlot/juliaplot_docs/wiki
# Author
Thomas Breloff (@tbreloff)
![waves](http://plots.readthedocs.io/en/latest/examples/img/waves.gif)
View the [full documentation](http://plots.readthedocs.io).
+5 -2
View File
@@ -1,4 +1,7 @@
julia 0.4-
julia 0.4
Colors
FactCheck
Reexport
Compat
Requires
FixedSizeArrays
Vendored Executable
+41
View File
File diff suppressed because one or more lines are too long
-118
View File
@@ -1,118 +0,0 @@
module PlotExamples
using Plots
using Colors
const DOCDIR = Pkg.dir("Plots") * "/docs"
const IMGDIR = Pkg.dir("Plots") * "/img"
doc"""
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
"""
type PlotExample
header::String
desc::String
exprs::Vector{Expr}
end
# the examples we'll run for each
const examples = PlotExample[
PlotExample("Lines",
"A simple line plot of the 3 columns.",
[:(plot(rand(100,3)))]),
PlotExample("Functions",
"Plot multiple functions",
[:(plot(0:0.01:4π, [sin,cos]))]),
PlotExample("Global",
"Change the guides/background without a separate call.",
[:(plot(rand(10); title="TITLE", xlabel="XLABEL", ylabel="YLABEL", background_color = RGB(0.5,0.5,0.5)))]),
PlotExample("Two-axis",
"Use the `axis` or `axiss` arguments.\n\nNote: This is only supported with Qwt right now",
[:(plot(Vector[randn(100), randn(100)*100]; axiss = [:left,:right]))]),
PlotExample("Vectors w/ pluralized args",
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).",
[:(plot(Vector[rand(10), rand(20)]; marker=:ellipse, markersize=8, colors=[:red,:blue]))]),
PlotExample("Build plot in pieces",
"Start with a base plot...",
[:(plot(rand(100)/3; reg=true, fillto=0))]),
PlotExample("",
"and add to it later.",
[:(scatter!(rand(100); markersize=6, color=:blue))]),
PlotExample("Heatmaps",
"",
[:(heatmap(randn(10000),randn(10000); nbins=200))]),
PlotExample("Lots of line types",
"Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar) \nNote: some may not work with all backends",
[:(plot(rand(20,4); linetypes=[:line, :step, :sticks, :dots]))]),
PlotExample("Bar",
"x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
[:(bar(randn(1000)))]),
PlotExample("Histogram",
"note: fillto isn't supported on all backends",
[:(histogram(randn(1000); nbins=50, fillto=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`.
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
""",
[:(subplot(randn(100,5); layout=[1,1,3], linetypes=[:line,:hist,:dots,:step,:bar], nbins=10, legend=false))]),
PlotExample("Adding to subplots",
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
[:(subplot(randn(100,5); n=4))]),
PlotExample("",
"",
[:(subplot!(randn(100,3)))]),
]
function generate_markdown(pkgname::Symbol)
# set up the plotter, and don't show the plots by default
plotter!(pkgname)
plotDefault!(:show, false)
# open the markdown file
md = open("$DOCDIR/$(pkgname)_examples.md", "w")
for (i,example) in enumerate(examples)
try
# run the code
map(eval, example.exprs)
# save the png
imgname = "$(pkgname)_example_$i.png"
savepng("$IMGDIR/$imgname")
# 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/$imgname)\n\n")
catch ex
# TODO: put error info into markdown?
warn("Example $pkgname:$i failed with: $ex")
end
#
end
close(md)
end
# run it!
map(generate_markdown, (:qwt, :gadfly))
end # module
-148
View File
@@ -1,148 +0,0 @@
### Lines
A simple line plot of the 3 columns.
```julia
plot(rand(100,3))
```
![](../img/gadfly_example_1.png)
### Functions
Plot multiple functions
```julia
plot(0:0.01:4π,[sin,cos])
```
![](../img/gadfly_example_2.png)
### Global
Change the guides/background without a separate call.
```julia
plot(rand(10); title="TITLE",xlabel="XLABEL",ylabel="YLABEL",background_color=RGB(0.5,0.5,0.5))
```
![](../img/gadfly_example_3.png)
### Two-axis
Use the `axis` or `axiss` arguments.
Note: This is only supported with Qwt right now
```julia
plot(Vector[randn(100),randn(100) * 100]; axiss=[:left,:right])
```
![](../img/gadfly_example_4.png)
### Vectors w/ pluralized args
Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).
```julia
plot(Vector[rand(10),rand(20)]; marker=:ellipse,markersize=8,colors=[:red,:blue])
```
![](../img/gadfly_example_5.png)
### Build plot in pieces
Start with a base plot...
```julia
plot(rand(100) / 3; reg=true,fillto=0)
```
![](../img/gadfly_example_6.png)
###
and add to it later.
```julia
scatter!(rand(100); markersize=6,color=:blue)
```
![](../img/gadfly_example_7.png)
### Heatmaps
```julia
heatmap(randn(10000),randn(10000); nbins=200)
```
![](../img/gadfly_example_8.png)
### Lots of line types
Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
Note: some may not work with all backends
```julia
plot(rand(20,4); linetypes=[:line,:step,:sticks,:dots])
```
![](../img/gadfly_example_9.png)
### Bar
x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)
```julia
bar(randn(1000))
```
![](../img/gadfly_example_10.png)
### Histogram
note: fillto isn't supported on all backends
```julia
histogram(randn(1000); nbins=50,fillto=20)
```
![](../img/gadfly_example_11.png)
### Subplots
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
number of columns `nc`), or you can set the layout directly with `layout`.
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
```julia
subplot(randn(100,5); layout=[1,1,3],linetypes=[:line,:hist,:dots,:step,:bar],nbins=10,legend=false)
```
![](../img/gadfly_example_12.png)
### Adding to subplots
Note here the automatic grid layout, as well as the order in which new series are added to the plots.
```julia
subplot(randn(100,5); n=4)
```
![](../img/gadfly_example_13.png)
###
```julia
subplot!(randn(100,3))
```
![](../img/gadfly_example_14.png)
-148
View File
@@ -1,148 +0,0 @@
### Lines
A simple line plot of the 3 columns.
```julia
plot(rand(100,3))
```
![](../img/qwt_example_1.png)
### Functions
Plot multiple functions
```julia
plot(0:0.01:4π,[sin,cos])
```
![](../img/qwt_example_2.png)
### Global
Change the guides/background without a separate call.
```julia
plot(rand(10); title="TITLE",xlabel="XLABEL",ylabel="YLABEL",background_color=RGB(0.5,0.5,0.5))
```
![](../img/qwt_example_3.png)
### Two-axis
Use the `axis` or `axiss` arguments.
Note: This is only supported with Qwt right now
```julia
plot(Vector[randn(100),randn(100) * 100]; axiss=[:left,:right])
```
![](../img/qwt_example_4.png)
### Vectors w/ pluralized args
Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).
```julia
plot(Vector[rand(10),rand(20)]; marker=:ellipse,markersize=8,colors=[:red,:blue])
```
![](../img/qwt_example_5.png)
### Build plot in pieces
Start with a base plot...
```julia
plot(rand(100) / 3; reg=true,fillto=0)
```
![](../img/qwt_example_6.png)
###
and add to it later.
```julia
scatter!(rand(100); markersize=6,color=:blue)
```
![](../img/qwt_example_7.png)
### Heatmaps
```julia
heatmap(randn(10000),randn(10000); nbins=200)
```
![](../img/qwt_example_8.png)
### Lots of line types
Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
Note: some may not work with all backends
```julia
plot(rand(20,4); linetypes=[:line,:step,:sticks,:dots])
```
![](../img/qwt_example_9.png)
### Bar
x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)
```julia
bar(randn(1000))
```
![](../img/qwt_example_10.png)
### Histogram
note: fillto isn't supported on all backends
```julia
histogram(randn(1000); nbins=50,fillto=20)
```
![](../img/qwt_example_11.png)
### Subplots
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
number of columns `nc`), or you can set the layout directly with `layout`.
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
```julia
subplot(randn(100,5); layout=[1,1,3],linetypes=[:line,:hist,:dots,:step,:bar],nbins=10,legend=false)
```
![](../img/qwt_example_12.png)
### Adding to subplots
Note here the automatic grid layout, as well as the order in which new series are added to the plots.
```julia
subplot(randn(100,5); n=4)
```
![](../img/qwt_example_13.png)
###
```julia
subplot!(randn(100,3))
```
![](../img/qwt_example_14.png)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

+4
View File
@@ -0,0 +1,4 @@
git checkout master
git merge --ff-only dev
git push origin master
git checkout dev
+248 -36
View File
@@ -1,76 +1,288 @@
__precompile__()
__precompile__()
module Plots
using Colors
using Compat
using Reexport
@reexport using Colors
using Requires
using FixedSizeArrays
export
plotter,
plot,
subplot,
AbstractPlot,
Plot,
Subplot,
SubplotLayout,
GridLayout,
RowsLayout,
FlexLayout,
AVec,
AMat,
KW,
plotter!,
plot!,
subplot!,
wrap,
set_theme,
add_theme,
currentPlot,
plotDefault,
scatter,
bar,
histogram,
heatmap,
plot,
plot!,
subplot,
subplot!,
currentPlot!,
plotDefault!,
scatter!,
bar!,
histogram!,
heatmap!,
current,
default,
with,
savepng
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!,
# ---------------------------------------------------------
title!,
xlabel!,
ylabel!,
xlims!,
ylims!,
zlims!,
xticks!,
yticks!,
annotate!,
xflip!,
yflip!,
xaxis!,
yaxis!,
savefig,
png,
gui,
const IMG_DIR = Pkg.dir("Plots") * "/img/"
backend,
backends,
backend_name,
aliases,
dataframes,
Shape,
text,
font,
stroke,
brush,
Surface,
OHLC,
colorscheme,
ColorScheme,
ColorGradient,
ColorVector,
ColorWrapper,
ColorFunction,
ColorZFunction,
getColor,
getColorZ,
debugplots,
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
# ---------------------------------------------------------
include("types.jl")
include("utils.jl")
# ---------------------------------------------------------
include("qwt.jl")
include("gadfly.jl")
include("plotter.jl")
# ---------------------------------------------------------
include("colors.jl")
include("components.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")
# ---------------------------------------------------------
scatter(args...; kw...) = plot(args...; kw..., linetype = :none, marker = :hexagon)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :none, marker = :hexagon)
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
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)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
hexbin(args...; kw...) = plot(args...; kw..., linetype = :hexbin)
hexbin!(args...; kw...) = plot!(args...; kw..., linetype = :hexbin)
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)
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)
wireframe!(args...; kw...) = plot!(args...; kw..., linetype = :wireframe)
path3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
path3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
plot3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
plot3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
scatter3d(args...; kw...) = plot(args...; kw..., linetype = :scatter3d)
scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d)
boxplot(args...; kw...) = plot(args...; kw..., linetype = :box)
boxplot!(args...; kw...) = plot!(args...; kw..., linetype = :box)
violin(args...; kw...) = plot(args...; kw..., linetype = :violin)
violin!(args...; kw...) = plot!(args...; kw..., linetype = :violin)
quiver(args...; kw...) = plot(args...; kw..., linetype = :quiver)
quiver!(args...; kw...) = plot!(args...; kw..., linetype = :quiver)
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
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<:AbstractString}(
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; xticks = (ticks,labels), kw...)
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...)
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
title!(plt::Plot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
zlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
xlims!(plt::Plot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
ylims!(plt::Plot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
zlims!(plt::Plot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
xticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
yticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
xticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
annotate!(plt::Plot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
annotate!{T<:Tuple}(plt::Plot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
xflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
yflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
xaxis!(plt::Plot, args...; kw...) = plot!(plt; xaxis = args, kw...)
yaxis!(plt::Plot, args...; kw...) = plot!(plt; yaxis = args, kw...)
# ---------------------------------------------------------
const CURRENT_BACKEND = CurrentBackend(:none)
savepng(args...; kw...) = savepng(currentPlot(), args...; kw...)
savepng(plt::PlottingObject, args...; kw...) = savepng(plt.plotter, plt, args...; kw...)
function __init__()
# override IJulia inline display
if isijulia()
@eval import IJulia
IJulia.display_dict(plt::AbstractPlot) = Dict{ASCIIString, ByteString}("text/html" => sprint(writemime, "text/html", plt))
end
setup_dataframes()
setup_atom()
end
# ---------------------------------------------------------
+136
View File
@@ -0,0 +1,136 @@
immutable Animation
dir::ASCIIString
frames::Vector{ASCIIString}
end
function Animation()
tmpdir = convert(ASCIIString, mktempdir())
Animation(tmpdir, ASCIIString[])
end
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
end
# -----------------------------------------------
"Wraps the location of an animated gif so that it can be displayed"
immutable AnimatedGif
filename::ASCIIString
end
function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Integer = 20)
fn = abspath(fn)
try
# high quality
speed = round(Int, 100 / fps)
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png -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...)")
# low quality
run(`ffmpeg -v 0 -framerate $fps -i $(anim.dir)/%06d.png -y $fn`)
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
end
info("Saved animation to ", fn)
AnimatedGif(fn)
end
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif)
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
end
# -----------------------------------------------
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.
Example:
```
p = plot(1)
anim = @animate for x=0:0.1:5
push!(p, 1, sin(x))
end
gif(anim)
```
"""
macro animate(forloop::Expr, args...)
_animate(forloop, args...)
end
+892 -101
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())
# ---------------------------------------------------------
+237
View File
@@ -0,0 +1,237 @@
# https://github.com/bokeh/Bokeh.jl
supportedArgs(::BokehBackend) = [
# :annotation,
# :axis,
# :background_color,
:linecolor,
# :color_palette,
# :fillrange,
# :fillcolor,
# :fillalpha,
# :foreground_color,
:group,
# :label,
# :layout,
# :legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
:linewidth,
# :linealpha,
:markershape,
:markercolor,
:markersize,
# :markeralpha,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
# :bins,
# :nc,
# :nr,
# :pos,
# :smooth,
# :show,
:size,
:title,
# :windowtitle,
:x,
# :xlabel,
# :xlims,
# :xticks,
:y,
# :ylabel,
# :ylims,
# :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
# :tickfont,
# :guidefont,
# :legendfont,
# :grid,
# :surface,
# :levels,
]
supportedAxes(::BokehBackend) = [:auto, :left]
supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedStyles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::BokehBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::BokehBackend) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::BokehBackend) = false
# --------------------------------------------------------------------------------------
function _initialize_backend(::BokehBackend; kw...)
@eval begin
warn("Bokeh is no longer supported... many features will likely be broken.")
import Bokeh
export Bokeh
end
end
# make255(x) = round(Int, 255 * x)
# function bokehcolor(c::Colorant)
# @sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
# end
# bokehcolor(cs::ColorScheme) = bokehcolor(getColor(cs))
const _glyphtypes = KW(
:ellipse => :Circle,
:rect => :Square,
:diamond => :Diamond,
:utriangle => :Triangle,
:dtriangle => :InvertedTriangle,
# :pentagon =>
# :hexagon =>
# :heptagon =>
# :octagon =>
:cross => :Cross,
:xcross => :X,
:star5 => :Asterisk,
)
function bokeh_glyph_type(d::KW)
lt = d[:linetype]
mt = d[:markershape]
if lt == :scatter && mt == :none
mt = :ellipse
end
# if we have a marker, use that
if lt == :scatter || mt != :none
return _glyphtypes[mt]
end
# otherwise return a line
return :Line
end
function get_stroke_vector(linestyle::Symbol)
dash = 12
dot = 3
gap = 2
linestyle == :solid && return Int[]
linestyle == :dash && return Int[dash, gap]
linestyle == :dot && return Int[dot, gap]
linestyle == :dashdot && return Int[dash, gap, dot, gap]
linestyle == :dashdotdot && return Int[dash, gap, dot, gap, dot, gap]
error("unsupported linestyle: ", linestyle)
end
# ---------------------------------------------------------------------------
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
datacolumns = Bokeh.BokehDataSet[]
tools = Bokeh.tools()
filename = tempname() * ".html"
title = d[:title]
w, h = d[:size]
xaxis_type = d[:xscale] == :log10 ? :log : :auto
yaxis_type = d[:yscale] == :log10 ? :log : :auto
# legend = d[:legend] ? xxxx : nothing
legend = nothing
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, KW[])
end
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
linewidth = d[:linewidth], # shape's stroke width or line width
fillcolor = webcolor(d[:markercolor]),
size = ceil(Int, d[:markersize] * 2.5), # magic number 2.5 to keep in same scale as other backends
dash = get_stroke_vector(d[:linestyle])
)
legend = nothing # TODO
push!(plt.o.datacolumns, Bokeh.BokehDataSet(bdata, glyph, legend))
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{BokehBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{BokehBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# 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{BokehBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
end
# ----------------------------------------------------------------
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{BokehBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
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::AbstractPlot{BokehBackend})
# TODO: write a png to io
warn("mime png not implemented")
end
function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
Bokeh.showplot(plt.o)
end
function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
# TODO: display/show the subplot
end
+742
View File
@@ -0,0 +1,742 @@
# https://github.com/dcjones/Gadfly.jl
supportedArgs(::GadflyBackend) = [
:annotation,
:background_color, :foreground_color, :color_palette,
:group, :label, :linetype,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:fillrange, :fillcolor, :fillalpha,
:bins, :n, :nc, :nr, :layout, :smooth,
:title, :windowtitle, :show, :size,
:x, :xlabel, :xlims, :xticks, :xscale, :xflip,
:y, :ylabel, :ylims, :yticks, :yscale, :yflip,
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
:z,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
:marker_z, :levels,
:xerror, :yerror,
:ribbon, :quiver,
:orientation,
]
supportedAxes(::GadflyBackend) = [:auto, :left]
supportedTypes(::GadflyBackend) = [
:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :hist2d, :hexbin, :hist,
:bar, :box, :violin, :quiver,
:hline, :vline, :contour, :shape
]
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::GadflyBackend) = true
# --------------------------------------------------------------------------------------
function _initialize_backend(::GadflyBackend; kw...)
@eval begin
import Gadfly, Compose
export Gadfly, Compose
include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
end
end
# ---------------------------------------------------------------------------
# immutable MissingVec <: AbstractVector{Float64} end
# Base.size(v::MissingVec) = (1,)
# Base.getindex(v::MissingVec, i::Integer) = 0.0
function createGadflyPlotObject(d::KW)
gplt = Gadfly.Plot()
gplt.mapping = Dict()
gplt.data_source = Gadfly.DataFrames.DataFrame()
# gplt.layers = gplt.layers[1:0]
gplt.layers = [Gadfly.layer(Gadfly.Geom.point(tag=:remove), x=zeros(1), y=zeros(1));] # x=MissingVec(), y=MissingVec());]
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
Gadfly.Guide.ylabel(d[:ylabel]),
Gadfly.Guide.title(d[:title])]
gplt
end
# ---------------------------------------------------------------------------
function getLineGeom(d::KW)
lt = d[:linetype]
xbins, ybins = maketuple(d[:bins])
if lt == :hexb
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
elseif lt == :hist2d
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
elseif lt == :hist
Gadfly.Geom.histogram(bincount = xbins,
orientation = isvertical(d) ? :vertical : :horizontal,
position = d[:bar_position] == :stack ? :stack : :dodge)
elseif lt == :path
Gadfly.Geom.path
elseif lt in (:bar, :sticks)
Gadfly.Geom.bar
elseif lt == :steppost
Gadfly.Geom.step
elseif lt == :steppre
Gadfly.Geom.step(direction = :vh)
elseif lt == :hline
Gadfly.Geom.hline
elseif lt == :vline
Gadfly.Geom.vline
elseif lt == :contour
Gadfly.Geom.contour(levels = d[:levels])
# elseif lt == :shape
# Gadfly.Geom.polygon(fill = true, preserve_order = true)
else
nothing
end
end
function get_extra_theme_args(d::KW, k::Symbol)
# gracefully handles old Gadfly versions
extra_theme_args = KW()
try
extra_theme_args[:line_style] = Gadfly.get_stroke_vector(d[k])
catch err
if string(err) == "UndefVarError(:get_stroke_vector)"
Base.warn_once("Gadfly.get_stroke_vector failed... do you have an old version of Gadfly?")
else
rethrow()
end
end
extra_theme_args
end
function getGadflyLineTheme(d::KW)
lt = d[:linetype]
lc = convertColor(getColor(d[:linecolor]), d[:linealpha])
fc = convertColor(getColor(d[:fillcolor]), d[:fillalpha])
Gadfly.Theme(;
default_color = (lt in (:hist,:hist2d,:hexbin,:bar,:sticks) ? fc : lc),
line_width = (lt == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
# line_style = Gadfly.get_stroke_vector(d[:linestyle]),
lowlight_color = x->RGB(fc), # fill/ribbon
lowlight_opacity = alpha(fc), # fill/ribbon
bar_highlight = RGB(lc), # bars
get_extra_theme_args(d, :linestyle)...
)
end
# add a line as a new layer
function addGadflyLine!(plt::Plot, numlayers::Int, d::KW, geoms...)
gplt = getGadflyContext(plt)
gfargs = vcat(geoms..., getGadflyLineTheme(d))
kwargs = KW()
lt = d[:linetype]
# add a fill?
if d[:fillrange] != nothing && lt != :contour
fillmin, fillmax = map(makevec, maketuple(d[:fillrange]))
nmin, nmax = length(fillmin), length(fillmax)
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
kwargs[:ymax] = Float64[max(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
push!(gfargs, Gadfly.Geom.ribbon)
end
if lt in (:hline, :vline)
kwargs[lt == :hline ? :yintercept : :xintercept] = d[:y]
else
if lt == :sticks
w = 0.01 * mean(diff(d[:x]))
kwargs[:xmin] = d[:x] - w
kwargs[:xmax] = d[:x] + w
elseif lt == :contour
kwargs[:z] = d[:z].surf
addGadflyContColorScale(plt, d[:linecolor])
end
kwargs[:x] = d[lt == :hist ? :y : :x]
kwargs[:y] = d[:y]
end
# # add the layer
Gadfly.layer(gfargs...; order=numlayers, kwargs...)
end
# ---------------------------------------------------------------------------
get_shape(sym::Symbol) = _shapes[sym]
get_shape(shape::Shape) = shape
# extract the underlying ShapeGeometry object(s)
getMarkerGeom(shapes::AVec) = gadflyshape(map(get_shape, shapes))
getMarkerGeom(other) = gadflyshape(get_shape(other))
# getMarkerGeom(shape::Shape) = gadflyshape(shape)
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
function getMarkerGeom(d::KW)
if d[:linetype] == :shape
Gadfly.Geom.polygon(fill = true, preserve_order = true)
else
getMarkerGeom(d[:markershape])
end
end
function getGadflyMarkerTheme(d::KW, plotargs::KW)
c = getColor(d[:markercolor])
α = d[:markeralpha]
if α != nothing
c = RGBA(RGB(c), α)
end
ms = d[:markersize]
ms = if typeof(ms) <: AVec
warn("Gadfly doesn't support variable marker sizes... using the average: $(mean(ms))")
mean(ms) * Gadfly.px
else
ms * Gadfly.px
end
Gadfly.Theme(;
default_color = c,
default_point_size = ms,
discrete_highlight_color = c -> RGB(getColor(d[:markerstrokecolor])),
highlight_width = d[:markerstrokewidth] * Gadfly.px,
line_width = d[:markerstrokewidth] * Gadfly.px,
# get_extra_theme_args(d, :markerstrokestyle)...
)
end
function addGadflyContColorScale(plt::Plot{GadflyBackend}, c)
plt.plotargs[:colorbar] == :none && return
if !isa(c, ColorGradient)
c = default_gradient()
end
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(c, p))))
end
function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, plotargs::KW, geoms...)
gfargs = vcat(geoms..., getGadflyMarkerTheme(d, plotargs), getMarkerGeom(d))
kwargs = KW()
# handle continuous color scales for the markers
zcolor = d[:marker_z]
if zcolor != nothing && typeof(zcolor) <: AVec
kwargs[:color] = zcolor
addGadflyContColorScale(plt, d[:markercolor])
end
Gadfly.layer(gfargs...; x = d[:x], y = d[:y], order=numlayers, kwargs...)
end
# ---------------------------------------------------------------------------
function addToGadflyLegend(plt::Plot, d::KW)
if plt.plotargs[:legend] != :none && d[:label] != ""
gplt = getGadflyContext(plt)
# add the legend if needed
if all(g -> !isa(g, Gadfly.Guide.ManualColorKey), gplt.guides)
unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", @compat(AbstractString)[], Color[]))
end
# now add the series to the legend
for guide in gplt.guides
if isa(guide, Gadfly.Guide.ManualColorKey)
# TODO: there's a BUG in gadfly if you pass in the same color more than once,
# since gadfly will call unique(colors), but doesn't also merge the rows that match
# Should ensure from this side that colors which are the same are merged together
c = getColor(d[d[:markershape] == :none ? :linecolor : :markercolor])
foundit = false
# extend the label if we found this color
for i in 1:length(guide.colors)
if RGB(c) == guide.colors[i]
guide.labels[i] *= ", " * d[:label]
foundit = true
end
end
# didn't find the color, so add a new entry into the legend
if !foundit
push!(guide.labels, d[:label])
push!(guide.colors, c)
end
end
end
end
end
getGadflySmoothing(smooth::Bool) = smooth ? [Gadfly.Geom.smooth(method=:lm)] : Any[]
getGadflySmoothing(smooth::Real) = [Gadfly.Geom.smooth(method=:loess, smoothing=float(smooth))]
function addGadflySeries!(plt::Plot, d::KW)
layers = Gadfly.Layer[]
gplt = getGadflyContext(plt)
# add a regression line?
# TODO: make more flexible
smooth = getGadflySmoothing(d[:smooth])
# lines
geom = getLineGeom(d)
if geom != nothing
prepend!(layers, addGadflyLine!(plt, length(gplt.layers), d, geom, smooth...))
smooth = Any[] # don't add a regression for markers too
end
# special handling for ohlc and scatter
lt = d[:linetype]
if lt == :ohlc
error("Haven't re-implemented after refactoring")
elseif lt in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
elseif lt == :scatter && d[:markershape] == :none
d[:markershape] = :ellipse
end
# markers
if d[:markershape] != :none || lt == :shape
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.plotargs, smooth...))
end
lt in (:hist2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
# now save the layers that apply to this series
d[:gadflylayers] = layers
prepend!(gplt.layers, layers)
end
# ---------------------------------------------------------------------------
# NOTE: I'm leaving this here and commented out just in case I want to implement again... it was hacky code to create multi-colored line segments
# # colorgroup
# z = d[:z]
# # handle line segments of different colors
# cscheme = d[:linecolor]
# if isa(cscheme, ColorVector)
# # create a color scale, and set the color group to the index of the color
# push!(gplt.scales, Gadfly.Scale.color_discrete_manual(cscheme.v...))
# # this is super weird, but... oh well... for some reason this creates n separate line segments...
# # create a list of vertices that go: [x1,x2,x2,x3,x3, ... ,xi,xi, ... xn,xn] (same for y)
# # then the vector passed to the "color" keyword should be a vector: [1,1,2,2,3,3,4,4, ..., i,i, ... , n,n]
# csindices = Int[mod1(i,length(cscheme.v)) for i in 1:length(d[:y])]
# cs = collect(repmat(csindices', 2, 1))[1:end-1]
# grp = collect(repmat((1:length(d[:y]))', 2, 1))[1:end-1]
# d[:x], d[:y] = map(createSegments, (d[:x], d[:y]))
# colorgroup = [(:linecolor, cs), (:group, grp)]
# ---------------------------------------------------------------------------
function addGadflyTicksGuide(gplt, ticks, isx::Bool)
ticks == :auto && return
# remove the ticks?
if ticks in (:none, false, nothing)
return addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
end
ttype = ticksType(ticks)
# just the values... put ticks here, but use standard labels
if ttype == :ticks
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
replaceType(gplt.guides, gtype(ticks = collect(ticks)))
# set the ticks and the labels
# Note: this is pretty convoluted, but I think it works. We set the ticks using Gadfly.Guide,
# and then set the label function (wraps a dict lookup) through a continuous Gadfly.Scale.
elseif ttype == :ticks_and_labels
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
replaceType(gplt.guides, gtype(ticks = collect(ticks[1])))
# # TODO add xtick_label function (given tick, return label??)
# # Scale.x_discrete(; labels=nothing, levels=nothing, order=nothing)
# filterGadflyScale(gplt, isx)
# gfunc = isx ? Gadfly.Scale.x_discrete : Gadfly.Scale.y_discrete
# labelmap = Dict(zip(ticks...))
# labelfunc = val -> labelmap[val]
# push!(gplt.scales, gfunc(levels = collect(ticks[1]), labels = labelfunc))
filterGadflyScale(gplt, isx)
gfunc = isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous
labelmap = Dict(zip(ticks...))
labelfunc = val -> labelmap[val]
push!(gplt.scales, gfunc(labels = labelfunc))
else
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
end
end
continuousAndSameAxis(scale, isx::Bool) = isa(scale, Gadfly.Scale.ContinuousScale) && scale.vars[1] == (isx ? :x : :y)
filterGadflyScale(gplt, isx::Bool) = filter!(scale -> !continuousAndSameAxis(scale, isx), gplt.scales)
function getGadflyScaleFunction(d::KW, isx::Bool)
scalekey = isx ? :xscale : :yscale
hasScaleKey = haskey(d, scalekey)
if hasScaleKey
scale = d[scalekey]
scale == :ln && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey, log
scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey, log2
scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey, log10
scale == :asinh && return isx ? Gadfly.Scale.x_asinh : Gadfly.Scale.y_asinh, hasScaleKey, asinh
scale == :sqrt && return isx ? Gadfly.Scale.x_sqrt : Gadfly.Scale.y_sqrt, hasScaleKey, sqrt
end
isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous, hasScaleKey, identity
end
function addGadflyLimitsScale(gplt, d::KW, isx::Bool)
gfunc, hasScaleKey, func = getGadflyScaleFunction(d, isx)
# do we want to add min/max limits for the axis?
limsym = isx ? :xlims : :ylims
limargs = Any[]
# map :auto to nothing, otherwise add to limargs
lims = get(d, limsym, :auto)
if lims == :auto
lims = nothing
else
if limsType(lims) == :limits
push!(limargs, (:minvalue, min(lims...)))
push!(limargs, (:maxvalue, max(lims...)))
else
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
end
end
# replace any current scales with this one
if hasScaleKey || !isempty(limargs)
filterGadflyScale(gplt, isx)
push!(gplt.scales, gfunc(; limargs...))
end
lims, func
end
function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims, xfunc, yfunc)
if isa(gplt.coord, Gadfly.Coord.Cartesian)
gplt.coord = Gadfly.Coord.cartesian(
gplt.coord.xvars,
gplt.coord.yvars;
xmin = xlims == nothing ? gplt.coord.xmin : xfunc(minimum(xlims)),
xmax = xlims == nothing ? gplt.coord.xmax : xfunc(maximum(xlims)),
ymin = ylims == nothing ? gplt.coord.ymin : yfunc(minimum(ylims)),
ymax = ylims == nothing ? gplt.coord.ymax : yfunc(maximum(ylims)),
xflip = get(d, :xflip, gplt.coord.xflip),
yflip = get(d, :yflip, gplt.coord.yflip),
fixed = gplt.coord.fixed,
aspect_ratio = gplt.coord.aspect_ratio,
raster = gplt.coord.raster
)
else
gplt.coord = Gadfly.Coord.Cartesian(
xflip = get(d, :xflip, false),
yflip = get(d, :yflip, false)
)
end
end
function findGuideAndSet(gplt, t::DataType, args...; kw...) #s::@compat(AbstractString))
for (i,guide) in enumerate(gplt.guides)
if isa(guide, t)
gplt.guides[i] = t(args...; kw...)
end
end
end
function updateGadflyGuides(plt::Plot, d::KW)
gplt = getGadflyContext(plt)
haskey(d, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(d[:title]))
haskey(d, :xlabel) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xlabel]))
haskey(d, :ylabel) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:ylabel]))
xlims, xfunc = addGadflyLimitsScale(gplt, d, true)
ylims, yfunc = addGadflyLimitsScale(gplt, d, false)
ticks = get(d, :xticks, :auto)
if ticks == :none
_remove_axis(plt, true)
else
addGadflyTicksGuide(gplt, ticks, true)
end
ticks = get(d, :yticks, :auto)
if ticks == :none
_remove_axis(plt, false)
else
addGadflyTicksGuide(gplt, ticks, false)
end
updateGadflyAxisFlips(gplt, d, xlims, ylims, xfunc, yfunc)
end
function updateGadflyPlotTheme(plt::Plot, d::KW)
kwargs = KW()
# colors
insidecolor, gridcolor, textcolor, guidecolor, legendcolor =
map(s -> getColor(d[s]), (
:background_color_inside,
:foreground_color_grid,
:foreground_color_text,
:foreground_color_guide,
:foreground_color_legend
))
# # hide the legend?
leg = d[d[:legend] == :none ? :colorbar : :legend]
if leg != :best
kwargs[:key_position] = leg == :inside ? :right : leg
end
if !get(d, :grid, true)
kwargs[:grid_color] = gridcolor
end
# fonts
tfont, gfont, lfont = d[:tickfont], d[:guidefont], d[:legendfont]
getGadflyContext(plt).theme = Gadfly.Theme(;
background_color = insidecolor,
minor_label_color = textcolor,
minor_label_font = tfont.family,
minor_label_font_size = tfont.pointsize * Gadfly.pt,
major_label_color = guidecolor,
major_label_font = gfont.family,
major_label_font_size = gfont.pointsize * Gadfly.pt,
key_title_color = guidecolor,
key_title_font = gfont.family,
key_title_font_size = gfont.pointsize * Gadfly.pt,
key_label_color = legendcolor,
key_label_font = lfont.family,
key_label_font_size = lfont.pointsize * Gadfly.pt,
plot_padding = 1 * Gadfly.mm,
kwargs...
)
end
# ----------------------------------------------------------------
function createGadflyAnnotationObject(x, y, val::@compat(AbstractString))
Gadfly.Guide.annotation(Compose.compose(
Compose.context(),
Compose.text(x, y, val)
))
end
function createGadflyAnnotationObject(x, y, txt::PlotText)
halign = (txt.font.halign == :hcenter ? Compose.hcenter : (txt.font.halign == :left ? Compose.hleft : Compose.hright))
valign = (txt.font.valign == :vcenter ? Compose.vcenter : (txt.font.valign == :top ? Compose.vtop : Compose.vbottom))
rotations = (txt.font.rotation == 0.0 ? [] : [Compose.Rotation(txt.font.rotation, Compose.Point(Compose.x_measure(x), Compose.y_measure(y)))])
Gadfly.Guide.annotation(Compose.compose(
Compose.context(),
Compose.text(x, y, txt.str, halign, valign, rotations...),
Compose.font(string(txt.font.family)),
Compose.fontsize(txt.font.pointsize * Gadfly.pt),
Compose.stroke(txt.font.color),
Compose.fill(txt.font.color)
))
end
function _add_annotations{X,Y,V}(plt::Plot{GadflyBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
end
end
# ---------------------------------------------------------------------------
# create a blank Gadfly.Plot object
function _create_plot(pkg::GadflyBackend, d::KW)
gplt = createGadflyPlotObject(d)
Plot(gplt, pkg, 0, d, KW[])
end
# plot one data series
function _add_series(::GadflyBackend, plt::Plot, d::KW)
# first clear out the temporary layer
gplt = getGadflyContext(plt)
if gplt.layers[1].geom.tag == :remove
gplt.layers = gplt.layers[2:end]
end
addGadflySeries!(plt, d)
push!(plt.seriesargs, d)
plt
end
function _update_plot(plt::Plot{GadflyBackend}, d::KW)
updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d)
end
# ----------------------------------------------------------------
# accessors for x/y data
# TODO: need to save all the layer indices which apply to this series
function getGadflyMappings(plt::Plot, i::Integer)
@assert i > 0 && i <= plt.n
mappings = [l.mapping for l in plt.seriesargs[i][:gadflylayers]]
end
function getxy(plt::Plot{GadflyBackend}, i::Integer)
mapping = getGadflyMappings(plt, i)[1]
mapping[:x], mapping[:y]
end
function setxy!{X,Y}(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer)
for mapping in getGadflyMappings(plt, i)
mapping[:x], mapping[:y] = xy
end
plt
end
# ----------------------------------------------------------------
# create the underlying object (each backend will do this differently)
function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
isbefore && return false # wait until after plotting to create the subplots
subplt.o = nothing
true
end
function _remove_axis(plt::Plot{GadflyBackend}, 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{GadflyBackend}, isx::Bool)
for l in getGadflyContext(plt).layers
_expand_limits(lims, l.mapping[isx ? :x : :y])
end
end
# ----------------------------------------------------------------
getGadflyContext(plt::Plot{GadflyBackend}) = plt.o
getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
# create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
function buildGadflySubplotContext(subplt::Subplot)
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
# add the Plot object to the row
push!(row, getGadflyContext(subplt.plts[i]))
# add the row
if c == ncols(subplt.layout, r)
push!(rows, Gadfly.hstack(row...))
row = Any[]
end
end
# stack the rows
Gadfly.vstack(rows...)
end
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.plotargs[:size]...)
setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getplotargs(subplt, 1)[:size]...)
# -------------------------------------------------------------------------
function dowritemime{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, func, plt::AbstractPlot{P})
gplt = getGadflyContext(plt)
setGadflyDisplaySize(plt)
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
end
getGadflyWriteFunc(::MIME"image/png") = Gadfly.PNG
getGadflyWriteFunc(::MIME"image/svg+xml") = Gadfly.SVG
# getGadflyWriteFunc(::MIME"text/html") = Gadfly.SVGJS
getGadflyWriteFunc(::MIME"application/pdf") = Gadfly.PDF
getGadflyWriteFunc(::MIME"application/postscript") = Gadfly.PS
getGadflyWriteFunc(::MIME"application/x-tex") = Gadfly.PGF
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript", MIME"application/x-tex")
@eval function Base.writemime{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, ::$mime, plt::AbstractPlot{P})
func = getGadflyWriteFunc($mime())
dowritemime(io, func, plt)
end
end
function Base.display(::PlotsDisplay, plt::Plot{GadflyBackend})
setGadflyDisplaySize(plt.plotargs[:size]...)
display(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
setGadflyDisplaySize(getplotargs(subplt,1)[:size]...)
ctx = buildGadflySubplotContext(subplt)
# taken from Gadfly since I couldn't figure out how to do it directly
filename = string(Gadfly.tempname(), ".html")
output = open(filename, "w")
plot_output = IOBuffer()
Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
Compose.default_graphic_height, false), ctx)
plotsvg = takebuf_string(plot_output)
write(output,
"""
<!DOCTYPE html>
<html>
<head>
<title>Gadfly Plot</title>
<meta charset="utf-8">
</head>
<body>
<script charset="utf-8">
$(readall(Compose.snapsvgjs))
</script>
<script charset="utf-8">
$(readall(Gadfly.gadflyjs))
</script>
$(plotsvg)
</body>
</html>
""")
close(output)
Gadfly.open_file(filename)
end
+93
View File
@@ -0,0 +1,93 @@
# Geometry which displays arbitrary shapes at given (x, y) positions.
# note: vertices is a list of shapes
immutable ShapeGeometry <: Gadfly.GeometryElement
vertices::AbstractVector #{Tuple{Float64,Float64}}
tag::Symbol
function ShapeGeometry(shape; tag::Symbol=Gadfly.Geom.empty_tag)
new(shape, tag)
end
end
# TODO: add for PR
# const shape = ShapeGeometry
function Gadfly.element_aesthetics(::ShapeGeometry)
[:x, :y, :size, :color]
end
# Generate a form for a shape geometry.
#
# Args:
# geom: shape geometry.
# theme: the plot's theme.
# aes: aesthetics.
#
# Returns:
# A compose Form.
#
function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics)
# TODO: add for PR
# Gadfly.assert_aesthetics_defined("Geom.shape", aes, :x, :y)
# Gadfly.assert_aesthetics_equal_length("Geom.shape", aes,
# element_aesthetics(geom)...)
default_aes = Gadfly.Aesthetics()
default_aes.color = Gadfly.DataFrames.PooledDataArray(RGBA{Float32}[theme.default_color])
default_aes.size = Compose.Measure[theme.default_point_size]
aes = Gadfly.inherit(aes, default_aes)
lw_hover_scale = 10
lw_ratio = theme.line_width / aes.size[1]
aes_x, aes_y = Gadfly.concretize(aes.x, aes.y)
ctx = Compose.compose!(
Compose.context(),
make_polygon(geom, aes.x, aes.y, aes.size),
Compose.fill(aes.color),
Compose.linewidth(theme.highlight_width))
if aes.color_key_continuous != nothing && aes.color_key_continuous
Compose.compose!(ctx,
Compose.stroke(map(theme.continuous_highlight_color, aes.color)))
else
Compose.compose!(ctx,
Compose.stroke(map(theme.discrete_highlight_color, aes.color)),
Compose.svgclass([Gadfly.svg_color_class_from_label(Gadfly.escape_id(aes.color_label([c])[1]))
for c in aes.color]))
end
return Compose.compose!(Compose.context(order=4), Compose.svgclass("geometry"), ctx)
end
function gadflyshape(sv::Shape)
ShapeGeometry(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 = 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 get_mod(geom.vertices, i)]
end
Gadfly.polygon(polys, geom.tag)
end
# ---------------------------------------------------------------------------------------------
+171
View File
@@ -0,0 +1,171 @@
# [WEBSITE]
supportedArgs(::GLVisualizeBackend) = [
# :annotation,
# :axis,
# :background_color,
# :color_palette,
# :fillrange,
# :fillcolor,
# :fillalpha,
# :foreground_color,
# :group,
# :label,
# :layout,
# :legend,
# :linecolor,
# :linestyle,
:linetype
# :seriescolor, :seriesalpha,
# :linewidth,
# :linealpha,
# :markershape,
# :markercolor,
# :markersize,
# :markeralpha,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
# :bins,
# :nc,
# :nr,
# :pos,
# :smooth,
# :show,
# :size,
# :title,
# :windowtitle,
# :x,
# :xlabel,
# :xlims,
# :xticks,
# :y,
# :ylabel,
# :ylims,
# :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
# :tickfont,
# :guidefont,
# :legendfont,
# :grid,
# :surface
# :levels,
]
supportedAxes(::GLVisualizeBackend) = [:auto, :left]
supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedStyles(::GLVisualizeBackend) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GLVisualizeBackend) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::GLVisualizeBackend) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
subplotSupported(::GLVisualizeBackend) = false
# --------------------------------------------------------------------------------------
function _initialize_backend(::GLVisualizeBackend; kw...)
@eval begin
import GLVisualize
export GLVisualize
end
end
# ---------------------------------------------------------------------------
immutable GLScreenWrapper
window
end
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=GLVisualize.glscreen()
@async GLVisualize.renderloop(w)
Plot(GLScreenWrapper(w), pkg, 0, d, KW[])
end
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)
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{GLVisualizeBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
end
# ----------------------------------------------------------------
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{GLVisualizeBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{GLVisualizeBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function getxy(plt::Plot{GLVisualizeBackend}, i::Int)
# TODO:
# series = plt.o.lines[i]
# series.x, series.y
nothing, nothing
end
function setxy!{X,Y}(plt::Plot{GLVisualizeBackend}, xy::Tuple{X,Y}, i::Integer)
# TODO:
# series = plt.o.lines[i]
# series.x, series.y = xy
plt
end
# ----------------------------------------------------------------
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{GLVisualizeBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
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::AbstractPlot{GLVisualizeBackend})
# TODO: write a png to io
end
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
# and adding to a plot will automatically open a window and draw to it, then the display
# wouldn't actually need to do anything
end
function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizeBackend})
# TODO: display/show the subplot
end
+910
View File
@@ -0,0 +1,910 @@
# https://github.com/jheinen/GR.jl
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 = KW(
:auto => 1, :solid => 1, :dash => 2, :dot => 3, :dashdot => 4,
:dashdotdot => -1 )
const gr_markertype = KW(
:auto => 1, :none => -1, :ellipse => -1, :rect => -7, :diamond => -13,
: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 = 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,
"newcenturyschlbk" => 18, "avantgarde" => 22, "palatino" => 26)
function gr_getcolorind(v)
c = getColor(v)
return convert(Int, GR.inqcolorfromrgb(c.r, c.g, c.b))
end
function gr_getaxisind(p)
axis = get(p, :axis, :none)
if axis in [:none, :left]
return 1
else
return 2
end
end
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
clear && GR.clearws()
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)
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)
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 lt == :bar
x, y = 1:length(p[:y]), p[:y]
elseif lt == :ohlc
x, y = 1:size(p[:y], 1), p[:y]
elseif lt in [:hist, :density]
x, y = Base.hist(p[:y])
elseif lt in [:hist2d, :hexbin]
E = zeros(length(p[:x]),2)
E[:,1] = p[:x]
E[:,2] = p[:y]
if isa(p[:bins], Tuple)
xbins, ybins = p[:bins]
else
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 lt in [:contour, :surface, :heatmap]
cmap = true
end
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
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
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.0525
end
if cmap
viewport[2] -= 0.1
end
GR.setviewport(viewport[1], viewport[2], viewport[3], viewport[4])
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
xmin, xmax = GR.adjustlimits(xmin, xmax)
majorx = 5
xtick = GR.tick(xmin, xmax) / majorx
else
xtick = majorx = 1
end
if scale & GR.OPTION_Y_LOG == 0
ymin, ymax = GR.adjustlimits(ymin, ymax)
majory = 5
ytick = GR.tick(ymin, ymax) / majory
else
ytick = majory = 1
end
if scale & GR.OPTION_FLIP_X == 0
xorg = (xmin, xmax)
else
xorg = (xmax, xmin)
end
if scale & GR.OPTION_FLIP_Y == 0
yorg = (ymin, ymax)
else
yorg = (ymax, ymin)
end
GR.setwindow(xmin, xmax, ymin, ymax)
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
GR.setlinewidth(1)
GR.setlinecolorind(fg)
ticksize = 0.0075 * diag
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)
elseif axis == 1
GR.axes(xtick, ytick, xorg[1], yorg[1], majorx, majory, ticksize)
else
GR.axes(xtick, ytick, xorg[2], yorg[2], -majorx, majory, -ticksize)
end
end
end
if get(d, :title, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
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.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.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.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 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 lt == :path
if haskey(p, :fillcolor)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
end
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
legend = true
end
if lt == :line
if length(p[:x]) > 1
gr_polyline(p[:x], p[:y])
end
legend = true
elseif lt in [:steppre, :steppost]
n = length(p[:x])
x = zeros(2*n + 1)
y = zeros(2*n + 1)
x[1], y[1] = p[:x][1], p[:y][1]
j = 2
for i = 2:n
if 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
x[j], x[j+1] = p[:x][i], p[:x][i]
y[j], y[j+1] = p[:y][i-1], p[:y][i]
end
j += 2
end
if n > 1
GR.polyline(x, y)
end
legend = true
elseif lt == :sticks
x, y = p[:x], p[:y]
for i = 1:length(y)
GR.polyline([x[i], x[i]], [ymin, y[i]])
end
legend = true
elseif lt == :scatter || (p[:markershape] != :none && axes_2d)
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
gr_setmarkershape(p)
if haskey(d, :markersize)
if typeof(p[:markersize]) <: Number
GR.setmarkersize(p[:markersize] / 4.0)
if length(p[:x]) > 0
gr_polymarker(p, p[:x], p[:y])
end
else
c = p[:markercolor]
GR.setcolormap(-GR.COLORMAP_GLOWING)
for i = 1:length(p[:x])
if isa(c, ColorGradient) && p[:marker_z] != nothing
ci = round(Int, 1000 + p[:marker_z][i] * 255)
GR.setmarkercolorind(ci)
end
GR.setmarkersize(p[:markersize][i] / 4.0)
gr_polymarker(p, [p[:x][i]], [p[:y][i]])
end
end
else
if length(p[:x]) > 0
gr_polymarker(p, p[:x], p[:y])
end
end
legend = true
elseif lt == :bar
y = p[:y]
for i = 1:length(y)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
GR.fillrect(i-0.4, i+0.4, max(0, ymin), y[i])
GR.setfillcolorind(1)
GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
GR.fillrect(i-0.4, i+0.4, max(0, ymin), y[i])
end
elseif lt in [:hist, :density]
h = Base.hist(p[:y])
x, y = float(collect(h[1])), float(h[2])
for i = 2:length(y)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
GR.fillrect(x[i-1], x[i], ymin, y[i])
GR.setfillcolorind(1)
GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
GR.fillrect(x[i-1], x[i], ymin, y[i])
end
elseif lt in [:hline, :vline]
for xy in p[:y]
if lt == :hline
GR.polyline([xmin, xmax], [xy, xy])
else
GR.polyline([xy, xy], [ymin, ymax])
end
end
elseif lt in [:hist2d, :hexbin]
E = zeros(length(p[:x]),2)
E[:,1] = p[:x]
E[:,2] = p[:y]
if isa(p[:bins], Tuple)
xbins, ybins = p[:bins]
else
xbins = ybins = p[:bins]
end
x, y, H = Base.hist2d(E, xbins, ybins)
counts = round(Int32, 1000 + 255 * H / maximum(H))
n, m = size(counts)
GR.setcolormap(GR.COLORMAP_COOLWARM)
GR.cellarray(xmin, xmax, ymin, ymax, n, m, counts)
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
viewport[3], viewport[4])
GR.setspace(0, maximum(counts), 0, 90)
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.016 * diag, 0.01)
GR.setcharheight(charheight)
GR.colormap()
elseif lt == :contour
x, y, z = p[:x], p[:y], p[:z].surf
zmin, zmax = minimum(z), maximum(z)
if typeof(p[:levels]) <: Array
h = p[:levels]
else
h = linspace(zmin, zmax, p[:levels])
end
GR.setspace(zmin, zmax, 0, 90)
GR.setcolormap(GR.COLORMAP_COOLWARM)
GR.contour(x, y, h, reshape(z, length(x) * length(y)), 1000)
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
viewport[3], viewport[4])
l = round(Int32, 1000 + (h - minimum(h)) / (maximum(h) - minimum(h)) * 255)
GR.setwindow(xmin, xmax, zmin, zmax)
GR.cellarray(xmin, xmax, zmax, zmin, 1, length(l), l)
ztick = 0.5 * GR.tick(zmin, zmax)
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.016 * diag, 0.01)
GR.setcharheight(charheight)
GR.axes(0, ztick, xmax, zmin, 0, 1, 0.005)
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)
xtick = GR.tick(xmin, xmax) / 2
ytick = GR.tick(ymin, ymax) / 2
ztick = GR.tick(zmin, zmax) / 2
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.setlinewidth(1)
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 lt == :surface
GR.setcolormap(GR.COLORMAP_COOLWARM)
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
else
GR.setfillcolorind(0)
GR.surface(x, y, z, GR.OPTION_FILLED_MESH)
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)
if cmap
GR.setviewport(viewport[2] + 0.07, viewport[2] + 0.1,
viewport[3], viewport[4])
GR.colormap()
end
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)
xtick = GR.tick(xmin, xmax) / 2
ytick = GR.tick(ymin, ymax) / 2
ztick = GR.tick(zmin, zmax) / 2
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.setlinewidth(1)
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]))
gr_setmarkershape(p)
for i = 1:length(z)
px, py = GR.wc3towc(x[i], y[i], z[i])
gr_polymarker(p, [px], [py])
end
else
haskey(p, :linewidth) && GR.setlinewidth(p[:linewidth])
if length(x) > 0
GR.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 lt == :ohlc
y = p[:y]
n = size(y, 1)
ticksize = 0.5 * (xmax - xmin) / n
for i in 1:n
GR.polyline([i-ticksize, i], [y[i].open, y[i].open])
GR.polyline([i, i], [y[i].low, y[i].high])
GR.polyline([i, i+ticksize], [y[i].close, y[i].close])
end
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
if d[:legend] != :none && legend
GR.savestate()
GR.selntran(0)
GR.setscale(0)
w = 0
i = 0
for p in plt.seriesargs
if typeof(p[:label]) <: Array
i += 1
lab = p[:label][i]
else
lab = p[:label]
end
tbx, tby = GR.inqtext(0, 0, lab)
w = max(w, tbx[3])
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 + dy, py - dy * length(plt.seriesargs))
GR.setlinetype(1)
GR.setlinewidth(1)
GR.drawrect(px - 0.08, px + w + 0.02, py + dy, py - dy * length(plt.seriesargs))
haskey(d, :linewidth) && GR.setlinewidth(d[:linewidth])
i = 0
for p in plt.seriesargs
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks]
haskey(p, :linecolor) && GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
haskey(p, :linestyle) && GR.setlinetype(gr_linetype[p[:linestyle]])
GR.polyline([px - 0.07, px - 0.01], [py, py])
end
if p[:linetype] == :scatter || p[:markershape] != :none
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
gr_setmarkershape(p)
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks]
gr_polymarker(p, [px - 0.06, px - 0.02], [py, py])
else
gr_polymarker(p, [px - 0.06, px - 0.04, px - 0.02], [py, py, py])
end
end
if typeof(p[:label]) <: Array
i += 1
lab = p[:label][i]
else
lab = p[:label]
end
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
GR.settextcolorind(1)
GR.text(px, py, lab)
py -= dy
end
GR.selntran(1)
GR.restorestate()
end
if haskey(d, :anns)
GR.savestate()
for ann in d[:anns]
x, y, val = ann
x, y = GR.wctondc(x, y)
alpha = val.font.rotation
family = lowercase(val.font.family)
GR.setcharheight(0.7 * val.font.pointsize / d[:size][2])
GR.setcharup(sin(val.font.rotation), cos(val.font.rotation))
if haskey(gr_font_family, family)
GR.settextfontprec(100 + gr_font_family[family], GR.TEXT_PRECISION_STRING)
end
GR.settextcolorind(gr_getcolorind(val.font.color))
GR.settextalign(gr_halign[val.font.halign], gr_valign[val.font.valign])
GR.text(x, y, val.str)
end
GR.restorestate()
end
update && GR.updatews()
end
function gr_display(subplt::Subplot{GRBackend})
clear = true
update = false
l = enumerate(subplt.layout)
nr = nrows(subplt.layout)
for (i, (r, c)) in l
nc = ncols(subplt.layout, r)
if i == length(l)
update = true
end
subplot = [(c-1)/nc, c/nc, 1-r/nr, 1-(r-1)/nr]
gr_display(subplt.plts[i], clear, update, subplot)
clear = false
end
end
function _create_plot(pkg::GRBackend, d::KW)
Plot(nothing, pkg, 0, d, KW[])
end
function _add_series(::GRBackend, plt::Plot, d::KW)
push!(plt.seriesargs, d)
plt
end
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
plt.plotargs[:anns] = anns
end
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{GRBackend})
end
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::AbstractPlot{GRBackend}, d::KW)
end
# ----------------------------------------------------------------
function getxy(plt::Plot{GRBackend}, i::Int)
d = plt.seriesargs[i]
d[:x], d[:y]
end
function setxy!{X,Y}(plt::Plot{GRBackend}, xy::Tuple{X,Y}, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y] = xy
plt
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{GRBackend}, isbefore::Bool)
true
end
function _expand_limits(lims, plt::Plot{GRBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
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::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::AbstractPlot{GRBackend})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "svg"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.svg"))
end
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{GRBackend})
gr_display(plt)
true
end
+180
View File
@@ -0,0 +1,180 @@
# https://github.com/JuliaGraphics/Immerse.jl
supportedArgs(::ImmerseBackend) = supportedArgs(GadflyBackend())
supportedAxes(::ImmerseBackend) = supportedAxes(GadflyBackend())
supportedTypes(::ImmerseBackend) = supportedTypes(GadflyBackend())
supportedStyles(::ImmerseBackend) = supportedStyles(GadflyBackend())
supportedMarkers(::ImmerseBackend) = supportedMarkers(GadflyBackend())
supportedScales(::ImmerseBackend) = supportedScales(GadflyBackend())
subplotSupported(::ImmerseBackend) = true
# --------------------------------------------------------------------------------------
function _initialize_backend(::ImmerseBackend; kw...)
@eval begin
import Immerse, Gadfly, Compose, Gtk
export Immerse, Gadfly, Compose, Gtk
include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
end
end
function createImmerseFigure(d::KW)
w,h = d[:size]
figidx = Immerse.figure(; name = d[:windowtitle], width = w, height = h)
Immerse.Figure(figidx)
end
# ----------------------------------------------------------------
# create a blank Gadfly.Plot object
function _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, KW[])
end
# plot one data series
function _add_series(::ImmerseBackend, plt::Plot, d::KW)
addGadflySeries!(plt, d)
push!(plt.seriesargs, d)
plt
end
function _update_plot(plt::Plot{ImmerseBackend}, d::KW)
updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d)
end
# ----------------------------------------------------------------
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
end
# ----------------------------------------------------------------
# accessors for x/y data
function getxy(plt::Plot{ImmerseBackend}, i::Integer)
mapping = getGadflyMappings(plt, i)[1]
mapping[:x], mapping[:y]
end
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
plt
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
return false
# isbefore && return false
end
function showSubplotObject(subplt::Subplot{ImmerseBackend})
# create the Gtk window with vertical box vsep
d = getplotargs(subplt,1)
w,h = d[:size]
vsep = Gtk.GtkBoxLeaf(:v)
win = Gtk.GtkWindowLeaf(vsep, d[:windowtitle], w, h)
figindices = []
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
for (i,(r,c)) in enumerate(subplt.layout)
plt = subplt.plts[i]
# get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
box, toolbar, canvas = Immerse.createPlotGuiComponents()
# add the plot's box to the row
push!(row, box)
# create the figure and store the index returned for destruction later
figidx = Immerse.figure(canvas)
push!(figindices, figidx)
fig = Immerse.figure(figidx)
plt.o = (fig, plt.o[2])
# add the row
if c == ncols(subplt.layout, r)
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
end
end
# destructor... clean up plots
Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
subplt.o = win
true
end
function _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{ImmerseBackend}, isx::Bool)
for l in getGadflyContext(plt).layers
_expand_limits(lims, l.mapping[isx ? :x : :y])
end
end
# ----------------------------------------------------------------
getGadflyContext(plt::Plot{ImmerseBackend}) = plt.o[2]
getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
fig, gplt = plt.o
if fig == nothing
fig = createImmerseFigure(plt.plotargs)
Gtk.on_signal_destroy((x...) -> (Immerse.dropfig(Immerse._display, fig.figno); plt.o = (nothing,gplt)), fig.canvas)
plt.o = (fig, gplt)
end
Immerse.figure(fig.figno; displayfig = false)
display(gplt)
end
function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
# if we haven't created the window yet, do it
if subplt.o == nothing
showSubplotObject(subplt)
end
# display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
for plt in subplt.plts
fig, gplt = plt.o
Immerse.figure(fig.figno; displayfig = false)
display(gplt)
end
# o is the window... show it
showall(subplt.o)
end
+354
View File
@@ -0,0 +1,354 @@
# https://github.com/sisl/PGFPlots.jl
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
# TODO: other initialization that needs to be eval-ed
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 _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 _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{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)
else
plt.plotargs[:annotation_list] = anns
end
end
# ----------------------------------------------------------------
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{PGFPlotsBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# 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{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{PGFPlotsBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
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{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/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::AbstractPlot{PGFPlotsBackend})
# end
function Base.display(::PlotsDisplay, plt::AbstractPlot{PGFPlotsBackend})
plt.o = _make_pgf_plot(plt)
display(plt.o)
end
# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsBackend})
# # TODO: display/show the subplot
# end
+592
View File
@@ -0,0 +1,592 @@
# https://plot.ly/javascript/getting-started
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, '"')
_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()
display("text/html", _js_script)
end
# if isatom()
# import Atom
# Atom.@msg evaljs(_js_code)
# end
end
# TODO: other initialization
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PlotlyBackend, d::KW)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
Plot(nothing, pkg, 0, d, KW[])
end
function _add_series(::PlotlyBackend, plt::Plot, d::KW)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
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)
else
plt.plotargs[:annotation_list] = anns
end
end
# ----------------------------------------------------------------
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{PlotlyBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{PlotlyBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# 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{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{PlotlyBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PlotlyBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
# TODO:
# _plotDefaults[:yrightlabel] = ""
# _plotDefaults[:xlims] = :auto
# _plotDefaults[:ylims] = :auto
# _plotDefaults[:xticks] = :auto
# _plotDefaults[:yticks] = :auto
# _plotDefaults[:xscale] = :identity
# _plotDefaults[:yscale] = :identity
# _plotDefaults[:xflip] = false
# _plotDefaults[:yflip] = false
function plotlyfont(font::Font, color = font.color)
KW(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
:color => webcolor(color),
)
end
function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
KW(
:text => val,
:xref => "x",
:x => x,
:yref => "y",
:y => y,
:showarrow => false,
)
end
function get_annotation_dict(x, y, ptxt::PlotText)
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,
:rotation => ptxt.font.rotation,
))
end
function plotlyscale(scale::Symbol)
if scale == :log10
"log"
else
"-"
end
end
use_axis_field(ticks) = !(ticks in (nothing, :none))
tickssym(isx::Bool) = symbol((isx ? "x" : "y") * "ticks")
limssym(isx::Bool) = symbol((isx ? "x" : "y") * "lims")
flipsym(isx::Bool) = symbol((isx ? "x" : "y") * "flip")
scalesym(isx::Bool) = symbol((isx ? "x" : "y") * "scale")
labelsym(isx::Bool) = symbol((isx ? "x" : "y") * "label")
function plotlyaxis(d::KW, isx::Bool)
ax = KW(
:title => d[labelsym(isx)],
:showgrid => d[:grid],
:zeroline => false,
)
fgcolor = webcolor(d[:foreground_color])
tsym = tickssym(isx)
if use_axis_field(d[tsym])
ax[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
ax[:type] = plotlyscale(d[scalesym(isx)])
ax[:tickfont] = plotlyfont(d[:tickfont], fgcolor)
ax[:tickcolor] = fgcolor
ax[:linecolor] = fgcolor
# xlims
lims = d[limssym(isx)]
if lims != :auto && limsType(lims) == :limits
ax[:range] = lims
end
# xflip
if d[flipsym(isx)]
ax[:autorange] = "reversed"
end
# xticks
ticks = d[tsym]
if ticks != :auto
ttype = ticksType(ticks)
if ttype == :ticks
ax[:tickmode] = "array"
ax[:tickvals] = ticks
elseif ttype == :ticks_and_labels
ax[:tickmode] = "array"
ax[:tickvals], ax[:ticktext] = ticks
end
end
ax
else
ax[:showticklabels] = false
ax[:showgrid] = false
end
ax
end
# function get_plot_json(plt::Plot{PlotlyBackend})
# d = plt.plotargs
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])
# set the fields for the plot
d_out[:title] = d[:title]
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:plot_bgcolor] = bgcolor
d_out[:paper_bgcolor] = bgcolor
# TODO: x/y axis tick values/labels
d_out[:xaxis] = plotlyaxis(d, true)
d_out[:yaxis] = plotlyaxis(d, false)
# legend
d_out[:showlegend] = d[:legend] != :none
if d[:legend] != :none
d_out[:legend] = KW(
:bgcolor => bgcolor,
:bordercolor => fgcolor,
:font => plotlyfont(d[:legendfont]),
)
end
# annotations
anns = get(d, :annotation_list, [])
if !isempty(anns)
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{PlotlyBackend})
JSON.json(plotly_layout(plt.plotargs))
end
function plotly_colorscale(grad::ColorGradient, alpha = nothing)
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
end
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(default_gradient(), alpha)
const _plotly_markers = KW(
:rect => "square",
:xcross => "x",
:utriangle => "triangle-up",
:dtriangle => "triangle-down",
:star5 => "star-triangle-up",
:vline => "line-ns",
:hline => "line-ew",
)
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
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]
lt = d[:linetype]
isscatter = lt in (:scatter, :scatter3d)
hasmarker = isscatter || d[:markershape] != :none
hasline = !isscatter
# set the "type"
if lt in (:line, :path, :scatter, :steppre, :steppost)
d_out[:type] = "scatter"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
if d[:fillrange] == true || d[:fillrange] == 0
d_out[:fill] = "tozeroy"
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
elseif !(d[:fillrange] in (false, nothing))
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(d[:fillrange])")
end
d_out[:x], d_out[:y] = x, y
elseif lt == :bar
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
elseif lt == :hist2d
d_out[:type] = "histogram2d"
d_out[:x], d_out[:y] = x, y
if isa(d[:bins], Tuple)
xbins, ybins = d[:bins]
else
xbins = ybins = d[:bins]
end
d_out[:nbinsx] = xbins
d_out[:nbinsy] = ybins
elseif lt in (:hist, :density)
d_out[:type] = "histogram"
isvert = isvertical(d)
d_out[isvert ? :x : :y] = y
d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
if lt == :density
d_out[:histnorm] = "probability density"
end
elseif lt == :heatmap
d_out[:type] = "heatmap"
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 == :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"
d_out[:labels] = x
d_out[:values] = y
d_out[:hoverinfo] = "label+percent+name"
elseif lt in (:path3d, :scatter3d)
d_out[:type] = "scatter3d"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
d_out[:x], d_out[:y] = x, y
d_out[:z] = collect(d[:z])
else
warn("Plotly: linetype $lt isn't supported.")
return KW()
end
# add "marker"
if hasmarker
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 => KW(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth],
),
)
# 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] = KW(
:color => webcolor(d[:linecolor], d[:linealpha]),
:width => d[:linewidth],
:shape => if lt == :steppre
"vh"
elseif lt == :steppost
"hv"
else
"linear"
end,
:dash => string(d[:linestyle]),
# :dash => "solid",
)
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)"
# d_out[:yaxis] = "y$(plot_index)"
# end
d_out
end
# get a list of dictionaries, each representing the series params
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{PlotlyBackend})
ds = KW[]
for (i,plt) in enumerate(subplt.plts)
for d in plt.seriesargs
push!(ds, plotly_series(d, plt.plotargs, plot_index = i))
end
end
JSON.json(ds)
end
# ----------------------------------------------------------------
function html_head(plt::AbstractPlot{PlotlyBackend})
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
end
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{PlotlyBackend})
w, h = subplt.plts[1].plotargs[:size]
html = ["<div style=\"width:$(w)px;height:$(h)px;\">"]
nr = nrows(subplt.layout)
ph = h / nr
for r in 1:nr
push!(html, "<div style=\"clear:both;\">")
nc = ncols(subplt.layout, r)
pw = w / nc
for c in 1:nc
plt = subplt[r,c]
push!(html, html_body(plt, "float:left; width:$(pw)px; height:$(ph)px;"))
end
push!(html, "</div>")
end
push!(html, "</div>")
join(html)
end
# ----------------------------------------------------------------
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::AbstractPlot{PlotlyBackend})
write(io, html_head(plt) * html_body(plt))
# write(io, html_body(plt))
end
function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend})
standalone_html_window(plt)
end
# function Base.display(::PlotsDisplay, plt::Subplot{PlotlyBackend})
# # TODO: display/show the subplot
# end
+206
View File
@@ -0,0 +1,206 @@
# https://github.com/spencerlyon2/PlotlyJS.jl
supportedArgs(::PlotlyJSBackend) = [
:annotation,
# :axis,
:background_color,
:color_palette,
:fillrange,
:fillcolor,
:fillalpha,
:foreground_color,
:group,
:label,
:layout,
:legend,
:seriescolor, :seriesalpha,
:linecolor,
:linestyle,
:linetype,
:linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokestyle,
:n,
:bins,
:nc,
:nr,
# :pos,
# :smooth,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z,
:marker_z,
:tickfont,
:guidefont,
:legendfont,
:grid,
:levels,
:xerror,
:yerror,
:ribbon,
:quiver,
:orientation,
:polar,
]
supportedAxes(::PlotlyJSBackend) = [:auto, :left]
supportedTypes(::PlotlyJSBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyJSBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyJSBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
supportedScales(::PlotlyJSBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::PlotlyJSBackend) = true
stringsSupported(::PlotlyJSBackend) = true
# --------------------------------------------------------------------------------------
function _initialize_backend(::PlotlyJSBackend; kw...)
@eval begin
import PlotlyJS
export PlotlyJS
end
for (mime, fmt) in PlotlyJS._mimeformats
# mime == "image/png" && continue # don't use plotlyjs's writemime for png
@eval Base.writemime(io::IO, m::MIME{symbol($mime)}, p::Plot{PlotlyJSBackend}) = writemime(io, m, p.o)
end
# override IJulia inline display
if isijulia()
IJulia.display_dict(plt::AbstractPlot{PlotlyJSBackend}) = IJulia.display_dict(plt.o)
end
end
# ---------------------------------------------------------------------------
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())
# T = isijulia() ? PlotlyJS.JupyterPlot : PlotlyJS.ElectronPlot
# o = T(PlotlyJS.Plot())
o = PlotlyJS.plot()
Plot(o, pkg, 0, d, KW[])
end
function _add_series(::PlotlyJSBackend, plt::Plot, d::KW)
syncplot = plt.o
# add to the data array
pdict = plotly_series(d, plt.plotargs)
typ = pop!(pdict, :type)
gt = PlotlyJS.GenericTrace(typ; pdict...)
PlotlyJS.addtraces!(syncplot, gt)
push!(plt.seriesargs, d)
plt
end
# ---------------------------------------------------------------------------
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)
end
# ----------------------------------------------------------------
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{PlotlyJSBackend}, d::KW)
pdict = plotly_layout(d)
syncplot = plt.o
w,h = d[:size]
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
end
function _update_plot_pos_size(plt::AbstractPlot{PlotlyJSBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# function getxy(plt::Plot{PlotlyJSBackend}, i::Int)
# d = plt.seriesargs[i]
# d[:x], d[:y]
# end
function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
d = plt.seriesargs[i]
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{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{PlotlyJSBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
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.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{PlotlyJSBackend})
error()
end
File diff suppressed because it is too large Load Diff
+331
View File
@@ -0,0 +1,331 @@
# https://github.com/tbreloff/Qwt.jl
supportedArgs(::QwtBackend) = [
:annotation,
:axis,
:background_color,
:linecolor,
:color_palette,
:fillrange,
:fillcolor,
:foreground_color,
:group,
:label,
:layout,
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
:linewidth,
:markershape,
:markercolor,
:markersize,
:n,
:bins,
:nc,
:nr,
:pos,
:smooth,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
:yrightlabel,
:yticks,
:xscale,
:yscale,
]
supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
supportedMarkers(::QwtBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
supportedScales(::QwtBackend) = [:identity, :log10]
subplotSupported(::QwtBackend) = true
# --------------------------------------------------------------------------------------
function _initialize_backend(::QwtBackend; kw...)
@eval begin
warn("Qwt is no longer supported... many features will likely be broken.")
import Qwt
export Qwt
end
end
# -------------------------------
@compat const _qwtAliases = KW(
:bins => :heatmap_n,
:fillrange => :fillto,
:linewidth => :width,
:markershape => :marker,
:hexbin => :heatmap,
:path => :line,
:steppost => :step,
:steppre => :stepinverted,
:star5 => :star1,
:star8 => :star2,
)
function fixcolors(d::KW)
for (k,v) in d
if typeof(v) <: ColorScheme
d[k] = getColor(v)
end
end
end
function replaceQwtAliases(d, s)
if haskey(_qwtAliases, d[s])
d[s] = _qwtAliases[d[s]]
end
end
function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
d = KW(kw)
lt = d[:linetype]
if lt == :scatter
d[:linetype] = :none
if d[:markershape] == :none
d[:markershape] = :ellipse
end
elseif lt in (:hline, :vline)
addLineMarker(plt, d)
d[:linetype] = :none
d[:markershape] = :ellipse
d[:markersize] = 1
if lt == :vline
d[:x], d[:y] = d[:y], d[:x]
end
elseif !iscreating && lt == :bar
d = barHack(; kw...)
elseif !iscreating && lt == :hist
d = barHack(; histogramHack(; kw...)...)
end
replaceQwtAliases(d, :linetype)
replaceQwtAliases(d, :markershape)
for k in keys(d)
if haskey(_qwtAliases, k)
d[_qwtAliases[k]] = d[k]
end
end
d[:x] = collect(d[:x])
d[:y] = collect(d[:y])
d
end
function _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, KW[])
plt
end
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...)
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
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]
if typeof(lims) <: @compat(Union{Tuple,AVec}) && length(lims) == 2
if isx
plt.o.autoscale_x = false
else
plt.o.autoscale_y = false
end
w[:setAxisScale](axisid, lims...)
end
if typeof(ticks) <: Range
if isx
plt.o.autoscale_x = false
else
plt.o.autoscale_y = false
end
w[:setAxisScale](axisid, float(minimum(ticks)), float(maximum(ticks)), float(step(ticks)))
elseif !(ticks in (nothing, :none, :auto))
warn("Only Range types are supported for Qwt xticks/yticks. typeof(ticks)=$(typeof(ticks))")
end
# change the scale
scalesym = isx ? :xscale : :yscale
if haskey(d, scalesym)
scaletype = d[scalesym]
scaletype == :identity && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLinearScaleEngine())
# scaletype == :log && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(e))
# scaletype == :log2 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(2))
scaletype == :log10 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLog10ScaleEngine())
scaletype in supportedScales() || warn("Unsupported scale type: ", scaletype)
end
end
function _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])
updateLimsAndTicks(plt, d, true)
updateLimsAndTicks(plt, d, false)
end
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
# ----------------------------------------------------------------
# curve.setPen(Qt.QPen(Qt.QColor(color), linewidth, self.getLineStyle(linestyle)))
function addLineMarker(plt::Plot{QwtBackend}, d::KW)
for yi in d[:y]
marker = Qwt.QWT.QwtPlotMarker()
ishorizontal = (d[:linetype] == :hline)
marker[:setLineStyle](ishorizontal ? 1 : 2)
marker[ishorizontal ? :setYValue : :setXValue](yi)
qcolor = Qwt.convertRGBToQColor(getColor(d[:linecolor]))
linestyle = plt.o.widget[:getLineStyle](string(d[:linestyle]))
marker[:setLinePen](Qwt.QT.QPen(qcolor, d[:linewidth], linestyle))
marker[:attach](plt.o.widget)
end
# marker[:setValue](x, y)
# marker[:setLabel](Qwt.QWT.QwtText(val))
# marker[:attach](plt.o.widget)
end
function createQwtAnnotation(plt::Plot, x, y, val::PlotText)
marker = Qwt.QWT.QwtPlotMarker()
marker[:setValue](x, y)
qwttext = Qwt.QWT.QwtText(val.str)
qwttext[:setFont](Qwt.QT.QFont(val.font.family, val.font.pointsize))
qwttext[:setColor](Qwt.convertRGBToQColor(getColor(val.font.color)))
marker[:setLabel](qwttext)
marker[:attach](plt.o.widget)
end
function createQwtAnnotation(plt::Plot, x, y, val::@compat(AbstractString))
marker = Qwt.QWT.QwtPlotMarker()
marker[:setValue](x, y)
marker[:setLabel](Qwt.QWT.QwtText(val))
marker[:attach](plt.o.widget)
end
function _add_annotations{X,Y,V}(plt::Plot{QwtBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createQwtAnnotation(plt, ann...)
end
end
# ----------------------------------------------------------------
# accessors for x/y data
function getxy(plt::Plot{QwtBackend}, i::Int)
series = plt.o.lines[i]
series.x, series.y
end
function setxy!{X,Y}(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer)
series = plt.o.lines[i]
series.x, series.y = xy
plt
end
# -------------------------------
# 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{QwtBackend}, isbefore::Bool)
isbefore && return false
i = 0
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
push!(row, subplt.plts[i].o)
if c == ncols(subplt.layout, r)
push!(rows, Qwt.hsplitter(row...))
row = Any[]
end
end
# for rowcnt in subplt.layout.rowcounts
# push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
# i += rowcnt
# end
subplt.o = Qwt.vsplitter(rows...)
# Qwt.resizewidget(subplt.o, getplotargs(subplt,1)[:size]...)
# Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
true
end
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{QwtBackend}, isx::Bool)
end
# ----------------------------------------------------------------
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{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
write(io, readall("/tmp/dfskjdhfkh.png"))
end
function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
Qwt.refresh(plt.o)
Qwt.showwidget(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.showwidget(subplt.o)
end
+698
View File
@@ -0,0 +1,698 @@
#
# 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())
# --------------------------------------------------------------------------------------
#
#
# supportedArgs(::GadflyBackend) = [
# :annotation,
# :background_color, :foreground_color, :color_palette,
# :group,
# :label,
# :linetype,
# :seriescolor, :seriesalpha,
# :linecolor, :linestyle, :linewidth, :linealpha,
# :markershape, :markercolor, :markersize, :markeralpha,
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
# :fillrange, :fillcolor, :fillalpha,
# :bins,
# :n, :nc, :nr, :layout,
# :smooth,
# :title, :windowtitle, :show, :size,
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
# # :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
# :z,
# :tickfont, :guidefont, :legendfont,
# :grid, :legend, :colorbar,
# :marker_z, :levels,
# :xerror, :yerror,
# :ribbon, :quiver,
# :orientation,
# ]
# supportedAxes(::GadflyBackend) = [:auto, :left]
# supportedTypes(::GadflyBackend) = [
# :none, :line, :path, :steppre, :steppost, :sticks,
# :scatter, :hist2d, :hexbin, :hist,
# :bar, :box, :violin, :quiver,
# :hline, :vline, :contour, :shape
# ]
# supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
# supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
# supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
# subplotSupported(::GadflyBackend) = true
#
#
# # --------------------------------------------------------------------------------------
#
# 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
#
# # --------------------------------------------------------------------------------------
#
#
# supportedArgs(::PyPlotBackend) = [
# :annotation,
# :background_color, :foreground_color, :color_palette,
# :background_color_legend, :background_color_inside, :background_color_outside,
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
# :foreground_color_text, :foreground_color_border,
# :group,
# :label,
# :linetype,
# :seriescolor, :seriesalpha,
# :linecolor, :linestyle, :linewidth, :linealpha,
# :markershape, :markercolor, :markersize, :markeralpha,
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
# :fillrange, :fillcolor, :fillalpha,
# :bins,
# :n, :nc, :nr, :layout,
# :smooth,
# :title, :windowtitle, :show, :size,
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
# :axis, :yrightlabel,
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
# :z,
# :tickfont, :guidefont, :legendfont,
# :grid, :legend, :colorbar,
# :marker_z, :levels,
# :xerror, :yerror,
# :ribbon, :quiver,
# :orientation,
# :overwrite_figure,
# :polar,
# :normalize, :weights, :contours, :aspect_ratio
# ]
# supportedAxes(::PyPlotBackend) = _allAxes
# supportedTypes(::PyPlotBackend) = [
# :none, :line, :path, :steppre, :steppost, :shape,
# :scatter, :hist2d, :hexbin, :hist, :density,
# :bar, :sticks, :box, :violin, :quiver,
# :hline, :vline, :heatmap, :pie,
# :contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
# ]
# supportedStyles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::PyPlotBackend) = vcat(_allMarkers, Shape)
# supportedScales(::PyPlotBackend) = [:identity, :ln, :log2, :log10]
# subplotSupported(::PyPlotBackend) = true
#
#
# # --------------------------------------------------------------------------------------
#
#
# 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
#
#
# # --------------------------------------------------------------------------------------
#
#
# 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
#
#
# # --------------------------------------------------------------------------------------
#
# 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
#
#
#
#
# # --------------------------------------------------------------------------------------
#
# 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
#
#
# # --------------------------------------------------------------------------------------
#
#
# 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
#
#
# # --------------------------------------------------------------------------------------
#
# 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
#
#
# # --------------------------------------------------------------------------------------
#
# 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
#
# # --------------------------------------------------------------------------------------
#
# 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
#
# # --------------------------------------------------------------------------------------
#
# 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
+87
View File
@@ -0,0 +1,87 @@
# TODO: find/replace all [PkgName] with CamelCase, all [pkgname] with lowercase
# [WEBSITE]
function _initialize_backend(::[PkgName]AbstractBackend; kw...)
@eval begin
import [PkgName]
export [PkgName]
# TODO: other initialization that needs to be eval-ed
end
# TODO: other initialization
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::[PkgName]AbstractBackend, d::KW)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
Plot(nothing, pkg, 0, d, KW[])
end
function _add_series(::[PkgName]AbstractBackend, plt::Plot, 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]AbstractBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
end
# ----------------------------------------------------------------
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]AbstractBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{[PkgName]AbstractBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# 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]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]AbstractBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
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::AbstractPlot{[PkgName]AbstractBackend})
# TODO: write a png to io
end
function Base.display(::PlotsDisplay, plt::Plot{[PkgName]AbstractBackend})
# TODO: display/show the plot
end
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]AbstractBackend})
# TODO: display/show the subplot
end
+263
View File
@@ -0,0 +1,263 @@
# https://github.com/Evizero/UnicodePlots.jl
supportedArgs(::UnicodePlotsBackend) = [
# :annotation,
# :args,
# :axis,
# :background_color,
# :linecolor,
# :fill,
# :foreground_color,
:group,
# :heatmap_c,
# :kwargs,
:label,
# :layout,
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
# :linewidth,
:markershape,
# :markercolor,
# :markersize,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
:bins,
# :nc,
# :nr,
# :pos,
# :reg,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
# :xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
]
supportedAxes(::UnicodePlotsBackend) = [:auto, :left]
supportedTypes(::UnicodePlotsBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
supportedStyles(::UnicodePlotsBackend) = [:auto, :solid]
supportedMarkers(::UnicodePlotsBackend) = [:none, :auto, :ellipse]
supportedScales(::UnicodePlotsBackend) = [:identity]
subplotSupported(::UnicodePlotsBackend) = true
# --------------------------------------------------------------------------------------
function _initialize_backend(::UnicodePlotsBackend; kw...)
@eval begin
import UnicodePlots
export UnicodePlots
end
end
# -------------------------------
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
function rebuildUnicodePlot!(plt::Plot)
# figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
sargs = plt.seriesargs
iargs = plt.plotargs
# get the x/y limits
if get(iargs, :xlims, :auto) == :auto
xlim = [Inf, -Inf]
for d in sargs
_expand_limits(xlim, d[:x])
end
else
xmin, xmax = iargs[:xlims]
xlim = [xmin, xmax]
end
if get(iargs, :ylims, :auto) == :auto
ylim = [Inf, -Inf]
for d in sargs
_expand_limits(ylim, d[:y])
end
else
ymin, ymax = iargs[:ylims]
ylim = [ymin, ymax]
end
# we set x/y to have a single point, since we need to create the plot with some data.
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
x = Float64[xlim[1]]
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
width, height = iargs[:size]
o = UnicodePlots.Plot(x, y; width = width,
height = height,
title = iargs[:title],
# labels = iargs[:legend],
xlim = xlim,
ylim = ylim)
# set the axis labels
UnicodePlots.xlabel!(o, iargs[:xlabel])
UnicodePlots.ylabel!(o, iargs[:ylabel])
# now use the ! functions to add to the plot
for d in sargs
addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
end
# save the object
plt.o = o
end
# add a single series
function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
# get the function, or special handling for step/bar/hist
lt = d[:linetype]
# handle hline/vline separately
if lt in (:hline,:vline)
for yi in d[:y]
if lt == :hline
UnicodePlots.lineplot!(o, xlim, [yi,yi])
else
UnicodePlots.lineplot!(o, [yi,yi], ylim)
end
end
return
end
stepstyle = :post
if lt == :path
func = UnicodePlots.lineplot!
elseif lt == :scatter || d[:markershape] != :none
func = UnicodePlots.scatterplot!
elseif lt == :steppost
func = UnicodePlots.stairs!
elseif lt == :steppre
func = UnicodePlots.stairs!
stepstyle = :pre
else
error("Linestyle $lt not supported by UnicodePlots")
end
# get the series data and label
x, y = [collect(float(d[s])) for s in (:x, :y)]
label = addlegend ? d[:label] : ""
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
# add the series
func(o, x, y; color = color, name = label, style = stepstyle)
end
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
# -------------------------------
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]
plt.plotargs[:size] = (60,20)
end
plt
end
function _add_series(::UnicodePlotsBackend, plt::Plot, d::KW)
if d[:linetype] in (:sticks, :bar)
d = barHack(; d...)
elseif d[:linetype] == :hist
d = barHack(; histogramHack(; d...)...)
end
push!(plt.seriesargs, d)
plt
end
function _update_plot(plt::Plot{UnicodePlotsBackend}, d::KW)
for k in (:title, :xlabel, :ylabel, :xlims, :ylims)
if haskey(d, k)
plt.plotargs[k] = d[k]
end
end
end
# -------------------------------
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::@compat(AbstractString))
fn = addExtension(fn, "png")
# make some whitespace and show the plot
println("\n\n\n\n\n\n")
gui(plt)
@osx_only begin
# BEGIN HACK
# wait while the plot gets drawn
sleep(0.5)
# use osx screen capture when my terminal is maximized and cursor starts at the bottom (I know, right?)
# TODO: compute size of plot to adjust these numbers (or maybe implement something good??)
run(`screencapture -R50,600,700,420 $fn`)
# END HACK (phew)
return
end
error("Can only savepng on osx with UnicodePlots (though even then I wouldn't do it)")
end
# -------------------------------
# we don't do very much for subplots... just stack them vertically
function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
isbefore && return false
true
end
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsBackend})
rebuildUnicodePlot!(plt)
show(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
for plt in subplt.plts
gui(plt)
end
end
+60
View File
@@ -0,0 +1,60 @@
# NOTE: backend should implement `html_body` and `html_head`
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.plotargs, :window_title, "Plots.jl"))
"""
<!DOCTYPE html>
<html>
<head>
<title>$title</title>
$(html_head(plt))
</head>
<body>
$(html_body(plt))
</body>
</html>
"""
end
function open_browser_window(filename::AbstractString)
@osx_only return run(`open $(filename)`)
@linux_only return run(`xdg-open $(filename)`)
@windows_only return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
warn("Unknown OS... cannot open browser window.")
end
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
+305
View File
@@ -0,0 +1,305 @@
# https://github.com/nolta/Winston.jl
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
supportedArgs(::WinstonBackend) = [
:annotation,
# :args,
# :axis,
# :background_color,
:linecolor,
:color_palette,
:fillrange,
:fillcolor,
# :foreground_color,
:group,
# :heatmap_c,
# :kwargs,
:label,
# :layout,
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
:linewidth,
:markershape,
:markercolor,
:markersize,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
:bins,
# :nc,
# :nr,
# :pos,
:smooth,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
# :xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
# :yticks,
:xscale,
:yscale,
# :xflip,
# :yflip,
# :z,
]
supportedAxes(::WinstonBackend) = [:auto, :left]
supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
supportedStyles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::WinstonBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
supportedScales(::WinstonBackend) = [:identity, :log10]
subplotSupported(::WinstonBackend) = false
# --------------------------------------------------------------------------------------
function _initialize_backend(::WinstonBackend; kw...)
@eval begin
# ENV["WINSTON_OUTPUT"] = "gtk"
warn("Winston is no longer supported... many features will likely be broken.")
import Winston, Gtk
export Winston, Gtk
end
end
# ---------------------------------------------------------------------------
## dictionaries for conversion of Plots.jl names to Winston ones.
@compat const winston_linestyle = KW(:solid=>"solid",
:dash=>"dash",
:dot=>"dotted",
:dashdot=>"dotdashed"
)
@compat const winston_marker = KW(:none=>".",
:rect => "square",
:ellipse=>"circle",
:diamond=>"diamond",
:utriangle=>"triangle",
:dtriangle=>"down-triangle",
:cross => "plus",
:xcross => "cross",
:star5 => "asterisk"
)
function _before_add_series(plt::Plot{WinstonBackend})
Winston.ghf(plt.o)
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::WinstonBackend, d::KW)
wplt = Winston.FramedPlot(title = d[:title], xlabel = d[:xlabel], ylabel = d[:ylabel])
Plot(wplt, pkg, 0, d, KW[])
end
copy_remove(d::KW, s::Symbol) = delete!(copy(d), s)
function addRegressionLineWinston(d::KW, wplt)
xs, ys = regressionXY(d[:x], d[:y])
Winston.add(wplt, Winston.Curve(xs, ys, kind="dotted"))
end
function getWinstonItems(plt::Plot)
if isa(plt.o, Winston.FramedPlot)
wplt = plt.o
window, canvas = nothing, nothing
else
window, canvas, wplt = plt.o
end
window, canvas, wplt
end
function _add_series(::WinstonBackend, plt::Plot, d::KW)
window, canvas, wplt = getWinstonItems(plt)
# until we call it normally, do the hack
if d[:linetype] == :bar
d = barHack(;d...)
end
e = KW()
e[:color] = getColor(d[:linecolor])
e[:linewidth] = d[:linewidth]
e[:kind] = winston_linestyle[d[:linestyle]]
e[:symbolkind] = winston_marker[d[:markershape]]
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
e[:symbolsize] = d[:markersize] / 5
# pos # (Int,Int), move the enclosing window to this position
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :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])))
elseif d[:linetype] == :path
x, y = d[:x], d[:y]
Winston.add(wplt, Winston.Curve(x, y; e...))
fillrange = d[:fillrange]
if fillrange != nothing
if isa(fillrange, AbstractVector)
y2 = fillrange
else
y2 = Float64[fillrange for yi in y]
end
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(d[:fillcolor])))
end
elseif d[:linetype] == :scatter
if d[:markershape] == :none
d[:markershape] = :ellipse
end
# elseif d[:linetype] == :step
# fn = Winston.XXX
# elseif d[:linetype] == :stepinverted
# fn = Winston.XXX
elseif d[:linetype] == :sticks
Winston.add(wplt, Winston.Stems(d[:x], d[:y]; e...))
# elseif d[:linetype] == :dots
# fn = Winston.XXX
# elseif d[:linetype] == :hist2d
# fn = Winston.XXX
# elseif d[:linetype] == :hexbin
# fn = Winston.XXX
elseif d[:linetype] == :hist
hst = hist(d[:y], d[:bins])
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
# elseif d[:linetype] == :bar
# # fn = Winston.XXX
else
error("linetype $(d[:linetype]) not supported by Winston.")
end
# markershape
if d[:markershape] != :none
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
end
# optionally add a regression line
d[:smooth] && d[:linetype] != :hist && addRegressionLineWinston(d, wplt)
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
@compat const _winstonNames = KW(
:xlims => :xrange,
:ylims => :yrange,
:xscale => :xlog,
:yscale => :ylog,
)
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)
Winston.setattr(wplt, string(get(_winstonNames, k, k)), d[k])
end
end
for k in (:xscale, :yscale)
if haskey(d, k)
islogscale = d[k] == :log10
Winston.setattr(wplt, (k == :xscale ? :xlog : :ylog), islogscale)
end
end
end
# ----------------------------------------------------------------
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::@compat(AbstractString))
Winston.text(x, y, val)
end
function _add_annotations{X,Y,V}(plt::Plot{WinstonBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createWinstonAnnotationObject(plt, ann...)
end
end
# ----------------------------------------------------------------
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
# ----------------------------------------------------------------
function addWinstonLegend(plt::Plot, wplt)
if plt.plotargs[:legend] != :none
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
end
end
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{WinstonBackend})
window, canvas, wplt = getWinstonItems(plt)
if window == nothing
if Winston.output_surface != :gtk
error("Gtk is the only supported display for Winston in Plots. Set `output_surface = gtk` in src/Winston.ini")
end
# initialize window
w,h = plt.plotargs[:size]
canvas = Gtk.GtkCanvasLeaf()
window = Gtk.GtkWindowLeaf(canvas, plt.plotargs[:windowtitle], w, h)
plt.o = (window, canvas, wplt)
end
addWinstonLegend(plt, wplt)
Winston.display(canvas, wplt)
Gtk.showall(window)
end
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
# TODO: display/show the Subplot object
end
File diff suppressed because it is too large Load Diff
+422
View File
@@ -0,0 +1,422 @@
abstract ColorScheme
getColor(scheme::ColorScheme) = getColor(scheme, 1)
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
colorscheme(scheme::ColorScheme) = scheme
colorscheme(s::Symbol; kw...) = haskey(_gradients, s) ? ColorGradient(s; kw...) : ColorWrapper(convertColor(s); kw...)
colorscheme{T<:Real}(s::Symbol, vals::AVec{T}; kw...) = ColorGradient(s, vals; kw...)
colorscheme(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
colorscheme{T<:Colorant}(cs::AVec{T}; kw...) = ColorGradient(cs; kw...)
colorscheme(f::Function; kw...) = ColorFunction(f; kw...)
colorscheme(v::AVec; kw...) = ColorVector(v; kw...)
colorscheme(m::AMat; kw...) = size(m,1) == 1 ? map(c->colorscheme(c; kw...), m) : [colorscheme(m[:,i]; kw...) for i in 1:size(m,2)]'
colorscheme(c::Colorant; kw...) = ColorWrapper(c; kw...)
# --------------------------------------------------------------
convertColor(c::@compat(Union{AbstractString, Symbol})) = parse(Colorant, string(c))
convertColor(c::Colorant) = c
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
convertColor(c::ColorScheme) = c
function convertColor(c, α::Real)
c = convertColor(c)
RGBA(RGB(c), α)
end
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
convertColor(c, α::@compat(Void)) = convertColor(c)
# backup... try to convert
getColor(c) = convertColor(c)
# --------------------------------------------------------------
function darken(c, v=0.1)
rgba = convert(RGBA, c)
r = max(0, min(rgba.r - v, 1))
g = max(0, min(rgba.g - v, 1))
b = max(0, min(rgba.b - v, 1))
RGBA(r,g,b,rgba.alpha)
end
function lighten(c, v=0.3)
darken(c, -v)
end
# --------------------------------------------------------------
const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"]
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
const _gradients = KW(
:blues => [colorant"lightblue", colorant"darkblue"],
:reds => [colorant"lightpink", colorant"darkred"],
:greens => [colorant"lightgreen", colorant"darkgreen"],
:redsblues => [colorant"darkred", RGB(0.8,0.85,0.8), colorant"darkblue"],
:bluesreds => [colorant"darkblue", RGB(0.8,0.85,0.8), colorant"darkred"],
:heat => [colorant"lightyellow", colorant"orange", colorant"darkred"],
:grays => [RGB(.95,.95,.95),RGB(.05,.05,.05)],
:rainbow => _rainbowColors,
:lightrainbow => map(lighten, _rainbowColors),
:darkrainbow => map(darken, _rainbowColors),
:darktest => _testColors,
:lighttest => map(c -> lighten(c, 0.3), _testColors),
)
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
values::Vector
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))]
# new(convertColor(cs,alpha), vs)
# interpolate the colors for each value
vals = merge(linspace(0, 1, length(cs)), vals)
grad = ColorGradient(cs)
cs = [getColorZ(grad, z) for z in linspace(0, 1, length(vals))]
new(convertColor(cs, alpha), vals)
end
end
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:0; kw...)
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
cs = _gradients[s]
if vals == 0:0
vals = linspace(0, 1, length(cs))
end
ColorGradient(cs, vals; kw...)
end
# 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))]
function getColorZ(gradient::ColorGradient, z::Real)
cs = gradient.colors
vs = gradient.values
n = length(cs)
@assert n > 0 && n == length(vs)
# can we just return the first color?
if z <= vs[1] || n == 1
return cs[1]
end
# find the bounding colors and interpolate
for i in 2:n
if z <= vs[i]
return interpolate_rgb(cs[i-1], cs[i], (z - vs[i-1]) / (vs[i] - vs[i-1]))
end
end
# if we get here, return the last color
cs[end]
end
getColorVector(gradient::ColorGradient) = gradient.colors
# for 0.3
Colors.RGBA(c::Colorant) = RGBA(red(c), green(c), blue(c), alpha(c))
Colors.RGB(c::Colorant) = RGB(red(c), green(c), blue(c))
function interpolate_rgb(c1::Colorant, c2::Colorant, w::Real)
rgb1 = RGBA(c1)
rgb2 = RGBA(c2)
r = interpolate(rgb1.r, rgb2.r, w)
g = interpolate(rgb1.g, rgb2.g, w)
b = interpolate(rgb1.b, rgb2.b, w)
a = interpolate(rgb1.alpha, rgb2.alpha, w)
RGBA(r, g, b, a)
end
function interpolate(v1::Real, v2::Real, w::Real)
(1-w) * v1 + w * v2
end
# --------------------------------------------------------------
"Wraps a function, taking an index and returning a Colorant"
immutable ColorFunction <: ColorScheme
f::Function
end
getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx)
# --------------------------------------------------------------
"Wraps a function, taking an z-value and returning a Colorant"
immutable ColorZFunction <: ColorScheme
f::Function
end
getColorZ(scheme::ColorFunction, z::Real) = scheme.f(z)
# --------------------------------------------------------------
"Wraps a vector of colors... may be vector of Symbol/String/Colorant"
immutable ColorVector <: ColorScheme
v::Vector{Colorant}
ColorVector(v::AVec; alpha = nothing) = new(convertColor(v,alpha))
end
getColor(scheme::ColorVector, idx::Int) = convertColor(scheme.v[mod1(idx, length(scheme.v))])
getColorVector(scheme::ColorVector) = scheme.v
# --------------------------------------------------------------
"Wraps a single color"
immutable ColorWrapper <: ColorScheme
c::RGBA
ColorWrapper(c::Colorant; alpha = nothing) = new(convertColor(c, alpha))
end
ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Colorant, s), alpha))
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
# --------------------------------------------------------------
isbackgrounddark(bgcolor::Color) = Lab(bgcolor).l < 0.5
# move closer to lighter/darker depending on background value
function adjustAway(val, bgval, vmin=0., vmax=100.)
if bgval < 0.5 * (vmax+vmin)
tmp = max(val, bgval)
return 0.5 * (tmp + max(tmp, vmax))
else
tmp = min(val, bgval)
return 0.5 * (tmp + min(tmp, vmin))
end
end
# borrowed from http://stackoverflow.com/a/1855903:
lightnessLevel(c::Colorant) = 0.299 * red(c) + 0.587 * green(c) + 0.114 * blue(c)
isdark(c::Colorant) = lightnessLevel(c) < 0.5
islight(c::Colorant) = !isdark(c)
function convertHexToRGB(h::Unsigned)
mask = 0x0000FF
RGB([(x & mask) / 0xFF for x in (h >> 16, h >> 8, h)]...)
end
# note: I found this list of hex values in a comment by Tatarize here: http://stackoverflow.com/a/12224359
const _masterColorList = [
0xFFFFFF, 0x000000, 0x0000FF, 0x00FF00, 0xFF0000, 0x01FFFE, 0xFFA6FE, 0xFFDB66, 0x006401, 0x010067,
0x95003A, 0x007DB5, 0xFF00F6, 0xFFEEE8, 0x774D00, 0x90FB92, 0x0076FF, 0xD5FF00, 0xFF937E, 0x6A826C,
0xFF029D, 0xFE8900, 0x7A4782, 0x7E2DD2, 0x85A900, 0xFF0056, 0xA42400, 0x00AE7E, 0x683D3B, 0xBDC6FF,
0x263400, 0xBDD393, 0x00B917, 0x9E008E, 0x001544, 0xC28C9F, 0xFF74A3, 0x01D0FF, 0x004754, 0xE56FFE,
0x788231, 0x0E4CA1, 0x91D0CB, 0xBE9970, 0x968AE8, 0xBB8800, 0x43002C, 0xDEFF74, 0x00FFC6, 0xFFE502,
0x620E00, 0x008F9C, 0x98FF52, 0x7544B1, 0xB500FF, 0x00FF78, 0xFF6E41, 0x005F39, 0x6B6882, 0x5FAD4E,
0xA75740, 0xA5FFD2, 0xFFB167, 0x009BFF, 0xE85EBE
]
const _allColors = map(convertHexToRGB, _masterColorList)
const _darkColors = filter(isdark, _allColors)
const _lightColors = filter(islight, _allColors)
const _sortedColorsForDarkBackground = vcat(_lightColors, reverse(_darkColors[2:end]))
const _sortedColorsForLightBackground = vcat(_darkColors, reverse(_lightColors[2:end]))
const _defaultNumColors = 17
# --------------------------------------------------------------
# Methods to automatically generate gradients for color selection based on
# background color and a short list of seed colors
# here are some magic constants that could be changed if you really want
const _lightness_darkbg = [80.0]
const _lightness_lightbg = [60.0]
const _lch_c_const = [60]
function adjust_lch(color, l, c)
lch = convert(LCHab, color)
convert(RGB, LCHab(l, c, lch.h))
end
function lightness_from_background(bgcolor)
bglight = convert(LCHab, bgcolor).l
bglight < 50.0 ? _lightness_darkbg[1] : _lightness_lightbg[1]
end
function gradient_from_list(cs)
zvalues = Plots.get_zvalues(length(cs))
indices = sortperm(zvalues)
sorted_colors = map(RGBA, cs[indices])
sorted_zvalues = zvalues[indices]
ColorGradient(sorted_colors, sorted_zvalues)
end
function generate_colorgradient(bgcolor = colorant"white";
color_bases = color_bases=[colorant"steelblue",colorant"orangered"],
lightness = lightness_from_background(bgcolor),
chroma = _lch_c_const[1],
n = _defaultNumColors)
seed_colors = vcat(bgcolor, map(c -> adjust_lch(c, lightness, chroma), color_bases))
colors = distinguishable_colors(n,
seed_colors,
lchoices=Float64[lightness],
cchoices=Float64[chroma],
hchoices=linspace(0, 340, 20)
)[2:end]
gradient_from_list(colors)
end
function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
grad = if palette == :auto
generate_colorgradient(bgcolor)
else
ColorGradient(palette)
end
zrng = get_zvalues(numcolors)
RGBA[getColorZ(grad, z) for z in zrng]
end
function get_color_palette(palette::Vector{RGBA}, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
palette
end
# ----------------------------------------------------------------------------------
function getpctrange(n::Int)
n > 0 || error()
n == 1 && return zeros(1)
zs = [0.0, 1.0]
for i in 3:n
sorted = sort(zs)
diffs = diff(sorted)
widestj = 0
widest = 0.0
for (j,d) in enumerate(diffs)
if d > widest
widest = d
widestj = j
end
end
push!(zs, sorted[widestj] + 0.5 * diffs[widestj])
end
zs
end
function get_zvalues(n::Int)
offsets = getpctrange(ceil(Int,n/4)+1)/4
offsets = vcat(offsets[1], offsets[3:end])
zvalues = Float64[]
for offset in offsets
append!(zvalues, offset + [0.0, 0.5, 0.25, 0.75])
end
vcat(zvalues[1], 1.0, zvalues[2:n-1])
end
# ----------------------------------------------------------------------------------
make255(x) = round(Int, 255 * x)
function webcolor(c::Color)
@sprintf("rgb(%d, %d, %d)", [make255(f(c)) for f in [red,green,blue]]...)
end
function webcolor(c::TransparentColor)
@sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
end
webcolor(cs::ColorScheme) = webcolor(getColor(cs))
webcolor(c) = webcolor(convertColor(c))
webcolor(c, α) = webcolor(convertColor(getColor(c), α))
# ----------------------------------------------------------------------------------
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
function handlePlotColors(::AbstractBackend, d::KW)
if :background_color in supportedArgs()
bgcolor = convertColor(d[:background_color])
else
bgcolor = _plotDefaults[:background_color]
if d[:background_color] != _plotDefaults[:background_color]
warn("Cannot set background_color with backend $(backend())")
end
end
d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100)
# set the foreground color (text, ticks, gridlines) to be white or black depending
# on how dark the background is.
fgcolor = get(d, :foreground_color, :auto)
fgcolor = if fgcolor == :auto
isdark(bgcolor) ? colorant"white" : colorant"black"
else
convertColor(fgcolor)
end
# 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
end
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
function getSeriesRGBColor(c, plotargs::KW, n::Int)
if c == :auto
c = autopick(plotargs[:color_palette], n)
end
# c should now be a subtype of ColorScheme
colorscheme(c)
end
+438
View File
@@ -0,0 +1,438 @@
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
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`"
function partialcircle(start_θ, end_θ, n = 20, r=1)
@compat(Tuple{Float64,Float64})[(r*cos(u),r*sin(u)) for u in linspace(start_θ, end_θ, n)]
end
"interleave 2 vectors into each other (like a zipper's teeth)"
function weave(x,y; ordering = Vector[x,y])
ret = eltype(x)[]
done = false
while !done
for o in ordering
try
push!(ret, shift!(o))
end
# try
# push!(ret, shift!(y))
# end
end
done = isempty(x) && isempty(y)
end
ret
end
"create a star by weaving together points from an outer and inner circle. `n` is the number of arms"
function makestar(n; offset = -0.5, radius = 1.0)
z1 = offset * π
z2 = z1 + π / (n)
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
Shape(weave(outercircle, innercircle)[1:end-2])
end
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
function makeshape(n; offset = -0.5, radius = 1.0)
z = offset * π
Shape(partialcircle(z, z + 2π, n+1, radius)[1:end-1])
end
function makecross(; offset = -0.5, radius = 1.0)
z2 = offset * π
z1 = z2 - π/8
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
Shape(weave(outercircle, innercircle,
ordering=Vector[outercircle,innercircle,outercircle])[1:end-2])
end
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),
:utriangle => makeshape(3),
:dtriangle => makeshape(3, offset=0.5),
:pentagon => makeshape(5),
:hexagon => makeshape(6),
:heptagon => makeshape(7),
:octagon => makeshape(8),
:cross => makecross(offset=-0.25),
:xcross => makecross(),
:vline => Shape([(0,1),(0,-1)]),
:hline => Shape([(1,0),(-1,0)]),
)
for n in [4,5,6,7,8]
_shapes[symbol("star$n")] = makestar(n)
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
pointsize::Int
halign::Symbol
valign::Symbol
rotation::Float64
color::Colorant
end
"Create a Font from a list of unordered features"
function font(args...)
# defaults
family = "Helvetica"
pointsize = 14
halign = :hcenter
valign = :vcenter
rotation = 0.0
color = colorant"black"
for arg in args
T = typeof(arg)
if arg == :center
halign = :hcenter
valign = :vcenter
elseif arg in (:hcenter, :left, :right)
halign = arg
elseif arg in (:vcenter, :top, :bottom)
valign = arg
elseif T <: Colorant
color = arg
elseif T <: @compat Union{Symbol,AbstractString}
try
color = parse(Colorant, string(arg))
catch
family = string(arg)
end
elseif typeof(arg) <: Integer
pointsize = arg
elseif typeof(arg) <: Real
rotation = convert(Float64, arg)
else
warn("Unused font arg: $arg ($(typeof(arg)))")
end
end
Font(family, pointsize, halign, valign, rotation, color)
end
"Wrap a string with font info"
immutable PlotText
str::@compat(AbstractString)
font::Font
end
PlotText(str) = PlotText(string(str), font())
function text(str, args...)
PlotText(string(str), font(args...))
end
# -----------------------------------------------------------------------
immutable Stroke
width
color
alpha
style
end
function stroke(args...; alpha = nothing)
# defaults
# width = 1
# color = colorant"black"
# style = :solid
width = nothing
color = nothing
style = nothing
for arg in args
T = typeof(arg)
# if arg in _allStyles
if allStyles(arg)
style = arg
elseif T <: Colorant
color = arg
elseif T <: @compat Union{Symbol,AbstractString}
try
color = parse(Colorant, string(arg))
end
# 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)))")
end
end
Stroke(width, color, alpha, style)
end
immutable Brush
size # fillrange, markersize, or any other sizey attribute
color
alpha
end
function brush(args...; alpha = nothing)
# defaults
# sz = 1
# color = colorant"black"
size = nothing
color = nothing
for arg in args
T = typeof(arg)
if T <: Colorant
color = arg
elseif T <: @compat Union{Symbol,AbstractString}
try
color = parse(Colorant, string(arg))
end
# 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)))")
end
end
Brush(size, color, alpha)
end
# -----------------------------------------------------------------------
"type which represents z-values for colors and sizes (and anything else that might come up)"
immutable ZValues
values::Vector{Float64}
zrange::Tuple{Float64,Float64}
end
function zvalues{T<:Real}(values::AVec{T}, zrange::Tuple{T,T} = (minimum(values), maximum(values)))
ZValues(collect(float(values)), map(Float64, zrange))
end
# -----------------------------------------------------------------------
abstract AbstractSurface
"represents a contour or surface mesh"
immutable Surface{M<:AMat} <: AbstractSurface
# x::AVec
# y::AVec
surf::M
end
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}
open::T
high::T
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
+63
View File
@@ -0,0 +1,63 @@
# TODO:
"""
- load Contours.jl similar to DataFrames
- method to build grid from x/y/z vectors
- method to wrap contours creation
- method to plot contours as custom shapes (TODO: create Stroke and Fill types and add markerstroke/markerfill args)
"""
# # ----------------------------------------------------------
# # ----------------------------------------------------------
# immutable Vertex
# x::Float64
# y::Float64
# z::Float64
# end
# immutable Edge
# v::Vertex
# u::Vertex
# end
# # ----------------------------------------------------------
# # one rectangle's z-values and the center vertex
# # z is ordered: topleft, topright, bottomright, bottomleft
# immutable GridRect
# z::Vector{Float64}
# center::Vertex
# data::Vector{Vertex}
# end
# type Grid
# xs::Vector{Float64}
# ys::Vector{Float64}
# rects::Matrix{GridRect}
# end
# function splitDataEvenly(v::AbstractVector{Float64}, n::Int)
# vs = sort(v)
# end
# # the goal here is to create the vertical and horizontal partitions
# # which define the grid, so that the data is somewhat evenly split
# function bucketData(x, y, z)
# end
# function buildGrid(x, y, z)
# # create
# end
-134
View File
@@ -1,134 +0,0 @@
# https://github.com/dcjones/Gadfly.jl
immutable GadflyPackage <: PlottingPackage end
# create a blank Gadfly.Plot object
function plot(pkg::GadflyPackage; kw...)
@eval import DataFrames
plt = Gadfly.Plot()
plt.mapping = Dict()
plt.data_source = DataFrames.DataFrame()
plt.layers = plt.layers[1:0]
# add the title, axis labels, and theme
d = Dict(kw)
plt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
Gadfly.Guide.ylabel(d[:ylabel]),
Gadfly.Guide.title(d[:title])]
# add the legend?
if d[:legend]
unshift!(plt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
end
plt.theme = Gadfly.Theme(background_color = (haskey(d, :background_color) ? d[:background_color] : colorant"white"))
Plot(plt, pkg, 0)
end
function getGeomFromLineType(linetype::Symbol, nbins::Int)
linetype == :line && return Gadfly.Geom.line
linetype == :dots && return Gadfly.Geom.point
linetype == :bar && return Gadfly.Geom.bar
linetype == :step && return Gadfly.Geom.step
linetype == :hist && return Gadfly.Geom.histogram(bincount=nbins)
linetype == :none && return Gadfly.Geom.point # change this? are we usually pairing no line with scatterplots?
linetype == :sticks && return Gadfly.Geom.bar
error("linetype $linetype not currently supported with Gadfly")
end
function getGeoms(linetype::Symbol, marker::Symbol, nbins::Int)
geoms = []
# handle heatmaps (hexbins) specially
if linetype in (:heatmap,:hexbin)
push!(geoms, Gadfly.Geom.hexbin(xbincount=nbins, ybincount=nbins))
else
# for other linetypes, get the correct Geom
push!(geoms, getGeomFromLineType(linetype, nbins))
# for any marker, add Geom.point
if marker != :none
push!(geoms, Gadfly.Geom.point)
end
end
geoms
end
# plot one data series
function plot!(::GadflyPackage, plt::Plot; kw...)
d = Dict(kw)
gfargs = []
# add the Geoms
append!(gfargs, getGeoms(d[:linetype], d[:marker], d[:nbins]))
# set color, line width, and point size
theme = Gadfly.Theme(default_color = d[:color],
line_width = d[:width] * Gadfly.px,
default_point_size = d[:markersize] * Gadfly.px)
push!(gfargs, theme)
# add a regression line?
if d[:reg]
push!(gfargs, Gadfly.Geom.smooth(method=:lm))
end
# for histograms, set x=y
x = d[d[:linetype] == :hist ? :y : :x]
# add to the legend
if length(plt.o.guides) > 0 && isa(plt.o.guides[1], Gadfly.Guide.ManualColorKey)
push!(plt.o.guides[1].labels, d[:label])
push!(plt.o.guides[1].colors, d[:color])
end
if d[:axis] != :left
warn("Gadly only supports one y axis")
end
# add the layer to the Gadfly.Plot
prepend!(plt.o.layers, Gadfly.layer(unique(gfargs)...; x = x, y = d[:y]))
plt
end
function Base.display(::GadflyPackage, plt::Plot)
display(plt.o)
end
# -------------------------------
function savepng(::GadflyPackage, plt::PlottingObject, fn::String;
w = 6 * Gadfly.inch,
h = 4 * Gadfly.inch)
Gadfly.draw(Gadfly.PNG(fn, w, h), plt.o)
end
# -------------------------------
# # create the underlying object (each backend will do this differently)
# o = buildSubplotObject(plts, pkg, layout)
function buildSubplotObject!(::GadflyPackage, subplt::Subplot)
i = 0
rows = []
for rowcnt in subplt.layout.rowcounts
push!(rows, Gadfly.hstack([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
i += rowcnt
end
subplt.o = Gadfly.vstack(rows...)
end
function Base.display(::GadflyPackage, subplt::Subplot)
display(subplt.o)
end
+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
+151
View File
@@ -0,0 +1,151 @@
defaultOutputFormat(plt::AbstractPlot) = "png"
function png(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "png")
io = open(fn, "w")
writemime(io, MIME("image/png"), plt)
close(io)
end
png(fn::@compat(AbstractString)) = png(current(), fn)
function svg(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "svg")
io = open(fn, "w")
writemime(io, MIME("image/svg+xml"), plt)
close(io)
end
svg(fn::@compat(AbstractString)) = svg(current(), fn)
function pdf(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "pdf")
io = open(fn, "w")
writemime(io, MIME("application/pdf"), plt)
close(io)
end
pdf(fn::@compat(AbstractString)) = pdf(current(), fn)
function ps(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "ps")
io = open(fn, "w")
writemime(io, MIME("application/postscript"), plt)
close(io)
end
ps(fn::@compat(AbstractString)) = ps(current(), fn)
function tex(plt::AbstractPlot, fn::@compat(AbstractString))
fn = addExtension(fn, "tex")
io = open(fn, "w")
writemime(io, MIME("application/x-tex"), plt)
close(io)
end
tex(fn::@compat(AbstractString)) = tex(current(), fn)
# ----------------------------------------------------------------
@compat const _savemap = Dict(
"png" => png,
"svg" => svg,
"pdf" => pdf,
"ps" => ps,
"tex" => tex,
)
function getExtension(fn::@compat(AbstractString))
pieces = split(fn, ".")
length(pieces) > 1 || error("Can't extract file extension: ", fn)
ext = pieces[end]
haskey(_savemap, ext) || error("Invalid file extension: ", fn)
ext
end
function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
try
oldext = getExtension(fn)
if oldext == ext
return fn
else
return "$fn.$ext"
end
catch
return "$fn.$ext"
end
end
function savefig(plt::AbstractPlot, fn::@compat(AbstractString))
# get the extension
local ext
try
ext = getExtension(fn)
catch
# if we couldn't extract the extension, add the default
ext = defaultOutputFormat(plt)
fn = addExtension(fn, ext)
end
# save it
func = get(_savemap, ext) do
error("Unsupported extension $ext with filename ", fn)
end
func(plt, fn)
end
savefig(fn::@compat(AbstractString)) = savefig(current(), fn)
# savepng(args...; kw...) = savepng(current(), args...; kw...)
# savepng(plt::AbstractPlot, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
# ---------------------------------------------------------
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::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
+225 -361
View File
@@ -1,414 +1,278 @@
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 currentPlot()
if isplotnull()
error("No current plot/subplot")
end
get(CURRENT_PLOT.nullableplot)
function current()
if isplotnull()
error("No current plot/subplot")
end
get(CURRENT_PLOT.nullableplot)
end
currentPlot!(plot::PlottingObject) = (CURRENT_PLOT.nullableplot = Nullable(plot))
current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = Nullable(plot))
# ---------------------------------------------------------
Base.string(plt::Plot) = "Plot{$(plt.plotter) n=$(plt.n)}"
Base.string(plt::Plot) = "Plot{$(plt.backend) n=$(plt.n)}"
Base.print(io::IO, plt::Plot) = print(io, string(plt))
Base.show(io::IO, plt::Plot) = print(io, string(plt))
getplot(plt::Plot) = plt
getplotargs(plt::Plot, idx::Int = 1) = plt.plotargs
convertSeriesIndex(plt::Plot, n::Int) = n
# ---------------------------------------------------------
doc"""
The main plot command. Call `plotter!(:module)` to set the current plotting backend.
Commands are converted into the relevant plotting commands for that package:
```
plotter!(:gadfly)
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
plotter!(:qwt)
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the currentPlot
plot!(args...; kw...) # adds to the `currentPlot`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
There are lots of ways to pass in data... just try it and it will likely work as expected.
When you pass in matrices, it splits by columns. See the documentation for more info.
Some keyword arguments you can set:
```
axis # :left or :right
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
label # string or symbol, applies to that line, may go in a legend
width # width of a line
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
markercolor # same choices as `color`
markersize # size of the marker
nbins # number of bins for heatmap/hexbin and histograms
heatmap_c # color cutoffs for Qwt heatmaps
fillto # fillto value for area plots
title # string or symbol, title of the plot
xlabel # string or symbol, label on the bottom (x) axis
ylabel # string or symbol, label on the left (y) axis
yrightlabel # string or symbol, label on the right (y) axis
reg # true or false, add a regression line for each line
size # (Int,Int), resize the enclosing window
pos # (Int,Int), move the enclosing window to this position
windowtitle # string or symbol, set the title of the enclosing windowtitle
screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
show # true or false, show the plot (in case you don't want the window to pop up right away)
```
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
(yes I know it's not gramatically correct, but it's easy to use and implement)
```
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
```
"""
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`
```
# -------------------------
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
When you pass in matrices, it splits by columns. See the documentation for more info.
"""
# this creates a new plot with args/kw and sets it to be the current plot
function plot(args...; kw...)
plt = plot(plotter(); getPlotKeywordArgs(kw, 1, 0)...) # create a new, blank plot
plot!(plt, args...; kw...) # add to it
pkg = backend()
d = 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
delete!(d, :background_color)
plot!(plt, args...; d...) # add to it
end
# this adds to the current plot
# this adds to the current plot, or creates a new plot if none are current
function plot!(args...; kw...)
plot!(currentPlot(), args...; kw...)
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 = KW(kw)
preprocessArgs!(d)
# # increment n if we're going directly to the package's plot method
# if length(args) == 0
# plt.n += 1
# end
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; kw...)
# plot!(plt.plotter, plt, args...; kw...)
dumpdict(d, "After plot! preprocessing")
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
groupby = if haskey(d, :group)
extractGroupArgs(d[:group], args...)
else
nothing
end
# merge plot args
if !haskey(d, :subplot)
for k in keys(_plotDefaults)
if haskey(d, k)
plt.plotargs[k] = d[k]
end
end
# merge!(plt.plotargs, d)
handlePlotColors(plt.backend, plt.plotargs)
end
_add_series(plt, d, groupby, args...)
_add_annotations(plt, d)
warnOnUnsupportedScales(plt.backend, d)
kwList = createKWargsList(plt, args...; kw...)
for (i,d) in enumerate(kwList)
plt.n += 1
plot!(plt.plotter, plt; d...)
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
currentPlot!(plt)
_update_plot_pos_size(plt, d)
# do we want to show it?
d = Dict(kw)
if haskey(d, :show) && d[:show]
display(plt)
end
current(plt)
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
plt
end
# show/update the plot
function Base.display(plt::PlottingObject)
display(plt.plotter, plt)
# 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
doc"Build a vector of dictionaries which hold the keyword arguments for a call to plot!"
# no args... 1 series
function createKWargsList(plt::PlottingObject; kw...)
d = Dict(kw)
@assert haskey(d, :y)
if !haskey(d, :x)
d[:x] = 1:length(d[:y])
end
[getPlotKeywordArgs(d, 1, plt.n + 1)]
function filter_data!(d::KW, idxfilter)
for s in (:x, :y, :z)
d[s] = filter_data(get(d, s, nothing), idxfilter)
end
end
# create one series where y is vectors of numbers
function createKWargsList{T<:Real}(plt::PlottingObject, y::AVec{T}; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d[:x] = 1:length(y)
d[:y] = y
[d]
# 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
# create one series where x/y are vectors of numbers
function createKWargsList{T<:Real,S<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec{S}; kw...)
@assert length(x) == length(y)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d[:x] = x
d[:y] = y
[d]
# --------------------------------------------------------------------
# if x or y are a vector of strings, we should create a list of unique strings,
# and map x/y to be the index of the string... then set the x/y tick labels
function setTicksFromStringVector(d::KW, di::KW, sym::Symbol, ticksym::Symbol)
# if the x or y values are strings, set ticks to the unique values, and x/y to the indices of the ticks
v = di[sym]
isa(v, AbstractArray) || return
T = eltype(v)
if T <: @compat(AbstractString) || (!isempty(T.types) && all(x -> x <: @compat(AbstractString), T.types))
ticks = unique(di[sym])
di[sym] = Int[findnext(ticks, v, 1) for v in di[sym]]
if !haskey(d, ticksym) || d[ticksym] == :auto
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
end
end
end
# create m series, 1 for each column of y
function createKWargsList(plt::PlottingObject, y::AMat; kw...)
n,m = size(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = 1:n
d[:y] = y[:,i]
push!(ret, d)
end
ret
# --------------------------------------------------------------------
_before_add_series(plt::Plot) = nothing
# --------------------------------------------------------------------
# should we update the x/y label given the meta info during input slicing?
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::KW, plotargs::KW, meta, isx::Bool) = nothing
# --------------------------------------------------------------------
annotations(::@compat(Void)) = []
annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v
annotations{X,Y,V}(t::@compat(Tuple{X,Y,V})) = [t]
annotations(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::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)]
end
_add_annotations(plt, anns)
end
end
# create m series, 1 for each column of y
function createKWargsList(plt::PlottingObject, x::AVec, y::AMat; kw...)
n,m = size(y)
@assert length(x) == n
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x
d[:y] = y[:,i]
push!(ret, d)
end
ret
# --------------------------------------------------------------------
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
end
# create m series, 1 for each column of y
function createKWargsList(plt::PlottingObject, x::AMat, y::AMat; kw...)
@assert size(x) == size(y)
n,m = size(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x[:,i]
d[:y] = y[:,i]
push!(ret, d)
end
ret
end
# create 1 series, y = f(x)
function createKWargsList(plt::PlottingObject, x::AVec, f::Function; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d[:x] = x
d[:y] = map(f, x)
[d]
end
# create m series, y = f(x), 1 for each column of x
function createKWargsList(plt::PlottingObject, x::AMat, f::Function; kw...)
n,m = size(x)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x[:,i]
d[:y] = map(f, d[:x])
push!(ret, d)
end
ret
end
# create m series, 1 for each item in y (assumes vectors of something other than numbers... functions? vectors?)
function createKWargsList(plt::PlottingObject, y::AVec; kw...)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = 1:length(y[i])
d[:y] = y[i]
push!(ret, d)
end
ret
end
function getyvec(x::AVec, y::AVec)
@assert length(x) == length(y)
y
end
getyvec(x::AVec, f::Function) = map(f, x)
getyvec(x, y) = error("Couldn't create yvec from types: x ($(typeof(x))), y ($(typeof(y)))")
# same, but given an x to use for all series
function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec; kw...)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x
d[:y] = getyvec(x, y[i])
push!(ret, d)
end
ret
end
# same, but m series of (x[i],y[i])
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec; kw...)
@assert length(x) == length(y)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x[i]
d[:y] = getyvec(x[i], y[i])
push!(ret, d)
end
ret
end
# n empty series
function createKWargsList(plt::PlottingObject, n::Integer; kw...)
ret = []
for i in 1:n
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = zeros(0)
d[:y] = zeros(0)
push!(ret, d)
end
ret
end
# TODO: handle DataFrames (might have NAs!)
# -------------------------
# # most calls should flow through here now... we create a Dict with the keyword args for each series, and plot them
# function plot!(pkg::PlottingPackage, plt::Plot, args...; kw...)
# kwList = createKWargsList(plt, args...; kw...)
# for (i,d) in enumerate(kwList)
# plt.n += 1
# plot!(pkg, plt; d...)
# end
# plt
# end
# -------------------------
# # These methods are various ways to add to an existing plot
# function plot!{T<:Real}(pkg::PlottingPackage, plt::Plot, y::AVec{T}; kw...)
# plt.n += 1
# # plot!(pkg, plt; x = 1:length(y), y = y, getPlotKeywordArgs(kw, 1, plt)...)
# end
# function plot!{T<:Real,S<:Real}(pkg::PlottingPackage, plt::Plot, x::AVec{T}, y::AVec{S}; kw...) # one line (will assert length(x) == length(y))
# @assert length(x) == length(y)
# plt.n += 1
# plot!(pkg, plt; x=x, y=y, getPlotKeywordArgs(kw, 1, plt)...)
# end
# function plot!(pkg::PlottingPackage, plt::Plot, y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
# n,m = size(y)
# for i in 1:m
# plt.n += 1
# plot!(pkg, plt; x = 1:n, y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
# n,m = size(y)
# for i in 1:m
# @assert length(x) == n
# plt.n += 1
# plot!(pkg, plt; x = x, y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
# @assert size(x) == size(y)
# for i in 1:size(x,2)
# plt.n += 1
# plot!(pkg, plt; x = x[:,i], y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, f::Function; kw...) # one line, y = f(x)
# plt.n += 1
# plot!(pkg, plt; x = x, y = map(f,x), getPlotKeywordArgs(kw, 1, plt)...)
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
# for i in 1:size(x,2)
# xi = x[:,i]
# plt.n += 1
# plot!(pkg, plt; x = xi, y = map(f, xi), getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# # function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
# # for i in 1:length(fs)
# # plt.n += 1
# # plot!(pkg, plt; x = x, y = map(fs[i], x), getPlotKeywordArgs(kw, i, plt)...)
# # end
# # plt
# # end
# function plot!(pkg::PlottingPackage, plt::Plot, y::AVec; kw...) # multiple lines, each with x = 1:length(y[i])
# for i in 1:length(y)
# plt.n += 1
# plot!(pkg, plt; x = 1:length(y[i]), y = y[i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!{T<:Real}(pkg::PlottingPackage, plt::Plot, x::AVec{T}, y::AVec; kw...) # multiple lines, will assert length(x) == length(y[i])
# for i in 1:length(y)
# if typeof(y[i]) <: AbstractVector
# @assert length(x) == length(y[i])
# plt.n += 1
# plot!(pkg, plt; x = x, y = y[i], getPlotKeywordArgs(kw, i, plt)...)
# elseif typeof(y[i]) == Function
# plt.n += 1
# plot!(pkg, plt; x = x, y = map(y[i], x), getPlotKeywordArgs(kw, 1, plt)...)
# end
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, y::AVec; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
# @assert length(x) == length(y)
# for i in 1:length(x)
# @assert length(x[i]) == length(y[i])
# plt.n += 1
# plot!(pkg, plt; x = x[i], y = y[i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, n::Integer; kw...) # n lines, all empty (for updating plots)
# for i in 1:n
# plt.n += 1
# plot(pkg, plt, x = zeros(0), y = zeros(0), getPlotKeywordArgs(kw, i, plt)...)
# end
# end
# -------------------------
# # this is the core method... add to a plot object using kwargs, with args already converted into kwargs
# function plot!(pkg::PlottingPackage, plt::Plot; kw...)
# plot!(pl, plt; kw...)
# end
# --------------------------------------------------------------------
-64
View File
@@ -1,64 +0,0 @@
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
Base.display(pkg::PlottingPackage, plt::Plot) = error("display($pkg, plt) is not implemented")
# ---------------------------------------------------------
const AVAILABLE_PACKAGES = [:qwt, :gadfly]
const INITIALIZED_PACKAGES = Set{Symbol}()
type CurrentPackage
sym::Symbol
pkg::PlottingPackage
end
# const CURRENT_PACKAGE = CurrentPackage(:qwt, QwtPackage())
const CURRENT_PACKAGE = CurrentPackage(:gadfly, GadflyPackage())
doc"""
Returns the current plotting package name. Initializes package on first call.
"""
function plotter()
currentPackageSymbol = CURRENT_PACKAGE.sym
if !(currentPackageSymbol in INITIALIZED_PACKAGES)
# initialize
print("[Plots.jl] Initializing package: $CURRENT_PACKAGE... ")
if currentPackageSymbol == :qwt
@eval import Qwt
elseif currentPackageSymbol == :gadfly
@eval import Gadfly
else
error("Unknown plotter $currentPackageSymbol. Choose from: $AVAILABLE_PACKAGES")
end
push!(INITIALIZED_PACKAGES, currentPackageSymbol)
println("done.")
end
CURRENT_PACKAGE.pkg
end
doc"""
Set the plot backend. Choose from: :qwt, :gadfly
"""
function plotter!(modname)
# set the PlottingPackage
if modname == :qwt
CURRENT_PACKAGE.pkg = QwtPackage()
elseif modname == :gadfly
CURRENT_PACKAGE.pkg = GadflyPackage()
else
error("Unknown plotter $modname. Choose from: $AVAILABLE_PACKAGES")
end
# update the symbol
CURRENT_PACKAGE.sym = modname
# return the package
CURRENT_PACKAGE.pkg
end
-67
View File
@@ -1,67 +0,0 @@
# https://github.com/tbreloff/Qwt.jl
immutable QwtPackage <: PlottingPackage end
# -------------------------------
function adjustQwtKeywords(iscreating::Bool; kw...)
d = Dict(kw)
d[:heatmap_n] = d[:nbins]
if d[:linetype] == :hexbin
d[:linetype] = :heatmap
elseif d[:linetype] == :dots
d[:linetype] = :none
d[:marker] = :hexagon
elseif !iscreating && d[:linetype] == :bar
return barHack(; kw...)
elseif !iscreating && d[:linetype] == :hist
return barHack(; histogramHack(; kw...)...)
end
d
end
function plot(pkg::QwtPackage; kw...)
kw = adjustQwtKeywords(true; kw...)
plt = Plot(Qwt.plot(zeros(0,0); kw..., show=false), pkg, 0)
plt
end
function plot!(::QwtPackage, plt::Plot; kw...)
kw = adjustQwtKeywords(false; kw...)
Qwt.oplot(plt.o; kw...)
end
function Base.display(::QwtPackage, plt::Plot)
Qwt.refresh(plt.o)
Qwt.showwidget(plt.o)
end
# -------------------------------
savepng(::QwtPackage, plt::PlottingObject, fn::String, args...) = Qwt.savepng(plt.o, fn)
# -------------------------------
# # create the underlying object (each backend will do this differently)
# o = buildSubplotObject(plts, pkg, layout)
function buildSubplotObject!(::QwtPackage, subplt::Subplot)
i = 0
rows = []
for rowcnt in subplt.layout.rowcounts
push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
i += rowcnt
end
subplt.o = Qwt.vsplitter(rows...)
end
function Base.display(::QwtPackage, subplt::Subplot)
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.showwidget(subplt.o)
end
+561
View File
@@ -0,0 +1,561 @@
# 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 AbstractPlot???
abstract PlotRecipe
getRecipeXY(recipe::PlotRecipe) = Float64[], Float64[]
getRecipeArgs(recipe::PlotRecipe) = ()
plot(recipe::PlotRecipe, args...; kw...) = plot(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
plot!(recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
num_series(x::AMat) = size(x,2)
num_series(x) = 1
_apply_recipe(d::KW; kw...) = ()
# if it's not a recipe, just do nothing and return the args
function _apply_recipe(d::KW, args...; issubplot=false, kw...)
if issubplot && !haskey(d, :n) && !haskey(d, :layout)
# put in a sensible default
d[:n] = maximum(map(num_series, args))
end
args
end
# ---------------------------------------------------------------------------
"""
`apply_series_recipe` should take a processed series KW dict and break it up
into component parts. For example, a box plot is made up of `shape` for the
boxes, `path` for the lines, and `scatter` for the outliers.
Returns a Vector{KW}.
"""
apply_series_recipe(d::KW, lt) = KW[d]
# ---------------------------------------------------------------------------
# Box Plot
const _box_halfwidth = 0.4
function apply_series_recipe(d::KW, ::Type{Val{:box}})
# dumpdict(d, "box before", true)
# TODO: add scatter series with outliers
# create a list of shapes, where each shape is a single boxplot
shapes = Shape[]
d[:linetype] = :shape
groupby = extractGroupArgs(d[:x])
for (i, glabel) in enumerate(groupby.groupLabels)
# filter y values, then compute quantiles
q1,q2,q3,q4,q5 = quantile(d[:y][groupby.groupIds[i]], linspace(0,1,5))
# make the shape
l, m, r = i - _box_halfwidth, i, i + _box_halfwidth
xcoords = [
m, l, r, m, m, NaN, # lower T
l, l, r, r, l, NaN, # lower box
l, l, r, r, l, NaN, # upper box
m, l, r, m, m # upper T
]
ycoords = [
q1, q1, q1, q1, q2, NaN, # lower T
q2, q3, q3, q2, q2, NaN, # lower box
q4, q3, q3, q4, q4, NaN, # upper box
q5, q5, q5, q5, q4, NaN, # upper T
]
push!(shapes, Shape(xcoords, ycoords))
end
d[:x], d[:y] = shape_coords(shapes)
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
KW[d]
end
# ---------------------------------------------------------------------------
# Violin Plot
# if the user has KernelDensity installed, use this for violin plots.
# otherwise, just use a histogram
try
Pkg.installed("KernelDensity")
import KernelDensity
# warn("using KD for violin")
@eval function violin_coords(y)
kd = KernelDensity.kde(y, npoints = 30)
kd.density, kd.x
end
catch
# warn("using hist for violin")
@eval function violin_coords(y)
edges, widths = hist(y, 20)
centers = 0.5 * (edges[1:end-1] + edges[2:end])
ymin, ymax = extrema(y)
vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
end
end
function apply_series_recipe(d::KW, ::Type{Val{:violin}})
# dumpdict(d, "box before", true)
# TODO: add scatter series with outliers
# create a list of shapes, where each shape is a single boxplot
shapes = Shape[]
d[:linetype] = :shape
groupby = extractGroupArgs(d[:x])
for (i, glabel) in enumerate(groupby.groupLabels)
# get the edges and widths
y = d[:y][groupby.groupIds[i]]
widths, centers = violin_coords(y)
# normalize
widths = _box_halfwidth * widths / maximum(widths)
# make the violin
xcoords = vcat(widths, -reverse(widths)) + i
ycoords = vcat(centers, reverse(centers))
push!(shapes, Shape(xcoords, ycoords))
end
d[:x], d[:y] = shape_coords(shapes)
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
KW[d]
end
# ---------------------------------------------------------------------------
# Error Bars
function error_style!(d::KW)
d[:linetype] = :path
d[:linecolor] = d[:markerstrokecolor]
d[:linewidth] = d[:markerstrokewidth]
d[:label] = ""
end
# if we're passed a tuple of vectors, convert to a vector of tuples
function error_zipit(ebar)
if istuple(ebar)
collect(zip(ebar...))
else
ebar
end
end
function error_coords(xorig, yorig, ebar)
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
x, y = zeros(0), zeros(0)
# for each point, create a line segment from the bottom to the top of the errorbar
for i = 1:max(length(xorig), length(yorig))
xi = get_mod(xorig, i)
yi = get_mod(yorig, i)
ebi = get_mod(ebar, i)
nanappend!(x, [xi, xi])
e1, e2 = if istuple(ebi)
first(ebi), last(ebi)
elseif isscalar(ebi)
ebi, ebi
else
error("unexpected ebi type $(typeof(ebi)) for errorbar: $ebi")
end
nanappend!(y, [yi - e1, yi + e2])
end
x, y
end
# we will create a series of path segments, where each point represents one
# side of an errorbar
function apply_series_recipe(d::KW, ::Type{Val{:yerror}})
error_style!(d)
d[:markershape] = :hline
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
KW[d]
end
function apply_series_recipe(d::KW, ::Type{Val{:xerror}})
error_style!(d)
d[:markershape] = :vline
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
KW[d]
end
# ---------------------------------------------------------------------------
# quiver
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
# 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
#
# # 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}(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"
function abline!(plt::Plot, a, b; kw...)
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
end
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
+270 -63
View File
@@ -1,52 +1,31 @@
# create a layout directly
SubplotLayout(rowcounts::AbstractVector{Int}) = SubplotLayout(sum(rowcounts), rowcounts)
# create a layout given counts... nr/nc == -1 implies we figure out a good number automatically
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
# create the rowcounts vector
i = 0
rowcounts = Int[]
for r in 1:nr
cnt = min(nc, numplts - i)
push!(rowcounts, cnt)
i += cnt
end
SubplotLayout(numplts, rowcounts)
end
Base.length(layout::SubplotLayout) = layout.numplts
# ------------------------------------------------------------
Base.string(subplt::Subplot) = "Subplot{$(subplt.plotter) p=$(subplt.p) n=$(subplt.n)}"
Base.string(subplt::Subplot) = "Subplot{$(subplt.backend) p=$(subplt.p) n=$(subplt.n)}"
Base.print(io::IO, subplt::Subplot) = print(io, string(subplt))
Base.show(io::IO, subplt::Subplot) = print(io, string(subplt))
getplot(subplt::Subplot) = subplt.plts[mod1(subplt.n, subplt.p)]
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)
# ------------------------------------------------------------
function validateSubplotSupported()
if !subplotSupported()
error(CURRENT_BACKEND.sym, " does not support the subplot/subplot! commands at this time. Try one of: ", join(filter(pkg->subplotSupported(_backend_instance(pkg)), backends()),", "))
end
end
doc"""
"""
Create a series of plots:
```
y = rand(100,3)
@@ -54,40 +33,154 @@ Create a series of plots:
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows to 1 (so there are n columns)
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns to 1 (so there are n rows)
subplot(y; layout = [1, 2]) # explicit layout by row... plot #1 goes by itself in the first row, plots 2 and 3 split the 2nd row (note the n kw is unnecessary)
subplot(plts, n; nr = -1, nc = -1) # build a layout from existing plots
subplot(plts, layout) # build a layout from existing plots
```
"""
function subplot(args...; kw...)
d = Dict(kw)
validateSubplotSupported()
d = KW(kw)
preprocessArgs!(d)
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; kw..., issubplot=true)
# figure out the layout
if haskey(d, :layout)
layout = SubplotLayout(d[:layout])
layoutarg = get(d, :layout, nothing)
if layoutarg != nothing
layout = subplotlayout(layoutarg)
else
if !haskey(d, :n)
n = get(d, :n, -1)
if n < 0
error("You must specify either layout or n when creating a subplot: ", d)
end
layout = SubplotLayout(d[:n], get(d, :nr, -1), get(d, :nc, -1))
layout = subplotlayout(n, get(d, :nr, -1), get(d, :nc, -1))
end
# initialize the individual plots
pkg = plotter()
kw0 = getPlotKeywordArgs(kw, 1, 0)
plts = Plot[plot(pkg; kw0..., show=false) for i in 1:length(layout)]
pkg = backend()
plts = Plot{typeof(pkg)}[]
for i in 1:length(layout)
di = getPlotArgs(pkg, d, i)
di[:subplot] = true
dumpdict(di, "Plot args (subplot $i)")
push!(plts, _create_plot(pkg, di))
end
# create the object and do the plotting
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout)
subplot!(subplt, args...; kw...)
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout, d, false, false, false, (r,c) -> (nothing,nothing))
subplot!(subplt, args...; d...)
subplt
end
doc"""
# ------------------------------------------------------------------------------------------------
# NOTE: for the subplot calls building from existing plots, we need the first plot to be separate to ensure dispatch calls this instead of the more general subplot(args...; kw...)
# grid layout
function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...)
d = 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, KW(kw))
end
# this will be called internally
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, KW(), false, false, false, (r,c) -> (nothing,nothing))
_preprocess_subplot(subplt, d)
_postprocess_subplot(subplt, d)
subplt
end
# TODO: hcat/vcat subplots and plots together arbitrarily
# ------------------------------------------------------------------------------------------------
function _preprocess_subplot(subplt::Subplot, d::KW, args = ())
validateSubplotSupported()
preprocessArgs!(d)
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; d..., issubplot=true)
dumpdict(d, "After subplot! preprocessing")
# get the full plotargs, overriding any new settings
# TODO: subplt.plotargs should probably be merged sooner and actually used
# for color selection, etc. (i.e. if we overwrite the subplot palettes to [:heat :rainbow])
# then we need to overwrite plt[1].plotargs[:color_palette] to :heat before it's actually used
# for color selection!
# first merge the new args into the subplot's plotargs. then process the plot args and merge
# those into the plot's plotargs. (example... `palette = [:blues :reds]` goes into subplt.plotargs,
# then the ColorGradient for :blues/:reds is merged into plot 1/2 plotargs, which is then used for color selection)
for i in 1:length(subplt.layout)
subplt.plts[i].plotargs = getPlotArgs(backend(), merge(subplt.plts[i].plotargs, d), i)
end
merge!(subplt.plotargs, d)
# process links. TODO: extract to separate function
for s in (:linkx, :linky, :linkfunc)
if haskey(d, s)
setfield!(subplt, s, d[s])
delete!(d, s)
end
end
args
end
function _postprocess_subplot(subplt::Subplot, d::KW)
# init (after plot creation)
if !subplt.initialized
subplt.initialized = _create_subplot(subplt, false)
end
# add title, axis labels, ticks, etc
for (i,plt) in enumerate(subplt.plts)
di = plt.plotargs
dumpdict(di, "Updating sp $i")
_update_plot(plt, di)
end
_update_plot_pos_size(subplt, d)
# handle links
subplt.linkx && link_axis(subplt, true)
subplt.linky && link_axis(subplt, false)
# set this to be current
current(subplt)
end
# ------------------------------------------------------------------------------------------------
"""
Adds to a subplot.
"""
# current subplot
function subplot!(args...; kw...)
subplot!(currentPlot(), args...; kw...)
validateSubplotSupported()
subplot!(current(), args...; kw...)
end
@@ -99,26 +192,140 @@ end
# # this adds to a specific subplot... most plot commands will flow through here
function subplot!(subplt::Subplot, args...; kw...)
kwList = createKWargsList(subplt, args...; kw...)
for (i,d) in enumerate(kwList)
subplt.n += 1
plt = getplot(subplt) # get the Plot object where this series will be drawn
plot!(plt; d...)
end
# validateSubplotSupported()
d = KW(kw)
args = _preprocess_subplot(subplt, d, args)
# create the underlying object (each backend will do this differently)
buildSubplotObject!(subplt.plotter, subplt)
# note: we call it once before doing the individual plots, and once after
# this is because some backends need to set up the subplots and then plot,
# and others need to do it the other way around
if !subplt.initialized
subplt.initialized = _create_subplot(subplt, true)
end
# set this to be current
currentPlot!(subplt)
groupby = if haskey(d, :group)
extractGroupArgs(d[:group], args...)
else
nothing
end
# do we want to show it?
d = Dict(kw)
_add_series_subplot(subplt, d, groupby, args...)
_postprocess_subplot(subplt, d)
# show it automatically?
if haskey(d, :show) && d[:show]
draw(subplt)
gui()
end
subplt
end
# not allowed:
function plot!(subplt::Subplot, args...; kw...)
error("Can't call plot! on a Subplot!")
end
# given a fully processed KW, add the series to the Plot
function _add_series_subplot(plt::Plot, d::KW)
setTicksFromStringVector(d, d, :x, :xticks)
setTicksFromStringVector(d, d, :y, :yticks)
# 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
# --------------------------------------------------------------------
# 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
+44 -17
View File
@@ -2,27 +2,54 @@
typealias AVec AbstractVector
typealias AMat AbstractMatrix
abstract PlottingObject
abstract PlottingPackage
immutable PlotsDisplay <: Display end
type Plot <: PlottingObject
o # the underlying object
plotter::PlottingPackage
n::Int # number of series
abstract AbstractBackend
abstract AbstractPlot{T<:AbstractBackend}
typealias KW Dict{Symbol,Any}
immutable InputWrapper{T}
obj::T
end
wrap{T}(obj::T) = InputWrapper{T}(obj)
Base.isempty(wrapper::InputWrapper) = false
type SubplotLayout
numplts::Int
rowcounts::AbstractVector{Int}
# -----------------------------------------------------------
# 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
# -----------------------------------------------------------
type Subplot <: PlottingObject
o # the underlying object
plts::Vector{Plot} # the individual plots
plotter::PlottingPackage
p::Int # number of plots
n::Int # number of series
layout::SubplotLayout
end
abstract SubplotLayout
# -----------------------------------------------------------
# Subplot
# -----------------------------------------------------------
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
# -----------------------------------------------------------------------
+542 -12
View File
@@ -1,7 +1,7 @@
calcMidpoints(edges::AbstractVector) = Float64[0.5 * (edges[i] + edges[i+1]) for i in 1:length(edges)-1]
doc"Make histogram-like bins of data"
"Make histogram-like bins of data"
function binData(data, nbins)
lo, hi = extrema(data)
edges = collect(linspace(lo, hi, nbins+1))
@@ -14,31 +14,31 @@ function binData(data, nbins)
edges, midpoints, buckets, counts
end
doc"""
"""
A hacky replacement for a histogram when the backend doesn't support histograms directly.
Convert it into a bar chart with the appropriate x/y values.
"""
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
d[:fillto] = d[:fillto] == nothing ? 0.0 : d[:fillto]
d[:fillrange] = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
d
end
doc"""
"""
A hacky replacement for a bar graph when the backend doesn't support bars directly.
Convert it into a line chart with fillto set.
Convert it into a line chart with fillrange set.
"""
function barHack(; kw...)
d = Dict(kw)
d = KW(kw)
midpoints = d[:x]
heights = d[:y]
fillto = d[:fillto] == nothing ? 0.0 : d[:fillto]
fillrange = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
# estimate the edges
dists = diff(midpoints) * 0.5
@@ -59,13 +59,543 @@ function barHack(; kw...)
for i in 1:length(heights)
e1, e2 = edges[i:i+1]
append!(x, [e1, e1, e2, e2])
append!(y, [fillto, heights[i], heights[i], fillto])
append!(y, [fillrange, heights[i], heights[i], fillrange])
end
d[:x] = x
d[:y] = y
d[:linetype] = :line
d[:fillto] = fillto
d[:linetype] = :path
d[:fillrange] = fillrange
d
end
"""
A hacky replacement for a sticks graph when the backend doesn't support sticks directly.
Convert it into a line chart that traces the sticks, and a scatter that sets markers at the points.
"""
function sticksHack(; kw...)
dLine = KW(kw)
dScatter = copy(dLine)
# these are the line vertices
x = Float64[]
y = Float64[]
fillrange = dLine[:fillrange] == nothing ? 0.0 : dLine[:fillrange]
# calculate the vertices
yScatter = dScatter[:y]
for (i,xi) in enumerate(dScatter[:x])
yi = yScatter[i]
for j in 1:3 push!(x, xi) end
append!(y, [fillrange, yScatter[i], fillrange])
end
# change the line args
dLine[:x] = x
dLine[:y] = y
dLine[:linetype] = :path
dLine[:markershape] = :none
dLine[:fillrange] = nothing
# change the scatter args
dScatter[:linetype] = :none
dLine, dScatter
end
function regressionXY(x, y)
# regress
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
# make a line segment
regx = [minimum(x), maximum(x)]
regy = β * regx + α
regx, regy
end
# ---------------------------------------------------------------
# ------------------------------------------------------------------------------------
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
makevec(v::AVec) = v
makevec{T}(v::T) = T[v]
"duplicate a single value, or pass the 2-tuple through"
maketuple(x::Real) = (x,x)
maketuple{T,S}(x::@compat(Tuple{T,S})) = x
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
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)
try
e1, e2 = extrema(x)
lims[1] = min(lims[1], e1)
lims[2] = max(lims[2], e2)
# catch err
# warn(err)
end
nothing
end
# if the type exists in a list, replace the first occurence. otherwise add it to the end
function addOrReplace(v::AbstractVector, t::DataType, args...; kw...)
for (i,vi) in enumerate(v)
if isa(vi, t)
v[i] = t(args...; kw...)
return
end
end
push!(v, t(args...; kw...))
return
end
function replaceType(vec, val)
filter!(x -> !isa(x, typeof(val)), vec)
push!(vec, val)
end
function replaceAliases!(d::KW, aliases::KW)
ks = collect(keys(d))
for k in ks
if haskey(aliases, k)
d[aliases[k]] = d[k]
delete!(d, k)
end
end
end
createSegments(z) = collect(repmat(z',2,1))[2:end]
Base.first(c::Colorant) = c
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...)
for r in 2:size(y,1)
y[r,:] = 0.95 * y[r-1,:] + randn(size(y,2))'
end
y
end
isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited
isatom() = isdefined(Main, :Atom) && Main.Atom.isconnected()
istuple(::Tuple) = true
istuple(::Any) = false
isvector(::AVec) = true
isvector(::Any) = false
ismatrix(::AMat) = true
ismatrix(::Any) = false
isscalar(::Real) = true
isscalar(::Any) = false
isvertical(d::KW) = get(d, :orientation, :vertical) in (:vertical, :v, :vert)
# ticksType{T<:Real,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
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
axis_symbol(letter, postfix) = symbol(letter * postfix)
axis_symbols(letter, postfix...) = map(s -> axis_symbol(letter, s), postfix)
Base.convert{T<:Real}(::Type{Vector{T}}, rng::Range{T}) = T[x for x in rng]
Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]
Base.merge(a::AbstractVector, b::AbstractVector) = sort(unique(vcat(a,b)))
nanpush!(a::AbstractVector, b) = (push!(a, NaN); push!(a, b))
nanappend!(a::AbstractVector, b) = (push!(a, NaN); append!(a, b))
# given an array of discrete values, turn it into an array of indices of the unique values
# returns the array of indices (znew) and a vector of unique values (vals)
function indices_and_unique_values(z::AbstractArray)
vals = sort(unique(z))
vmap = Dict([(v,i) for (i,v) in enumerate(vals)])
newz = map(zi -> vmap[zi], z)
newz, vals
end
# ---------------------------------------------------------------
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:
```
with(:gadfly, size=(400,400), type=:hist) do
plot(rand(10))
plot(rand(10))
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
olddefs = KW()
for k in keys(newdefs)
olddefs[k] = default(k)
end
# save the backend
if CURRENT_BACKEND.sym == :none
pickDefaultBackend()
end
oldbackend = CURRENT_BACKEND.sym
for arg in args
# change backend?
if arg in backends()
backend(arg)
end
# # TODO: generalize this strategy to allow args as much as possible
# # as in: with(:gadfly, :scatter, :legend, :grid) do; ...; end
# # TODO: can we generalize this enough to also do something similar in the plot commands??
# k = :linetype
# if arg in _allTypes
# olddefs[k] = default(k)
# newdefs[k] = arg
# elseif haskey(_typeAliases, arg)
# olddefs[k] = default(k)
# newdefs[k] = _typeAliases[arg]
# end
k = :legend
if arg in (k, :leg)
olddefs[k] = default(k)
newdefs[k] = true
end
k = :grid
if arg == k
olddefs[k] = default(k)
newdefs[k] = true
end
end
# display(olddefs)
# display(newdefs)
# now set all those defaults
default(; newdefs...)
# call the function
ret = f()
# put the defaults back
default(; olddefs...)
# revert the backend
if CURRENT_BACKEND.sym != oldbackend
backend(oldbackend)
end
# return the result of the function
ret
end
# ---------------------------------------------------------------
# ---------------------------------------------------------------
type DebugMode
on::Bool
end
const _debugMode = DebugMode(false)
function debugplots(on = true)
_debugMode.on = on
end
debugshow(x) = show(x)
debugshow(x::AbstractArray) = print(summary(x))
function dumpdict(d::KW, prefix = "", alwaysshow = false)
_debugMode.on || alwaysshow || return
println()
if prefix != ""
println(prefix, ":")
end
for k in sort(collect(keys(d)))
@printf("%14s: ", k)
debugshow(d[k])
println()
end
println()
end
function dumpcallstack()
error() # well... you wanted the stacktrace, didn't you?!?
end
# ---------------------------------------------------------------
# ---------------------------------------------------------------
# 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)
# -------------------------------------------------------
# NOTE: backends should implement the following methods to get/set the x/y/z data objects
tovec(v::AbstractVector) = v
tovec(v::Void) = zeros(0)
function getxy(plt::Plot, i::Integer)
d = plt.seriesargs[i]
tovec(d[:x]), tovec(d[:y])
end
function getxyz(plt::Plot, i::Integer)
d = plt.seriesargs[i]
tovec(d[:x]), tovec(d[:y]), tovec(d[:z])
end
function setxy!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y] = xy
end
function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y], d[:z] = xyz
end
# -------------------------------------------------------
# indexing notation
Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
Base.setindex!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer) = setxy!(plt, xy, i)
Base.setindex!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) = setxyz!(plt, xyz, i)
# -------------------------------------------------------
# push/append for one series
# push value to first series
Base.push!(plt::Plot, y::Real) = push!(plt, 1, y)
Base.push!(plt::Plot, x::Real, y::Real) = push!(plt, 1, x, y)
Base.push!(plt::Plot, x::Real, y::Real, z::Real) = push!(plt, 1, x, y, z)
# y only
function Base.push!(plt::Plot, i::Integer, y::Real)
xdata, ydata = getxy(plt, i)
setxy!(plt, (extendSeriesByOne(xdata), extendSeriesData(ydata, y)), i)
plt
end
function Base.append!(plt::Plot, i::Integer, y::AVec)
xdata, ydata = plt[i]
if !isa(xdata, UnitRange{Int})
error("Expected x is a UnitRange since you're trying to push a y value only")
end
plt[i] = (extendSeriesByOne(xdata, length(y)), extendSeriesData(ydata, y))
plt
end
# x and y
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real)
xdata, ydata = getxy(plt, i)
setxy!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y)), i)
plt
end
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec)
@assert length(x) == length(y)
xdata, ydata = getxy(plt, i)
setxy!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y)), i)
plt
end
# x, y, and z
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real, z::Real)
# @show i, x, y, z
xdata, ydata, zdata = getxyz(plt, i)
# @show xdata, ydata, zdata
setxyz!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y), extendSeriesData(zdata, z)), i)
plt
end
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec, z::AVec)
@assert length(x) == length(y) == length(z)
xdata, ydata, zdata = getxyz(plt, i)
setxyz!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y), extendSeriesData(zdata, z)), i)
plt
end
# tuples
Base.push!{X,Y}(plt::Plot, xy::Tuple{X,Y}) = push!(plt, 1, xy...)
Base.push!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}) = push!(plt, 1, xyz...)
Base.push!{X,Y}(plt::Plot, i::Integer, xy::Tuple{X,Y}) = push!(plt, i, xy...)
Base.push!{X,Y,Z}(plt::Plot, i::Integer, xyz::Tuple{X,Y,Z}) = push!(plt, i, xyz...)
# -------------------------------------------------------
# push/append for all series
# push y[i] to the ith series
function Base.push!(plt::Plot, y::AVec)
ny = length(y)
for i in 1:plt.n
push!(plt, i, y[mod1(i,ny)])
end
plt
end
# push y[i] to the ith series
# same x for each series
function Base.push!(plt::Plot, x::Real, y::AVec)
push!(plt, [x], y)
end
# push (x[i], y[i]) to the ith series
function Base.push!(plt::Plot, x::AVec, y::AVec)
nx = length(x)
ny = length(y)
for i in 1:plt.n
push!(plt, i, x[mod1(i,nx)], y[mod1(i,ny)])
end
plt
end
# push (x[i], y[i], z[i]) to the ith series
function Base.push!(plt::Plot, x::AVec, y::AVec, z::AVec)
nx = length(x)
ny = length(y)
nz = length(z)
for i in 1:plt.n
push!(plt, i, x[mod1(i,nx)], y[mod1(i,ny)], z[mod1(i,nz)])
end
plt
end
# ---------------------------------------------------------------
# ---------------------------------------------------------------
# graphs detailing the features that each backend supports
function supportGraph(allvals, func)
vals = reverse(sort(allvals))
bs = sort(backends())
x = ASCIIString[]
y = ASCIIString[]
for val in vals
for b in bs
supported = func(Plots._backend_instance(b))
if val in supported
push!(x, string(b))
push!(y, string(val))
end
end
end
n = length(vals)
scatter(x, y, m=:rect, ms=10, size=(300,100+18*n), leg=false)
end
supportGraphArgs() = supportGraph(_allArgs, supportedArgs)
supportGraphTypes() = supportGraph(_allTypes, supportedTypes)
supportGraphStyles() = supportGraph(_allStyles, supportedStyles)
supportGraphMarkers() = supportGraph(_allMarkers, supportedMarkers)
supportGraphScales() = supportGraph(_allScales, supportedScales)
supportGraphAxes() = supportGraph(_allAxes, supportedAxes)
function dumpSupportGraphs()
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
supportGraphMarkers, supportGraphScales, supportGraphAxes)
plt = func()
png(joinpath(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)
"Smallest x in plot"
xmin(plt::Plot) = minimum([minimum(d[:x]) for d in plt.seriesargs])
"Largest x in plot"
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))
+16
View File
@@ -0,0 +1,16 @@
julia 0.4
Colors
Reexport
Requires
FactCheck
Cairo
Gadfly
Images
PyPlot
@osx QuartzImageIO
GR
DataFrames
RDatasets
VisualRegressionTests
UnicodePlots
+69
View File
@@ -0,0 +1,69 @@
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"
try
@eval import PyPlot
info("Matplotlib version: $(PyPlot.matplotlib[:__version__])")
end
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
# 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
Plots._debugMode.on = debug
example = ExamplePlots._examples[idx]
info("Testing plot: $pkg:$idx:$(example.header)")
backend(pkg)
backend()
# 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
try
run(`mkdir -p $refdir`)
catch err
display(err)
end
reffn = joinpath(refdir, "ref$idx.png")
# the test
vtest = VisualTest(func, reffn, idx)
test_images(vtest, popup=popup, sigma=sigma, eps=eps)
end
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
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
+58 -60
View File
@@ -1,78 +1,76 @@
module PlotsTests
using Plots
using FactCheck
include("imgcomp.jl")
# don't actually show the plots
plotDefault!(:show, false)
srand(1234)
default(show=false, reuse=true)
img_eps = 5e-2
facts("Gadfly") do
@fact gadfly() --> Plots.GadflyBackend()
@fact backend() --> Plots.GadflyBackend()
# note: we wrap in a try block so that the tests only run if we have the backend installed
try
Pkg.installed("Gadfly")
facts("Gadfly") do
@fact plotter!(:gadfly) --> Plots.GadflyPackage()
@fact plotter() --> Plots.GadflyPackage()
@fact typeof(plot(1:10)) --> Plot
# plot(x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
@fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
@fact plot(Int[1,2,3], rand(3)) --> not(nothing)
@fact_throws plot(1:5, 1:4)
# plot(x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
@fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
@fact_throws(plot!(rand(10), rand(9,2)))
# plot(x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
@fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
@fact_throws plot!(rand(10,3), rand(10,2))
# plot(x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
# plot(x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
# plot(n::Integer; kw...) # n lines, all empty (for updating plots)
end
image_comparison_facts(:gadfly, skip=[4,6,19,23,24,27], eps=img_eps)
end
# note: we wrap in a try block so that the tests only run if we have the backend installed
try
Pkg.installed("Qwt")
facts("Qwt") do
@fact plotter!(:qwt) --> Plots.QwtPackage()
@fact plotter() --> Plots.QwtPackage()
@fact typeof(plot(1:10)) --> Plot
facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend()
# plot(y::AVec; kw...) # one line... x = 1:length(y)
@fact plot(1:10) --> not(nothing)
@fact length(currentPlot().o.lines) --> 1
# plot(x::AVec, f::Function; kw...) # one line, y = f(x)
@fact plot(1:10, sin) --> not(nothing)
@fact currentPlot().o.lines[1].y --> sin(collect(1:10))
# plot(x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
@fact plot(rand(10,2), sin) --> not(nothing)
@fact length(currentPlot().o.lines) --> 2
# plot(y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
@fact plot!(rand(10,2)) --> not(nothing)
@fact length(currentPlot().o.lines) --> 4
# plot(x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
@fact plot(1:10, Function[sin,cos]) --> not(nothing)
@fact currentPlot().o.lines[1].y --> sin(collect(1:10))
@fact currentPlot().o.lines[2].y --> cos(collect(1:10))
# plot(y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
@fact plot([11:20 ; rand(10)]) --> not(nothing)
@fact currentPlot().o.lines[1].x[4] --> 4
@fact currentPlot().o.lines[1].y[4] --> 14
end
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
-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
+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)