Compare commits

..

198 Commits

Author SHA1 Message Date
Thomas Breloff 0cc15102fb NEWS and bump version 2016-09-21 11:18:26 -04:00
Thomas Breloff 22aaefd1a0 _apply_type_recipe fix 2016-09-20 16:40:49 -04:00
Thomas Breloff 8638626ff5 Formatted and elementwise type recipes 2016-09-20 16:23:59 -04:00
Thomas Breloff 24fa5c8cbe glvisualize 2016-09-20 12:53:32 -04:00
Thomas Breloff 90d054c2ec glvisualize fixes; limit ticks to amin/amax closes #495 2016-09-20 12:05:16 -04:00
Thomas Breloff 555145a650 added pixel marker for pyplot 2016-09-19 22:23:35 -04:00
Thomas Breloff ec17d557a7 add imperfect shape support to unicodeplots; closes #490 2016-09-16 16:22:11 -04:00
Tom Breloff 792df32e69 Merge pull request #491 from KristofferC/patch-1
stop double adding style options to the axis in pgfplots
2016-09-16 11:12:55 -04:00
Kristoffer Carlsson adc96917fc stop double adding style options to the axis in pgfplots 2016-09-16 08:48:46 +02:00
Tom Breloff 9b0e35b4b1 Merge pull request #478 from tbreloff/sd/dev
text rotation, move commands into module, is_marker_supported
2016-09-15 11:13:39 -04:00
Simon 9d652c8fe2 remove module and still don't overwrite scatter 2016-09-15 16:05:21 +02:00
Thomas Breloff 1be5794e05 Merge remote-tracking branch 'origin/master' into dev 2016-09-15 09:12:17 -04:00
Tom Breloff 0e561e1cd6 Merge pull request #488 from KristofferC/kc/fix_ticks
fix ticks for pgfplots
2016-09-15 09:11:24 -04:00
Kristoffer Carlsson 4475c38140 fix ticks for pgfplots 2016-09-15 14:58:28 +02:00
Tom Breloff cf02408620 Merge pull request #483 from sivark/pull-request/9c317b8c
gif() now accepts an argument specifying number of times to loop
2016-09-15 08:52:58 -04:00
Thomas Breloff 87ab1d9ca4 added colorbar_title attr and pyplot impl 2016-09-13 12:50:28 -04:00
Sivaramakrishnan Swaminathan 9c317b8c3f gif() now accepts an argument specifying number of times to loop
Note that GIF playback might have some variability depending on the
player used: https://github.com/ImageMagick/ImageMagick/issues/263
2016-09-13 10:12:44 -04:00
Thomas Breloff ea18b11475 pyplot font family 2016-09-12 19:34:34 -04:00
Thomas Breloff 17afa11723 plotly_data for Surface; improve add_backend(:plotlyjs) 2016-09-09 22:37:55 -04:00
Simon fc660e3486 correctly implement is_marker_supported 2016-09-09 12:34:24 +02:00
Simon dbc12bb9ba add rotations and move plotting commands to module 2016-09-09 12:34:24 +02:00
Simon 2c1f58e773 adhere to new scale primitive api 2016-09-09 12:34:24 +02:00
Thomas Breloff 2bde412fa3 xlims/ylims/zlims convenience methods 2016-09-07 12:22:06 -04:00
Thomas Breloff 03a9fc3419 is_supported refactor; removed deprecated backends 2016-09-06 14:01:39 -04:00
Tom Breloff 2ca7324a45 Merge pull request #474 from KristofferC/patch-1
forget plot if no label is provided
2016-09-06 10:00:04 -04:00
Kristoffer Carlsson cf381880a5 forget plot if no label is provided 2016-09-06 15:48:27 +02:00
Thomas Breloff 12230f1fc9 Merge remote-tracking branch 'origin/master' into dev 2016-09-05 21:35:23 -04:00
Tom Breloff 6145214734 Merge pull request #471 from fredrikekre/fe/kw
Puts some values in the correct spot for PGFPlot
2016-09-05 08:28:04 -04:00
Tom Breloff 412801821f Merge pull request #472 from jheinen/dev
gr: added support for aspect_ratio
2016-09-05 08:26:27 -04:00
Josef Heinen c65c7ae252 gr: added support for aspect_ratio 2016-09-05 13:23:43 +02:00
Fredrik Ekre c3511005c7 put values in correct spot 2016-09-05 11:29:46 +02:00
Thomas Breloff 31ea149820 glvisualize: checkout packages in add_backend 2016-09-01 21:36:51 -04:00
Thomas Breloff a0aa702725 glvisualize: checkout packages in add_backend 2016-09-01 21:26:28 -04:00
Thomas Breloff ac1e82075c revert change to axes.jl 2016-09-01 21:10:23 -04:00
Tom Breloff 65649ade14 Merge pull request #469 from jheinen/dev
Added support for clims kwarg
2016-09-01 21:00:11 -04:00
Tom Breloff 34135f8e6b Merge pull request #374 from SimonDanisch/dev
glvisualize backend
2016-09-01 20:59:13 -04:00
Simon 31c8a04856 fix api change 2016-09-01 12:11:35 +02:00
Josef Heinen 234f9b202e Added support for clims kwarg 2016-09-01 11:59:28 +02:00
Thomas Breloff 4035c0bbe0 allow integers for colors, maps to palette index 2016-08-31 22:34:41 -04:00
Thomas Breloff 03a228fbe7 centering in bbox 2016-08-31 11:56:20 -04:00
Thomas Breloff 6e3de26477 backend_object; pgfplots _update_plot_object fix 2016-08-29 09:18:08 -04:00
Thomas Breloff d892c57596 Atom fixes 2016-08-26 18:05:37 -04:00
Thomas Breloff 576ed7b7b3 Atom: add back PLOTS_USE_ATOM_PLOTPANE; initialize backend when backend is set and removed the println 2016-08-26 17:46:12 -04:00
Thomas Breloff 966dede6e8 Atom PlotPane finally working 2016-08-26 16:14:05 -04:00
Simon af32590314 fix hover cam and title pos 2016-08-26 16:48:55 +02:00
Simon 475eb8f263 add GLVisualize backend 2016-08-26 15:11:47 +02:00
Thomas Breloff 8a40ac8991 added Volume with recipe, conversion, expand_extrema, and :volume type 2016-08-25 14:30:08 -04:00
Thomas Breloff 3335bf21fa bump version and NEWS 2016-08-25 13:04:58 -04:00
Thomas Breloff 011f51fbd9 recipe postprocessing for aliases and magic args 2016-08-19 19:04:10 -04:00
Tom Breloff 333714911c Merge pull request #452 from TotalVerb/master
Remove unnecessary `_print` extension
2016-08-19 15:49:09 -04:00
Tom Breloff f165423527 Merge pull request #453 from maximsch2/master
add DateTime recepie
2016-08-19 15:48:56 -04:00
Maxim Grechkin 428fbb9cb9 add DateTime recepie 2016-08-19 12:43:30 -07:00
Thomas Breloff 342f9c60b2 improved twinx 2016-08-19 09:59:50 -04:00
Thomas Breloff e13070cd6d working on twinx 2016-08-19 02:22:12 -04:00
Thomas Breloff 76bb6bab96 gr support for mirror; switch Axis.sp for sps; fixes to inset sp setup and linking 2016-08-19 01:55:55 -04:00
Fengyang Wang ff7b147888 Remove unnecessary _print extension 2016-08-19 01:40:20 -04:00
Thomas Breloff 55a598b796 fix mirror default 2016-08-18 21:23:50 -04:00
Thomas Breloff f63eac9a73 added mirror and implemented in pyplot 2016-08-18 21:11:03 -04:00
Thomas Breloff 8d7c4cd108 type recipe for Dates; closes #451 2016-08-18 17:33:26 -04:00
Thomas Breloff 99f608defb bar fix: reset orientation; layout fixes 2016-08-18 17:33:26 -04:00
Thomas Breloff 34596ada49 orientation extrema fix 2016-08-18 17:33:26 -04:00
Thomas Breloff 60c8f6f578 atom fix; add_backend for pyplot/plotly/plotlyjs; closes #449 2016-08-18 17:33:26 -04:00
Tom Breloff 959da6c5ae Merge pull request #450 from anriseth/anriseth/saveeps
Add eps to savefig
2016-08-18 17:32:53 -04:00
Asbjørn Nilsen Riseth 5c127f945f Add eps to savefig 2016-08-18 15:51:51 +01:00
Thomas Breloff 74ec733444 check for empty inputs; closes #448 2016-08-16 09:47:14 -04:00
Thomas Breloff f560884436 _plotly_js_path fix 2016-08-12 11:02:14 -04:00
Thomas Breloff f68f82e3f4 gr: fix image and alpha; switch transpose_z to use permutedims 2016-08-12 10:30:53 -04:00
Tom Breloff 56bd8006bc Merge pull request #446 from tkelman/patch-1
another Pkg.dir -> dirname(@__FILE__) replacement
2016-08-12 10:23:24 -04:00
Tony Kelman ca062c3ce2 another Pkg.dir -> dirname(@__FILE__) replacement
ref #425
2016-08-12 02:46:33 -07:00
Thomas Breloff 90aba32ca5 fix for spurious scale warnings; closes #444 2016-08-11 13:11:22 -04:00
Thomas Breloff e164a8274a fix pyplot heatmap; closes #388 2016-08-10 14:32:54 -04:00
Thomas Breloff 23b25d4954 fixed rotate 2016-08-10 09:42:21 -04:00
Patrick Kofod Mogensen bc7c8993c0 Support *ticks in PGFplots. 2016-08-10 09:41:43 -04:00
Thomas Breloff 9db7de8373 added skinny markers + and x; closes #436 2016-08-09 17:09:53 -04:00
Thomas Breloff f8fddb49e2 fix for sticks default fillrange for log scale; closes #435 2016-08-09 16:38:32 -04:00
Thomas Breloff 991b2e7ca5 fix clims for line_z in pyplot; closes #395 2016-08-09 16:28:40 -04:00
Thomas Breloff 54cd96e5e0 handle axis=nothing; closes #428 2016-08-09 15:42:09 -04:00
Thomas Breloff 599f91860a allow failures: nightly 2016-08-09 15:03:29 -04:00
Thomas Breloff d59564999c Atom inline display 2016-08-09 13:15:04 -04:00
Tom Breloff 5a87adaa7f Merge pull request #425 from tkelman/nopkgdir
Use dirname(@__FILE__) instead of Pkg.dir
2016-08-05 16:57:56 -04:00
Tony Kelman 4e6150fe1e use S3 links on appveyor
and add fast-fail snippet
2016-08-02 20:39:49 -07:00
Tony Kelman 553125713d 0.4 no longer supported on master 2016-08-02 20:37:39 -07:00
Tony Kelman 5583d9f315 Use dirname(@__FILE__) instead of Pkg.dir
This allows installing the package elsewhere.

Add testing against 0.5 to Travis - this runs the most
recent RC now, release once final tags are done
2016-08-02 22:52:33 -04:00
Thomas Breloff 20df4967a4 NEWS 2016-08-02 21:24:17 -04:00
Thomas Breloff 6a30435071 gr: added GKS_DOUBLE_BUF to display and removed forgotten debugging statement 2016-08-02 21:04:36 -04:00
Thomas Breloff 5776ff1fdf clear insets after use; closes #416 2016-07-26 15:48:41 -04:00
Thomas Breloff bbb00816f1 axis formatter 2016-07-26 12:34:06 -04:00
Thomas Breloff 3b1c409fd7 reset GR viewport for annotations; fixes #398 2016-07-25 16:06:24 -04:00
Thomas Breloff ee68798de3 add back single function recipe; closes #401 2016-07-25 15:40:28 -04:00
Thomas Breloff 3ae8dc9bd7 fix cycle; fix GR fill 2016-07-25 15:25:50 -04:00
Thomas Breloff e9dca92c11 remove mkdir call in tests; closes #397 2016-07-22 11:18:28 -04:00
Thomas Breloff 98cce223e9 unicodeplots size fix 2016-07-22 09:43:43 -04:00
Thomas Breloff 1d7a470f78 plotly ticks fix 2016-07-21 10:52:06 -04:00
Thomas Breloff 461d5efc4b removed release from travis; push for series; update for subplot 2016-07-20 20:44:28 -04:00
Thomas Breloff 705dbf60fa removed compat; added series update methods 2016-07-20 15:15:40 -04:00
Thomas Breloff bb180ad59a UTF8String to String 2016-07-20 09:40:10 -04:00
Thomas Breloff 463e1bcb90 require julia 0.5-; writemime to show 2016-07-19 16:51:05 -04:00
Thomas Breloff 22ac2df595 bump version; NEWS 2016-07-19 12:13:40 -04:00
Thomas Breloff 73af635be6 bump version 2016-07-19 11:17:27 -04:00
Thomas Breloff c1c97a5fc1 labelfunc for pyplot 2016-07-19 10:32:23 -04:00
Thomas Breloff 6a97dc8825 extrema fix for bool/nothing 2016-07-18 16:19:58 -04:00
Thomas Breloff d832f34733 travis 2016-07-18 15:53:02 -04:00
Thomas Breloff 859a600b92 travis 2016-07-18 15:38:07 -04:00
Thomas Breloff 6b3cf50f38 travis 2016-07-18 15:21:51 -04:00
Thomas Breloff 3ccd1bc368 added Showoff dependency; fix PLOTS_DEFAULTS; fix auto fill to 0; fix scale warning; tick labels with Showoff; fontsize in GR; travis deps 2016-07-18 15:00:02 -04:00
Thomas Breloff 624d33b96f Merge branch 'master' into dev 2016-07-18 09:13:46 -04:00
Tom Breloff 156f61f526 Merge pull request #391 from odow/axisstyle
pass axis style to PGFPlots
2016-07-16 13:13:59 -04:00
odow fd3a04fce3 pass axis style to PGFPlots 2016-07-16 16:48:46 +12:00
Thomas Breloff 62be4e1274 scalefunc fix and cleanup 2016-07-15 09:09:46 -04:00
Thomas Breloff db096196c9 Merge branch 'master' into dev 2016-07-14 15:47:40 -04:00
Thomas Breloff a0ac70be3c axes log scales, colors, ticks, and more; proper 2D axis drawing in GR 2016-07-14 15:46:44 -04:00
Tom Breloff 1f0f89f83c Merge pull request #389 from odow/patch-1
Legend location in subplot
2016-07-14 13:52:37 -04:00
Thomas Breloff ee706ad8c7 axis_drawing_info and get_ticks; parameterized Segments; GR manual drawing of 2D axes 2016-07-13 16:31:21 -04:00
Oscar Dowson ce31ea8bf3 Legend location in subplot 2016-07-13 10:01:48 +12:00
Thomas Breloff dcab8e3ac2 travis fix 2016-07-12 13:14:53 -04:00
Thomas Breloff 1ad9a7e176 fix fillrange for shape; travis fix 2016-07-12 12:53:03 -04:00
Thomas Breloff f1bd7ab1dc travis fix 2016-07-12 11:53:37 -04:00
Thomas Breloff 79399913d7 NEWS, bumped version; travis fixes; added warnings for moved recipes 2016-07-12 11:23:04 -04:00
Thomas Breloff 4a2e88a81c switched Colors dep for PlotUtils dep; removed DataFrames, boxplot, violin, density and added StatPlots to tests 2016-07-12 10:45:58 -04:00
Thomas Breloff 8d5b748b09 gr heatmap fix; pipeline reorg 2016-07-10 23:26:52 -04:00
Thomas Breloff b2dba8af73 split plot into pipeline 2016-07-10 22:59:05 -04:00
Thomas Breloff c258bc49c3 gr trisurface; closes #387 2016-07-10 22:07:00 -04:00
Thomas Breloff e3e2a354e7 reorg of update_subplot_args; add back recipe, DataFrames; random type stability fixes 2016-07-10 18:12:49 -04:00
Thomas Breloff 32c1c31139 working on _plot organization; switch alias dicts to Dict{Symbol,Symbol}; other type stability changes 2016-07-10 15:50:29 -04:00
Thomas Breloff b8b5a33833 margin default to 1mm 2016-07-09 14:54:44 -04:00
Thomas Breloff 5407fa73f8 plotly redesign for splitting shapes 2016-07-09 13:30:39 -04:00
Thomas Breloff 1cb0c0071b improvements to layout padding; handle axis attr better; fix histogram2d 2016-07-09 12:40:27 -04:00
Thomas Breloff a456ac4c90 pyplot line segments fixes; curves remove line_z logic 2016-07-08 14:36:02 -04:00
Thomas Breloff 71b48427c5 pyplot line segments fix; bar fixes 2016-07-08 13:09:36 -04:00
Thomas Breloff ec06a01b6c pgf fixes 2016-07-08 11:21:26 -04:00
Thomas Breloff e3ccc34a2c ensure_gradient; plotly fixes 2016-07-08 10:12:48 -04:00
Thomas Breloff 396721568f color fixes; add PlotUtils to travis_commands 2016-07-08 09:17:39 -04:00
Thomas Breloff e53ab85745 color fixes; pyplot shape iteration; KernelDensity in travis and warning; attribute accessors 2016-07-07 21:46:34 -04:00
Thomas Breloff 4d99b37def working on colors overhaul; fix for rationals in plotly; closes #382 2016-07-07 14:40:09 -04:00
Thomas Breloff 2e24da3dcd update matrix of Shapes recipe 2016-07-07 09:36:03 -04:00
Thomas Breloff 0cc1bd2dc6 sample ticks if too many discrete values; use get_ticks in plotly to fix discrete ticks; allow ticks==false in pyplot; closes #356 2016-07-06 16:51:54 -04:00
Thomas Breloff aec162c50e fix for discrete bars; closes #365 2016-07-06 16:22:11 -04:00
Thomas Breloff d5e9340f8f bar fix for horizontal orientation 2016-07-06 16:10:01 -04:00
Thomas Breloff 6fe87c4c69 bar_width support in bar and boxplot; close the shape for bar plots; closes #366; closes #377 2016-07-06 15:55:38 -04:00
Tom Breloff 85f0a87213 Merge pull request #372 from anowacki/rainbow_palette
Switch blue and violet in rainbow colour palette.
2016-07-05 10:22:03 -04:00
Thomas Breloff 853f85be2e bug fix for passing vectors to bins; closes #375 2016-07-05 10:18:48 -04:00
Andy Nowacki a7448e3a1d Switch blue and violet in rainbow color palette. 2016-07-04 17:43:25 +01:00
Thomas Breloff 521e753183 string fix; iter_segments change to use unused and skip initial NaNs; working on glvisualize shape type 2016-07-01 17:23:25 -04:00
Thomas Breloff 9193941fd0 version bump; NEWS 2016-07-01 09:01:38 -04:00
Tom Breloff f56c6effe4 Merge pull request #368 from jheinen/dev
Added iTerm2 inline display support for gr
2016-07-01 08:09:34 -04:00
Josef Heinen 4cfcb239c1 Added iTerm2 inline display support for gr
Example:
--------
    using Plots
    gr(display_type=:inline)
    plot(Plots.fakedata(50,5), w=3)
2016-07-01 14:03:16 +02:00
Thomas Breloff a4be274718 pyplot display hack 2016-06-30 21:05:32 -04:00
Thomas Breloff 120f861a27 surface fixes for mis-typed matrices; getindex for Series 2016-06-30 14:08:05 -04:00
Thomas Breloff 7e56d85b83 args fix 2016-06-29 21:58:07 -04:00
Thomas Breloff 20af495581 added display_type and extra_kwargs plot attributes 2016-06-29 16:34:06 -04:00
Thomas Breloff bf94c48225 pyplot fix; new flexible logic for DataFrames 2016-06-29 16:22:16 -04:00
Thomas Breloff 27a68333b1 readme 2016-06-29 14:51:24 -04:00
Thomas Breloff 63f18dd26a vector of seriestypes fix; pycall changed strings to symbols 2016-06-29 14:45:33 -04:00
Thomas Breloff 4ea787743e added contourf; getindex for plt/sp; plot/plot! on a Subplot; fix for pyplot zorder 2016-06-29 13:53:22 -04:00
Thomas Breloff 3a4b881576 switched docs url 2016-06-29 12:31:01 -04:00
Thomas Breloff f087594331 switched docs url 2016-06-29 12:23:42 -04:00
Thomas Breloff 5491e40fd1 bump version; img_eps; remove gr 30 test; attempted fixes for appveyor 2016-06-28 18:04:40 -04:00
Tom Breloff 4b690ec9ad Merge pull request #361 from jheinen/dev
gr: allow simple formulas or LaTeX equations
2016-06-28 10:03:23 -04:00
Josef Heinen 9fecb03b5f gr: allow simple formulas or LaTeX equations 2016-06-28 15:35:32 +02:00
Thomas Breloff de6e3d46ec appveyor fix 2016-06-27 21:49:45 -04:00
Thomas Breloff 3006a26500 revert precompile; add appveyor; gr fixes for markersize, shapes, and legend entries; add GR to tests again; recipe fixes and cleanup 2016-06-27 21:43:57 -04:00
Thomas Breloff 6ffed9387c snoop and precompile; bumped version 2016-06-27 16:52:50 -04:00
Thomas Breloff 558627e7a9 NEWS 2016-06-27 14:02:01 -04:00
Thomas Breloff 4b276f977c revert pyplot heatmap change 2016-06-27 12:25:45 -04:00
Thomas Breloff 99fe4f615d show arg fix; add image to all3D 2016-06-27 12:09:08 -04:00
Thomas Breloff 928c306803 dpi fixes 2016-06-27 10:06:40 -04:00
Thomas Breloff a3eddf8ba7 dpi support for pyplot 2016-06-27 09:20:42 -04:00
Thomas Breloff 78e0ae31af removed chorddiagram 2016-06-26 12:37:06 -04:00
Thomas Breloff 1a90ce0950 recoded boxplot recipe to match violin approach; switched marker to line/fill for shape seriestype: ref #347 2016-06-26 11:36:09 -04:00
Thomas Breloff 2465b3eb5a fallback warnings for missing _writemime and _display 2016-06-26 11:08:06 -04:00
Thomas Breloff 16a2e34e95 writemime fix for Interact; working on glvisualize 2016-06-26 10:34:34 -04:00
Thomas Breloff 5c52d68091 working on glvisualize 2016-06-25 18:06:27 -04:00
Thomas Breloff 1a2e180f4f working on glvisualize 2016-06-25 17:00:45 -04:00
Thomas Breloff 9048053669 working on glvisualize 2016-06-25 10:56:51 -04:00
Thomas Breloff 69b69714cb working on glvisualize 2016-06-24 12:15:21 -04:00
Thomas Breloff 1ecd9f9e19 working on glvisualize 2016-06-24 11:52:33 -04:00
Thomas Breloff 2bca9ca9ec square link 2016-06-24 11:00:28 -04:00
Thomas Breloff d66028ed0d glvisualize: support for path/path3d with or without markers, 3d markers are spheres 2016-06-23 17:00:08 -04:00
Thomas Breloff 1cf33044bc scatter and scatter3d support in glvisualize 2016-06-23 14:59:04 -04:00
Thomas Breloff 2c138c8355 readme 2016-06-22 13:22:25 -04:00
Thomas Breloff 73ffcb9bc1 changed convertToAnyVector AMat so that all matrices use the version previously for numbers; violin cleanup 2016-06-22 13:20:09 -04:00
Thomas Breloff 730025e144 recoded violin; removed try/catch around apply_recipe; default values for xtick/ytick in GR 2016-06-22 11:45:30 -04:00
Thomas Breloff 91aa1d718a animations: use tmp.gif for ijulia, remove prefix from convert call; closes #346 2016-06-22 11:01:00 -04:00
Thomas Breloff f881bfc4fc add Segments; pyplot: switch bar/hist/hist2d to recipes and path fix; change bar to shape; removed GR from tests 2016-06-20 23:11:23 -04:00
Thomas Breloff e76c3fff69 refactored gr markers fixing bugs and enabling features; clear subplot/axis args from d in _plot 2016-06-20 19:51:03 -04:00
Thomas Breloff 9f168071ff subplot attributes fixes 2016-06-20 15:11:25 -04:00
Thomas Breloff b6652b7619 improvements to bbox construction and inset subplots 2016-06-20 10:13:03 -04:00
Thomas Breloff f6d501f69e plot recipe fixes 2016-06-20 00:11:06 -04:00
Thomas Breloff 4c052cb3b3 colors cgrad and getindex 2016-06-19 11:25:25 -04:00
Thomas Breloff ea8ccf38db plot recipe fixes 2016-06-18 22:30:47 -04:00
Thomas Breloff cf12ff8070 plot logic cleanup; added plot recipes 2016-06-18 09:33:34 -04:00
Thomas Breloff b7a95244db handle seriestype aliases; handle vector of seriestypes; hvline_limits; added cycle for InputWrapper; turn on GR tests for OSX 2016-06-18 00:14:20 -04:00
Tom Breloff 4c884651a8 Merge pull request #343 from jheinen/dev
Added suport for marker borders
2016-06-18 00:02:42 -04:00
Thomas Breloff e9eca577aa plotlyjs supported_scales; violin check 2016-06-17 15:31:01 -04:00
Josef Heinen cff78b477c Added suport for marker borders 2016-06-17 20:21:07 +02:00
Tom Breloff b232410d25 Merge pull request #338 from jheinen/dev
use temporary files when generating MIME files
2016-06-17 08:09:53 -04:00
Josef Heinen 5ff338d4ac use temporary files when generating MIME files 2016-06-17 09:34:10 +02:00
Thomas Breloff a5ceea153d bump plots version 0.7.3 2016-06-16 21:00:25 -04:00
48 changed files with 4519 additions and 2932 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ os:
- linux
- osx
julia:
- release
- 0.5
- nightly
matrix:
allow_failures:
+159 -4
View File
@@ -3,11 +3,166 @@
#### notes on release changes, ongoing development, and future planned work
- All new development should target 0.7!
- All new development should target 0.9!
- Minor version 0.8 is the last one to support Julia 0.4!!
- Critical bugfixes only
- `backports` branch is for Julia 0.4
---
## 0.7 (current master/dev)
## 0.9 (current master/dev)
#### 0.9.2
- glvisualize backend (@SimonDanisch)
- too much to list! ready for alpha testing
- Volume and volume seriestype
- Atom: support for PlotPane and proper gui display
- gr:
- clims
- aspect ratio
- pgfplots:
- fixes for ticks, axes, and more
- pyplot:
- font families
- colorbar guide
- pixel marker
- unicodeplots
- basic support for shapes
- improved add_backend
- refactor of is_supported methods
- element-wise type recipes (see https://github.com/tbreloff/Plots.jl/issues/460#issuecomment-248428908)
- several other fixes/improvements
#### 0.9.1
- Pkg.dir --> dirname (@tkelman)
- `axis = nothing` magic
- fixes:
- clim for line_z
- sticks default range for log scale
- rotate method
- pyplot heatmap
- spurious scale warnings
- gr image/alpha
- plotly.js path
- orientation extrema
- bar, reset orientation
- switch transpose_z to use permutedims
- skinny x/+ markers
- ticks in pgfplots
- eps in savefig (@anriseth)
- add_backend convenience
- type recipes for Date/DateTime (@maximsch2)
- mirror attribute and twinx convenience
- Axis.sp --> Axis.sps
- recipe postprocessing for allowing aliases and magic args in recipe bodies
#### 0.9.0
- fixes to cycle
- add back single function recipe: `plot!(cos)`
- new axis formatter attribute... accepts functions to convert numbers to strings
- fix for inset plots
- GR:
- fillrange fix
- annotations fix
- force double buffering in display
---
## 0.8
#### 0.8.2 (backported bug fixes for julia 0.4)
- plotly ticks fix
- unicodeplots size fix
- remove mkdir call in tests
#### 0.8.1
- manual drawing of axes/ticks/labels
- get_ticks uses optimize_ticks and Showoff
- changed PLOTS_DEFAULTS to be a global variable, not ENV key
- parameterized Segments for pushing tuples
- fix to axis extrema for Bool/nothing
- GR:
- manually draw 2D axes... fixes several issues and missing features
- fontsize fix
- PGFPlots: pass axis syle
#### 0.8.0
- added dependency on PlotUtils
- BREAKING: removed DataFrames support (now in StatPlots.jl)
- BREAKING: removed boxplot/violin/density recipes (now in StatPlots.jl)
- GR:
- inline iterm2 support
- trisurface support
- heatmap fix
- PyPlot:
- ijulia display fix
- GLVisualize:
- first try with shapes
- iter_segments improvements
- bar_width support
- horizontal bars
- improve tick display
- better shape handling in pyplot, plotly
- improved padding calcs
- internal reorg of _plots method, add pipeline.jl
---
## 0.7
#### 0.7.5
- GR: LaTeX support
- Changed docs url to juliaplots.github.io
- added `contourf` seriestype
- allow `plt[1]` to return first Subplot
- allow `sp[1]` to return the first Series of the Subplot
- `series[k]` now passes through to `series.d[k]`
- allow calling `plot!(sp, ...)` to update a target Subplot
- PyPlot: zorder fix
- new DataFrames logic/recipe: more flexible/robust and allow Symbols for:
- `(:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror)`
- new `display_type` and `extra_kwargs` plot attributes
- surface fix
#### 0.7.4
- added snooped precompiles, but left commented out
- GR fixes: markersize, shapes, legends
- fixes to recipes
- turned on Appveyor
#### 0.7.3
- rebuild violin and boxplot recipes
- "plot recipes"
- `cgrad` method for easy color gradient creation
- improvements to inset subplots
- Segments and iter_segments for NaN-separated vectors
- `bar` recipe now creates a `shape` series
- writemime fix for Interact.jl
- `link = :square` option
- !!! set `shape` attributes with line/fill, NOT marker/markerstroke !!!
- basic DPI support
- moved chorddiagram to PlotRecipes
- GR:
- use temp files for img output
- basic support for marker strokes and other marker fixes
- PyPlot:
- Switch to recipes for bar, histogram, histogram2d
- GLVisualize
- subplots
- path/scatter and path3d/scatter3d
- initial drawing of axes
- many smaller fixes and improvements
#### 0.7.2
@@ -55,7 +210,7 @@
#### 0.7.0
- Check out [the summary](http://plots.readthedocs.io/en/latest/plots_v0.7/)
- Check out [the summary](http://juliaplots.github.io/plots_v0.7/)
- Revamped and simplified internals
- [Recipes, recipes, recipes](https://github.com/JuliaPlots/RecipesBase.jl/issues/6)
- [Layouts and Subplots](https://github.com/tbreloff/Plots.jl/issues/60)
@@ -187,7 +342,7 @@
- Integration with [PlotlyJS.jl](https://github.com/spencerlyon2/PlotlyJS.jl) for using Plotly inside a Blink window @spencerlyon2
- The Plotly backend has been split into my built-in version (`plotly()`) and @spencerlyon2's backend (`plotlyjs()`)
- Revamped backend setup code for easily adding new backends
- New docs (WIP) at http://plots.readthedocs.org/
- New docs (WIP) at http://juliaplots.github.io/
- Overhaul to `:legend` keyword (see https://github.com/tbreloff/Plots.jl/issues/135)
- New dependency on Requires, allows auto-loading of DataFrames support
- Support for plotting lists of Tuples and FixedSizeArrays
+5 -18
View File
@@ -12,24 +12,11 @@
Plots is a plotting API and toolset. My goals with the package are:
- **Powerful**. Do more with less. Complex visualizations become easy.
- **Intuitive**. Start generating plots without reading volumes of documentation. Commands should "just work".
- **Intuitive**. Stop reading so much 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.
- **Consistent**. Don't commit to one graphics package, use the same code everywhere.
- **Lightweight**. Very few dependencies.
- **Smart**. Attempts to figure out what you **want** it to do... not just what you **tell** it.
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.
```julia
using Plots
pyplot(reuse=true)
@gif for i in linspace(0,2π,100)
X = Y = linspace(-5,5,40)
surface(X, Y, (x,y) -> sin(x+10sin(i))+cos(y))
end
```
![waves](http://plots.readthedocs.io/en/latest/examples/img/waves.gif)
View the [full documentation](http://plots.readthedocs.io).
View the [full documentation](http://juliaplots.github.io).
+3 -2
View File
@@ -1,8 +1,9 @@
julia 0.4
julia 0.5-
RecipesBase
Colors
PlotUtils
Reexport
Compat
FixedSizeArrays
Measures
Showoff
+11 -13
View File
@@ -1,15 +1,10 @@
environment:
matrix:
- JULIAVERSION: "julialang/bin/winnt/x86/0.3/julia-0.3-latest-win32.exe"
- JULIAVERSION: "julialang/bin/winnt/x64/0.3/julia-0.3-latest-win64.exe"
- JULIAVERSION: "julialang/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
- JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe"
branches:
only:
- master
- /release-.*/
notifications:
- provider: Email
on_build_success: false
@@ -17,18 +12,21 @@ notifications:
on_build_status_changed: false
install:
# If there's a newer build queued for the same PR, cancel this one
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
# Download most recent Julia Windows binary
- ps: (new-object net.webclient).DownloadFile(
$("http://s3.amazonaws.com/"+$env:JULIAVERSION),
"C:\projects\julia-binary.exe")
- ps: (new-object net.webclient).DownloadFile($("http://s3.amazonaws.com/"+$env:JULIAVERSION), "C:\projects\julia-binary.exe")
# Run installer silently, output to C:\projects\julia
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
build_script:
# Need to convert from shallow to complete for Pkg.clone to work
- IF EXIST .git\shallow (git fetch --unshallow)
- C:\projects\julia\bin\julia -e "versioninfo();
Pkg.clone(pwd(), \"Plots\"); Pkg.build(\"Plots\")"
- C:\projects\julia\bin\julia -e "versioninfo(); Pkg.clone(pwd(), \"Plots\"); Pkg.build(\"Plots\")"
test_script:
- C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"Plots\")"
# - C:\projects\julia\bin\julia -e "Pkg.test(\"Plots\")"
- C:\projects\julia\bin\julia -e "include(Pkg.dir(\"Plots\", \"test\", \"travis_commands.jl\"))"
+30 -31
View File
@@ -1,15 +1,17 @@
__precompile__()
__precompile__()
module Plots
using Compat
# using Compat
using Reexport
@reexport using Colors
# @reexport using Colors
# using Requires
using FixedSizeArrays
@reexport using RecipesBase
using Base.Meta
@reexport using PlotUtils
import Showoff
export
AbstractPlot,
@@ -32,10 +34,12 @@ export
plot,
plot!,
update!,
current,
default,
with,
twinx,
@userplot,
@shorthands,
@@ -59,6 +63,10 @@ export
xaxis!,
yaxis!,
xlims,
ylims,
zlims,
savefig,
png,
gui,
@@ -66,8 +74,10 @@ export
backend,
backends,
backend_name,
backend_object,
add_backend,
aliases,
dataframes,
# dataframes,
Shape,
text,
@@ -78,24 +88,8 @@ export
Surface,
OHLC,
arrow,
colorscheme,
ColorScheme,
ColorGradient,
ColorVector,
ColorWrapper,
ColorFunction,
ColorZFunction,
getColor,
getColorZ,
debugplots,
supported_args,
supported_types,
supported_styles,
supported_markers,
is_subplot_supported,
Segments,
Formatted,
Animation,
frame,
@@ -103,13 +97,11 @@ export
@animate,
@gif,
PlotRecipe,
spy,
arcdiagram,
chorddiagram,
test_examples,
iter_segments,
coords,
translate,
translate!,
@@ -133,15 +125,14 @@ export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h
include("types.jl")
include("utils.jl")
include("colors.jl")
include("components.jl")
include("axes.jl")
include("backends.jl")
include("args.jl")
include("backends.jl")
include("themes.jl")
include("plot.jl")
include("series_args.jl")
include("series_new.jl")
include("pipeline.jl")
include("series.jl")
include("layouts.jl")
include("subplots.jl")
include("recipes.jl")
@@ -176,6 +167,7 @@ end
@shorthands vline
@shorthands ohlc
@shorthands contour
@shorthands contourf
@shorthands contour3d
@shorthands surface
@shorthands wireframe
@@ -246,8 +238,8 @@ function __init__()
setup_ijulia()
setup_atom()
if haskey(ENV, "PLOTS_DEFAULTS")
for (k,v) in eval(parse(ENV["PLOTS_DEFAULTS"]))
if isdefined(Main, :PLOTS_DEFAULTS)
for (k,v) in Main.PLOTS_DEFAULTS
default(k, v)
end
end
@@ -255,4 +247,11 @@ end
# ---------------------------------------------------------
# if VERSION >= v"0.4.0-dev+5512"
# include("precompile.jl")
# _precompile_()
# end
# ---------------------------------------------------------
end # module
+11 -11
View File
@@ -1,12 +1,12 @@
immutable Animation
dir::Compat.ASCIIString
frames::Vector{Compat.ASCIIString}
dir::String
frames::Vector{String}
end
function Animation()
tmpdir = convert(Compat.ASCIIString, mktempdir())
Animation(tmpdir, Compat.ASCIIString[])
tmpdir = convert(String, mktempdir())
Animation(tmpdir, String[])
end
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
@@ -21,10 +21,10 @@ end
"Wraps the location of an animated gif so that it can be displayed"
immutable AnimatedGif
filename::Compat.ASCIIString
filename::String
end
function gif(anim::Animation, fn = tempname()*".gif"; fps::Integer = 20)
function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif"); fps::Integer = 20, loop::Integer = 0)
fn = abspath(fn)
try
@@ -35,8 +35,8 @@ function gif(anim::Animation, fn = tempname()*".gif"; fps::Integer = 20)
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
include(file)
end
prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
run(`$(joinpath(prefix, "convert")) -delay $speed -loop 0 $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
run(`convert -delay $speed -loop $loop $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
catch err
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
@@ -44,7 +44,7 @@ function gif(anim::Animation, fn = tempname()*".gif"; fps::Integer = 20)
Will 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 0 -framerate $fps -loop $loop -i $(anim.dir)/%06d.png -y $fn`)
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
end
@@ -55,8 +55,8 @@ 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())>\" />")
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
end
+6
View File
@@ -62,6 +62,9 @@ const _arg_desc = KW(
:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).",
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
:dpi => "Number. Dots Per Inch of output figures",
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
# subplot args
:title => "String. Subplot title.",
@@ -89,6 +92,7 @@ const _arg_desc = KW(
:right_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the right of the subplot.",
:bottom_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the bottom of the subplot.",
:subplot_index => "Integer. Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list.",
:colorbar_title => "String. Title of colorbar.",
# axis args
:guide => "String. Axis guide (label).",
@@ -97,11 +101,13 @@ const _arg_desc = KW(
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
:rotation => "Number. Degrees rotation of tick labels.",
:flip => "Bool. Should we flip (reverse) the axis?",
:formatter => "Function, :scientific, or :auto. A method which converts a number to a string for tick labeling.",
:tickfont => "Font. Font of axis tick labels.",
:guidefont => "Font. Font of axis guide (label).",
:foreground_color_axis => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis ticks.",
:foreground_color_border => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of plot area border (spines).",
:foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.",
:foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).",
:mirror => "Bool. Switch the side of the tick labels (right or top).",
)
+323 -154
View File
@@ -1,6 +1,6 @@
const _keyAliases = KW()
const _keyAliases = Dict{Symbol,Symbol}()
function add_aliases(sym::Symbol, aliases::Symbol...)
for alias in aliases
@@ -11,7 +11,7 @@ function add_aliases(sym::Symbol, aliases::Symbol...)
end
end
function add_non_underscore_aliases!(aliases::KW)
function add_non_underscore_aliases!(aliases::Dict{Symbol,Symbol})
for (k,v) in aliases
s = string(k)
if '_' in s
@@ -24,14 +24,14 @@ end
# ------------------------------------------------------------
const _allAxes = [:auto, :left, :right]
const _axesAliases = KW(
const _axesAliases = Dict{Symbol,Symbol}(
:a => :auto,
:l => :left,
:r => :right
)
const _3dTypes = [
:path3d, :scatter3d, :surface, :wireframe, :contour3d
:path3d, :scatter3d, :surface, :wireframe, :contour3d, :volume
]
const _allTypes = vcat([
:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
@@ -39,7 +39,7 @@ const _allTypes = vcat([
:contour, :pie, :shape, :image
], _3dTypes)
@compat const _typeAliases = KW(
const _typeAliases = Dict{Symbol,Symbol}(
:n => :none,
:no => :none,
:l => :line,
@@ -79,11 +79,11 @@ add_non_underscore_aliases!(_typeAliases)
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
like_surface(seriestype::Symbol) = seriestype in (:contour, :contour3d, :heatmap, :surface, :wireframe, :image)
like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image)
is3d(seriestype::Symbol) = seriestype in _3dTypes
is3d(series::Series) = is3d(series.d)
is3d(d::KW) = trueOrAllTrue(is3d, d[:seriestype])
is3d(d::KW) = trueOrAllTrue(is3d, Symbol(d[:seriestype]))
is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d"
ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar"
@@ -92,7 +92,7 @@ ispolar(series::Series) = ispolar(series.d[:subplot])
# ------------------------------------------------------------
const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
@compat const _styleAliases = KW(
const _styleAliases = Dict{Symbol,Symbol}(
:a => :auto,
:s => :solid,
:d => :dash,
@@ -101,7 +101,7 @@ const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
)
const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))))
@compat const _markerAliases = KW(
const _markerAliases = Dict{Symbol,Symbol}(
:n => :none,
:no => :none,
:a => :auto,
@@ -123,9 +123,9 @@ const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))
:dtri => :dtriangle,
:downtri => :dtriangle,
:downtriangle => :dtriangle,
:+ => :cross,
# :+ => :cross,
:plus => :cross,
:x => :xcross,
# :x => :xcross,
:X => :xcross,
:star => :star5,
:s => :star5,
@@ -143,7 +143,7 @@ const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))
)
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
@compat const _scaleAliases = KW(
const _scaleAliases = Dict{Symbol,Symbol}(
:none => :identity,
:log => :log10,
)
@@ -216,6 +216,9 @@ const _plot_defaults = KW(
:html_output_format => :auto,
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
# the parent layout and the relative bounding box of inset subplots
:dpi => DPI, # dots per inch for images, etc
:display_type => :auto,
:extra_kwargs => KW(),
)
@@ -239,12 +242,13 @@ const _subplot_defaults = KW(
:annotations => [], # annotation tuples... list of (x,y,annotation)
:projection => :none, # can also be :polar or :3d
:aspect_ratio => :none, # choose from :none or :equal
:margin => 2mm,
:margin => 1mm,
:left_margin => :match,
:top_margin => :match,
:right_margin => :match,
:bottom_margin => :match,
:subplot_index => -1,
:colorbar_title => "",
)
const _axis_defaults = KW(
@@ -262,6 +266,8 @@ const _axis_defaults = KW(
:foreground_color_text => :match, # tick text color,
:foreground_color_guide => :match, # guide text color,
:discrete_values => [],
:formatter => :auto,
:mirror => false,
)
const _suppress_warnings = Set{Symbol}([
@@ -281,22 +287,24 @@ const _suppress_warnings = Set{Symbol}([
# add defaults for the letter versions
const _axis_defaults_byletter = KW()
for letter in (:x,:y,:z)
for k in (
:guide,
:lims,
:ticks,
:scale,
:rotation,
:flip,
:link,
:tickfont,
:guidefont,
:foreground_color_axis,
:foreground_color_border,
:foreground_color_text,
:foreground_color_guide,
:discrete_values
)
for k in keys(_axis_defaults)
# for k in (
# :guide,
# :lims,
# :ticks,
# :scale,
# :rotation,
# :flip,
# :link,
# :tickfont,
# :guidefont,
# :foreground_color_axis,
# :foreground_color_border,
# :foreground_color_text,
# :foreground_color_guide,
# :discrete_values,
# :formatter,
# )
_axis_defaults_byletter[Symbol(letter,k)] = :match
# allow the underscore version too: xguide or x_guide
@@ -312,10 +320,8 @@ const _all_defaults = KW[
]
const _all_args = sort(collect(union(map(keys, _all_defaults)...)))
supported_args(::AbstractBackend) = error("supported_args not defined") #_all_args
supported_args() = supported_args(backend())
RecipesBase.is_key_supported(k::Symbol) = (k in supported_args())
RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k)
# -----------------------------------------------------------------------------
@@ -327,7 +333,7 @@ autopick(notarr, idx::Integer) = notarr
autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
autopick_ignore_none_auto(notarr, idx::Integer) = notarr
function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plotIndex::Int)
function aliasesAndAutopick(d::KW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
if d[sym] == :auto
d[sym] = autopick_ignore_none_auto(options, plotIndex)
elseif haskey(aliases, d[sym])
@@ -335,7 +341,7 @@ function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plot
end
end
function aliases(aliasMap::KW, val)
function aliases(aliasMap::Dict{Symbol,Symbol}, val)
sortedkeys(filter((k,v)-> v==val, aliasMap))
end
@@ -432,6 +438,7 @@ add_aliases(:title_location, :title_loc, :titleloc, :title_position, :title_pos,
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation)
add_aliases(:html_output_format, :format, :fmt, :html_format)
add_aliases(:orientation, :direction, :dir)
add_aliases(:inset_subplots, :inset, :floating)
# add all pluralized forms to the _keyAliases dict
@@ -494,7 +501,8 @@ function handleColors!(d::KW, arg, csym::Symbol)
if arg == :auto
d[csym] = :auto
else
c = colorscheme(arg)
# c = colorscheme(arg)
c = plot_color(arg)
d[csym] = c
end
return true
@@ -515,13 +523,13 @@ function processLineArg(d::KW, arg)
elseif typeof(arg) <: Stroke
arg.width == nothing || (d[:linewidth] = arg.width)
arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
arg.alpha == nothing || (d[:linealpha] = arg.alpha)
arg.style == nothing || (d[:linestyle] = arg.style)
elseif typeof(arg) <: Brush
arg.size == nothing || (d[:fillrange] = arg.size)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
elseif typeof(arg) <: Arrow || arg in (:arrow, :arrows)
@@ -554,13 +562,13 @@ function processMarkerArg(d::KW, arg)
elseif typeof(arg) <: Stroke
arg.width == nothing || (d[:markerstrokewidth] = arg.width)
arg.color == nothing || (d[:markerstrokecolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.color == nothing || (d[:markerstrokecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
arg.alpha == nothing || (d[:markerstrokealpha] = arg.alpha)
arg.style == nothing || (d[:markerstrokestyle] = arg.style)
elseif typeof(arg) <: Brush
arg.size == nothing || (d[:markersize] = arg.size)
arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : plot_color(arg.color))
arg.alpha == nothing || (d[:markeralpha] = arg.alpha)
# linealpha
@@ -580,9 +588,10 @@ end
function processFillArg(d::KW, arg)
# fr = get(d, :fillrange, 0)
if typeof(arg) <: Brush
arg.size == nothing || (d[:fillrange] = arg.size)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
# fillrange function
@@ -597,6 +606,8 @@ function processFillArg(d::KW, arg)
d[:fillrange] = arg
end
# d[:fillrange] = fr
return
end
_replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
@@ -616,6 +627,21 @@ end
function preprocessArgs!(d::KW)
replaceAliases!(d, _keyAliases)
# clear all axis stuff
if haskey(d, :axis) && d[:axis] in (:none, nothing, false)
d[:ticks] = nothing
d[:foreground_color_border] = RGBA(0,0,0,0)
d[:grid] = false
delete!(d, :axis)
end
# for letter in (:x, :y, :z)
# asym = Symbol(letter, :axis)
# if haskey(d, asym) || d[asym] in (:none, nothing, false)
# d[Symbol(letter, :ticks)] = nothing
# d[Symbol(letter, :foreground_color_border)] = RGBA(0,0,0,0)
# end
# end
# handle axis args
for letter in (:x, :y, :z)
asym = Symbol(letter, :axis)
@@ -661,7 +687,7 @@ function preprocessArgs!(d::KW)
a = d[:arrow]
d[:arrow] = if a == true
arrow()
elseif a == false
elseif a in (false, nothing, :none)
nothing
elseif !(typeof(a) <: Arrow)
arrow(wraptuple(a)...)
@@ -671,9 +697,9 @@ function preprocessArgs!(d::KW)
end
if get(d, :arrow, false) == true
d[:arrow] = arrow()
end
# if get(d, :arrow, false) == true
# d[:arrow] = arrow()
# end
# legends
if haskey(d, :legend)
@@ -683,21 +709,13 @@ function preprocessArgs!(d::KW)
d[:colorbar] = convertLegendValue(d[:colorbar])
end
# # handle subplot links
# if haskey(d, :link)
# l = d[:link]
# if isa(l, Bool)
# d[:linkx] = l
# d[:linky] = l
# elseif isa(l, Function)
# d[:linkx] = true
# d[:linky] = true
# d[:linkfunc] = l
# else
# warn("Unhandled/invalid link $l. Should be a Bool or a function mapping (row,column) -> (linkx, linky), where linkx/y can be Bool or Void (nothing)")
# end
# delete!(d, :link)
# end
# warnings for moved recipes
st = get(d, :seriestype, :path)
if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatPlots)
warn("seriestype $st has been moved to StatPlots. To use: \`Pkg.add(\"StatPlots\"); using StatPlots\`")
end
return
end
# -----------------------------------------------------------------------------
@@ -747,43 +765,51 @@ end
# -----------------------------------------------------------------------------
const _already_warned = Set()
const _already_warned = Dict{Symbol,Set{Symbol}}()
const _to_warn = Set{Symbol}()
function warnOnUnsupported_args(pkg::AbstractBackend, d::KW)
for k in sortedkeys(d)
k in supported_args(pkg) && continue
empty!(_to_warn)
bend = backend_name(pkg)
already_warned = get!(_already_warned, bend, Set{Symbol}())
for k in keys(d)
is_attr_supported(pkg, k) && continue
k in _suppress_warnings && continue
if d[k] != default(k)
if !((pkg, k) in _already_warned)
push!(_already_warned, (pkg,k))
warn("Keyword argument $k not supported with $pkg. Choose from: $(supported_args(pkg))")
end
k in already_warned || push!(_to_warn, k)
end
end
if !isempty(_to_warn)
for k in sort(collect(_to_warn))
push!(already_warned, k)
warn("Keyword argument $k not supported with $pkg. Choose from: $(supported_attrs(pkg))")
end
end
end
_markershape_supported(pkg::AbstractBackend, shape::Symbol) = shape in supported_markers(pkg)
_markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg)
_markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
# _markershape_supported(pkg::AbstractBackend, shape::Symbol) = shape in supported_markers(pkg)
# _markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg)
# _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
function warnOnUnsupported(pkg::AbstractBackend, d::KW)
(d[:seriestype] == :none
|| d[:seriestype] in supported_types(pkg)
|| warn("seriestype $(d[:seriestype]) is unsupported with $pkg. Choose from: $(supported_types(pkg))"))
(d[:linestyle] in supported_styles(pkg)
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supported_styles(pkg))"))
(d[:markershape] == :none
|| _markershape_supported(pkg, d[:markershape])
|| warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supported_markers(pkg))"))
if !is_seriestype_supported(pkg, d[:seriestype])
warn("seriestype $(d[:seriestype]) is unsupported with $pkg. Choose from: $(supported_seriestypes(pkg))")
end
if !is_style_supported(pkg, d[:linestyle])
warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supported_styles(pkg))")
end
if !is_marker_supported(pkg, d[:markershape])
warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supported_markers(pkg))")
end
end
function warnOnUnsupported_scales(pkg::AbstractBackend, d::KW)
for k in (:xscale, :yscale, :zscale)
for k in (:xscale, :yscale, :zscale, :scale)
if haskey(d, k)
v = d[k]
v = get(_scaleAliases, v, v)
if !(v in supported_scales(pkg))
warn("scale $(d[k]) is unsupported with $pkg. Choose from: $(supported_scales(pkg))")
if !is_scale_supported(pkg, v)
warn("scale $v is unsupported with $pkg. Choose from: $(supported_scales(pkg))")
end
end
end
@@ -805,6 +831,7 @@ function convertLegendValue(val::Symbol)
end
convertLegendValue(val::Bool) = val ? :best : :none
convertLegendValue(val::Void) = :none
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
# -----------------------------------------------------------------------------
@@ -824,9 +851,9 @@ slice_arg(v, idx) = v
# given an argument key (k), we want to extract the argument value for this index.
# matrices are sliced by column, otherwise we
# if nothing is set (or container is empty), return the default or the existing value.
function slice_arg!(d_in::KW, d_out::KW, k::Symbol, default_value, idx::Int = 1; new_key::Symbol = k, remove_pair::Bool = true)
v = get(d_in, k, get(d_out, new_key, default_value))
d_out[new_key] = if haskey(d_in, k) && typeof(v) <: AMat && !isempty(v)
function slice_arg!(d_in::KW, d_out::KW, k::Symbol, default_value, idx::Int, remove_pair::Bool)
v = get(d_in, k, get(d_out, k, default_value))
d_out[k] = if haskey(d_in, k) && typeof(v) <: AMat && !isempty(v)
slice_arg(v, idx)
else
v
@@ -834,6 +861,7 @@ function slice_arg!(d_in::KW, d_out::KW, k::Symbol, default_value, idx::Int = 1;
if remove_pair
delete!(d_in, k)
end
return
end
# -----------------------------------------------------------------------------
@@ -845,7 +873,7 @@ end
# d[k] = if v == :match
# match_color
# elseif v == nothing
# colorscheme(RGBA(0,0,0,0))
# plot_color(RGBA(0,0,0,0))
# else
# v
# end
@@ -854,10 +882,13 @@ end
function color_or_nothing!(d::KW, k::Symbol)
v = d[k]
d[k] = if v == nothing || v == false
colorscheme(RGBA(0,0,0,0))
RGBA{Float64}(0,0,0,0)
elseif v != :match
plot_color(v)
else
v
end
return
end
# -----------------------------------------------------------------------------
@@ -917,7 +948,7 @@ function Base.getindex(axis::Axis, k::Symbol)
v = axis.d[k]
if v == :match
if haskey(_match_map2, k)
axis.sp[_match_map2[k]]
axis.sps[1][_match_map2[k]]
else
axis[_match_map[k]]
end
@@ -926,36 +957,43 @@ function Base.getindex(axis::Axis, k::Symbol)
end
end
function Base.getindex(series::Series, k::Symbol)
series.d[k]
end
Base.setindex!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
Base.setindex!(sp::Subplot, v, k::Symbol) = (sp.attr[k] = v)
Base.setindex!(axis::Axis, v, k::Symbol) = (axis.d[k] = v)
Base.setindex!(series::Series, v, k::Symbol) = (series.d[k] = v)
Base.get(plt::Plot, k::Symbol, v) = get(plt.attr, k, v)
Base.get(sp::Subplot, k::Symbol, v) = get(sp.attr, k, v)
Base.get(axis::Axis, k::Symbol, v) = get(axis.d, k, v)
Base.get(series::Series, k::Symbol, v) = get(series.d, k, v)
# -----------------------------------------------------------------------------
# update attr from an input dictionary
function _update_plot_args(plt::Plot, d_in::KW)
for (k,v) in _plot_defaults
slice_arg!(d_in, plt.attr, k, v)
slice_arg!(d_in, plt.attr, k, v, 1, true)
end
# handle colors
bg = convertColor(plt.attr[:background_color])
bg = plot_color(plt.attr[:background_color])
fg = plt.attr[:foreground_color]
if fg == :auto
fg = isdark(bg) ? colorant"white" : colorant"black"
end
plt.attr[:background_color] = bg
plt.attr[:foreground_color] = convertColor(fg)
# color_or_match!(plt.attr, :background_color_outside, bg)
plt.attr[:foreground_color] = plot_color(fg)
color_or_nothing!(plt.attr, :background_color_outside)
end
# -----------------------------------------------------------------------------
# update a subplots args and axes
function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::Integer; remove_pair = true)
anns = pop!(sp.attr, :annotations, [])
# grab those args which apply to this subplot
for (k,v) in _subplot_defaults
slice_arg!(d_in, sp.attr, k, v, subplot_index, remove_pair = remove_pair)
end
function _update_subplot_periphery(sp::Subplot, anns::AVec)
# extend annotations
sp.attr[:annotations] = vcat(anns, sp[:annotations])
@@ -965,11 +1003,13 @@ function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::I
if sp.attr[:colorbar] == :legend
sp.attr[:colorbar] = sp.attr[:legend]
end
return
end
function _update_subplot_colors(sp::Subplot)
# background colors
# bg = color_or_match!(sp.attr, :background_color_subplot, plt.attr[:background_color])
color_or_nothing!(sp.attr, :background_color_subplot)
bg = convertColor(sp[:background_color_subplot])
bg = plot_color(sp[:background_color_subplot])
sp.attr[:color_palette] = get_color_palette(sp.attr[:color_palette], bg, 30)
color_or_nothing!(sp.attr, :background_color_legend)
color_or_nothing!(sp.attr, :background_color_inside)
@@ -979,64 +1019,193 @@ function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::I
color_or_nothing!(sp.attr, :foreground_color_legend)
color_or_nothing!(sp.attr, :foreground_color_grid)
color_or_nothing!(sp.attr, :foreground_color_title)
return
end
# for k in (:left_margin, :top_margin, :right_margin, :bottom_margin)
# if sp.attr[k] == :match
# sp.attr[k] = sp.attr[:margin]
# end
# end
function _update_axis(plt::Plot, sp::Subplot, d_in::KW, letter::Symbol, subplot_index::Int)
# get (maybe initialize) the axis
axis = get_axis(sp, letter)
_update_axis(axis, d_in, letter, subplot_index)
# convert a bool into auto or nothing
if isa(axis[:ticks], Bool)
axis[:ticks] = axis[:ticks] ? :auto : nothing
end
_update_axis_colors(axis)
_update_axis_links(plt, axis, letter)
return
end
function _update_axis(axis::Axis, d_in::KW, letter::Symbol, subplot_index::Int)
# grab magic args (for example `xaxis = (:flip, :log)`)
args = wraptuple(get(d_in, Symbol(letter, :axis), ()))
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
kw = KW()
for (k,v) in _axis_defaults
# first get the args without the letter: `tickfont = font(10)`
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
if haskey(d_in, k)
kw[k] = slice_arg(d_in[k], subplot_index)
end
# then get those args that were passed with a leading letter: `xlabel = "X"`
lk = Symbol(letter, k)
if haskey(d_in, lk)
kw[k] = slice_arg(d_in[lk], subplot_index)
end
end
# update the axis
update!(axis, args...; kw...)
return
end
function _update_axis_colors(axis::Axis)
# # update the axis colors
color_or_nothing!(axis.d, :foreground_color_axis)
color_or_nothing!(axis.d, :foreground_color_border)
color_or_nothing!(axis.d, :foreground_color_guide)
color_or_nothing!(axis.d, :foreground_color_text)
return
end
function _update_axis_links(plt::Plot, axis::Axis, letter::Symbol)
# handle linking here. if we're passed a list of
# other subplots to link to, link them together
link = axis[:link]
if !isempty(link)
for other_sp in link
other_sp = get_subplot(plt, other_sp)
link_axes!(axis, get_axis(other_sp, letter))
end
axis.d[:link] = []
end
return
end
# update a subplots args and axes
function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::Int, remove_pair::Bool)
anns = pop!(sp.attr, :annotations, [])
# grab those args which apply to this subplot
for (k,v) in _subplot_defaults
slice_arg!(d_in, sp.attr, k, v, subplot_index, remove_pair)
end
_update_subplot_periphery(sp, anns)
_update_subplot_colors(sp)
for letter in (:x, :y, :z)
# get (maybe initialize) the axis
axis = get_axis(sp, letter)
# grab magic args (for example `xaxis = (:flip, :log)`)
args = wraptuple(get(d_in, Symbol(letter, :axis), ()))
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
kw = KW()
for (k,v) in _axis_defaults
# first get the args without the letter: `tickfont = font(10)`
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
if haskey(d_in, k)
kw[k] = slice_arg(d_in[k], subplot_index)
end
# then get those args that were passed with a leading letter: `xlabel = "X"`
lk = Symbol(letter, k)
if haskey(d_in, lk)
kw[k] = slice_arg(d_in[lk], subplot_index)
end
end
# update the axis
update!(axis, args...; kw...)
# convert a bool into auto or nothing
if isa(axis[:ticks], Bool)
axis[:ticks] = axis[:ticks] ? :auto : nothing
end
# # update the axis colors
color_or_nothing!(axis.d, :foreground_color_axis)
color_or_nothing!(axis.d, :foreground_color_border)
color_or_nothing!(axis.d, :foreground_color_guide)
color_or_nothing!(axis.d, :foreground_color_text)
# handle linking here. if we're passed a list of
# other subplots to link to, link them together
link = axis[:link]
if !isempty(link)
for other_sp in link
other_sp = get_subplot(plt, other_sp)
link_axes!(axis, get_axis(other_sp, letter))
end
axis.d[:link] = []
end
_update_axis(plt, sp, d_in, letter, subplot_index)
end
end
# -----------------------------------------------------------------------------
function has_black_border_for_default(st::Symbol)
like_histogram(st) || st in (:hexbin, :bar)
like_histogram(st) || st in (:hexbin, :bar, :shape)
end
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
function getSeriesRGBColor(c, α, sp::Subplot, n::Int)
if c == :auto
c = autopick(sp[:color_palette], n)
elseif isa(c, Int)
c = autopick(sp[:color_palette], c)
end
plot_color(c, α)
end
function ensure_gradient!(d::KW, csym::Symbol, asym::Symbol)
if !isa(d[csym], ColorGradient)
d[csym] = cgrad(alpha = d[asym])
end
end
function _replace_linewidth(d::KW)
# get a good default linewidth... 0 for surface and heatmaps
if get(d, :linewidth, :auto) == :auto
d[:linewidth] = (get(d, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
end
end
function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
pkg = plt.backend
globalIndex = d[:series_plotindex]
# add default values to our dictionary, being careful not to delete what we just added!
for (k,v) in _series_defaults
slice_arg!(d, d, k, v, commandIndex, false)
end
# this is how many series belong to this subplot
plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
if get(d, :primary, true)
plotIndex += 1
end
aliasesAndAutopick(d, :linestyle, _styleAliases, supported_styles(pkg), plotIndex)
aliasesAndAutopick(d, :markershape, _markerAliases, supported_markers(pkg), plotIndex)
# update alphas
for asym in (:linealpha, :markeralpha, :fillalpha)
if d[asym] == nothing
d[asym] = d[:seriesalpha]
end
end
if d[:markerstrokealpha] == nothing
d[:markerstrokealpha] = d[:markeralpha]
end
# update series color
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], d[:seriesalpha], sp, plotIndex)
# update other colors
for s in (:line, :marker, :fill)
csym, asym = Symbol(s,:color), Symbol(s,:alpha)
d[csym] = if d[csym] == :match
plot_color(if has_black_border_for_default(d[:seriestype]) && s == :line
sp[:foreground_color_subplot]
else
d[:seriescolor]
end, d[asym])
else
getSeriesRGBColor(d[csym], d[asym], sp, plotIndex)
end
end
# update markerstrokecolor
d[:markerstrokecolor] = if d[:markerstrokecolor] == :match
plot_color(sp[:foreground_color_subplot], d[:markerstrokealpha])
else
getSeriesRGBColor(d[:markerstrokecolor], d[:markerstrokealpha], sp, plotIndex)
end
# if marker_z or line_z are set, ensure we have a gradient
if d[:marker_z] != nothing
ensure_gradient!(d, :markercolor, :markeralpha)
end
if d[:line_z] != nothing
ensure_gradient!(d, :linecolor, :linealpha)
end
# scatter plots don't have a line, but must have a shape
if d[:seriestype] in (:scatter, :scatter3d)
d[:linewidth] = 0
if d[:markershape] == :none
d[:markershape] = :circle
end
end
# set label
label = d[:label]
label = (label == "AUTO" ? "y$globalIndex" : label)
d[:label] = label
_replace_linewidth(d)
d
end
+175 -18
View File
@@ -23,13 +23,13 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
lk = Symbol(letter, k)
lv = _axis_defaults_byletter[lk]
d[k] = (lv == :match ? v : lv)
end
end
# merge!(d, _axis_defaults)
d[:discrete_values] = []
# update the defaults
update!(Axis(sp, d), args...; kw...)
update!(Axis([sp], d), args...; kw...)
end
function get_axis(sp::Subplot, letter::Symbol)
@@ -38,7 +38,7 @@ function get_axis(sp::Subplot, letter::Symbol)
sp.attr[axissym]
else
sp.attr[axissym] = Axis(sp, letter)
end
end::Axis
end
function process_axis_arg!(d::KW, arg, letter = "")
@@ -73,6 +73,9 @@ function process_axis_arg!(d::KW, arg, letter = "")
elseif typeof(arg) <: Number
d[Symbol(letter,:rotation)] = arg
elseif typeof(arg) <: Function
d[Symbol(letter,:formatter)] = arg
else
warn("Skipped $(letter)axis arg $arg")
@@ -117,14 +120,105 @@ Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
# get discrete ticks, or not
const _scale_funcs = Dict{Symbol,Function}(
:log10 => log10,
:log2 => log2,
:ln => log,
)
const _inv_scale_funcs = Dict{Symbol,Function}(
:log10 => exp10,
:log2 => exp2,
:ln => exp,
)
# const _label_func = Dict{Symbol,Function}(
# :log10 => x -> "10^$x",
# :log2 => x -> "2^$x",
# :ln => x -> "e^$x",
# )
const _label_func = Dict{Symbol,Function}(
:log10 => x -> "10^$x",
:log2 => x -> "2^$x",
:ln => x -> "e^$x",
)
scalefunc(scale::Symbol) = x -> get(_scale_funcs, scale, identity)(Float64(x))
invscalefunc(scale::Symbol) = x -> get(_inv_scale_funcs, scale, identity)(Float64(x))
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
amin,amax = axis_limits(axis)
# scale the limits
scale = axis[:scale]
sf = scalefunc(scale)
# get a list of well-laid-out ticks
scaled_ticks = if ticks == nothing
optimize_ticks(
sf(amin),
sf(amax);
k_min = 5, # minimum number of ticks
k_max = 8, # maximum number of ticks
)[1]
else
map(sf, filter(t -> amin <= t <= amax, ticks))
end
unscaled_ticks = map(invscalefunc(scale), scaled_ticks)
labels = if any(isfinite, unscaled_ticks)
formatter = axis[:formatter]
if formatter == :auto
# the default behavior is to make strings of the scaled values and then apply the labelfunc
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :plain))
elseif formatter == :scientific
Showoff.showoff(unscaled_ticks, :scientific)
else
# there was an override for the formatter... use that on the unscaled ticks
map(formatter, unscaled_ticks)
end
else
# no finite ticks to show...
String[]
end
# @show unscaled_ticks labels
# labels = Showoff.showoff(unscaled_ticks, scale == :log10 ? :scientific : :auto)
unscaled_ticks, labels
end
# return (continuous_values, discrete_values) for the ticks on this axis
function get_ticks(axis::Axis)
ticks = axis[:ticks]
ticks in (nothing, false) && return nothing
dvals = axis[:discrete_values]
if !isempty(dvals) && ticks == :auto
cv, dv = if !isempty(dvals) && ticks == :auto
# discrete ticks...
axis[:continuous_values], dvals
else
elseif ticks == :auto
# compute optimal ticks and labels
optimal_ticks_and_labels(axis)
elseif typeof(ticks) <: AVec
# override ticks, but get the labels
optimal_ticks_and_labels(axis, ticks)
elseif typeof(ticks) <: NTuple{2}
# assuming we're passed (ticks, labels)
ticks
else
error("Unknown ticks type in get_ticks: $(typeof(ticks))")
end
# @show ticks dvals cv dv
# TODO: better/smarter cutoff values for sampling ticks
if length(cv) > 30
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
cv[rng], dv[rng]
else
cv, dv
end
end
@@ -139,6 +233,12 @@ end
function expand_extrema!(axis::Axis, v::Number)
expand_extrema!(axis[:extrema], v)
end
# these shouldn't impact the extrema
expand_extrema!(axis::Axis, ::Void) = axis[:extrema]
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
ex = axis[:extrema]
ex.emin = min(v[1], ex.emin)
@@ -155,20 +255,33 @@ end
function expand_extrema!(sp::Subplot, d::KW)
vert = isvertical(d)
# first expand for the data
for letter in (:x, :y, :z)
data = d[letter]
axis = sp.attr[Symbol(letter, "axis")]
if eltype(data) <: Number
expand_extrema!(axis, data)
elseif isa(data, Surface) && eltype(data.surf) <: Number
data = d[if vert
letter
else
letter == :x ? :y : letter == :y ? :x : :z
end]
axis = sp[Symbol(letter, "axis")]
if isa(data, Volume)
expand_extrema!(sp[:xaxis], data.x_extents)
expand_extrema!(sp[:yaxis], data.y_extents)
expand_extrema!(sp[:zaxis], data.z_extents)
elseif eltype(data) <: Number || (isa(data, Surface) && all(di -> isa(di, Number), data.surf))
if !(eltype(data) <: Number)
# huh... must have been a mis-typed surface? lets swap it out
data = d[letter] = Surface(Matrix{Float64}(data.surf))
end
expand_extrema!(axis, data)
elseif data != nothing
# TODO: need more here... gotta track the discrete reference value
# as well as any coord offset (think of boxplot shape coords... they all
# correspond to the same x-value)
# @show letter,eltype(data),typeof(data)
d[letter], d[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
expand_extrema!(axis, d[letter])
end
end
@@ -179,7 +292,6 @@ function expand_extrema!(sp::Subplot, d::KW)
# end
# expand for fillrange
vert = isvertical(d)
fr = d[:fillrange]
if fr == nothing && d[:seriestype] == :bar
fr = 0.0
@@ -204,8 +316,6 @@ function expand_extrema!(sp::Subplot, d::KW)
if bw == nothing
bw = d[:bar_width] = mean(diff(data))
end
# @show data bw
axis = sp.attr[Symbol(dsym, :axis)]
expand_extrema!(axis, maximum(data) + 0.5maximum(bw))
expand_extrema!(axis, minimum(data) - 0.5minimum(bw))
@@ -233,9 +343,11 @@ end
function default_should_widen(axis::Axis)
should_widen = false
if axis[:scale] == :identity
for series in series_list(axis.sp)
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
should_widen = true
for sp in axis.sps
for series in series_list(sp)
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
should_widen = true
end
end
end
end
@@ -330,3 +442,48 @@ function pie_labels(sp::Subplot, series::Series)
d[:x]
end
end
# -------------------------------------------------------------------------
# compute the line segments which should be drawn for this axis
function axis_drawing_info(sp::Subplot)
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
xmin, xmax = axis_limits(xaxis)
ymin, ymax = axis_limits(yaxis)
xticks = get_ticks(xaxis)
yticks = get_ticks(yaxis)
spine_segs = Segments(2)
grid_segs = Segments(2)
if !(xaxis[:ticks] in (nothing, false))
f = scalefunc(yaxis[:scale])
invf = invscalefunc(yaxis[:scale])
t1 = invf(f(ymin) + 0.015*(f(ymax)-f(ymin)))
t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin)))
push!(spine_segs, (xmin,ymin), (xmax,ymin)) # bottom spine
push!(spine_segs, (xmin,ymax), (xmax,ymax)) # top spine
for xtick in xticks[1]
push!(spine_segs, (xtick, ymin), (xtick, t1)) # bottom tick
push!(grid_segs, (xtick, t1), (xtick, t2)) # vertical grid
push!(spine_segs, (xtick, ymax), (xtick, t2)) # top tick
end
end
if !(yaxis[:ticks] in (nothing, false))
f = scalefunc(xaxis[:scale])
invf = invscalefunc(xaxis[:scale])
t1 = invf(f(xmin) + 0.015*(f(xmax)-f(xmin)))
t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin)))
push!(spine_segs, (xmin,ymin), (xmin,ymax)) # left spine
push!(spine_segs, (xmax,ymin), (xmax,ymax)) # right spine
for ytick in yticks[1]
push!(spine_segs, (xmin, ytick), (t1, ytick)) # left tick
push!(grid_segs, (t1, ytick), (t2, ytick)) # horizontal grid
push!(spine_segs, (xmax, ytick), (t2, ytick)) # right tick
end
end
xticks, yticks, spine_segs, grid_segs
end
+77 -37
View File
@@ -26,24 +26,14 @@ macro init_backend(s)
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")
# ---------------------------------------------------------
add_backend(pkg::Symbol) = add_backend(_backend_instance(pkg))
add_backend(b::AbstractBackend) = warn("No custom install defined for $(backend_name(b))")
# don't do anything as a default
_create_backend_figure(plt::Plot) = nothing
_prepare_plot_object(plt::Plot) = nothing
@@ -57,13 +47,35 @@ _before_layout_calcs(plt::Plot) = nothing
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
# TODO: this should account for both tick font and the size/length/rotation of tick labels
function tick_padding(axis::Axis)
ptsz = axis[:tickfont].pointsize * pt
if axis[:ticks] in (nothing,false)
0mm
elseif axis[:letter] == :x
2mm + ptsz
else
8mm
end
end
# Set the (left, top, right, bottom) minimum padding around the plot area
# to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot)
leftpad = 10mm + sp[:left_margin] + guide_padding(sp[:yaxis])
toppad = 2mm + sp[:top_margin] + title_padding(sp)
rightpad = 3mm + sp[:right_margin]
bottompad = 5mm + sp[:bottom_margin] + guide_padding(sp[:xaxis])
# TODO: something different when `is3d(sp) == true`
leftpad = tick_padding(sp[:yaxis]) + sp[:left_margin] + guide_padding(sp[:yaxis])
toppad = sp[:top_margin] + title_padding(sp)
rightpad = sp[:right_margin]
bottompad = tick_padding(sp[:xaxis]) + sp[:bottom_margin] + guide_padding(sp[:xaxis])
# switch them?
if sp[:xaxis][:mirror]
bottompad, toppad = toppad, bottompad
end
if sp[:yaxis][:mirror]
leftpad, rightpad = rightpad, leftpad
end
# @show (leftpad, toppad, rightpad, bottompad)
sp.minpad = (leftpad, toppad, rightpad, bottompad)
end
@@ -127,8 +139,8 @@ function backend()
sym = CURRENT_BACKEND.sym
if !(sym in _initialized_backends)
# initialize
println("[Plots.jl] Initializing backend: ", sym)
# # initialize
# println("[Plots.jl] Initializing backend: ", sym)
inst = _backend_instance(sym)
try
@@ -151,12 +163,14 @@ function backend(pkg::AbstractBackend)
CURRENT_BACKEND.sym = backend_name(pkg)
warn_on_deprecated_backend(CURRENT_BACKEND.sym)
CURRENT_BACKEND.pkg = pkg
backend()
end
function backend(modname::Symbol)
warn_on_deprecated_backend(modname)
CURRENT_BACKEND.sym = modname
CURRENT_BACKEND.pkg = _backend_instance(modname)
backend()
end
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse]
@@ -167,23 +181,7 @@ function warn_on_deprecated_backend(bsym::Symbol)
end
end
# ---------------------------------------------------------
supported_types(::AbstractBackend) = []
supported_styles(::AbstractBackend) = [:solid]
supported_markers(::AbstractBackend) = [:none]
supported_scales(::AbstractBackend) = [:identity]
is_subplot_supported(::AbstractBackend) = false
is_string_supported(::AbstractBackend) = false
nativeImagesSupported(b::AbstractBackend) = :image in supported_types(b)
supported_types() = supported_types(backend())
supported_styles() = supported_styles(backend())
supported_markers() = supported_markers(backend())
supported_scales() = supported_scales(backend())
is_subplot_supported() = is_subplot_supported(backend())
is_string_supported() = is_string_supported(backend())
nativeImagesSupported() = nativeImagesSupported(backend())
# ---------------------------------------------------------
@@ -213,7 +211,7 @@ const _base_supported_args = [
:subplot_index,
:discrete_values,
:projection,
]
function merge_with_base_supported(v::AVec)
@@ -225,5 +223,47 @@ function merge_with_base_supported(v::AVec)
end
end
end
v
Set(v)
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
# ---------------------------------------------------------
# create the various `is_xxx_supported` and `supported_xxxs` methods
# by default they pass through to checking membership in `_gr_xxx`
for s in (:attr, :seriestype, :marker, :style, :scale)
f = Symbol("is_", s, "_supported")
f2 = Symbol("supported_", s, "s")
@eval begin
$f(::AbstractBackend, $s) = false
$f(bend::AbstractBackend, $s::AbstractVector) = all(v -> $f(bend, v), $s)
$f($s) = $f(backend(), $s)
$f2() = $f2(backend())
end
for bend in backends()
bend_type = typeof(_backend_instance(bend))
v = Symbol("_", bend, "_", s)
@eval begin
$f(::$bend_type, $s::Symbol) = $s in $v
$f2(::$bend_type) = $v
end
end
end
# is_subplot_supported(::AbstractBackend) = false
# is_subplot_supported() = is_subplot_supported(backend())
File diff suppressed because it is too large Load Diff
+322 -187
View File
@@ -3,7 +3,8 @@
# significant contributions by @jheinen
supported_args(::GRBackend) = merge_with_base_supported([
const _gr_attr = merge_with_base_supported([
:annotations,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
@@ -28,18 +29,18 @@ supported_args(::GRBackend) = merge_with_base_supported([
:aspect_ratio,
:normalize, :weights,
:inset_subplots,
:bar_width,
])
supported_types(::GRBackend) = [
const _gr_seriestype = [
:path, :scatter,
:heatmap, :pie, :image,
:contour, :path3d, :scatter3d, :surface, :wireframe,
:shape
]
supported_styles(::GRBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supported_markers(::GRBackend) = vcat(_allMarkers, Shape)
supported_scales(::GRBackend) = [:identity, :log10]
is_subplot_supported(::GRBackend) = true
const _gr_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
const _gr_marker = _allMarkers
const _gr_scale = [:identity, :log10]
is_marker_supported(::GRBackend, shape::Shape) = true
function _initialize_backend(::GRBackend; kw...)
@@ -74,6 +75,8 @@ const gr_markertype = KW(
:octagon => -24,
:cross => 2,
:xcross => 5,
:+ => 2,
:x => 5,
:star4 => -25,
:star5 => -26,
:star6 => -27,
@@ -107,47 +110,18 @@ const gr_font_family = Dict(
# --------------------------------------------------------------------------------------
function gr_getcolorind(v, a =nothing)
c = getColor(v)
idx = convert(Int, GR.inqcolorfromrgb(c.r, c.g, c.b))
GR.settransparency(float(a==nothing ? alpha(c) : a))
idx
function gr_getcolorind(c)
GR.settransparency(float(alpha(c)))
convert(Int, GR.inqcolorfromrgb(red(c), green(c), blue(c)))
end
gr_set_linecolor(c, a=nothing) = GR.setlinecolorind(gr_getcolorind(c, a))
gr_set_fillcolor(c, a=nothing) = GR.setfillcolorind(gr_getcolorind(c, a))
gr_set_markercolor(c, a=nothing) = GR.setmarkercolorind(gr_getcolorind(c, a))
gr_set_textcolor(c, a=nothing) = GR.settextcolorind(gr_getcolorind(c, a))
gr_set_linecolor(c) = GR.setlinecolorind(gr_getcolorind(cycle(c,1)))
gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(cycle(c,1)))
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(cycle(c,1)))
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(cycle(c,1)))
# --------------------------------------------------------------------------------------
function gr_setmarkershape(d)
if d[:markershape] != :none
shape = d[:markershape]
if isa(shape, Shape)
d[:vertices] = vertices(shape)
else
GR.setmarkertype(gr_markertype[shape])
d[:vertices] = :none
end
end
end
function gr_polymarker(d, x, y)
if d[:vertices] != :none
vertices= d[:vertices]
dx = Float64[el[1] for el in vertices] * 0.03
dy = Float64[el[2] for el in vertices] * 0.03
GR.selntran(0)
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
# draw line segments, splitting x/y into contiguous/finite segments
# note: this can be used for shapes by passing func `GR.fillarea`
@@ -185,6 +159,25 @@ function gr_polyline(x, y, func = GR.polyline)
end
end
function gr_inqtext(x, y, s)
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
GR.inqtextext(x, y, s[2:end-1])
elseif search(s, '\\') != 0 || search(s, '_') != 0 || search(s, '^') != 0
GR.inqtextext(x, y, s)
else
GR.inqtext(x, y, s)
end
end
function gr_text(x, y, s)
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
GR.mathtex(x, y, s[2:end-1])
elseif search(s, '\\') != 0 || search(s, '_') != 0 || search(s, '^') != 0
GR.textext(x, y, s)
else
GR.text(x, y, s)
end
end
function gr_polaraxes(rmin, rmax)
GR.savestate()
@@ -207,14 +200,14 @@ function gr_polaraxes(rmin, rmax)
GR.drawarc(-r, r, -r, r, 0, 359)
end
end
for alpha in 0:45:315
a = alpha + 90
for α in 0:45:315
a = α + 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"))
GR.textext(x, y, string(α, "^o"))
end
GR.restorestate()
end
@@ -260,59 +253,77 @@ function normalize_zvals(zv::AVec)
end
end
# ---------------------------------------------------------
function gr_draw_markers(d::KW, x, y, msize, mz, c, a)
if length(x) > 0
mz == nothing && gr_set_markercolor(c, a)
if typeof(msize) <: Number && mz == nothing
# draw the markers all the same
GR.setmarkersize(msize)
gr_polymarker(d, x, y)
else
# draw each marker differently
for i = 1:length(x)
if mz != nothing
ci = round(Int, 1000 + mz[i] * 255)
GR.setmarkercolorind(ci)
end
GR.setmarkersize(isa(msize, Number) ? msize : msize[mod1(i, length(msize))])
gr_polymarker(d, [x[i]], [y[i]])
# draw ONE Shape
function gr_draw_marker(xi, yi, msize, shape::Shape)
sx, sy = shape_coords(shape)
GR.selntran(0)
xi, yi = GR.wctondc(xi, yi)
GR.fillarea(xi + sx * 0.0015msize,
yi + sy * 0.0015msize)
GR.selntran(1)
end
# draw ONE symbol marker
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
GR.setmarkertype(gr_markertype[shape])
GR.setmarkersize(0.3msize)
GR.polymarker([xi], [yi])
end
# draw the markers, one at a time
function gr_draw_markers(series::Series, x, y, msize, mz)
shape = series[:markershape]
if shape != :none
for i=1:length(x)
msi = cycle(msize, i)
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
# draw a filled in shape, slightly bigger, to estimate a stroke
if series[:markerstrokewidth] > 0
cfunc(cycle(series[:markerstrokecolor], i)) #, series[:markerstrokealpha])
gr_draw_marker(x[i], y[i], msi + series[:markerstrokewidth], shape)
end
# draw the shape
if mz == nothing
cfunc(cycle(series[:markercolor], i)) #, series[:markeralpha])
else
# pick a color from the pre-loaded gradient
ci = round(Int, 1000 + cycle(mz, i) * 255)
cfuncind(ci)
GR.settransparency(_gr_gradient_alpha[ci-999])
end
gr_draw_marker(x[i], y[i], msi, shape)
end
end
end
function gr_draw_markers(series::Series, x, y)
d = series.d
msize = 0.5 * d[:markersize]
mz = normalize_zvals(d[:marker_z])
# draw the marker
gr_setmarkershape(d)
isempty(x) && return
mz = normalize_zvals(series[:marker_z])
GR.setfillintstyle(GR.INTSTYLE_SOLID)
gr_draw_markers(d, x, y, msize, mz, d[:markercolor], d[:markeralpha])
# # draw the stroke
# GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
# gr_draw_markers(d, x, y, msize, mz, d[:markerstrokecolor], d[:markerstrokealpha])
gr_draw_markers(series, x, y, series[:markersize], mz)
if mz != nothing
gr_colorbar(d[:subplot])
gr_colorbar(series[:subplot])
end
end
# ---------------------------------------------------------
function gr_set_line(w, style, c, a)
function gr_set_line(w, style, c) #, a)
GR.setlinetype(gr_linetype[style])
GR.setlinewidth(w)
gr_set_linecolor(c, a)
gr_set_linecolor(c) #, a)
end
function gr_set_fill(c, a)
gr_set_fillcolor(c, a)
function gr_set_fill(c) #, a)
gr_set_fillcolor(c) #, a)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
end
@@ -320,15 +331,15 @@ end
const _gr_point_mult = zeros(1)
# set the font attributes... assumes _gr_point_mult has been populated already
function gr_set_font(f::Font)
function gr_set_font(f::Font; halign = f.halign, valign = f.valign, color = f.color)
family = lowercase(f.family)
GR.setcharheight(_gr_point_mult[1] * f.pointsize)
GR.setcharup(sin(f.rotation), cos(f.rotation))
if haskey(gr_font_family, family)
GR.settextfontprec(100 + gr_font_family[family], GR.TEXT_PRECISION_STRING)
end
gr_set_textcolor(f.color)
GR.settextalign(gr_halign[f.halign], gr_valign[f.valign])
gr_set_textcolor(color)
GR.settextalign(gr_halign[halign], gr_valign[valign])
end
# --------------------------------------------------------------------------------------
@@ -395,13 +406,14 @@ gr_view_ydiff() = viewport_plotarea[4] - viewport_plotarea[3]
# --------------------------------------------------------------------------------------
const _gr_gradient_alpha = ones(256)
function gr_set_gradient(c, a)
grad = isa(c, ColorGradient) ? c : default_gradient()
grad = ColorGradient(grad, alpha=a)
function gr_set_gradient(c)
grad = isa(c, ColorGradient) ? c : cgrad()
for (i,z) in enumerate(linspace(0, 1, 256))
c = getColorZ(grad, z)
c = grad[z]
GR.setcolorrep(999+i, red(c), green(c), blue(c))
_gr_gradient_alpha[i] = alpha(c)
end
grad
end
@@ -439,7 +451,7 @@ function gr_display(plt::Plot)
# update point mult
px_per_pt = px / pt
_gr_point_mult[1] = px_per_pt / h
_gr_point_mult[1] = 1.5 * px_per_pt / max(h,w)
# subplots:
for sp in plt.subplots
@@ -454,9 +466,31 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# the viewports for this subplot
viewport_subplot = gr_viewport_from_bbox(bbox(sp), w, h, viewport_canvas)
viewport_plotarea[:] = gr_viewport_from_bbox(plotarea(sp), w, h, viewport_canvas)
# get data limits
data_lims = gr_xy_axislims(sp)
ratio = sp[:aspect_ratio]
if ratio != :none
if ratio == :equal
ratio = 1
end
viewport_ratio = (viewport_plotarea[2] - viewport_plotarea[1]) / (viewport_plotarea[4] - viewport_plotarea[3])
window_ratio = (data_lims[2] - data_lims[1]) / (data_lims[4] - data_lims[3]) / ratio
if window_ratio < viewport_ratio
viewport_center = 0.5 * (viewport_plotarea[1] + viewport_plotarea[2])
viewport_size = (viewport_plotarea[2] - viewport_plotarea[1]) * window_ratio / viewport_ratio
viewport_plotarea[1] = viewport_center - 0.5 * viewport_size
viewport_plotarea[2] = viewport_center + 0.5 * viewport_size
elseif window_ratio > viewport_ratio
viewport_center = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
viewport_size = (viewport_plotarea[4] - viewport_plotarea[3]) * viewport_ratio / window_ratio
viewport_plotarea[3] = viewport_center - 0.5 * viewport_size
viewport_plotarea[4] = viewport_center + 0.5 * viewport_size
end
end
# fill in the plot area background
bg = getColor(sp[:background_color_inside])
bg = plot_color(sp[:background_color_inside])
gr_fill_viewport(viewport_plotarea, bg)
# reduced from before... set some flags based on the series in this subplot
@@ -466,8 +500,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
draw_axes = true
# axes_2d = true
for series in series_list(sp)
st = series.d[:seriestype]
if st in (:contour, :surface, :heatmap) || series.d[:marker_z] != nothing
st = series[:seriestype]
if st in (:contour, :surface, :heatmap) || series[:marker_z] != nothing
cmap = true
end
if st == :pie
@@ -491,10 +525,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
yaxis = sp[:yaxis]
zaxis = sp[:zaxis]
# get data limits and set the scale flags and window
data_lims = gr_xy_axislims(sp)
# set the scale flags and window
xmin, xmax, ymin, ymax = data_lims
scale = 0
xtick, ytick = 1, 1
if xmax > xmin && ymax > ymin
# NOTE: for log axes, the major_x and major_y - if non-zero (omit labels) - control the minor grid lines (1 = draw 9 minor grid lines, 2 = no minor grid lines)
# NOTE: for log axes, the x_tick and y_tick - if non-zero (omit axes) - only affect the output appearance (1 = nomal, 2 = scientiic notation)
@@ -531,6 +565,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if is3d(sp)
zmin, zmax = gr_lims(zaxis, true)
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (zmin = clims[1])
isfinite(clims[2]) && (zmax = clims[2])
end
GR.setspace(zmin, zmax, 40, 70)
xtick = GR.tick(xmin, xmax) / 2
ytick = GR.tick(ymin, ymax) / 2
@@ -548,29 +587,69 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
elseif ispolar(sp)
r = gr_set_viewport_polar()
rmin, rmax = GR.adjustrange(minimum(r), maximum(r))
# rmin, rmax = axis_limits(sp[:yaxis])
gr_polaraxes(rmin, rmax)
elseif draw_axes
if xmax > xmin && ymax > ymin
GR.setwindow(xmin, xmax, ymin, ymax)
end
xticks, yticks, spine_segs, grid_segs = axis_drawing_info(sp)
# @show xticks yticks #spine_segs grid_segs
# draw the grid lines
# TODO: control line style/width
# GR.setlinetype(GR.LINETYPE_DOTTED)
if sp[:grid]
gr_set_linecolor(sp[:foreground_color_grid])
GR.grid(xtick, ytick, 0, 0, majorx, majory)
# gr_set_linecolor(sp[:foreground_color_grid])
# GR.grid(xtick, ytick, 0, 0, majorx, majory)
gr_set_line(1, :dot, sp[:foreground_color_grid])
GR.settransparency(0.5)
gr_polyline(coords(grid_segs)...)
end
GR.settransparency(1.0)
# spine (border) and tick marks
gr_set_line(1, :solid, sp[:xaxis][:foreground_color_axis])
gr_polyline(coords(spine_segs)...)
if !(xticks in (nothing, false))
# x labels
flip = sp[:yaxis][:flip]
mirror = sp[:xaxis][:mirror]
gr_set_font(sp[:xaxis][:tickfont], valign = (mirror ? :bottom : :top), color = sp[:xaxis][:foreground_color_axis])
for (cv, dv) in zip(xticks...)
# use xor ($) to get the right y coords
xi, yi = GR.wctondc(cv, (flip $ mirror) ? ymax : ymin)
# @show cv dv ymin xi yi flip mirror (flip $ mirror)
gr_text(xi, yi + (mirror ? 1 : -1) * 0.01, string(dv))
end
end
window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
ticksize = 0.0075 * window_diag
if outside_ticks
ticksize = -ticksize
if !(yticks in (nothing, false))
# y labels
flip = sp[:xaxis][:flip]
mirror = sp[:yaxis][:mirror]
gr_set_font(sp[:yaxis][:tickfont], halign = (mirror ? :left : :right), color = sp[:yaxis][:foreground_color_axis])
for (cv, dv) in zip(yticks...)
# use xor ($) to get the right y coords
xi, yi = GR.wctondc((flip $ mirror) ? xmax : xmin, cv)
# @show cv dv xmin xi yi
gr_text(xi + (mirror ? 1 : -1) * 0.01, yi, string(dv))
end
end
# TODO: this should be done for each axis separately
gr_set_linecolor(xaxis[:foreground_color_axis])
x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
# window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
# ticksize = 0.0075 * window_diag
# if outside_ticks
# ticksize = -ticksize
# end
# # TODO: this should be done for each axis separately
# gr_set_linecolor(xaxis[:foreground_color_axis])
# x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
# y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
# GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
# GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
end
# end
@@ -580,14 +659,14 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
gr_set_font(sp[:titlefont])
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
gr_set_textcolor(sp[:foreground_color_title])
GR.text(gr_view_xcenter(), viewport_subplot[4], sp[:title])
gr_text(gr_view_xcenter(), viewport_subplot[4], sp[:title])
end
if xaxis[:guide] != ""
gr_set_font(xaxis[:guidefont])
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM)
gr_set_textcolor(xaxis[:foreground_color_guide])
GR.text(gr_view_xcenter(), viewport_subplot[3], xaxis[:guide])
gr_text(gr_view_xcenter(), viewport_subplot[3], xaxis[:guide])
end
if yaxis[:guide] != ""
@@ -595,27 +674,27 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
GR.setcharup(-1, 0)
gr_set_textcolor(yaxis[:foreground_color_guide])
GR.text(viewport_subplot[1], gr_view_ycenter(), yaxis[:guide])
gr_text(viewport_subplot[1], gr_view_ycenter(), yaxis[:guide])
end
GR.restorestate()
# TODO: can we remove?
gr_set_font(xaxis[:tickfont])
GR.setcolormap(1000 + GR.COLORMAP_COOLWARM)
# GR.setcolormap(1000 + GR.COLORMAP_COOLWARM)
for (idx, series) in enumerate(series_list(sp))
d = series.d
st = d[:seriestype]
st = series[:seriestype]
# update the current stored gradient
if st in (:contour, :surface, :wireframe, :heatmap)
gr_set_gradient(d[:fillcolor], d[:fillalpha])
elseif d[:marker_z] != nothing
d[:markercolor] = gr_set_gradient(d[:markercolor], d[:markeralpha])
gr_set_gradient(series[:fillcolor]) #, series[:fillalpha])
elseif series[:marker_z] != nothing
series[:markercolor] = gr_set_gradient(series[:markercolor])
end
GR.savestate()
# update the bounding window
if ispolar(sp)
gr_set_viewport_polar()
@@ -626,12 +705,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
end
x, y, z = d[:x], d[:y], d[:z]
frng = d[:fillrange]
x, y, z = series[:x], series[:y], series[:z]
frng = series[:fillrange]
# recompute data
if st in (:contour, :surface, :wireframe)
z = vec(transpose_z(d, z.surf, false))
if typeof(z) <: Surface
if st == :heatmap
expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
end
z = vec(transpose_z(series, z.surf, false))
elseif ispolar(sp)
if frng != nothing
_, frng = convert_to_polar(x, frng, (rmin, rmax))
@@ -641,48 +724,51 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if st in (:path, :scatter)
if length(x) > 1
# do area fill
if frng != nothing
gr_set_fillcolor(d[:fillcolor], d[:fillalpha])
GR.setfillintstyle(GR.INTSTYLE_SOLID)
frng = isa(frng, Number) ? Float64[frng] : frng
nx, ny, nf = length(x), length(y), length(frng)
n = max(nx, ny)
fx, fy = zeros(2n), zeros(2n)
for i=1:n
fx[i] = fx[end-i+1] = cycle(x,i)
fy[i] = cycle(y,i)
fy[end-i+1] = cycle(frng,i)
fr_from, fr_to = (is_2tuple(frng) ? frng : (y, frng))
for (i,rng) in enumerate(iter_segments(series[:x], series[:y]))
if length(rng) > 1
gr_set_fillcolor(cycle(series[:fillcolor], i))
fx = cycle(x, vcat(rng, reverse(rng)))
fy = vcat(cycle(fr_from,rng), cycle(fr_to,reverse(rng)))
# @show i rng fx fy
GR.fillarea(fx, fy)
end
end
GR.fillarea(fx, fy)
end
# draw the line(s)
if st == :path
gr_set_line(d[:linewidth], d[:linestyle], d[:linecolor], d[:linealpha])
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
gr_polyline(x, y)
end
end
if d[:markershape] != :none
if series[:markershape] != :none
gr_draw_markers(series, x, y)
end
elseif st == :contour
zmin, zmax = gr_lims(zaxis, false)
if typeof(d[:levels]) <: Array
h = d[:levels]
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (zmin = clims[1])
isfinite(clims[2]) && (zmax = clims[2])
end
if typeof(series[:levels]) <: Array
h = series[:levels]
else
h = linspace(zmin, zmax, d[:levels])
h = linspace(zmin, zmax, series[:levels])
end
GR.setspace(zmin, zmax, 0, 90)
if d[:fillrange] != nothing
if series[:fillrange] != nothing
GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
else
GR.contour(x, y, h, z, 1000)
end
# create the colorbar of contour levels
if sp[:colorbar] != :none
gr_set_viewport_cmap(sp)
@@ -696,7 +782,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
elseif st in [:surface, :wireframe]
if st == :surface
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
if length(x) == length(y) == length(z)
GR.trisurface(x, y, z)
else
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
end
else
GR.setfillcolorind(0)
GR.surface(x, y, z, GR.OPTION_FILLED_MESH)
@@ -704,23 +794,29 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
cmap && gr_colorbar(sp)
elseif st == :heatmap
z = vec(transpose_z(d, z.surf, false))
# z = vec(transpose_z(series, z.surf, false))
zmin, zmax = gr_lims(zaxis, true)
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (zmin = clims[1])
isfinite(clims[2]) && (zmax = clims[2])
end
GR.setspace(zmin, zmax, 0, 90)
GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
# GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
GR.surface(x, y, z, GR.OPTION_HEATMAP)
cmap && gr_colorbar(sp)
elseif st in (:path3d, :scatter3d)
# draw path
if st == :path3d
if length(x) > 1
gr_set_line(d[:linewidth], d[:linestyle], d[:linecolor], d[:linealpha])
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
GR.polyline3d(x, y, z)
end
end
# draw markers
if st == :scatter3d || d[:markershape] != :none
if st == :scatter3d || series[:markershape] != :none
x2, y2 = unzip(map(GR.wc3towc, x, y, z))
gr_draw_markers(series, x2, y2)
end
@@ -741,7 +837,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
ymin, ymax = ycenter - r, ycenter + r
end
labels = pie_labels(sp, series)
slices = d[:y]
slices = series[:y]
numslices = length(slices)
total = sum(slices)
a1 = 0
@@ -751,22 +847,22 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
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)
α = 0.5 * (a1 + a2)
cosf = r * cos(α * pi / 180)
sinf = r * sin(α * 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
if 90 <= α < 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]))
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]))
gr_text(x[3] + 0.01, y[3], string(labels[i]))
end
gr_polyline(x, y)
a1 = a2
@@ -774,27 +870,36 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.selntran(1)
elseif st == :shape
# draw the shapes
gr_set_line(d[:markerstrokewidth], :solid, d[:markerstrokecolor], d[:markerstrokealpha])
gr_polyline(d[:x], d[:y])
for (i,rng) in enumerate(iter_segments(series[:x], series[:y]))
if length(rng) > 1
# connect to the beginning
rng = vcat(rng, rng[1])
# draw the interior
gr_set_fill(d[:markercolor], d[:markeralpha])
gr_polyline(d[:x], d[:y], GR.fillarea)
# get the segments
x, y = series[:x][rng], series[:y][rng]
# draw the interior
gr_set_fill(cycle(series[:fillcolor], i))
GR.fillarea(x, y)
# draw the shapes
gr_set_line(series[:linewidth], :solid, cycle(series[:linecolor], i))
GR.polyline(x, y)
end
end
elseif st == :image
img = d[:z].surf
w, h = size(img)
if eltype(img) <: Colors.AbstractGray
grey = round(UInt8, float(img) * 255)
img = series[:z].surf
h, w = size(img)
if eltype(z) <: Colors.AbstractGray
grey = round(UInt8, float(z) * 255)
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
else
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
round(Int, blue(c) * 255) << 16 +
round(Int, green(c) * 255) << 8 +
round(Int, red(c) * 255) ), img)
round(Int, red(c) * 255) ), z)
end
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
end
@@ -815,13 +920,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
for series in series_list(sp)
should_add_to_legend(series) || continue
n += 1
if typeof(series.d[:label]) <: Array
if typeof(series[:label]) <: Array
i += 1
lab = series.d[:label][i]
lab = series[:label][i]
else
lab = series.d[:label]
lab = series[:label]
end
tbx, tby = GR.inqtext(0, 0, lab)
tbx, tby = gr_inqtext(0, 0, lab)
w = max(w, tbx[3] - tbx[1])
end
if w > 0
@@ -837,32 +942,33 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
i = 0
for series in series_list(sp)
should_add_to_legend(series) || continue
d = series.d
st = d[:seriestype]
GR.setlinewidth(d[:linewidth])
st = series[:seriestype]
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
if st == :path
gr_set_linecolor(d[:linecolor], d[:linealpha])
GR.setlinetype(gr_linetype[d[:linestyle]])
GR.polyline([xpos - 0.07, xpos - 0.01], [ypos, ypos])
elseif st == :shape
gr_set_fill(series[:fillcolor]) #, series[:fillalpha])
l, r = xpos-0.07, xpos-0.01
b, t = ypos-0.4dy, ypos+0.4dy
x = [l, r, r, l, l]
y = [b, b, t, t, b]
gr_polyline(x, y, GR.fillarea)
gr_polyline(x, y)
end
if st == :scatter || d[:markershape] != :none
gr_set_markercolor(d[:markercolor], d[:markeralpha])
gr_setmarkershape(d)
if st == :path
gr_polymarker(d, [xpos - 0.06, xpos - 0.02], [ypos, ypos])
else
gr_polymarker(d, [xpos - 0.06, xpos - 0.04, xpos - 0.02], [ypos, ypos, ypos])
end
if series[:markershape] != :none
gr_draw_markers(series, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
end
if typeof(d[:label]) <: Array
if typeof(series[:label]) <: Array
i += 1
lab = d[:label][i]
lab = series[:label][i]
else
lab = d[:label]
lab = series[:label]
end
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
gr_set_textcolor(sp[:foreground_color_legend])
GR.text(xpos, ypos, lab)
gr_text(xpos, ypos, lab)
ypos -= dy
end
end
@@ -872,11 +978,24 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# add annotations
GR.savestate()
# update the bounding window
if ispolar(sp)
gr_set_viewport_polar()
else
xmin, xmax, ymin, ymax = data_lims
if xmax > xmin && ymax > ymin
GR.setwindow(xmin, xmax, ymin, ymax)
end
end
for ann in sp[:annotations]
x, y, val = ann
x, y = GR.wctondc(x, y)
x, y = if is3d(sp)
# GR.wc3towc(x, y, z)
else
GR.wctondc(x, y)
end
gr_set_font(val.font)
GR.text(x, y, val.str)
gr_text(x, y, val.str)
end
GR.restorestate()
end
@@ -893,17 +1012,33 @@ const _gr_mimeformats = Dict(
for (mime, fmt) in _gr_mimeformats
@eval function _writemime(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
GR.emergencyclosegks()
wstype = haskey(ENV, "GKS_WSTYPE") ? ENV["GKS_WSTYPE"] : "0"
filepath = tempname() * "." * $fmt
ENV["GKS_WSTYPE"] = $fmt
ENV["GKS_FILEPATH"] = filepath
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks." * $fmt))
write(io, readstring(filepath))
ENV["GKS_WSTYPE"] = wstype
rm(filepath)
end
end
function _display(plt::Plot{GRBackend})
gr_display(plt)
if plt[:display_type] == :inline
GR.emergencyclosegks()
filepath = tempname() * ".pdf"
ENV["GKS_WSTYPE"] = "pdf"
ENV["GKS_FILEPATH"] = filepath
gr_display(plt)
GR.emergencyclosegks()
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(readbytes, filepath)), "\a")
println(content)
rm(filepath)
else
ENV["GKS_DOUBLE_BUF"] = "true"
gr_display(plt)
end
end
+34 -25
View File
@@ -2,7 +2,7 @@
# significant contributions by: @pkofod
supported_args(::PGFPlotsBackend) = merge_with_base_supported([
const _pgfplots_attr = merge_with_base_supported([
# :annotations,
# :background_color_legend,
:background_color_inside,
@@ -32,11 +32,10 @@ supported_args(::PGFPlotsBackend) = merge_with_base_supported([
:aspect_ratio,
# :match_dimensions,
])
supported_types(::PGFPlotsBackend) = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
supported_styles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supported_markers(::PGFPlotsBackend) = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
supported_scales(::PGFPlotsBackend) = [:identity, :ln, :log2, :log10]
is_subplot_supported(::PGFPlotsBackend) = false
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
# --------------------------------------------------------------------------------------
@@ -64,6 +63,8 @@ const _pgfplots_markers = KW(
:none => "none",
:cross => "+",
:xcross => "x",
:+ => "+",
:x => "x",
:utriangle => "triangle*",
:dtriangle => "triangle*",
:circle => "*",
@@ -94,20 +95,18 @@ const _pgf_series_extrastyle = KW(
# --------------------------------------------------------------------------------------
# takes in color,alpha, and returns color and alpha appropriate for pgf style
function pgf_color(c, a = nothing)
c = getColor(c)
function pgf_color(c)
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c))
a = float(a == nothing ? alpha(c) : a)
cstr, a
cstr, alpha(c)
end
function pgf_fillstyle(d::KW)
cstr,a = pgf_color(d[:fillcolor], d[:fillalpha])
cstr,a = pgf_color(d[:fillcolor])
"fill = $cstr, fill opacity=$a"
end
function pgf_linestyle(d::KW)
cstr,a = pgf_color(d[:linecolor], d[:linealpha])
cstr,a = pgf_color(d[:linecolor])
"""
color = $cstr,
draw opacity=$a,
@@ -117,8 +116,8 @@ end
function pgf_marker(d::KW)
shape = d[:markershape]
cstr, a = pgf_color(d[:markercolor], d[:markeralpha])
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor], d[:markerstrokealpha])
cstr, a = pgf_color(d[:markercolor])
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor])
"""
mark = $(get(_pgfplots_markers, shape, "*")),
mark size = $(0.5 * d[:markersize]),
@@ -149,6 +148,8 @@ function pgf_series(sp::Subplot, series::Series)
# add to legend?
if sp[:legend] != :none && should_add_to_legend(series)
kw[:legendentry] = d[:label]
else
push!(style, "forget plot")
end
# function args
@@ -214,6 +215,11 @@ function pgf_axis(sp::Subplot, letter)
scale == :ln || push!(style, "log basis $letter=$(scale == :log2 ? 2 : 10)")
end
# ticks on or off
if axis[:ticks] in (nothing, false)
push!(style, "$(letter)majorticks=false")
end
# limits
# TODO: support zlims
if letter != :z
@@ -222,6 +228,12 @@ function pgf_axis(sp::Subplot, letter)
kw[Symbol(letter,:max)] = lims[2]
end
if !(axis[:ticks] in (nothing, false, :none, :auto))
ticks = get_ticks(axis)
push!(style, string(letter, "tick = {", join(ticks[1],","), "}"))
push!(style, string(letter, "ticklabels = {", join(ticks[2],","), "}"))
end
# return the style list and KW args
style, kw
end
@@ -229,7 +241,7 @@ end
# ----------------------------------------------------------------
function _make_pgf_plot!(plt::Plot)
function _update_plot_object(plt::Plot{PGFPlotsBackend})
plt.o = PGFPlots.Axis[]
for sp in plt.subplots
# first build the PGFPlots.Axis object
@@ -240,6 +252,7 @@ function _make_pgf_plot!(plt::Plot)
for letter in (:x, :y, :z)
if letter != :z || is3d(sp)
axisstyle, axiskw = pgf_axis(sp, letter)
append!(style, axisstyle)
merge!(kw, axiskw)
end
end
@@ -250,13 +263,13 @@ function _make_pgf_plot!(plt::Plot)
push!(style, """
xshift = $(left(bb).value)mm,
yshift = $((height(bb) - (bottom(bb))).value)mm,
width = $(width(bb).value)mm,
height = $(height(bb).value)mm,
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
""")
kw[:width] = "$(width(bb).value)mm"
kw[:height] = "$(height(bb).value)mm"
if sp[:title] != ""
push!(style, "title = $(sp[:title])")
kw[:title] = "$(sp[:title])"
end
sp[:grid] && push!(style, "grid = major")
@@ -282,14 +295,11 @@ function _make_pgf_plot!(plt::Plot)
end
function _writemime(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
writemime(io, mime, plt.o)
function _show(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
show(io, mime, plt.o)
end
function _writemime(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
# prepare the object
pgfplt = PGFPlots.plot(plt.o)
@@ -306,7 +316,6 @@ end
function _display(plt::Plot{PGFPlotsBackend})
# prepare the object
_make_pgf_plot!(plt)
pgfplt = PGFPlots.plot(plt.o)
# save an svg
+135 -108
View File
@@ -1,7 +1,7 @@
# https://plot.ly/javascript/getting-started
supported_args(::PlotlyBackend) = merge_with_base_supported([
const _plotly_attr = merge_with_base_supported([
:annotations,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_legend, :foreground_color_guide,
@@ -31,29 +31,34 @@ supported_args(::PlotlyBackend) = merge_with_base_supported([
:inset_subplots,
])
supported_types(::PlotlyBackend) = [
const _plotly_seriestype = [
:path, :scatter, :bar, :pie, :heatmap,
:contour, :surface, :path3d, :scatter3d, :shape, :scattergl,
]
supported_styles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supported_markers(::PlotlyBackend) = [
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
const _plotly_marker = [
:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle,
:cross, :xcross, :pentagon, :hexagon, :octagon, :vline, :hline
]
supported_scales(::PlotlyBackend) = [:identity, :log10]
const _plotly_scale = [:identity, :log10]
is_subplot_supported(::PlotlyBackend) = true
is_string_supported(::PlotlyBackend) = true
# is_string_supported(::PlotlyBackend) = true
# --------------------------------------------------------------------------------------
function add_backend(::PlotlyBackend)
Pkg.build("Plots")
end
const _plotly_js_path = joinpath(dirname(@__FILE__), "..", "..", "deps", "plotly-latest.min.js")
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(@compat(readstring), _js_path, "r")
_js_code = open(readstring, _plotly_js_path, "r")
# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2
_js_script = """
@@ -88,7 +93,7 @@ function plotly_font(font::Font, color = font.color)
KW(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
:color => webcolor(color),
:color => rgba_string(color),
)
end
@@ -126,7 +131,7 @@ end
# # :ay => -40,
# :ax => 10xdiff / dist,
# :ay => -10ydiff / dist,
# :arrowcolor => webcolor(d[:linecolor], d[:linealpha]),
# :arrowcolor => rgba_string(d[:linecolor]),
# :xref => "x",
# :yref => "y",
# :arrowsize => 10a.headwidth,
@@ -175,8 +180,8 @@ function plotly_axis(axis::Axis, sp::Subplot)
ax[:titlefont] = plotly_font(axis[:guidefont], axis[:foreground_color_guide])
ax[:type] = plotly_scale(axis[:scale])
ax[:tickfont] = plotly_font(axis[:tickfont], axis[:foreground_color_text])
ax[:tickcolor] = webcolor(axis[:foreground_color_border])
ax[:linecolor] = webcolor(axis[:foreground_color_border])
ax[:tickcolor] = rgba_string(axis[:foreground_color_border])
ax[:linecolor] = rgba_string(axis[:foreground_color_border])
# lims
lims = axis[:lims]
@@ -190,7 +195,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
end
# ticks
ticks = axis[:ticks]
ticks = get_ticks(axis)
if ticks != :auto
ttype = ticksType(ticks)
if ttype == :ticks
@@ -214,7 +219,7 @@ function plotly_layout(plt::Plot)
w, h = plt[:size]
d_out[:width], d_out[:height] = w, h
d_out[:paper_bgcolor] = webcolor(plt[:background_color_outside])
d_out[:paper_bgcolor] = rgba_string(plt[:background_color_outside])
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>20)
d_out[:annotations] = KW[]
@@ -239,7 +244,7 @@ function plotly_layout(plt::Plot)
push!(d_out[:annotations], plotly_annotation_dict(titlex, titley, text(sp[:title], titlefont)))
end
d_out[:plot_bgcolor] = webcolor(sp[:background_color_inside])
d_out[:plot_bgcolor] = rgba_string(sp[:background_color_inside])
# TODO: x/y axis tick values/labels
@@ -259,8 +264,8 @@ function plotly_layout(plt::Plot)
d_out[:showlegend] = sp[:legend] != :none
if sp[:legend] != :none
d_out[:legend] = KW(
:bgcolor => webcolor(sp[:background_color_legend]),
:bordercolor => webcolor(sp[:foreground_color_legend]),
:bgcolor => rgba_string(sp[:background_color_legend]),
:bordercolor => rgba_string(sp[:foreground_color_legend]),
:font => plotly_font(sp[:legendfont], sp[:foreground_color_legend]),
)
end
@@ -298,14 +303,16 @@ function plotly_layout_json(plt::Plot)
end
function plotly_colorscale(grad::ColorGradient, alpha = nothing)
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
function plotly_colorscale(grad::ColorGradient, α)
[[grad.values[i], rgb_string(grad.colors[i])] for i in 1:length(grad.colors)]
end
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(default_gradient(), alpha)
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
# plotly_colorscale(c, alpha = nothing) = plotly_colorscale(cgrad(), alpha)
const _plotly_markers = KW(
:rect => "square",
:xcross => "x",
:x => "x",
:utriangle => "triangle-up",
:dtriangle => "triangle-down",
:star5 => "star-triangle-up",
@@ -330,10 +337,18 @@ function plotly_close_shapes(x, y)
nanvcat(xs), nanvcat(ys)
end
plotly_data(v) = collect(v)
plotly_data(surf::Surface) = surf.surf
plotly_data{R<:Rational}(v::AbstractArray{R}) = float(v)
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
function plotly_series(plt::Plot, series::Series)
d = series.d
sp = d[:subplot]
st = series[:seriestype]
if st == :shape
return plotly_series_shapes(plt, series)
end
sp = series[:subplot]
d_out = KW()
# these are the axes that the series should be mapped to
@@ -342,12 +357,11 @@ function plotly_series(plt::Plot, series::Series)
d_out[:yaxis] = "y$spidx"
d_out[:showlegend] = should_add_to_legend(series)
x, y = collect(d[:x]), collect(d[:y])
d_out[:name] = d[:label]
st = d[:seriestype]
x, y = plotly_data(series[:x]), plotly_data(series[:y])
d_out[:name] = series[:label]
isscatter = st in (:scatter, :scatter3d, :scattergl)
hasmarker = isscatter || d[:markershape] != :none
# hasline = !isscatter
hasmarker = isscatter || series[:markershape] != :none
hasline = st in (:path, :path3d)
# set the "type"
@@ -358,76 +372,36 @@ function plotly_series(plt::Plot, series::Series)
else
hasline ? "lines" : "none"
end
if d[:fillrange] == true || d[:fillrange] == 0
if series[:fillrange] == true || series[: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])")
d_out[:fillcolor] = rgba_string(series[:fillcolor])
elseif !(series[:fillrange] in (false, nothing))
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(series[:fillrange])")
end
d_out[:x], d_out[:y] = x, y
elseif st == :shape
# to draw polygons, we actually draw lines with fill
d_out[:type] = "scatter"
d_out[:mode] = "lines"
d_out[:x], d_out[:y] = plotly_close_shapes(x, y)
# @show map(length, (x,y,d_out[:x],d_out[:y]))
# @show d_out[:x] d_out[:y]
d_out[:fill] = "tozeroy"
d_out[:fillcolor] = webcolor(d[:markercolor], d[:markeralpha])
if d[:markerstrokewidth] > 0
d_out[:line] = KW(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth],
:dash => string(d[:markerstrokestyle]),
)
end
elseif st == :bar
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
d_out[:orientation] = isvertical(d) ? "v" : "h"
# elseif st == :histogram2d
# 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
# d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
# elseif st in (:histogram, :density)
# d_out[:type] = "histogram"
# isvert = isvertical(d)
# d_out[isvert ? :x : :y] = y
# d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
# if st == :density
# d_out[:histogramnorm] = "probability density"
# end
d_out[:orientation] = isvertical(series) ? "v" : "h"
elseif st == :heatmap
d_out[:type] = "heatmap"
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
elseif st == :contour
d_out[:type] = "contour"
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
# 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])
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
# d_out[:showscale] = series[:colorbar] != :none
d_out[:ncontours] = series[:levels]
d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines")
d_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
elseif st in (:surface, :wireframe)
d_out[:type] = "surface"
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
elseif st == :pie
d_out[:type] = "pie"
@@ -443,7 +417,7 @@ function plotly_series(plt::Plot, series::Series)
hasline ? "lines" : "none"
end
d_out[:x], d_out[:y] = x, y
d_out[:z] = collect(d[:z])
d_out[:z] = plotly_data(series[:z])
else
warn("Plotly: seriestype $st isn't supported.")
@@ -453,32 +427,32 @@ function plotly_series(plt::Plot, series::Series)
# 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]),
:symbol => get(_plotly_markers, series[:markershape], string(series[:markershape])),
# :opacity => series[:markeralpha],
:size => 2 * series[:markersize],
# :color => rgba_string(series[:markercolor]),
:line => KW(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth],
:color => rgba_string(series[:markerstrokecolor]),
:width => series[: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]]
d_out[:marker][:color] = if series[:marker_z] == nothing
rgba_string(series[:markercolor])
else
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
grad = series[:markercolor]
zmin, zmax = extrema(series[:marker_z])
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]]
end
end
# add "line"
if hasline
d_out[:line] = KW(
:color => webcolor(d[:linecolor], d[:linealpha]),
:width => d[:linewidth],
:color => rgba_string(series[:linecolor]),
:width => series[:linewidth],
:shape => if st == :steppre
"vh"
elseif st == :steppost
@@ -486,38 +460,91 @@ function plotly_series(plt::Plot, series::Series)
else
"linear"
end,
:dash => string(d[:linestyle]),
:dash => string(series[:linestyle]),
# :dash => "solid",
)
end
plotly_polar!(d_out, series)
plotly_hover!(d_out, series[:hover])
[d_out]
end
function plotly_series_shapes(plt::Plot, series::Series)
d_outs = []
# TODO: create a d_out for each polygon
# x, y = series[:x], series[:y]
# these are the axes that the series should be mapped to
spidx = plotly_subplot_index(series[:subplot])
base_d = KW()
base_d[:xaxis] = "x$spidx"
base_d[:yaxis] = "y$spidx"
base_d[:name] = series[:label]
# base_d[:legendgroup] = series[:label]
x, y = plotly_data(series[:x]), plotly_data(series[:y])
for (i,rng) in enumerate(iter_segments(x,y))
length(rng) < 2 && continue
# to draw polygons, we actually draw lines with fill
d_out = merge(base_d, KW(
:type => "scatter",
:mode => "lines",
:x => vcat(x[rng], x[rng[1]]),
:y => vcat(y[rng], y[rng[1]]),
:fill => "tozeroy",
:fillcolor => rgba_string(cycle(series[:fillcolor], i)),
))
if series[:markerstrokewidth] > 0
d_out[:line] = KW(
:color => rgba_string(cycle(series[:linecolor], i)),
:width => series[:linewidth],
:dash => string(series[:linestyle]),
)
end
d_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
plotly_polar!(d_out, series)
plotly_hover!(d_out, cycle(series[:hover], i))
push!(d_outs, d_out)
end
d_outs
end
function plotly_polar!(d_out::KW, series::Series)
# convert polar plots x/y to theta/radius
if ispolar(d[:subplot])
if ispolar(series[:subplot])
d_out[:t] = rad2deg(pop!(d_out, :x))
d_out[:r] = pop!(d_out, :y)
end
end
function plotly_hover!(d_out::KW, hover)
# hover text
hover = d[:hover]
if hover in (:none, false)
d_out[:hoverinfo] = "none"
elseif hover != nothing
d_out[:hoverinfo] = "text"
d_out[:text] = hover
end
d_out
end
# get a list of dictionaries, each representing the series params
function plotly_series_json(plt::Plot)
JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
slist = []
for series in plt.series_list
append!(slist, plotly_series(plt, series))
end
JSON.json(slist)
# JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
end
# ----------------------------------------------------------------
function html_head(plt::Plot{PlotlyBackend})
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
"<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
end
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
@@ -547,11 +574,11 @@ end
# ----------------------------------------------------------------
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
writemime_png_from_html(io, plt)
function _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
show_png_from_html(io, plt)
end
function _writemime(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
write(io, html_head(plt) * html_body(plt))
end
+27 -13
View File
@@ -1,15 +1,27 @@
# https://github.com/spencerlyon2/PlotlyJS.jl
supported_args(::PlotlyJSBackend) = supported_args(PlotlyBackend())
supported_types(::PlotlyJSBackend) = supported_types(PlotlyBackend())
supported_styles(::PlotlyJSBackend) = supported_styles(PlotlyBackend())
supported_markers(::PlotlyJSBackend) = supported_markers(PlotlyBackend())
is_subplot_supported(::PlotlyJSBackend) = true
is_string_supported(::PlotlyJSBackend) = true
const _plotlyjs_attr = _plotly_attr
const _plotlyjs_seriestype = _plotly_seriestype
const _plotlyjs_style = _plotly_style
const _plotlyjs_marker = _plotly_marker
const _plotlyjs_scale = _plotly_scale
# --------------------------------------------------------------------------------------
function add_backend(::PlotlyJSBackend)
if !is_installed("PlotlyJS")
Pkg.add("PlotlyJS")
end
if !is_installed("Rsvg")
Pkg.add("Rsvg")
end
@eval import Blink
Blink.AtomShell.install()
end
function _initialize_backend(::PlotlyJSBackend; kw...)
@eval begin
import PlotlyJS
@@ -32,10 +44,12 @@ end
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
syncplot = plt.o
pdict = plotly_series(plt, series)
typ = pop!(pdict, :type)
gt = PlotlyJS.GenericTrace(typ; pdict...)
PlotlyJS.addtraces!(syncplot, gt)
pdicts = plotly_series(plt, series)
for pdict in pdicts
typ = pop!(pdict, :type)
gt = PlotlyJS.GenericTrace(typ; pdict...)
PlotlyJS.addtraces!(syncplot, gt)
end
end
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
@@ -60,11 +74,11 @@ end
# ----------------------------------------------------------------
function _writemime(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
writemime(io, MIME("text/html"), plt.o)
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
show(io, MIME("text/html"), plt.o)
end
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
function _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
tmpfn = tempname() * ".png"
PlotlyJS.savefig(plt.o, tmpfn)
write(io, read(open(tmpfn)))
+307 -373
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -67,7 +67,7 @@ end
# "image/png" => "png",
# "application/postscript" => "ps",
# "image/svg+xml" => "svg"
function _writemime(io::IO, ::MIME"image/png", plt::Plot{[PkgName]Backend})
function _show(io::IO, ::MIME"image/png", plt::Plot{[PkgName]Backend})
end
# Display/show the plot (open a GUI window, or browser page, for example).
+33 -22
View File
@@ -1,7 +1,7 @@
# https://github.com/Evizero/UnicodePlots.jl
supported_args(::UnicodePlotsBackend) = merge_with_base_supported([
const _unicodeplots_attr = merge_with_base_supported([
:label,
:legend,
:seriescolor,
@@ -12,18 +12,19 @@ supported_args(::UnicodePlotsBackend) = merge_with_base_supported([
:title,
:guide, :lims,
])
supported_types(::UnicodePlotsBackend) = [
const _unicodeplots_seriestype = [
:path, :scatter,
# :bar,
:shape,
:histogram2d
]
supported_styles(::UnicodePlotsBackend) = [:auto, :solid]
supported_markers(::UnicodePlotsBackend) = [:none, :auto, :circle]
supported_scales(::UnicodePlotsBackend) = [:identity]
is_subplot_supported(::UnicodePlotsBackend) = true
const _unicodeplots_style = [:auto, :solid]
const _unicodeplots_marker = [:none, :auto, :circle]
const _unicodeplots_scale = [:identity]
# don't warn on unsupported... there's just too many warnings!!
warnOnUnsupported_args(pkg::UnicodePlotsBackend, d::KW) = nothing
warnOnUnsupported_args(::UnicodePlotsBackend, d::KW) = nothing
# --------------------------------------------------------------------------------------
@@ -38,7 +39,7 @@ 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)
function rebuildUnicodePlot!(plt::Plot, width, height)
plt.o = []
for sp in plt.subplots
@@ -57,8 +58,13 @@ function rebuildUnicodePlot!(plt::Plot)
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
width, height = plt[:size]
canvas_type = isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
# # make it a bar canvas if plotting bar
# if any(series -> series[:seriestype] == :bar, series_list(sp))
# canvas_type = UnicodePlots.BarplotGraphics
# end
o = UnicodePlots.Plot(x, y, canvas_type;
width = width,
height = height,
@@ -96,6 +102,10 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
func = UnicodePlots.lineplot!
elseif st == :scatter || d[:markershape] != :none
func = UnicodePlots.scatterplot!
# elseif st == :bar
# func = UnicodePlots.barplot!
elseif st == :shape
func = UnicodePlots.lineplot!
else
error("Linestyle $st not supported by UnicodePlots")
end
@@ -108,12 +118,13 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
# add the series
x, y = Plots.unzip(collect(filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
func(o, x, y; color = color, name = label)
end
# -------------------------------
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
# since this is such a hack, it's only callable using `png`... should error during normal `show`
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
fn = addExtension(fn, "png")
@@ -122,7 +133,7 @@ function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
gui(plt)
# @osx_only begin
@compat @static if is_apple()
@static if is_apple()
# BEGIN HACK
# wait while the plot gets drawn
@@ -143,21 +154,21 @@ end
# we don't do very much for subplots... just stack them vertically
function _update_plot_object(plt::Plot{UnicodePlotsBackend})
w, h = plt[:size]
plt.attr[:size] = div(w, 10), div(h, 20)
plt.attr[:color_palette] = [RGB(0,0,0)]
rebuildUnicodePlot!(plt)
function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend})
w, h = plt[:size]
plt.attr[:color_palette] = [RGB(0,0,0)]
rebuildUnicodePlot!(plt, div(w, 10), div(h, 20))
end
function _writemime(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
map(show, plt.o)
nothing
function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
unicodeplots_rebuild(plt)
map(show, plt.o)
nothing
end
function _display(plt::Plot{UnicodePlotsBackend})
map(show, plt.o)
nothing
unicodeplots_rebuild(plt)
map(show, plt.o)
nothing
end
+4 -4
View File
@@ -20,13 +20,13 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr
end
function open_browser_window(filename::AbstractString)
@compat @static if is_apple()
@static if is_apple()
return run(`open $(filename)`)
end
@compat @static if is_linux()
@static if is_linux()
return run(`xdg-open $(filename)`)
end
@compat @static if is_windows()
@static if is_windows()
return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
end
warn("Unknown OS... cannot open browser window.")
@@ -51,7 +51,7 @@ 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)
function show_png_from_html(io::IO, plt::AbstractPlot)
# write html to a temporary file
html_fn = write_temp_html(plt)
+52 -13
View File
@@ -14,13 +14,13 @@ compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : ang
immutable Shape
x::Vector{Float64}
y::Vector{Float64}
function Shape(x::AVec, y::AVec)
if x[1] != x[end] || y[1] != y[end]
new(vcat(x, x[1]), vcat(y, y[1]))
else
new(x, y)
end
end
# function Shape(x::AVec, y::AVec)
# # if x[1] != x[end] || y[1] != y[end]
# # new(vcat(x, x[1]), vcat(y, y[1]))
# # else
# new(x, y)
# end
# end
end
Shape(verts::AVec) = Shape(unzip(verts)...)
@@ -47,7 +47,7 @@ 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)]
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)"
@@ -119,6 +119,8 @@ const _shape_keys = Symbol[
:star8,
:vline,
:hline,
:+,
:x,
]
const _shapes = KW(
@@ -209,8 +211,9 @@ 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)
xi = rotate_x(x[i], y[i], Θ, cx, cy)
yi = rotate_y(x[i], y[i], Θ, cx, cy)
x[i], y[i] = xi, yi
end
shape
end
@@ -399,8 +402,8 @@ 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))
Base.eltype(surf::Surface) = eltype(surf.surf)
Base.copy(surf::Surface) = Surface{typeof(surf.surf)}(copy(surf.surf))
Base.eltype{T}(surf::Surface{T}) = eltype(T)
function expand_extrema!(a::Axis, surf::Surface)
ex = a[:extrema]
@@ -415,6 +418,35 @@ immutable SurfaceFunction <: AbstractSurface
f::Function
end
# -----------------------------------------------------------------------
# # I don't want to clash with ValidatedNumerics, but this would be nice:
# ..(a::T, b::T) = (a,b)
immutable Volume{T}
v::Array{T,3}
x_extents::Tuple{T,T}
y_extents::Tuple{T,T}
z_extents::Tuple{T,T}
end
default_extents{T}(::Type{T}) = (zero(T), one(T))
function Volume{T}(v::Array{T,3},
x_extents = default_extents(T),
y_extents = default_extents(T),
z_extents = default_extents(T))
Volume(v, x_extents, y_extents, z_extents)
end
Base.Array(vol::Volume) = vol.v
for f in (:length, :size)
@eval Base.$f(vol::Volume, args...) = $f(vol.v, args...)
end
Base.copy{T}(vol::Volume{T}) = Volume{T}(copy(vol.v), vol.x_extents, vol.y_extents, vol.z_extents)
Base.eltype{T}(vol::Volume{T}) = T
# -----------------------------------------------------------------------
# style is :open or :closed (for now)
@@ -465,6 +497,13 @@ function add_arrows(func::Function, x::AVec, y::AVec)
end
end
# -----------------------------------------------------------------------
"Represents data values with formatting that should apply to the tick labels."
immutable Formatted{T}
data::T
formatter::Function
end
# -----------------------------------------------------------------------
@@ -472,7 +511,7 @@ type BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
end
@compat function (bc::BezierCurve)(t::Real)
function (bc::BezierCurve)(t::Real)
p = zero(P2)
n = length(bc.control_points)-1
for i in 0:n
@@ -2,7 +2,7 @@
# https://github.com/bokeh/Bokeh.jl
supported_args(::BokehBackend) = merge_with_base_supported([
supported_attrs(::BokehBackend) = merge_with_base_supported([
# :annotations,
# :axis,
# :background_color,
@@ -194,7 +194,7 @@ end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
# TODO: write a png to io
warn("mime png not implemented")
end
@@ -2,7 +2,7 @@
# https://github.com/dcjones/Gadfly.jl
supported_args(::GadflyBackend) = merge_with_base_supported([
supported_attrs(::GadflyBackend) = merge_with_base_supported([
:annotations,
:background_color, :foreground_color, :color_palette,
:group, :label, :seriestype,
@@ -41,7 +41,7 @@ function _initialize_backend(::GadflyBackend; kw...)
@eval begin
import Gadfly, Compose
export Gadfly, Compose
include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
end
end
@@ -244,7 +244,7 @@ function addToGadflyLegend(plt::Plot, d::KW)
# 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[]))
unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
end
# now add the series to the legend
@@ -456,7 +456,7 @@ function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims, xfunc, yfunc)
end
function findGuideAndSet(gplt, t::DataType, args...; kw...) #s::@compat(AbstractString))
function findGuideAndSet(gplt, t::DataType, args...; kw...)
for (i,guide) in enumerate(gplt.guides)
if isa(guide, t)
gplt.guides[i] = t(args...; kw...)
@@ -537,7 +537,7 @@ end
# ----------------------------------------------------------------
function createGadflyAnnotationObject(x, y, val::@compat(AbstractString))
function createGadflyAnnotationObject(x, y, val::AbstractString)
Gadfly.Guide.annotation(Compose.compose(
Compose.context(),
Compose.text(x, y, val)
@@ -558,7 +558,7 @@ function createGadflyAnnotationObject(x, y, txt::PlotText)
))
end
function _add_annotations{X,Y,V}(plt::Plot{GadflyBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{GadflyBackend}, anns::AVec{Tuple{X,Y,V}})
for ann in anns
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
end
@@ -677,7 +677,7 @@ setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.attr[:size]...)
# -------------------------------------------------------------------------
function dowritemime{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, func, plt::AbstractPlot{P})
function doshow{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)
@@ -692,9 +692,9 @@ 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})
@eval function Base.show{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, ::$mime, plt::AbstractPlot{P})
func = getGadflyWriteFunc($mime())
dowritemime(io, func, plt)
doshow(io, func, plt)
end
end
@@ -1,7 +1,7 @@
# https://github.com/JuliaGraphics/Immerse.jl
supported_args(::ImmerseBackend) = supported_args(GadflyBackend())
supported_attrs(::ImmerseBackend) = supported_attrs(GadflyBackend())
supported_types(::ImmerseBackend) = supported_types(GadflyBackend())
supported_styles(::ImmerseBackend) = supported_styles(GadflyBackend())
supported_markers(::ImmerseBackend) = supported_markers(GadflyBackend())
@@ -14,7 +14,7 @@ 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"))
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
end
end
@@ -61,7 +61,7 @@ end
# ----------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{ImmerseBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{ImmerseBackend}, anns::AVec{Tuple{X,Y,V}})
for ann in anns
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
end
@@ -2,7 +2,7 @@
# https://github.com/tbreloff/Qwt.jl
supported_args(::QwtBackend) = merge_with_base_supported([
supported_attrs(::QwtBackend) = merge_with_base_supported([
:annotations,
:linecolor,
:fillrange,
@@ -39,7 +39,7 @@ end
# -------------------------------
@compat const _qwtAliases = KW(
const _qwtAliases = KW(
:bins => :heatmap_n,
:fillrange => :fillto,
:linewidth => :width,
@@ -133,7 +133,7 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
w = plt.o.widget
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
if typeof(lims) <: @compat(Union{Tuple,AVec}) && length(lims) == 2
if typeof(lims) <: Union{Tuple,AVec} && length(lims) == 2
if isx
plt.o.autoscale_x = false
else
@@ -211,14 +211,14 @@ function createQwtAnnotation(plt::Plot, x, y, val::PlotText)
marker[:attach](plt.o.widget)
end
function createQwtAnnotation(plt::Plot, x, y, val::@compat(AbstractString))
function createQwtAnnotation(plt::Plot, x, y, val::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})})
function _add_annotations{X,Y,V}(plt::Plot{QwtBackend}, anns::AVec{Tuple{X,Y,V}})
for ann in anns
createQwtAnnotation(plt, ann...)
end
@@ -242,8 +242,6 @@ end
# -------------------------------
# savepng(::QwtBackend, plt::AbstractPlot, fn::@compat(AbstractString), args...) = Qwt.savepng(plt.o, fn)
# -------------------------------
# # create the underlying object (each backend will do this differently)
@@ -282,13 +280,13 @@ end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
function Base.show(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})
# function Base.show(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
# for plt in subplt.plts
# Qwt.refresh(plt.o)
# end
@@ -3,7 +3,7 @@
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
supported_args(::WinstonBackend) = merge_with_base_supported([
supported_attrs(::WinstonBackend) = merge_with_base_supported([
:annotations,
:linecolor,
:fillrange,
@@ -44,13 +44,13 @@ end
## dictionaries for conversion of Plots.jl names to Winston ones.
@compat const winston_linestyle = KW(:solid=>"solid",
const winston_linestyle = KW(:solid=>"solid",
:dash=>"dash",
:dot=>"dotted",
:dashdot=>"dotdashed"
)
@compat const winston_marker = KW(:none=>".",
const winston_marker = KW(:none=>".",
:rect => "square",
:circle=>"circle",
:diamond=>"diamond",
@@ -185,7 +185,7 @@ end
# ----------------------------------------------------------------
@compat const _winstonNames = KW(
const _winstonNames = KW(
:xlims => :xrange,
:ylims => :yrange,
:xscale => :xlog,
@@ -213,11 +213,11 @@ end
# ----------------------------------------------------------------
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::@compat(AbstractString))
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::AbstractString)
Winston.text(x, y, val)
end
function _add_annotations{X,Y,V}(plt::Plot{WinstonBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
function _add_annotations{X,Y,V}(plt::Plot{WinstonBackend}, anns::AVec{Tuple{X,Y,V}})
for ann in anns
createWinstonAnnotationObject(plt, ann...)
end
@@ -238,10 +238,10 @@ function addWinstonLegend(plt::Plot, wplt)
end
end
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{WinstonBackend})
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{WinstonBackend})
window, canvas, wplt = getWinstonItems(plt)
addWinstonLegend(plt, wplt)
writemime(io, "image/png", wplt)
show(io, "image/png", wplt)
end
+39 -4
View File
@@ -1,10 +1,38 @@
abstract ColorScheme
Base.getindex(scheme::ColorScheme, i::Integer) = getColor(scheme, i)
export
cgrad
cgrad() = default_gradient()
function cgrad(arg, values = nothing; alpha = nothing, scale = :identity)
colors = ColorGradient(arg, alpha=alpha).colors
values = if values != nothing
values
elseif scale in (:log, :log10)
log10(linspace(1,10,30))
elseif scale == :log2
log2(linspace(1,2,30))
elseif scale == :ln
log(linspace(1,pi,30))
elseif scale in (:exp, :exp10)
(exp10(linspace(0,1,30)) - 1) / 9
else
linspace(0, 1, length(colors))
end
ColorGradient(colors, values)
end
# --------------------------------------------------------------
getColor(scheme::ColorScheme) = getColor(scheme, 1)
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
colorscheme(scheme::ColorScheme) = scheme
colorscheme(s::AbstractString; kw...) = colorscheme(Symbol(s); kw...)
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...)
@@ -28,10 +56,10 @@ convertColor(b::Bool) = b ? RGBA(0,0,0,1) : RGBA(0,0,0,0)
function convertColor(c, α::Real)
c = convertColor(c)
RGBA(RGB(c), α)
RGBA(RGB(getColor(c)), α)
end
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
convertColor(c, α::@compat(Void)) = convertColor(c)
convertColor(c, α::Void) = convertColor(c)
# backup... try to convert
getColor(c) = convertColor(c)
@@ -51,7 +79,7 @@ end
# --------------------------------------------------------------
const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"]
const _rainbowColors = [colorant"purple", colorant"blue", 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)]
@@ -103,6 +131,12 @@ immutable ColorGradient <: ColorScheme
end
end
Base.getindex(cs::ColorGradient, i::Integer) = getColor(cs, i)
Base.getindex(cs::ColorGradient, z::Number) = getColorZ(cs, z)
# 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))))
@@ -187,7 +221,7 @@ immutable ColorZFunction <: ColorScheme
f::Function
end
getColorZ(scheme::ColorFunction, z::Real) = scheme.f(z)
getColorZ(scheme::ColorZFunction, z::Real) = scheme.f(z)
# --------------------------------------------------------------
@@ -213,6 +247,7 @@ ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Color
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
convertColor(c::ColorWrapper, α::Void) = c.c
# --------------------------------------------------------------
+99
View File
@@ -0,0 +1,99 @@
# 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(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :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
function convertToAnyVector(v::AMat, d::KW)
if all3D(d)
Any[Surface(v)]
else
Any[v[:,i] for i in 1:size(v,2)]
end, 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
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
function convertToAnyVector(args...)
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
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)
nobigs(v::AVec{BigFloat}) = map(Float64, v)
nobigs(v::AVec{BigInt}) = map(Int64, v)
nobigs(v) = v
@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)
nobigs(x), nobigs(y), nobigs(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!")
# --------------------------------------------------------------------
+6 -12
View File
@@ -61,7 +61,7 @@ PlotExample("Global",
# "Use the `axis` arguments.",
# [
# :(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT", xlabel="X", title="TITLE"))
# ]),
# ]),
PlotExample("Images",
"Plot an image. y-axis is set to flipped",
@@ -115,7 +115,7 @@ PlotExample("Line types",
PlotExample("Line styles",
"",
[:(begin
styles = filter(s -> s in supported_styles(), [:solid, :dash, :dot, :dashdot, :dashdotdot])'
styles = filter(s -> s in Plots.supported_styles(), [:solid, :dash, :dot, :dashdot, :dashdotdot])'
n = length(styles)
y = cumsum(randn(20,n),1)
plot(y, line = (5, styles), label = map(string,styles))
@@ -125,7 +125,7 @@ PlotExample("Line styles",
PlotExample("Marker types",
"",
[:(begin
markers = filter(m -> m in supported_markers(), Plots._shape_keys)'
markers = filter(m -> m in Plots.supported_markers(), Plots._shape_keys)'
n = length(markers)
x = linspace(0,10,n+2)[2:end-1]
y = repmat(reverse(x)', n, 1)
@@ -221,12 +221,6 @@ PlotExample("Contours",
p2 = contour(x, y, Z)
plot(p1, p2)
end)]
# [:(begin
# x = 1:0.3:20
# y = x
# f(x,y) = sin(x)+cos(y)
# contour(x, y, f, fill=true)
# end)]
),
PlotExample("Pie",
@@ -258,7 +252,7 @@ PlotExample("DataFrames",
iris = RDatasets.dataset("datasets", "iris")
scatter(iris, :SepalLength, :SepalWidth, group=:Species,
title = "My awesome plot", xlabel = "Length", ylabel = "Width",
marker = (0.5, [:+ :h :star7], 12), bg=RGB(.2,.2,.2))
marker = (0.5, [:cross :hex :star7], 12), bg=RGB(.2,.2,.2))
end)]
),
@@ -303,8 +297,8 @@ PlotExample("Boxplot and Violin series recipes",
[:(begin
import RDatasets
singers = RDatasets.dataset("lattice", "singer")
violin(singers, :VoicePart, :Height, marker = (0.2, :blue, stroke(0)))
boxplot!(singers, :VoicePart, :Height, marker = (0.3, :orange, stroke(2)))
violin(singers, :VoicePart, :Height, line = 0, fill = (0.2, :blue))
boxplot!(singers, :VoicePart, :Height, line = (2,:black), fill = (0.3, :orange))
end)]
)
+91 -30
View File
@@ -9,10 +9,10 @@ to_pixels(m::AbsoluteLength) = m.value / 0.254
const _cbar_width = 5mm
@compat Base.:.*(m::Measure, n::Number) = m * n
@compat Base.:.*(n::Number, m::Measure) = m * n
@compat Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
@compat Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
Base.:.*(m::Measure, n::Number) = m * n
Base.:.*(n::Number, m::Measure) = m * n
Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
Base.zero(::Type{typeof(mm)}) = 0mm
Base.one(::Type{typeof(mm)}) = 1mm
Base.typemin(::typeof(mm)) = -Inf*mm
@@ -20,15 +20,15 @@ Base.typemax(::typeof(mm)) = Inf*mm
Base.convert{F<:AbstractFloat}(::Type{F}, l::AbsoluteLength) = convert(F, l.value)
# TODO: these are unintuitive and may cause tricky bugs
# @compat Base.:+(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
# @compat Base.:+(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (1 + m1.value))
# @compat Base.:-(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 - m2.value))
# @compat Base.:-(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (m1.value - 1))
# Base.:+(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
# Base.:+(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (1 + m1.value))
# Base.:-(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 - m2.value))
# Base.:-(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (m1.value - 1))
@compat Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
@compat Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
@compat Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
@compat Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
Base.zero(::Type{typeof(pct)}) = 0pct
@@ -44,11 +44,11 @@ right(bbox::BoundingBox) = left(bbox) + width(bbox)
bottom(bbox::BoundingBox) = top(bbox) + height(bbox)
Base.size(bbox::BoundingBox) = (width(bbox), height(bbox))
# @compat Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value)
# Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value)
ispositive(m::Measure) = m.value > 0
# union together bounding boxes
@compat function Base.:+(bb1::BoundingBox, bb2::BoundingBox)
function Base.:+(bb1::BoundingBox, bb2::BoundingBox)
# empty boxes don't change the union
ispositive(width(bb1)) || return bb2
ispositive(height(bb1)) || return bb2
@@ -101,19 +101,52 @@ end
Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))")
make_measure_hor(n::Number) = n * w
make_measure_hor(m::Measure) = m
make_measure_vert(n::Number) = n * h
make_measure_vert(m::Measure) = m
function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...)
oargs = vcat(oarg1, originargs...)
orighor = :left
origver = :top
for oarg in oargs
if oarg == :center
orighor = origver = oarg
elseif oarg in (:left, :right, :hcenter)
orighor = oarg
elseif oarg in (:top, :bottom, :vcenter)
origver = oarg
else
warn("Unused origin arg in bbox construction: $oarg")
end
end
bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver)
end
# create a new bbox
function bbox(x, y, w, h; h_anchor = :left, v_anchor = :top)
function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top)
x = make_measure_hor(x)
y = make_measure_vert(y)
width = make_measure_hor(width)
height = make_measure_vert(height)
left = if h_anchor == :left
x
elseif h_anchor in (:center, :hcenter)
0.5w - 0.5width + x
else
x - w * (h_anchor == :right ? 1.0 : 0.5)
1w - x - width
end
top = if v_anchor == :top
y
elseif v_anchor in (:center, :vcenter)
0.5h - 0.5height + y
else
y - h * (v_anchor == :bottom ? 1.0 : 0.5)
1h - y - height
end
BoundingBox(left, top, w, h)
BoundingBox(left, top, width, height)
end
# this is the available area for drawing everything in this layout... as percentages of total canvas
@@ -212,7 +245,9 @@ function GridLayout(dims...;
# convert(Vector{Float64}, widths),
# convert(Vector{Float64}, heights),
KW(kw))
fill!(grid, EmptyLayout(layout))
for i in eachindex(grid)
grid[i] = EmptyLayout(layout)
end
layout
end
@@ -229,7 +264,7 @@ rightpad(layout::GridLayout) = layout.minpad[3]
bottompad(layout::GridLayout) = layout.minpad[4]
# here's how this works... first we recursively "update the minimum padding" (which
# here's how this works... first we recursively "update the minimum padding" (which
# means to calculate the minimum size needed from the edge of the subplot to plot area)
# for the whole layout tree. then we can compute the "padding borders" of this
# layout as the biggest padding of the children on the perimeter. then we need to
@@ -284,7 +319,6 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
minpad_top = map(toppad, layout.grid)
minpad_right = map(rightpad, layout.grid)
minpad_bottom = map(bottompad, layout.grid)
# @show minpad_left minpad_top minpad_right minpad_bottom
# get the max horizontal (left and right) padding over columns,
# and max vertical (bottom and top) padding over rows
@@ -293,7 +327,6 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
pad_top = maximum(minpad_top, 2)
pad_right = maximum(minpad_right, 1)
pad_bottom = maximum(minpad_bottom, 2)
# @show pad_left pad_top pad_right pad_bottom
# make sure the perimeter match the parent
pad_left[1] = max(pad_left[1], minimum_perimeter[1])
@@ -304,22 +337,18 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
# scale this up to the total padding in each direction
total_pad_horizontal = sum(pad_left + pad_right)
total_pad_vertical = sum(pad_top + pad_bottom)
# @show total_pad_horizontal total_pad_vertical
# now we can compute the total plot area in each direction
total_plotarea_horizontal = width(layout) - total_pad_horizontal
total_plotarea_vertical = height(layout) - total_pad_vertical
# @show total_plotarea_horizontal total_plotarea_vertical
# recompute widths/heights
layout.widths = recompute_lengths(layout.widths)
layout.heights = recompute_lengths(layout.heights)
# @show layout.widths layout.heights
# normalize widths/heights so they sum to 1
# denom_w = sum(layout.widths)
# denom_h = sum(layout.heights)
# @show layout.widths layout.heights denom_w, denom_h
# we have all the data we need... lets compute the plot areas and set the bounding boxes
for r=1:nr, c=1:nc
@@ -360,7 +389,6 @@ end
function update_inset_bboxes!(plt::Plot)
for sp in plt.inset_subplots
p_area = Measures.resolve(plotarea(sp.parent), sp[:relative_bbox])
# @show bbox(sp.parent) sp[:relative_bbox] p_area
plotarea!(sp, p_area)
bbox!(sp, bbox(
@@ -570,7 +598,7 @@ function create_grid(expr::Expr)
create_grid_curly(expr)
else
# if it's something else, just return that (might be an existing layout?)
expr
esc(expr)
end
end
@@ -612,7 +640,6 @@ function create_grid_curly(expr::Expr)
for (i,arg) in enumerate(expr.args[2:end])
add_layout_pct!(kw, arg, i, length(expr.args)-1)
end
# @show kw
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
end
@@ -627,14 +654,23 @@ end
# -------------------------------------------------------------------------
# make all reference the same axis extrema/values
# make all reference the same axis extrema/values.
# merge subplot lists.
function link_axes!(axes::Axis...)
a1 = axes[1]
for i=2:length(axes)
a2 = axes[i]
expand_extrema!(a1, extrema(a2))
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
a2[k] = a1[k]
end
# make a2's subplot list refer to a1's and add any missing values
sps2 = a2.sps
for sp in sps2
sp in a1.sps || push!(a1.sps, sp)
end
a2.sps = a1.sps
end
end
@@ -664,6 +700,16 @@ function link_axes!(layout::GridLayout, link::Symbol)
link_axes!(layout.grid[r,:], :yaxis)
end
end
if link == :square
sps = filter(l -> isa(l, Subplot), layout.grid)
if !isempty(sps)
base_axis = sps[1][:xaxis]
for sp in sps
link_axes!(base_axis, sp[:xaxis])
link_axes!(base_axis, sp[:yaxis])
end
end
end
if link == :all
link_axes!(layout.grid, :xaxis)
link_axes!(layout.grid, :yaxis)
@@ -672,3 +718,18 @@ function link_axes!(layout::GridLayout, link::Symbol)
link_axes!(l, link)
end
end
# -------------------------------------------------------------------------
"Adds a new, empty subplot overlayed on top of `sp`, with a mirrored y-axis and linked x-axis."
function twinx(sp::Subplot)
sp[:right_margin] = max(sp[:right_margin], 30px)
plot!(sp.plt, inset = (sp[:subplot_index], bbox(0,0,1,1)))
twinsp = sp.plt.subplots[end]
twinsp[:yaxis][:mirror] = true
twinsp[:background_color_inside] = RGBA{Float64}(0,0,0,0)
link_axes!(sp[:xaxis], twinsp[:xaxis])
twinsp
end
twinx(plt::Plot = current()) = twinx(plt[1])
+81 -48
View File
@@ -5,7 +5,7 @@ defaultOutputFormat(plt::Plot) = "png"
function png(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "png")
io = open(fn, "w")
writemime(io, MIME("image/png"), plt)
show(io, MIME("image/png"), plt)
close(io)
end
png(fn::AbstractString) = png(current(), fn)
@@ -13,7 +13,7 @@ png(fn::AbstractString) = png(current(), fn)
function svg(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "svg")
io = open(fn, "w")
writemime(io, MIME("image/svg+xml"), plt)
show(io, MIME("image/svg+xml"), plt)
close(io)
end
svg(fn::AbstractString) = svg(current(), fn)
@@ -22,7 +22,7 @@ svg(fn::AbstractString) = svg(current(), fn)
function pdf(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "pdf")
io = open(fn, "w")
writemime(io, MIME("application/pdf"), plt)
show(io, MIME("application/pdf"), plt)
close(io)
end
pdf(fn::AbstractString) = pdf(current(), fn)
@@ -31,16 +31,23 @@ pdf(fn::AbstractString) = pdf(current(), fn)
function ps(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "ps")
io = open(fn, "w")
writemime(io, MIME("application/postscript"), plt)
show(io, MIME("application/postscript"), plt)
close(io)
end
ps(fn::AbstractString) = ps(current(), fn)
function eps(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "eps")
io = open(fn, "w")
writemime(io, MIME("image/eps"), plt)
close(io)
end
eps(fn::AbstractString) = eps(current(), fn)
function tex(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "tex")
io = open(fn, "w")
writemime(io, MIME("application/x-tex"), plt)
show(io, MIME("application/x-tex"), plt)
close(io)
end
tex(fn::AbstractString) = tex(current(), fn)
@@ -49,11 +56,12 @@ tex(fn::AbstractString) = tex(current(), fn)
# ----------------------------------------------------------------
@compat const _savemap = Dict(
const _savemap = Dict(
"png" => png,
"svg" => svg,
"pdf" => pdf,
"ps" => ps,
"eps" => eps,
"tex" => tex,
)
@@ -126,32 +134,41 @@ const _mimeformats = Dict(
const _best_html_output_type = KW(
:pyplot => :png,
:unicodeplots => :txt,
:glvisualize => :png
)
# a backup for html... passes to svg or png depending on the html_output_format arg
function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
function Base.show(io::IO, ::MIME"text/html", plt::Plot)
output_type = Symbol(plt.attr[:html_output_format])
if output_type == :auto
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
end
if output_type == :png
# info("writing png to html output")
print(io, "<img src=\"data:image/png;base64,", base64encode(writemime, MIME("image/png"), plt), "\" />")
print(io, "<img src=\"data:image/png;base64,", base64encode(show, MIME("image/png"), plt), "\" />")
elseif output_type == :svg
# info("writing svg to html output")
writemime(io, MIME("image/svg+xml"), plt)
show(io, MIME("image/svg+xml"), plt)
elseif output_type == :txt
writemime(io, MIME("text/plain"), plt)
show(io, MIME("text/plain"), plt)
else
error("only png or svg allowed. got: $output_type")
end
end
function _show{B}(io::IO, m, plt::Plot{B})
# Base.show_backtrace(STDOUT, backtrace())
warn("_show is not defined for this backend. m=", string(m))
end
function _display(plt::Plot)
warn("_display is not defined for this backend.")
end
# for writing to io streams... first prepare, then callback
for mime in keys(_mimeformats)
@eval function Base.writemime(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
@eval function Base.show{B}(io::IO, m::MIME{Symbol($mime)}, plt::Plot{B})
prepare_output(plt)
_writemime(io, m, plt)
_show(io, m, plt)
end
end
@@ -160,24 +177,22 @@ end
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
if is_installed("FileIO")
@eval begin
import FileIO
function _writemime(io::IO, ::MIME"image/png", plt::Plot)
fn = tempname()
@eval import FileIO
function _show(io::IO, ::MIME"image/png", plt::Plot)
fn = tempname()
# first save a pdf file
pdf(plt, fn)
# first save a pdf file
pdf(plt, fn)
# load that pdf into a FileIO Stream
s = FileIO.load(fn * ".pdf")
# load that pdf into a FileIO Stream
s = FileIO.load(fn * ".pdf")
# save a png
pngfn = fn * ".png"
FileIO.save(pngfn, s)
# save a png
pngfn = fn * ".png"
FileIO.save(pngfn, s)
# now write from the file
write(io, readall(open(pngfn)))
end
# now write from the file
write(io, readall(open(pngfn)))
end
end
@@ -187,12 +202,12 @@ end
# function html_output_format(fmt)
# if fmt == "png"
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
# print(io, "<img src=\"data:image/png;base64,", base64(writemime, MIME("image/png"), plt), "\" />")
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
# print(io, "<img src=\"data:image/png;base64,", base64(show, MIME("image/png"), plt), "\" />")
# end
# elseif fmt == "svg"
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
# writemime(io, MIME("image/svg+xml"), plt)
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
# show(io, MIME("image/svg+xml"), plt)
# end
# else
# error("only png or svg allowed. got: $fmt")
@@ -205,7 +220,7 @@ end
# IJulia
# ---------------------------------------------------------
const _ijulia_output = Compat.ASCIIString["text/html"]
const _ijulia_output = String["text/html"]
function setup_ijulia()
# override IJulia inline display
@@ -220,7 +235,12 @@ function setup_ijulia()
end
function IJulia.display_dict(plt::Plot)
global _ijulia_output
Dict{Compat.ASCIIString, ByteString}(_ijulia_output[1] => sprint(writemime, _ijulia_output[1], plt))
Dict{String, String}(_ijulia_output[1] => sprint(show, _ijulia_output[1], plt))
end
# default text/plain passes to html... handles Interact issues
function Base.show(io::IO, m::MIME"text/plain", plt::Plot)
show(io, MIME("text/html"), plt)
end
end
set_ijulia_output("text/html")
@@ -232,28 +252,41 @@ end
# ---------------------------------------------------------
function setup_atom()
# @require Atom begin
if isatom() && get(ENV, "PLOTS_USE_ATOM_PLOTPANE", false) in (true, 1, "1", "true", "yes")
# @eval import Atom, Media
@eval import Atom
if isatom()
@eval import Atom, Media
Media.media(Plot, Media.Plot)
# connects the render function
for T in (GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend)
Atom.Media.media(Plot{T}, Atom.Media.Plot)
# default text/plain so it doesn't complain
function Base.show{B}(io::IO, ::MIME"text/plain", plt::Plot{B})
print(io, "Plot{$B}()")
end
function Media.render(e::Atom.Editor, plt::Plot)
Media.render(e, nothing)
end
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
# this is like "display"... sends an html div with the plot to the PlotPane
function Media.render(pane::Atom.PlotPane, plt::Plot)
Media.render(pane, Atom.div(Atom.HTML(stringmime(MIME("text/html"), plt))))
end
else
#
function Media.render(pane::Atom.PlotPane, plt::Plot)
display(Plots.PlotsDisplay(), plt)
s = "PlotPane turned off. Unset ENV[\"PLOTS_USE_ATOM_PLOTPANE\"] and restart Julia to enable it."
Media.render(pane, Atom.div(Atom.HTML(s)))
end
end
# Atom.Media.media{T <: Union{GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend}}(Plot{T}, Atom.Media.Plot)
# Atom.displaysize(::Plot) = (535, 379)
# Atom.displaytitle(plt::Plot) = "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::Plot)
Atom.Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
# special handling for plotly/plotlyjs
function Media.render{B<:Union{PlotlyBackend,PlotlyJSBackend}}(pane::Atom.PlotPane, plt::Plot{B})
display(Plots.PlotsDisplay(), plt)
s = "PlotPane turned off. The plotly and plotlyjs backends cannot render in the PlotPane due to javascript issues."
Media.render(pane, Atom.div(Atom.HTML(s)))
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
+406
View File
@@ -0,0 +1,406 @@
# ------------------------------------------------------------------
# preprocessing
function command_idx(kw_list::AVec{KW}, kw::KW)
Int(kw[:series_plotindex]) - Int(kw_list[1][:series_plotindex]) + 1
end
function _expand_seriestype_array(d::KW, args)
sts = get(d, :seriestype, :path)
if typeof(sts) <: AbstractArray
delete!(d, :seriestype)
RecipeData[begin
dc = copy(d)
dc[:seriestype] = sts[r:r,:]
RecipeData(dc, args)
end for r=1:size(sts,1)]
else
RecipeData[RecipeData(copy(d), args)]
end
end
function _preprocess_args(d::KW, args, still_to_process::Vector{RecipeData})
# the grouping mechanism is a recipe on a GroupBy object
# we simply add the GroupBy object to the front of the args list to allow
# the recipe to be applied
if haskey(d, :group)
args = (extractGroupArgs(d[:group], args...), args...)
end
# if we were passed a vector/matrix of seriestypes and there's more than one row,
# we want to duplicate the inputs, once for each seriestype row.
if !isempty(args)
append!(still_to_process, _expand_seriestype_array(d, args))
end
# remove subplot and axis args from d... they will be passed through in the kw_list
if !isempty(args)
for (k,v) in d
for defdict in (_subplot_defaults,
_axis_defaults,
_axis_defaults_byletter)
if haskey(defdict, k)
delete!(d, k)
end
end
end
end
args
end
# ------------------------------------------------------------------
# user recipes
function _process_userrecipes(plt::Plot, d::KW, args)
still_to_process = RecipeData[]
args = _preprocess_args(d, args, still_to_process)
# for plotting recipes, swap out the args and update the parameter dictionary
# we are keeping a queue of series that still need to be processed.
# each pass through the loop, we pop one off and apply the recipe.
# the recipe will return a list a Series objects... the ones that are
# finished (no more args) get added to the kw_list, and the rest go into the queue
# for processing.
kw_list = KW[]
while !isempty(still_to_process)
# grab the first in line to be processed and pass it through apply_recipe
# to generate a list of RecipeData objects (data + attributes)
next_series = shift!(still_to_process)
rd_list = RecipesBase.apply_recipe(next_series.d, next_series.args...)
for recipedata in rd_list
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
if !(typeof(recipedata) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $recipedata")
end
if isempty(recipedata.args)
_process_userrecipe(plt, kw_list, recipedata)
else
# args are non-empty, so there's still processing to do... add it back to the queue
push!(still_to_process, recipedata)
end
end
end
# don't allow something else to handle it
d[:smooth] = false
kw_list
end
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
# when the arg tuple is empty, that means there's nothing left to recursively
# process... finish up and add to the kw_list
kw = recipedata.d
preprocessArgs!(kw)
_preprocess_userrecipe(kw)
warnOnUnsupported_scales(plt.backend, kw)
# add the plot index
plt.n += 1
kw[:series_plotindex] = plt.n
push!(kw_list, kw)
_add_errorbar_kw(kw_list, kw)
_add_smooth_kw(kw_list, kw)
return
end
function _preprocess_userrecipe(kw::KW)
_add_markershape(kw)
# if there was a grouping, filter the data here
_filter_input_data!(kw)
# map marker_z if it's a Function
if isa(get(kw, :marker_z, nothing), Function)
# TODO: should this take y and/or z as arguments?
kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
end
# map line_z if it's a Function
if isa(get(kw, :line_z, nothing), Function)
kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z])
end
# convert a ribbon into a fillrange
if get(kw, :ribbon, nothing) != nothing
make_fillrange_from_ribbon(kw)
end
return
end
function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
# handle error bars by creating new recipedata data... these will have
# the same recipedata index as the recipedata they are copied from
for esym in (:xerror, :yerror)
if get(kw, esym, nothing) != nothing
# we make a copy of the KW and apply an errorbar recipe
errkw = copy(kw)
errkw[:seriestype] = esym
errkw[:label] = ""
errkw[:primary] = false
push!(kw_list, errkw)
end
end
end
function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
# handle smoothing by adding a new series
if get(kw, :smooth, false)
x, y = kw[:x], kw[:y]
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
sx = [minimum(x), maximum(x)]
sy = β * sx + α
push!(kw_list, merge(copy(kw), KW(
:seriestype => :path,
:x => sx,
:y => sy,
:fillrange => nothing,
:label => "",
:primary => false,
)))
end
end
# ------------------------------------------------------------------
# plot recipes
# Grab the first in line to be processed and pass it through apply_recipe
# to generate a list of RecipeData objects (data + attributes).
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
# otherwise we just add the original KW.
function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_process::Vector{KW})
if !isa(get(kw, :seriestype, nothing), Symbol)
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
push!(kw_list, kw)
return
end
try
st = kw[:seriestype]
st = kw[:seriestype] = get(_typeAliases, st, st)
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
for data in datalist
preprocessArgs!(data.d)
if data.d[:seriestype] == st
error("Plot recipe $st returned the same seriestype: $(data.d)")
end
push!(still_to_process, data.d)
end
catch err
if isa(err, MethodError)
push!(kw_list, kw)
else
rethrow()
end
end
return
end
# ------------------------------------------------------------------
# setup plot and subplot
function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
# merge in anything meant for the Plot
for kw in kw_list, (k,v) in kw
haskey(_plot_defaults, k) && (d[k] = pop!(kw, k))
end
# TODO: init subplots here
_update_plot_args(plt, d)
if !plt.init
plt.o = _create_backend_figure(plt)
# create the layout and subplots from the inputs
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
for (idx,sp) in enumerate(plt.subplots)
sp.plt = plt
sp.attr[:subplot_index] = idx
end
plt.init = true
end
# handle inset subplots
insets = plt[:inset_subplots]
if insets != nothing
if !(typeof(insets) <: AVec)
insets = [insets]
end
for inset in insets
parent, bb = is_2tuple(inset) ? inset : (nothing, inset)
P = typeof(parent)
if P <: Integer
parent = plt.subplots[parent]
elseif P == Symbol
parent = plt.spmap[parent]
else
parent = plt.layout
end
sp = Subplot(backend(), parent=parent)
sp.plt = plt
push!(plt.subplots, sp)
push!(plt.inset_subplots, sp)
sp.attr[:relative_bbox] = bb
sp.attr[:subplot_index] = length(plt.subplots)
end
end
plt[:inset_subplots] = nothing
end
function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
# we'll keep a map of subplot to an attribute override dict.
# Subplot/Axis attributes set by a user/series recipe apply only to the
# Subplot object which they belong to.
# TODO: allow matrices to still apply to all subplots
sp_attrs = Dict{Subplot,Any}()
for kw in kw_list
# get the Subplot object to which the series belongs.
sps = get(kw, :subplot, :auto)
sp = get_subplot(plt, cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
kw[:subplot] = sp
# extract subplot/axis attributes from kw and add to sp_attr
attr = KW()
for (k,v) in kw
if haskey(_subplot_defaults, k) || haskey(_axis_defaults_byletter, k)
attr[k] = pop!(kw, k)
end
if haskey(_axis_defaults, k)
v = pop!(kw, k)
for letter in (:x,:y,:z)
attr[Symbol(letter,k)] = v
end
end
end
sp_attrs[sp] = attr
end
# override subplot/axis args. `sp_attrs` take precendence
for (idx,sp) in enumerate(plt.subplots)
attr = merge(d, get(sp_attrs, sp, KW()))
_update_subplot_args(plt, sp, attr, idx, false)
end
# do we need to link any axes together?
link_axes!(plt.layout, plt[:link])
end
# getting ready to add the series... last update to subplot from anything
# that might have been added during series recipes
function _prepare_subplot{T}(plt::Plot{T}, d::KW)
st::Symbol = d[:seriestype]
sp::Subplot{T} = d[:subplot]
sp_idx = get_subplot_index(plt, sp)
_update_subplot_args(plt, sp, d, sp_idx, true)
st = _override_seriestype_check(d, st)
# change to a 3d projection for this subplot?
if is3d(st)
sp.attr[:projection] = "3d"
end
# initialize now that we know the first series type
if !haskey(sp.attr, :init)
_initialize_subplot(plt, sp)
sp.attr[:init] = true
end
sp
end
# ------------------------------------------------------------------
# series types
function _override_seriestype_check(d::KW, st::Symbol)
# do we want to override the series type?
if !is3d(st)
z = d[:z]
if !isa(z, Void) && (size(d[:x]) == size(d[:y]) == size(z))
st = (st == :scatter ? :scatter3d : :path3d)
d[:seriestype] = st
end
end
st
end
function _prepare_annotations(sp::Subplot, d::KW)
# strip out series annotations (those which are based on series x/y coords)
# and add them to the subplot attr
sp_anns = annotations(sp[:annotations])
anns = annotations(pop!(d, :series_annotations, []))
if length(anns) > 0
x, y = d[:x], d[:y]
nx, ny, na = map(length, (x,y,anns))
n = max(nx, ny, na)
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
end
sp.attr[:annotations] = vcat(sp_anns, anns)
end
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
# adjust extrema and discrete info
if st == :image
w, h = size(d[:z])
expand_extrema!(sp[:xaxis], (0,w))
expand_extrema!(sp[:yaxis], (0,h))
sp[:yaxis].d[:flip] = true
elseif !(st in (:pie, :histogram, :histogram2d))
expand_extrema!(sp, d)
end
end
function _add_the_series(plt, d)
warnOnUnsupported_args(plt.backend, d)
warnOnUnsupported(plt.backend, d)
series = Series(d)
push!(plt.series_list, series)
_series_added(plt, series)
end
# -------------------------------------------------------------------------------
# this method recursively applies series recipes when the seriestype is not supported
# natively by the backend
function _process_seriesrecipe(plt::Plot, d::KW)
# replace seriestype aliases
st = Symbol(d[:seriestype])
st = d[:seriestype] = get(_typeAliases, st, st)
# shapes shouldn't have fillrange set
if d[:seriestype] == :shape
d[:fillrange] = nothing
end
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
if is_seriestype_supported(st)
sp = _prepare_subplot(plt, d)
_prepare_annotations(sp, d)
_expand_subplot_extrema(sp, d, st)
_add_the_series(plt, d)
else
# get a sub list of series for this seriestype
datalist = RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
# assuming there was no error, recursively apply the series recipes
for data in datalist
if isa(data, RecipeData)
preprocessArgs!(data.d)
if data.d[:seriestype] == st
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
end
_process_seriesrecipe(plt, data.d)
else
warn("Unhandled recipe: $(data)")
break
end
end
end
nothing
end
+59 -408
View File
@@ -49,7 +49,7 @@ function plot(args...; kw...)
# create an empty Plot then process
plt = Plot()
# plt.user_attr = d
_plot!(plt, d, args...)
_plot!(plt, d, args)
end
# build a new plot from existing plots
@@ -105,22 +105,11 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
end
end
# # just in case the backend needs to set up the plot (make it current or something)
# _prepare_plot_object(plt)
# first apply any args for the subplots
for (idx,sp) in enumerate(plt.subplots)
_update_subplot_args(plt, sp, d, idx, remove_pair = false)
_update_subplot_args(plt, sp, d, idx, false)
end
# # now we can get rid of the axis keys without a letter
# for k in keys(_axis_defaults)
# delete!(d, k)
# for letter in (:x,:y,:z)
# delete!(d, Symbol(letter,k))
# end
# end
# do we need to link any axes together?
link_axes!(plt.layout, plt[:link])
@@ -150,341 +139,70 @@ function plot!(plt::Plot, args...; kw...)
d = KW(kw)
preprocessArgs!(d)
# merge!(plt.user_attr, d)
_plot!(plt, d, args...)
end
function strip_first_letter(s::Symbol)
str = string(s)
str[1:1], Symbol(str[2:end])
end
# this method recursively applies series recipes when the seriestype is not supported
# natively by the backend
function _apply_series_recipe(plt::Plot, d::KW)
st = d[:seriestype]
# @show st
if st in supported_types()
# getting ready to add the series... last update to subplot from anything
# that might have been added during series recipes
sp = d[:subplot]
sp_idx = get_subplot_index(plt, sp)
_update_subplot_args(plt, sp, d, sp_idx)
# do we want to override the series type?
if !is3d(st) && d[:z] != nothing && (size(d[:x]) == size(d[:y]) == size(d[:z]))
st = d[:seriestype] = (st == :scatter ? :scatter3d : :path3d)
end
# change to a 3d projection for this subplot?
if is3d(st)
sp.attr[:projection] = "3d"
end
# initialize now that we know the first series type
if !haskey(sp.attr, :init)
_initialize_subplot(plt, sp)
sp.attr[:init] = true
end
# strip out series annotations (those which are based on series x/y coords)
# and add them to the subplot attr
sp_anns = annotations(sp[:annotations])
anns = annotations(pop!(d, :series_annotations, []))
if length(anns) > 0
x, y = d[:x], d[:y]
nx, ny, na = map(length, (x,y,anns))
n = max(nx, ny, na)
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
end
sp.attr[:annotations] = vcat(sp_anns, anns)
# adjust extrema and discrete info
if st == :image
w, h = size(d[:z])
expand_extrema!(sp[:xaxis], (0,w))
expand_extrema!(sp[:yaxis], (0,h))
sp[:yaxis].d[:flip] = true
elseif !(st in (:pie, :histogram, :histogram2d))
expand_extrema!(sp, d)
end
# add the series!
warnOnUnsupported_args(plt.backend, d)
warnOnUnsupported(plt.backend, d)
series = Series(d)
push!(plt.series_list, series)
# @show series
_series_added(plt, series)
else
# get a sub list of series for this seriestype
datalist = try
RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
catch
warn("Exception during apply_recipe(Val{$st}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))")
rethrow()
end
# assuming there was no error, recursively apply the series recipes
for data in datalist
if isa(data, RecipeData)
_apply_series_recipe(plt, data.d)
else
warn("Unhandled recipe: $(data)")
break
end
end
end
_plot!(plt, d, args)
end
# -------------------------------------------------------------------------------
# this is the core plotting function. recursively apply recipes to build
# a list of series KW dicts.
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
function _plot!(plt::Plot, d::KW, args...)
# d = plt.user_attr
function _plot!(plt::Plot, d::KW, args::Tuple)
d[:plot_object] = plt
# the grouping mechanism is a recipe on a GroupBy object
# we simply add the GroupBy object to the front of the args list to allow
# the recipe to be applied
if haskey(d, :group)
args = (extractGroupArgs(d[:group], args...), args...)
end
# --------------------------------
# "USER RECIPES"
# --------------------------------
kw_list = _process_userrecipes(plt, d, args)
# info(1)
# map(DD, kw_list)
# for plotting recipes, swap out the args and update the parameter dictionary
# we are keeping a queue of series that still need to be processed.
# each pass through the loop, we pop one off and apply the recipe.
# the recipe will return a list a Series objects... the ones that are
# finished (no more args) get added to the kw_list, and the rest go into the queue
# for processing.
# --------------------------------
# "PLOT RECIPES"
# --------------------------------
# "plot recipe", which acts like a series type, and is processed before
# the plot layout is created, which allows for setting layouts and other plot-wide attributes.
# we get inputs which have been fully processed by "user recipes" and "type recipes",
# so we can expect standard vectors, surfaces, etc. No defaults have been set yet.
still_to_process = kw_list
kw_list = KW[]
still_to_process = isempty(args) ? [] : [RecipeData(copy(d), args)]
while !isempty(still_to_process)
# grab the first in line to be processed and pass it through apply_recipe
# to generate a list of RecipeData objects (data + attributes)
next_series = shift!(still_to_process)
for recipedata in RecipesBase.apply_recipe(next_series.d, next_series.args...)
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
if !(typeof(recipedata) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $recipedata")
end
if isempty(recipedata.args)
# when the arg tuple is empty, that means there's nothing left to recursively
# process... finish up and add to the kw_list
kw = recipedata.d
_add_markershape(kw)
# if there was a grouping, filter the data here
_filter_input_data!(kw)
# map marker_z if it's a Function
if isa(get(kw, :marker_z, nothing), Function)
# TODO: should this take y and/or z as arguments?
kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
end
# map line_z if it's a Function
if isa(get(kw, :line_z, nothing), Function)
kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z])
end
# convert a ribbon into a fillrange
if get(kw, :ribbon, nothing) != nothing
make_fillrange_from_ribbon(kw)
end
# add the plot index
plt.n += 1
kw[:series_plotindex] = plt.n
# check that the backend will support the command and add it to the list
warnOnUnsupported_scales(plt.backend, kw)
push!(kw_list, kw)
# handle error bars by creating new recipedata data... these will have
# the same recipedata index as the recipedata they are copied from
for esym in (:xerror, :yerror)
if get(d, esym, nothing) != nothing
# we make a copy of the KW and apply an errorbar recipe
errkw = copy(kw)
errkw[:seriestype] = esym
errkw[:label] = ""
errkw[:primary] = false
push!(kw_list, errkw)
end
end
# handle smoothing by adding a new series
if get(d, :smooth, false)
x, y = kw[:x], kw[:y]
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
sx = [minimum(x), maximum(x)]
sy = β * sx + α
push!(kw_list, merge(copy(kw), KW(
:seriestype => :path,
:x => sx,
:y => sy,
:fillrange => nothing,
:label => "",
:primary => false,
)))
end
else
# args are non-empty, so there's still processing to do... add it back to the queue
push!(still_to_process, recipedata)
end
end
next_kw = shift!(still_to_process)
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
end
# don't allow something else to handle it
d[:smooth] = false
# info(2)
# map(DD, kw_list)
# merge in anything meant for plot/subplot/axis
for kw in kw_list
for (k,v) in kw
for defdict in (_plot_defaults,)
# _subplot_defaults,
# _axis_defaults,
# _axis_defaults_byletter)
if haskey(defdict, k)
d[k] = pop!(kw, k)
end
end
end
end
# TODO: init subplots here
_update_plot_args(plt, d)
if !plt.init
plt.o = _create_backend_figure(plt)
# DD(d)
# create the layout and subplots from the inputs
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
for (idx,sp) in enumerate(plt.subplots)
sp.plt = plt
sp.attr[:subplot_index] = idx
end
plt.init = true
end
# handle inset subplots
insets = plt[:inset_subplots]
if insets != nothing
for inset in insets
parent, bb = is_2tuple(inset) ? inset : (nothing, inset)
P = typeof(parent)
if P <: Integer
parent = plt.subplots[parent]
elseif P == Symbol
parent = plt.spmap[parent]
else
parent = plt.layout
end
sp = Subplot(backend(), parent=parent)
sp.plt = plt
sp.attr[:relative_bbox] = bb
push!(plt.subplots, sp)
sp.attr[:subplot_index] = length(plt.subplots)
push!(plt.inset_subplots, sp)
end
end
# we'll keep a map of subplot to an attribute override dict.
# any series which belong to that subplot
sp_attrs = Dict{Subplot,Any}()
for kw in kw_list
# get the Subplot object to which the series belongs
sp = get(kw, :subplot, :auto)
command_idx = kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
sp = if sp == :auto
cycle(plt.subplots, command_idx)
# mod1(command_idx, length(plt.subplots))
else
slice_arg(sp, command_idx)
end
sp = kw[:subplot] = get_subplot(plt, sp)
# idx = get_subplot_index(plt, sp)
attr = KW()
for (k,v) in kw
for defdict in (_subplot_defaults,
_axis_defaults,
_axis_defaults_byletter)
if haskey(defdict, k)
attr[k] = pop!(kw, k)
end
end
end
sp_attrs[sp] = attr
end
# # just in case the backend needs to set up the plot (make it current or something)
# _prepare_plot_object(plt)
# first apply any args for the subplots
for (idx,sp) in enumerate(plt.subplots)
# if we picked up any subplot-specific overrides, merge them here
attr = merge(d, get(sp_attrs, sp, KW()))
# DD(attr, "sp$idx")
_update_subplot_args(plt, sp, attr, idx, remove_pair = false)
end
# do we need to link any axes together?
link_axes!(plt.layout, plt[:link])
# --------------------------------
# Plot/Subplot/Layout setup
# --------------------------------
_plot_setup(plt, d, kw_list)
_subplot_setup(plt, d, kw_list)
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
# this is it folks!
# TODO: we probably shouldn't use i for tracking series index, but rather explicitly track it in recipes
# --------------------------------
# "SERIES RECIPES"
# --------------------------------
# info(3)
# map(DD, kw_list)
for kw in kw_list
command_idx = kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
sp::Subplot = kw[:subplot]
# idx = get_subplot_index(plt, sp)
# # get the Subplot object to which the series belongs
# sp = get(kw, :subplot, :auto)
# sp = if sp == :auto
# mod1(i,length(plt.subplots))
# else
# slice_arg(sp, i)
# end
# sp = kw[:subplot] = get_subplot(plt, sp)
sp = kw[:subplot]
idx = get_subplot_index(plt, sp)
# # strip out series annotations (those which are based on series x/y coords)
# # and add them to the subplot attr
# sp_anns = annotations(sp[:annotations])
# anns = annotations(pop!(kw, :series_annotations, []))
# if length(anns) > 0
# x, y = kw[:x], kw[:y]
# nx, ny, na = map(length, (x,y,anns))
# n = max(nx, ny, na)
# anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
# end
# sp.attr[:annotations] = vcat(sp_anns, anns)
# we update subplot args in case something like the color palatte is part of the recipe
_update_subplot_args(plt, sp, kw, idx)
# # we update subplot args in case something like the color palatte is part of the recipe
# _update_subplot_args(plt, sp, kw, idx, true)
# set default values, select from attribute cycles, and generally set the final attributes
_add_defaults!(kw, plt, sp, command_idx)
_add_defaults!(kw, plt, sp, command_idx(kw_list,kw))
# now we have a fully specified series, with colors chosen. we must recursively handle
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
@@ -492,29 +210,22 @@ function _plot!(plt::Plot, d::KW, args...)
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
# be able to support step, bar, and histogram plots (and any recipes that use those components).
_apply_series_recipe(plt, kw)
_process_seriesrecipe(plt, kw)
end
# --------------------------------
current(plt)
# note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
# # do we want to show it?
# if haskey(d, :show) && d[:show]
if get(d, :show, default(:show))
gui()
# do we want to force display?
if plt[:show]
gui(plt)
end
plt
end
function _replace_linewidth(d::KW)
# get a good default linewidth... 0 for surface and heatmaps
if get(d, :linewidth, :auto) == :auto
d[:linewidth] = (get(d, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
end
end
# we're getting ready to display/output. prep for layout calcs, then update
# the plot object after
function prepare_output(plt::Plot)
@@ -540,81 +251,21 @@ function prepare_output(plt::Plot)
_update_plot_object(plt)
end
function prepared_object(plt::Plot)
function backend_object(plt::Plot)
prepare_output(plt)
plt.o
end
# --------------------------------------------------------------------
# plot to a Subplot
# function get_indices(orig, labels)
# Int[findnext(labels, l, 1) for l in orig]
# end
# # TODO: remove?? this is the old way of handling discrete data... should be
# # replaced by the Axis type and logic
# function setTicksFromStringVector(plt::Plot, d::KW, di::KW, letter)
# sym = Symbol(letter)
# ticksym = Symbol(letter * "ticks")
# pargs = plt.attr
# v = di[sym]
#
# # do we really want to do this?
# typeof(v) <: AbstractArray || return
# isempty(v) && return
# trueOrAllTrue(_ -> typeof(_) <: AbstractString, v) || return
#
# # compute the ticks and labels
# ticks, labels = if ticksType(pargs[ticksym]) == :ticks_and_labels
# # extend the existing ticks and labels. only add to labels if they're new!
# ticks, labels = pargs[ticksym]
# newlabels = filter(_ -> !(_ in labels), unique(v))
# newticks = if isempty(ticks)
# collect(1:length(newlabels))
# else
# maximum(ticks) + collect(1:length(newlabels))
# end
# ticks = vcat(ticks, newticks)
# labels = vcat(labels, newlabels)
# ticks, labels
# else
# # create new ticks and labels
# newlabels = unique(v)
# collect(1:length(newlabels)), newlabels
# end
#
# d[ticksym] = ticks, labels
# plt.attr[ticksym] = ticks, labels
#
# # add an origsym field so that later on we can re-compute the x vector if ticks change
# origsym = Symbol(letter * "orig")
# di[origsym] = v
# di[sym] = get_indices(v, labels)
#
# # loop through existing plt.seriesargs and adjust indices if there is an origsym key
# for sargs in plt.seriesargs
# if haskey(sargs, origsym)
# # TODO: might need to call the setindex function instead to trigger a plot update for some backends??
# sargs[sym] = get_indices(sargs[origsym], labels)
# end
# end
# end
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# function Base.copy(plt::Plot)
# backend(plt.backend)
# plt2 = plot(; plt.attr...)
# for sargs in plt.seriesargs
# sargs = filter((k,v) -> haskey(_series_defaults,k), sargs)
# plot!(plt2; sargs...)
# end
# plt2
# end
function plot(sp::Subplot, args...; kw...)
plt = sp.plt
plot(plt, args...; kw..., subplot = findfirst(plt.subplots, sp))
end
function plot!(sp::Subplot, args...; kw...)
plt = sp.plt
plot!(plt, args...; kw..., subplot = findfirst(plt.subplots, sp))
end
# --------------------------------------------------------------------
+3
View File
@@ -0,0 +1,3 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
end
+297 -567
View File
File diff suppressed because it is too large Load Diff
+182 -105
View File
@@ -1,88 +1,130 @@
# we are going to build recipes to do the processing and splitting of the args
function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
pkg = plt.backend
globalIndex = d[:series_plotindex]
# 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
# add default values to our dictionary, being careful not to delete what we just added!
for (k,v) in _series_defaults
slice_arg!(d, d, k, v, commandIndex, remove_pair = false)
end
typealias FuncOrFuncs Union{Function, AVec{Function}}
# this is how many series belong to this subplot
plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
if get(d, :primary, true)
plotIndex += 1
end
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
aliasesAndAutopick(d, :linestyle, _styleAliases, supported_styles(pkg), plotIndex)
aliasesAndAutopick(d, :markershape, _markerAliases, supported_markers(pkg), plotIndex)
# missing
convertToAnyVector(v::Void, d::KW) = Any[nothing], nothing
# update color
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], sp, plotIndex)
# fixed number of blank series
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
# update colors
for csym in (:linecolor, :markercolor, :fillcolor)
d[csym] = if d[csym] == :match
if has_black_border_for_default(d[:seriestype]) && csym == :linecolor
:black
else
d[:seriescolor]
end
else
getSeriesRGBColor(d[csym], sp, plotIndex)
end
end
# numeric vector
convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
# update markerstrokecolor
c = d[:markerstrokecolor]
c = if c == :match
sp[:foreground_color_subplot]
# string vector
convertToAnyVector{T<:AbstractString}(v::AVec{T}, d::KW) = Any[v], nothing
function convertToAnyVector(v::AMat, d::KW)
if all3D(d)
Any[Surface(v)]
else
getSeriesRGBColor(c, sp, plotIndex)
end
d[:markerstrokecolor] = c
# update alphas
for asym in (:linealpha, :markeralpha, :fillalpha)
if d[asym] == nothing
d[asym] = d[:seriesalpha]
end
end
if d[:markerstrokealpha] == nothing
d[:markerstrokealpha] = d[:markeralpha]
end
# scatter plots don't have a line, but must have a shape
if d[:seriestype] in (:scatter, :scatter3d)
d[:linewidth] = 0
if d[:markershape] == :none
d[:markershape] = :circle
end
end
# set label
label = d[:label]
label = (label == "AUTO" ? "y$globalIndex" : label)
d[:label] = label
_replace_linewidth(d)
d
Any[v[:,i] for i in 1:size(v,2)]
end, nothing
end
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# function
convertToAnyVector(f::Function, d::KW) = Any[f], nothing
# instead of process_inputs:
# surface
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
# volume
convertToAnyVector(v::Volume, d::KW) = Any[v], 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
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
function convertToAnyVector(args...)
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
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)
nobigs(v::AVec{BigFloat}) = map(Float64, v)
nobigs(v::AVec{BigInt}) = map(Int64, v)
nobigs(v) = v
@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)
nobigs(x), nobigs(y), nobigs(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!")
# --------------------------------------------------------------------
# we are going to build recipes to do the processing and splitting of the args
# ensure we dispatch to the slicer
immutable SliceIt end
# the catch-all recipes
@recipe function f(::Type{SliceIt}, x, y, z)
# @show "HERE", typeof((x,y,z))
# handle data with formatting attached
if typeof(x) <: Formatted
xformatter := x.formatter
x = x.data
end
if typeof(y) <: Formatted
yformatter := y.formatter
y = y.data
end
if typeof(z) <: Formatted
zformatter := z.formatter
z = z.data
end
xs, _ = convertToAnyVector(x, d)
ys, _ = convertToAnyVector(y, d)
zs, _ = convertToAnyVector(z, d)
@@ -100,21 +142,19 @@ immutable SliceIt end
mx = length(xs)
my = length(ys)
mz = length(zs)
# ret = Any[]
for i in 1:max(mx, my, mz)
# add a new series
di = copy(d)
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
# @show i, typeof((xi, yi, zi))
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
# @show i, typeof((di[:x], di[:y], di[:z]))
if mx > 0 && my > 0 && mz > 0
for i in 1:max(mx, my, mz)
# add a new series
di = copy(d)
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
# handle fillrange
fr = fillranges[mod1(i,mf)]
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
# handle fillrange
fr = fillranges[mod1(i,mf)]
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
# @show i, di[:x], di[:y], di[:z]
push!(series_list, RecipeData(di, ()))
push!(series_list, RecipeData(di, ()))
end
end
nothing # don't add a series for the main block
end
@@ -127,6 +167,38 @@ end
_apply_type_recipe(d, v) = RecipesBase.apply_recipe(d, typeof(v), v)[1].args[1]
# Handle type recipes when the recipe is defined on the elements.
# This sort of recipe should return a pair of functions... one to convert to number,
# and one to format tick values.
function _apply_type_recipe(d, v::AbstractArray)
args = RecipesBase.apply_recipe(d, typeof(v[1]), v[1])[1].args
if length(args) == 2 && typeof(args[1]) <: Function && typeof(args[2]) <: Function
numfunc, formatter = args
Formatted(map(numfunc, v), formatter)
else
v
end
end
# # special handling for Surface... need to properly unwrap and re-wrap
# function _apply_type_recipe(d, v::Surface)
# T = eltype(v.surf)
# @show T
# if T <: Integer || T <: AbstractFloat
# v
# else
# ret = _apply_type_recipe(d, v.surf)
# if typeof(ret) <: Formatted
# Formatted(Surface(ret.data), ret.formatter)
# else
# v
# end
# end
# end
# don't do anything for ints or floats
_apply_type_recipe{T<:Union{Integer,AbstractFloat}}(d, v::AbstractArray{T}) = v
# handle "type recipes" by converting inputs, and then either re-calling or slicing
@recipe function f(x, y, z)
did_replace = false
@@ -180,24 +252,15 @@ end
newargs
end
# @recipe f(x, y, z) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), apply_recipe(typeof(z), z)
# @recipe f(x, y) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), nothing
# @recipe f(y) = SliceIt, nothing, apply_recipe(typeof(y), y), nothing
# # pass these through to the slicer
# @recipe f(x, y, z) = SliceIt, x, y, z
# @recipe f(x, y) = SliceIt, x, y, nothing
# @recipe f(y) = SliceIt, nothing, y, nothing
# # --------------------------------------------------------------------
# # 1 argument
# # --------------------------------------------------------------------
@recipe f(n::Integer) = n, n, n
@recipe f(n::Integer) = is3d(get(d,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
# return a surface if this is a 3d plot, otherwise let it be sliced up
@recipe function f{T<:Number}(mat::AMat{T})
@recipe function f{T<:Union{Integer,AbstractFloat}}(mat::AMat{T})
if all3D(d)
n,m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
@@ -206,11 +269,28 @@ end
end
end
# if a matrix is wrapped by Formatted, do similar logic, but wrap data with Surface
@recipe function f{T<:AbstractMatrix}(fmt::Formatted{T})
if all3D(d)
mat = fmt.data
n,m = size(mat)
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
else
SliceIt, nothing, fmt, nothing
end
end
# assume this is a Volume, so construct one
@recipe function f{T<:Number}(vol::AbstractArray{T,3}, args...)
seriestype := :volume
SliceIt, nothing, Volume(vol, args...), nothing
end
# # images - grays
@recipe function f{T<:Gray}(mat::AMat{T})
if nativeImagesSupported()
if is_seriestype_supported(:image)
seriestype := :image
n, m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
@@ -225,7 +305,7 @@ end
# # images - colors
@recipe function f{T<:Colorant}(mat::AMat{T})
if nativeImagesSupported()
if is_seriestype_supported(:image)
seriestype := :image
n, m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
@@ -241,32 +321,30 @@ end
# # plotting arbitrary shapes/polygons
@recipe function f(shape::Shape)
seriestype := :shape
seriestype --> :shape
shape_coords(shape)
end
@recipe function f(shapes::AVec{Shape})
seriestype := :shape
seriestype --> :shape
shape_coords(shapes)
end
@recipe function f(shapes::AMat{Shape})
seriestype --> :shape
for j in 1:size(shapes,2)
# create one series for each column
# @series shape_coords(vec(shapes[:,j]))
di = copy(d)
push!(series_list, RecipeData(di, shape_coords(vec(shapes[:,j]))))
@series shape_coords(vec(shapes[:,j]))
end
nothing # don't create a series for the main block
end
#
#
# # function without range... use the current range of the x-axis
# function without range... use the current range of the x-axis
@recipe function f(f::FuncOrFuncs)
plt = d[:plot_object]
f, xmin(plt), xmax(plt)
xmin,xmax = axis_limits(plt[1][:xaxis])
f, xmin, xmax
end
#
@@ -413,4 +491,3 @@ end
end
end
end
-550
View File
@@ -1,550 +0,0 @@
# 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(st -> st in (:contour, :heatmap, :surface, :wireframe, :contour3d), get(d, :seriestype, :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
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
function convertToAnyVector(args...)
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
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)
nobigs(v::AVec{BigFloat}) = map(Float64, v)
nobigs(v::AVec{BigInt}) = map(Int64, v)
nobigs(v) = v
@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)
nobigs(x), nobigs(y), nobigs(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, getattr(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)])
# st = d[:seriestype]
#
# # for seriestype `line`, need to sort by x values
# if st == :line
# # order by x
# indices = sortperm(d[:x])
# d[:x] = d[:x][indices]
# d[:y] = d[:y][indices]
# d[:seriestype] = :path
# end
#
# # special handling for missing x in box plot... all the same category
# if st == :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
# # either a series of velocity vectors are passed in (`:quiver` keyword),
# # or we just add arrows to the path
#
# # if st == :quiver
# # d[:seriestype] = st = :path
# # d[:linewidth] = 0
# # end
# if get(d, :quiver, nothing) != nothing
# append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
# elseif st == :quiver
# d[:seriestype] = st = :path
# d[:arrow] = arrow()
# end
#
# # now that we've processed a given series... optionally split into
# # multiple dicts through a recipe (for example, a box plot is split into component
# # parts... polygons, lines, and scatters)
# # note: we pass in a Val type (i.e. Val{:box}) so that we can dispatch on the seriestype
# kwlist = apply_series_recipe(d, Val{st})
# 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
#
# # images - grays
# function process_inputs{T<:Gray}(plt::AbstractPlot, d::KW, mat::AMat{T})
# d[:seriestype] = :image
# n,m = size(mat)
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
# # handle images... when not supported natively, do a hack to use heatmap machinery
# if !nativeImagesSupported()
# d[:seriestype] = :heatmap
# d[:yflip] = true
# d[:z] = Surface(convert(Matrix{Float64}, mat.surf))
# d[:fillcolor] = ColorGradient([:black, :white])
# end
# end
#
# # images - colors
# function process_inputs{T<:Colorant}(plt::AbstractPlot, d::KW, mat::AMat{T})
# d[:seriestype] = :image
# n,m = size(mat)
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
# # handle images... when not supported natively, do a hack to use heatmap machinery
# if !nativeImagesSupported()
# d[:yflip] = true
# imageHack(d)
# end
# end
#
#
# # plotting arbitrary shapes/polygons
# function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
# d[:x], d[:y] = shape_coords(shape)
# d[:seriestype] = :shape
# end
# function process_inputs(plt::AbstractPlot, d::KW, shapes::AVec{Shape})
# d[:x], d[:y] = shape_coords(shapes)
# d[:seriestype] = :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[:seriestype] = :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 seriestype
# st = get(d, :seriestype, :none)
# if st == :scatter
# d[:seriestype] = :scatter3d
# elseif !(st in _3dTypes)
# d[:seriestype] = :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, :seriestype, :none))
# d[:seriestype] = :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, :seriestype, :none))
# d[:seriestype] = :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.attr, :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
# # @eval begin
# # import DataFrames
#
# DFS = Union{Symbol, AbstractArray{Symbol}}
#
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS)
# if isa(dfs, Symbol)
# get!(d, Symbol(letter * "label"), string(dfs))
# collect(df[dfs])
# else
# get!(d, :label, reshape(dfs, 1, length(dfs)))
# Any[collect(df[s]) for s in dfs]
# end
# end
#
# function handle_group(df::DataFrames.AbstractDataFrame, d::KW)
# if haskey(d, :group)
# g = d[:group]
# if isa(g, Symbol)
# d[:group] = collect(df[g])
# end
# end
# end
#
# @recipe function plot(df::DataFrames.AbstractDataFrame, sy::DFS)
# handle_group(df, d)
# handle_dfs(df, d, "y", sy)
# end
#
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS)
# handle_group(df, d)
# x = handle_dfs(df, d, "x", sx)
# y = handle_dfs(df, d, "y", sy)
# x, y
# end
#
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS)
# handle_group(df, d)
# x = handle_dfs(df, d, "x", sx)
# y = handle_dfs(df, d, "y", sy)
# z = handle_dfs(df, d, "z", sz)
# x, y, z
# end
#
# # get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
# # get_data(df::DataFrames.AbstractDataFrame, arg) = arg
# #
# # function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
# # # d[:dataframe] = df
# # process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
# # end
# #
# # # expecting the column name of a dataframe that was passed in... anything else should error
# # function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
# # if haskey(df, s)
# # return extractGroupArgs(df[s])
# # else
# # error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
# # end
# # end
#
# # function getDataFrameFromKW(d::KW)
# # get(d, :dataframe) do
# # error("Missing dataframe argument!")
# # 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
+2 -2
View File
@@ -30,7 +30,7 @@ get_subplot(plt::Plot, i::Integer) = plt.subplots[i]
get_subplot(plt::Plot, k) = plt.spmap[k]
get_subplot(series::Series) = series.d[:subplot]
get_subplot_index(plt::Plot, idx::Integer) = idx
get_subplot_index(plt::Plot, idx::Integer) = Int(idx)
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(_ -> _ === sp, plt.subplots)
series_list(sp::Subplot) = filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
@@ -39,7 +39,7 @@ function should_add_to_legend(series::Series)
series.d[:primary] && series.d[:label] != "" &&
!(series.d[:seriestype] in (
:hexbin,:histogram2d,:hline,:vline,
:contour,:contour3d,:surface,:wireframe,
:contour,:contourf,:contour3d,:surface,:wireframe,
:heatmap, :pie, :image
))
end
+10 -7
View File
@@ -34,11 +34,13 @@ type Subplot{T<:AbstractBackend} <: AbstractLayout
plt # the enclosing Plot object (can't give it a type because of no forward declarations)
end
Base.show(io::IO, sp::Subplot) = print(io, "Subplot{$(sp[:subplot_index])}")
# -----------------------------------------------------------
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
type Axis
sp::Subplot
sps::Vector{Subplot}
d::KW
end
@@ -83,12 +85,13 @@ function Plot()
Subplot[], false)
end
# TODO: make a decision... should plt[1] return the first subplot or the first series??
# Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
Base.getindex(plt::Plot, s::Symbol) = plt.spmap[s]
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
attr(plt::Plot, k::Symbol) = plt.attr[k]
attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
# -----------------------------------------------------------------------
Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
# attr(plt::Plot, k::Symbol) = plt.attr[k]
# attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
Base.getindex(sp::Subplot, i::Integer) = series_list(sp)[i]
# -----------------------------------------------------------------------
+151 -31
View File
@@ -131,39 +131,85 @@ function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
end
function imageHack(d::KW)
:heatmap in supported_types() || error("Neither :image or :heatmap are supported!")
is_seriestype_supported(:heatmap) || error("Neither :image or :heatmap are supported!")
d[:seriestype] = :heatmap
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
end
# ---------------------------------------------------------------
type Segments{T}
pts::Vector{T}
end
# Segments() = Segments{Float64}(zeros(0))
Segments() = Segments(Float64)
Segments{T}(::Type{T}) = Segments(T[])
Segments(p::Int) = Segments(NTuple{2,Float64}[])
# Segments() = Segments(zeros(0))
to_nan(::Type{Float64}) = NaN
to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN)
coords(segs::Segments{Float64}) = segs.pts
coords(segs::Segments{NTuple{2,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
function Base.push!{T}(segments::Segments{T}, vs...)
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
for v in vs
push!(segments.pts, convert(T,v))
end
segments
end
function Base.push!{T}(segments::Segments{T}, vs::AVec)
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
for v in vs
push!(segments.pts, convert(T,v))
end
segments
end
# -----------------------------------------------------
# helper to manage NaN-separated segments
type SegmentsIterator
args::Tuple
nextidx::Int
n::Int
end
function iter_segments(args...)
tup = Plots.wraptuple(args)
n = maximum(map(length, tup))
SegmentsIterator(tup, 0, n)
SegmentsIterator(tup, n)
end
# helpers to figure out if there are NaN values in a list of array types
anynan(i::Int, args...) = any(a -> !isfinite(cycle(a,i)), args)
anynan(istart::Int, iend::Int, args...) = any(i -> anynan(i, args...), istart:iend)
allnan(istart::Int, iend::Int, args...) = all(i -> anynan(i, args...), istart:iend)
anynan(i::Int, args::Tuple) = any(a -> !isfinite(cycle(a,i)), args)
anynan(istart::Int, iend::Int, args::Tuple) = any(i -> anynan(i, args), istart:iend)
allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:iend)
Base.start(itr::SegmentsIterator) = (itr.nextidx = 1) #resets
Base.done(itr::SegmentsIterator, unused::Int) = itr.nextidx > itr.n
function Base.next(itr::SegmentsIterator, unused::Int)
i = istart = iend = itr.nextidx
function Base.start(itr::SegmentsIterator)
nextidx = 1
if anynan(1, itr.args)
_, nextidx = next(itr, 1)
end
nextidx
end
Base.done(itr::SegmentsIterator, nextidx::Int) = nextidx > itr.n
function Base.next(itr::SegmentsIterator, nextidx::Int)
i = istart = iend = nextidx
# find the next NaN, and iend is the one before
while i <= itr.n + 1
if i > itr.n || anynan(i, itr.args...)
if i > itr.n || anynan(i, itr.args)
# done... array end or found NaN
iend = i-1
break
@@ -171,16 +217,15 @@ function Base.next(itr::SegmentsIterator, unused::Int)
i += 1
end
# find the next non-NaN, and set itr.nextidx
# find the next non-NaN, and set nextidx
while i <= itr.n
if !anynan(i, itr.args...)
if !anynan(i, itr.args)
break
end
i += 1
end
itr.nextidx = i
istart:iend, 0
istart:iend, i
end
# ------------------------------------------------------------------------------------
@@ -189,20 +234,26 @@ end
nop() = nothing
notimpl() = error("This has not been implemented yet")
Base.cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
Base.cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
Base.cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
Base.cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
Base.cycle(v, idx::Int) = v
Base.cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
Base.cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
Base.cycle(v, idx::AVec{Int}) = v
Base.cycle(v, indices::AVec{Int}) = fill(v, length(indices))
Base.cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
Base.cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
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
maketuple{T,S}(x::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]
@@ -232,6 +283,7 @@ function _expand_limits(lims, x)
nothing
end
expand_data(v, n::Integer) = [cycle(v, i) for i=1:n]
# 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...)
@@ -250,20 +302,16 @@ function replaceType(vec, val)
push!(vec, val)
end
function replaceAlias!(d::KW, k::Symbol, aliases::KW)
function replaceAlias!(d::KW, k::Symbol, aliases::Dict{Symbol,Symbol})
if haskey(aliases, k)
d[aliases[k]] = pop!(d, k)
end
end
function replaceAliases!(d::KW, aliases::KW)
function replaceAliases!(d::KW, aliases::Dict{Symbol,Symbol})
ks = collect(keys(d))
for k in ks
replaceAlias!(d, k, aliases)
# if haskey(aliases, k)
# d[aliases[k]] = d[k]
# delete!(d, k)
# end
end
end
@@ -336,15 +384,15 @@ is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2
isvertical(d::KW) = get(d, :orientation, :vertical) in (:vertical, :v, :vert)
isvertical(series::Series) = isvertical(series.d)
# 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{T<:AVec,S<:AVec}(ticks::Tuple{T,S}) = :ticks_and_labels
ticksType(ticks) = :invalid
limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
limsType{T<:Real,S<:Real}(lims::Tuple{T,S}) = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :invalid
@@ -395,9 +443,10 @@ end
# this is a helper function to determine whether we need to transpose a surface matrix.
# it depends on whether the backend matches rows to x (transpose_on_match == true) or vice versa
# for example: PyPlot sends rows to y, so transpose_on_match should be true
function transpose_z(d::KW, z, transpose_on_match::Bool = true)
function transpose_z(d, z, transpose_on_match::Bool = true)
if d[:match_dimensions] == transpose_on_match
z'
# z'
permutedims(z, [2,1])
else
z
end
@@ -426,9 +475,22 @@ function make_fillrange_from_ribbon(kw::KW)
kw[:fillrange] = make_fillrange_side(y, rib1), make_fillrange_side(y, rib2)
end
function get_sp_lims(sp::Subplot, letter::Symbol)
axis_limits(sp[Symbol(letter, :axis)])
end
xlims(sp::Subplot) = get_sp_lims(sp, :x)
ylims(sp::Subplot) = get_sp_lims(sp, :y)
zlims(sp::Subplot) = get_sp_lims(sp, :z)
xlims(plt::Plot, sp_idx::Int = 1) = xlims(plt[sp_idx])
ylims(plt::Plot, sp_idx::Int = 1) = ylims(plt[sp_idx])
zlims(plt::Plot, sp_idx::Int = 1) = zlims(plt[sp_idx])
xlims(sp_idx::Int = 1) = xlims(current(), sp_idx)
ylims(sp_idx::Int = 1) = ylims(current(), sp_idx)
zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
# ---------------------------------------------------------------
wraptuple(x::@compat(Tuple)) = x
wraptuple(x::Tuple) = x
wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
@@ -436,7 +498,7 @@ 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) ||
allShapes(arg) = trueOrAllTrue(a -> is_marker_supported(get(_markerAliases, a, a)), 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)
@@ -615,10 +677,68 @@ end
# -------------------------------------------------------
# indexing notation
Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
# 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)
# -------------------------------------------------------
# operate on individual series
function push_x!(series::Series, xi)
push!(series[:x], xi)
expand_extrema!(series[:subplot][:xaxis], xi)
return
end
function push_y!(series::Series, yi)
push!(series[:y], yi)
expand_extrema!(series[:subplot][:yaxis], yi)
return
end
function push_z!(series::Series, zi)
push!(series[:z], zi)
expand_extrema!(series[:subplot][:zaxis], zi)
return
end
function Base.push!(series::Series, yi)
x = extendSeriesByOne(series[:x])
expand_extrema!(series[:subplot][:xaxis], x[end])
series[:x] = x
push_y!(series, yi)
end
Base.push!(series::Series, xi, yi) = (push_x!(series,xi); push_y!(series,yi))
Base.push!(series::Series, xi, yi, zi) = (push_x!(series,xi); push_y!(series,yi); push_z!(series,zi))
# -------------------------------------------------------
function update!(series::Series; kw...)
d = KW(kw)
preprocessArgs!(d)
for (k,v) in d
if haskey(_series_defaults, k)
series[k] = v
else
warn("unused key $k in series update")
end
end
_series_updated(series[:subplot].plt, series)
series
end
function update!(sp::Subplot; kw...)
d = KW(kw)
preprocessArgs!(d)
for (k,v) in d
if haskey(_subplot_defaults, k)
sp[k] = v
else
warn("unused key $k in subplot update")
end
end
sp
end
# -------------------------------------------------------
# push/append for one series
+2 -8
View File
@@ -1,15 +1,9 @@
julia 0.4
RecipesBase
Colors
Reexport
Measures
StatPlots
FactCheck
Images
PyPlot
ImageMagick
@osx QuartzImageIO
GR
DataFrames
RDatasets
VisualRegressionTests
UnicodePlots
+13 -10
View File
@@ -13,7 +13,9 @@ try
end
using Plots, FactCheck
using Plots
using StatPlots
using FactCheck
using Glob
default(size=(500,300))
@@ -22,7 +24,7 @@ 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)
const _current_plots_version = v"0.7.2"
const _current_plots_version = v"0.9.3"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
@@ -41,9 +43,10 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
fn = "ref$idx.png"
# firgure out version info
G = glob(relpath(refdir) * "/*")
G = glob(joinpath(relpath(refdir), "*"))
# @show refdir fn G
versions = map(fn -> VersionNumber(split(fn,"/")[end]), G)
slash = (@static is_windows() ? "\\" : "/")
versions = map(fn -> VersionNumber(split(fn, slash)[end]), G)
versions = reverse(sort(versions))
versions = filter(v -> v <= _current_plots_version, versions)
# @show refdir fn versions
@@ -75,12 +78,12 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
png(fn)
end
try
run(`mkdir -p $newdir`)
catch err
display(err)
end
# reffn = joinpath(refdir, "ref$idx.png")
# try
# run(`mkdir -p $newdir`)
# catch err
# display(err)
# end
# # reffn = joinpath(refdir, "ref$idx.png")
# the test
vtest = VisualTest(func, reffn, idx)
+44 -4
View File
@@ -5,7 +5,7 @@ include("imgcomp.jl")
# don't actually show the plots
srand(1234)
default(show=false, reuse=true)
img_eps = 5e-2
img_eps = isinteractive() ? 1e-2 : 10e-2
# facts("Gadfly") do
# @fact gadfly() --> Plots.GadflyBackend()
@@ -23,14 +23,16 @@ facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend()
image_comparison_facts(:pyplot, skip=[30], eps=img_eps)
image_comparison_facts(:pyplot, skip=[25,30], eps=img_eps)
end
facts("GR") do
@fact gr() --> Plots.GRBackend()
@fact backend() --> Plots.GRBackend()
@linux_only image_comparison_facts(:gr, skip=[30], eps=img_eps)
if is_linux() && isinteractive()
image_comparison_facts(:gr, skip=[2,25,30], eps=img_eps)
end
end
facts("Plotly") do
@@ -38,7 +40,7 @@ facts("Plotly") do
@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)
# @static is_linux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
end
@@ -85,6 +87,44 @@ facts("Axes") do
end
# tests for preprocessing recipes
# facts("recipes") do
# user recipe
# type T end
# @recipe function f(::T)
# line := (3,0.3,:red)
# marker := (20,0.5,:blue,:o)
# bg := :yellow
# rand(10)
# end
# plot(T())
# plot recipe
# @recipe function f(::Type{Val{:hiplt}},plt::Plot)
# line := (3,0.3,:red)
# marker := (20,0.5,:blue,:o)
# t := :path
# bg:=:green
# ()
# end
# plot(rand(10),t=:hiplt)
# series recipe
# @recipe function f(::Type{Val{:hi}},x,y,z)
# line := (3,0.3,:red)
# marker := (20,0.5,:blue,:o)
# t := :path
# ()
# end
# plot(rand(10),t=:hiplt)
# end
FactCheck.exitstatus()
+36
View File
@@ -0,0 +1,36 @@
import SnoopCompile
### Log the compiles
# This only needs to be run once (to generate "/tmp/plots_compiles.csv")
# SnoopCompile.@snoop "/tmp/plots_compiles.csv" begin
# include(joinpath(dirname(@__FILE__), "runtests.jl"))
# end
# ----------------------------------------------------------
### Parse the compiles and generate precompilation scripts
# This can be run repeatedly to tweak the scripts
# IMPORTANT: we must have the module(s) defined for the parcelation
# step, otherwise we will get no precompiles for the Plots module
using Plots
data = SnoopCompile.read("/tmp/plots_compiles.csv")
# The Plots tests are run inside a module PlotsTest, so all
# the precompiles get credited to PlotsTest. Credit them to Plots instead.
subst = Dict("PlotsTests"=>"Plots")
# Blacklist helps fix problems:
# - MIME uses type-parameters with symbols like :image/png, which is
# not parseable
blacklist = ["MIME"]
# Use these two lines if you want to create precompile functions for
# individual packages
pc, discards = SnoopCompile.parcel(data[end:-1:1,2], subst=subst, blacklist=blacklist)
SnoopCompile.write("/tmp/precompile", pc)
pdir = joinpath(dirname(@__FILE__), "..")
run(`cp /tmp/precompile/precompile_Plots.jl $pdir/src/precompile.jl`)
+12 -11
View File
@@ -1,25 +1,26 @@
# Pkg.clone(pwd())
# Pkg.build("Plots")
# Pkg.clone("ImageMagick")
# Pkg.build("ImageMagick")
Pkg.clone("ImageMagick")
Pkg.build("ImageMagick")
# Pkg.clone("GR")
# Pkg.build("GR")
Pkg.clone("GR")
Pkg.build("GR")
# Pkg.clone("https://github.com/JuliaPlots/RecipesBase.jl.git")
# Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
# Pkg.clone("https://github.com/JuliaStats/KernelDensity.jl.git")
Pkg.clone("StatPlots")
Pkg.checkout("PlotUtils")
# 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")
Pkg.checkout("RecipesBase")
Pkg.clone("VisualRegressionTests")
# Pkg.checkout("RecipesBase")
# Pkg.clone("VisualRegressionTests")
# need this to use Conda
ENV["PYTHON"] = ""
Pkg.add("PyPlot")
Pkg.build("PyPlot")