Compare commits

...

273 Commits

Author SHA1 Message Date
t-bltg d988d4c9d1 1.22.0 [skip ci] 2021-09-15 12:54:42 +02:00
github-actions[bot] 85bd3ea53f Update precompile_*.jl file [skip ci] (#3810)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-09-15 12:53:58 +02:00
t-bltg cd6a8c87b6 GR: fix NaN tick size (#3813) 2021-09-15 12:28:53 +02:00
t-bltg 175c87cec6 Avoid Vararg usage (#3809) 2021-09-15 12:28:24 +02:00
t-bltg c3f45dd712 UnicodePlots: minor fixes (#3812) 2021-09-15 11:21:41 +02:00
t-bltg 198a59b621 UnicodePlots: propagate xyscale (#3811) 2021-09-15 01:29:23 +02:00
t-bltg c5d7283473 Update examples.jl
UnicodePlots: annotations are supported
2021-09-14 21:02:30 +02:00
t-bltg 7a1bc88f66 Use hyphens to specify version ranges 2021-09-14 20:27:44 +02:00
github-actions[bot] ad5cd2f1da Update precompile_*.jl file [skip ci] (#3807)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-09-14 19:01:40 +02:00
t-bltg 6cf01229bb UnicodePlots: support :annotations (#3804) 2021-09-14 18:46:46 +02:00
t-bltg 0a9d30f7ac Fix invalid axes aliases (#3803)
* Fix invalid axes aliases

* Add test

* Enhance testing
2021-09-14 18:00:23 +02:00
github-actions[bot] 7da990b23f Format .jl files [skip ci] (#3801)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-09-13 09:18:59 +02:00
github-actions[bot] 942fb38dcd Update precompile_*.jl file [skip ci] (#3797)
Co-authored-by: isentropic <isentropic@users.noreply.github.com>
2021-09-13 09:18:18 +02:00
Zhanibek 7ba99d1b06 handling fonts better, moving to stable 3.4 (#3793)
* handling fonts better, moving to stable 3.4

* better math parent font matching
2021-09-11 13:43:42 +09:00
t-bltg 6b82a82d04 Update examples.jl 2021-09-06 13:18:22 +02:00
t-bltg 7c882a7288 Update Project.toml 2021-09-06 12:48:26 +02:00
github-actions[bot] 5343873f72 Update precompile_*.jl file [skip ci] (#3752)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-09-06 12:47:06 +02:00
t-bltg e3a5f2b11e UnicodePlots: support log scales (#3796) 2021-09-06 12:45:29 +02:00
github-actions[bot] 34758c82a3 Format .jl files [skip ci] (#3795)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-09-06 11:52:32 +02:00
t-bltg 748104c807 UnicodePlots: fix NaN segments plot (#3794) 2021-09-04 12:46:34 +02:00
t-bltg 0d2bc3f227 UnicodePlots: Update URL 2021-09-02 22:20:54 +02:00
t-bltg c45352d153 fix UnicodePlots.jl/issues/125 (#3791) 2021-09-02 17:59:02 +02:00
Josef Heinen b833b6582a Update Project.toml
Bump GR version
2021-09-01 16:55:16 +02:00
t-bltg b28a261b39 1.21.3 [skip ci] 2021-09-01 14:06:42 +02:00
t-bltg 1a1cc08992 UnicodePlots: fixes 2021-09-01 13:10:09 +02:00
t-bltg 31d33e6be4 UnicodePlots: Allow simple nesting 2021-09-01 12:48:31 +02:00
t-bltg 2dc812aa15 UnicodePlots: support layout (#3787) 2021-09-01 11:26:29 +02:00
t-bltg bdbe300412 Pyplot: fix building docs (#3775) 2021-09-01 11:24:16 +02:00
Nicholas Bauer 70b635dbe1 Cache axis args in a dictionary (#3775)
Co-authored-by: t-bltg <tf.bltg@gmail.com>
Co-authored-by: Simon Christ <SimonChrist@gmx.de>
2021-08-31 21:36:58 +02:00
t-bltg 1e44dd7035 Unicodeplots: "join" subplots 2021-08-31 00:21:11 +02:00
t-bltg 4744152f86 Add newline between subplots 2021-08-30 23:25:19 +02:00
t-bltg dc51f2c159 v1.21.2 [skip ci] 2021-08-30 22:22:05 +02:00
t-bltg cc1f662b3a UnicodePlots: rm forced width/height (arbitrary) 2021-08-30 17:50:41 +02:00
t-bltg 7b77527f80 UnicodePlots: display fix, simplify axis labels 2021-08-30 14:04:00 +02:00
github-actions[bot] 8b6073c088 Format .jl files [skip ci] (#3780)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-08-29 10:53:46 +02:00
t-bltg 09d35d7b9f UnicodePlots: heatmap support (#3774)
* UnicodePlots: heatmap support

* Fix type check
2021-08-28 21:29:04 +02:00
Simon Christ 0a9da21443 sanitize axis strings (#3772) 2021-08-28 21:08:21 +02:00
Simon Christ ea686c331b improve axis decorations (#3756)
* improve handling of LaTeXStrings in tick labels

* respect guidefonthalign
2021-08-28 21:08:03 +02:00
t-bltg 25a36bab85 merge root codecov.yml [skip ci] 2021-08-28 17:05:13 +02:00
t-bltg b227fb8943 move codecov.yml 2021-08-28 00:12:43 +02:00
t-bltg 22651e8728 UnicodePlots: disable some unsupported examples 2021-08-26 19:54:58 +02:00
Nicholas Bauer 5acf967a9e v1.21.1 | No exception thrown in apply_recipe fallback (#3765)
* No exception thrown in `apply_recipe` fallback

Throwing an exception here can vastly slow down plotting, as it happens frequently. Returning an error value instead, which the calling function can check, can speed up plotting 2x. An accompanying change is necessary in RecipesPipeline.jl, assuming this value is suitable.

* Bumped compat for RecipesPipeline, tentative

* Bump patch version

* Fix wrong versioning

Co-authored-by: t-bltg <tf.bltg@gmail.com>
2021-08-26 17:46:28 +02:00
Simon Christ 5afeba656b check if .zenodo.json is valid 2021-08-26 17:17:26 +02:00
LukasKrumwiede 7b8bd5b253 Implementation of filling the space between and under curves for gaston (#3755) 2021-08-26 16:58:05 +02:00
Pearl Li ded808477d Add hatched fill for GR and PyPlot (#3107) 2021-08-26 16:55:56 +02:00
Simon Christ 854d5ba5c9 1.21.0 [skip ci] 2021-08-26 16:21:47 +02:00
t-bltg 07a619e5ae Replace deprecated Base.download with Downloads.download (#3766)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-08-26 00:12:33 +02:00
t-bltg 1426c95fd4 fix formatting action 2021-08-25 22:42:23 +02:00
Andy Nowacki 52ec432cfa Plotly: Enable specified contour values for ranges; warn otherwise (#3757) 2021-08-25 13:40:54 +02:00
t-bltg 0742d475bc CI: julia-uploadcodecov is deprecated (#3761) 2021-08-22 12:00:12 +02:00
Josef Heinen 10802baf6e Added support for 3d surface meshes 2021-08-22 09:17:42 +02:00
t-bltg c67da7d7b8 UnicodePlots: allow passing arbitrary colors (RGBA, symbols, integers, ...) (#3758) 2021-08-19 22:19:38 +02:00
t-bltg f85f668f18 Merge pull request #3759 from t-bltg/fmt
CI: run JuliaFormatter on schedule
2021-08-19 22:17:47 +02:00
Simon Christ c4208c7560 fix broken file 2021-08-19 21:07:04 +02:00
t-bltg 35fd604e07 run JuliaFormatter on schedule 2021-08-19 17:51:53 +02:00
t-bltg 214663aa4b Merge pull request #3750 from t-bltg/col
UnicodePlots: fix setting linecolor
2021-08-15 12:40:19 +02:00
t-bltg d56a03975f UnicodePlots: fix setting linecolor 2021-08-15 00:39:04 +02:00
t-bltg ede106073f 1.20.1 [skip ci] 2021-08-12 21:58:21 +02:00
t-bltg 6e8228826d Merge pull request #3747 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles [skip ci]
2021-08-12 21:57:16 +02:00
t-bltg 8147c59b2f Update precompile_*.jl file [skip ci] 2021-08-12 17:16:25 +00:00
t-bltg 54a6518d59 Merge pull request #3749 from t-bltg/format
Code style - format
2021-08-12 18:48:42 +02:00
t-bltg c5db139bbb run format(["src", "test"]) 2021-08-11 08:14:51 +02:00
t-bltg a3eca8244e disable formatting on exports 2021-08-11 07:54:35 +02:00
t-bltg 9164ee09cb fix wrong method signature 2021-08-08 23:18:30 +02:00
t-bltg ffca3c1305 update JuliaFormatter config file 2021-08-08 20:38:23 +02:00
t-bltg 1c84704a81 move format to separate action 2021-08-08 20:24:18 +02:00
t-bltg 9e824367bb add configuration file 2021-08-08 20:23:22 +02:00
t-bltg 10e9325635 avoid ambiguous += 2021-08-08 20:23:22 +02:00
t-bltg 13141feb4f Fix #3462 2021-08-04 13:11:15 +02:00
t-bltg f9833214ce Merge pull request #3740 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles [skip ci]
2021-08-03 22:02:32 +02:00
t-bltg a67a006501 Update precompile_*.jl file [skip ci] 2021-08-03 19:20:52 +00:00
t-bltg 40b5df38f4 Merge pull request #3743 from t-bltg/ann
Allow passing collection of tuples to series_annotations
2021-08-03 20:58:12 +02:00
t-bltg c171cd0cf5 Merge pull request #3745 from t-bltg/mesh3d
Move mesh3d triangle to utils - gaston: implement :mesh3d
2021-08-03 20:57:45 +02:00
t-bltg b7b4c850b6 move mesh3d triangle to utils - gaston: implement :mesh3d 2021-08-03 20:07:56 +02:00
t-bltg ba4c4d7bfb Update backends.jl 2021-08-03 17:58:50 +02:00
t-bltg 62b6075407 Gaston: support :arrow for :quiver 2021-08-03 17:56:54 +02:00
t-bltg c0ff6e3dee Gaston: initial :quiver support 2021-08-03 17:55:52 +02:00
t-bltg 67f441f152 Gaston: swap :image axes 2021-08-03 17:54:49 +02:00
t-bltg ed7b6f7f64 examples, missing number 2021-08-03 17:26:23 +02:00
t-bltg 1ccd4ae699 Gaston: remove :histogram2d 2021-08-03 17:19:40 +02:00
t-bltg bd70951afa Gaston: flip y for :image series 2021-08-03 17:19:09 +02:00
t-bltg 39cb596868 allow passing vectors/tuples of vectors to series_annotations 2021-08-03 13:39:20 +02:00
t-bltg 5c13f00716 cleanup 2021-08-03 10:37:05 +02:00
t-bltg f331bfa010 allow passing tuple to series_annotations 2021-08-03 10:34:53 +02:00
Simon Christ c4681682a1 1.20.0 [skip ci] 2021-08-03 09:58:28 +02:00
t-bltg a72b4a3481 specify relative coordinates 2021-08-02 23:30:54 +02:00
t-bltg ff2ae4fe4f typo 2021-08-02 21:54:35 +02:00
t-bltg 2df85eb0fd Update examples.jl 2021-08-02 20:14:40 +02:00
t-bltg 508c47895c Merge pull request #3634 from t-bltg/annotate
Allow relative positioning of annotations
2021-08-02 17:28:30 +02:00
t-bltg 07d32197a1 add regression test 2021-08-02 16:55:51 +02:00
t-bltg d6878a8ea0 remove the need for Plots.pct in annotations 2021-08-02 13:06:02 +02:00
Daniel Schwabeneder b79a2d32fe fix series_annotations for bar (#3742)
* fix series_annotations for bar

* fix my typos in bar recipe

* fix fillrange of annotation series for groupedbar

* fix series_annotations for bar

* fix my typos in bar recipe

* fix fillrange of annotation series for groupedbar

* fix series_annotations for bar

* fix my typos in bar recipe

* fix fillrange of annotation series for groupedbar

* fix series_annotations for bar

* fix fillrange of annotation series for groupedbar
2021-08-02 12:58:16 +02:00
t-bltg 7b1941b129 Typo 2021-08-01 18:04:10 +02:00
t-bltg c6d9332cf1 Gaston: fix polar ticks 2021-08-01 18:03:44 +02:00
t-bltg 670fba56a7 Update examples.jl 2021-08-01 17:55:35 +02:00
t-bltg 9985157eb8 Update backends.jl 2021-08-01 17:54:54 +02:00
t-bltg 9308dd74a8 Update gaston.jl 2021-08-01 17:54:15 +02:00
t-bltg 52be7387f2 Gaston: support polar 2021-08-01 17:53:08 +02:00
t-bltg a94c49ab95 Gaston: remove unsupported :line, :vline markers 2021-08-01 16:19:16 +02:00
t-bltg b7c6a65921 Gaston: remove :vline, :hline markers 2021-08-01 16:18:19 +02:00
t-bltg d8a40f5400 Gaston: remove debug show(...) 2021-08-01 16:12:07 +02:00
t-bltg e4c8921ae9 Gaston: remove more key entries (e.g. :shape) 2021-08-01 16:11:17 +02:00
t-bltg fe8d08123c Gaston: fix wrong series_list causing segfault 2021-08-01 16:00:43 +02:00
t-bltg defe808619 Gaston: update attributes 2021-08-01 15:23:42 +02:00
t-bltg 85fa05464d Gaston: reset key on contours 2021-08-01 15:16:46 +02:00
t-bltg a9a24841fe Gaston: remove debug 2021-08-01 15:04:52 +02:00
t-bltg 0b989cd33e Gaston: rework multiplot 2021-08-01 15:04:09 +02:00
t-bltg 773c7a90b4 Gaston: conditional add_to_legend 2021-08-01 14:16:07 +02:00
t-bltg f49f5d1638 Update gaston.jl 2021-08-01 09:47:36 +02:00
t-bltg 478868a6a2 Merge pull request #3741 from t-bltg/scalefontsizes
Rework fontscales - cleanup
2021-08-01 09:31:34 +02:00
t-bltg 806dc3aa43 rework fontscales - cleanup 2021-08-01 09:07:29 +02:00
t-bltg 0f54c86865 Gaston: update examples 2021-08-01 08:46:38 +02:00
t-bltg 5a5864d087 Gaston: rework margins, terminal fontsize and size 2021-08-01 08:35:16 +02:00
t-bltg baac498532 Merge pull request #3739 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles [skip ci]
2021-08-01 08:07:35 +02:00
t-bltg 9fba70db5e Update precompile_*.jl file [skip ci] 2021-08-01 06:00:06 +00:00
t-bltg af610735eb Gaston: fix legend font 2021-08-01 07:38:28 +02:00
t-bltg 204adc3e36 Gaston: add flip - add :log2, :ln scales 2021-08-01 07:22:20 +02:00
t-bltg 603dc10788 Gaston: fix broken multiplot 2021-08-01 01:23:44 +02:00
t-bltg bf16a97cd5 Gaston: fix legendtitle 2021-08-01 01:16:41 +02:00
t-bltg 8ebe9e8204 Gaston: allow additional series 2021-08-01 00:59:02 +02:00
t-bltg 40cc74bea6 Gaston: fix :steppre, :steppost, add histeps 2021-08-01 00:32:33 +02:00
t-bltg 92cad5376b Update gaston.jl 2021-08-01 00:21:13 +02:00
t-bltg a3e1a43358 Merge pull request #3738 from t-bltg/segments
Gaston: add support for img - rework fonts - fix undef
2021-08-01 00:09:28 +02:00
t-bltg 2fcd907165 Gaston: add support for img - rework fonts - fix undef 2021-08-01 00:08:24 +02:00
t-bltg 2769702687 Merge pull request #3734 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles [skip ci]
2021-07-31 22:13:31 +02:00
t-bltg bf43b9ecec Update precompile_*.jl file [skip ci] 2021-07-31 19:31:14 +00:00
t-bltg 80748eb5e9 Merge pull request #3735 from JuliaPlots/t-bltg-patch-1
GR: remove obsolete method
2021-07-31 21:13:59 +02:00
t-bltg 7863ab9b3b Update gaston.jl 2021-07-31 20:46:03 +02:00
t-bltg 5bc96cfe68 GR: remove obsolete method 2021-07-31 20:31:58 +02:00
t-bltg 2938950bda Gaston: missing markers 2021-07-31 20:30:05 +02:00
t-bltg 88b86be17a Gaston: :hline and :vline are now valid markers 2021-07-31 20:28:37 +02:00
t-bltg aa6b636793 Gaston: add support for :hline, :vline markers 2021-07-31 20:27:26 +02:00
t-bltg dc3d302353 Gaston: typo 2021-07-31 19:44:57 +02:00
t-bltg 4ed13644c4 Gaston: minor fixes 2021-07-31 19:21:55 +02:00
t-bltg 34bf55c033 Gaston: blank plot fixes 2021-07-31 19:09:26 +02:00
t-bltg 982c79baa7 Gaston: drop setting termopts in _create_backend_figure 2021-07-31 19:02:29 +02:00
t-bltg e9b3208d41 Gaston: typo on eval _show 2021-07-31 18:59:49 +02:00
t-bltg 77a9fe3b47 Gaston: add support for background color 2021-07-31 18:50:35 +02:00
t-bltg 4078424f3c Gaston: add support for annotations 2021-07-31 18:14:40 +02:00
t-bltg b45400b1d3 Merge pull request #3731 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/t-bltg-patch-1
[AUTO] Update precompiles [skip ci]
2021-07-31 16:43:54 +02:00
t-bltg dc3e15b4c9 Merge pull request #3730 from JuliaPlots/t-bltg-patch-1
gr_draw_minorgrid should read minorgrid not grid
2021-07-31 16:06:30 +02:00
t-bltg 1e3cb4b4ea Update precompile_*.jl file [skip ci] 2021-07-31 13:07:29 +00:00
t-bltg 57b48da670 Gaston: conditional label in legend 2021-07-31 15:02:40 +02:00
t-bltg e9e95968a9 GR: code style 2021-07-31 14:50:16 +02:00
t-bltg 1889f5b7f9 Gaston: add support for major/minor grids 2021-07-31 14:44:34 +02:00
t-bltg 2c72882865 gr_draw_minorgrid should read minorgrid not grid 2021-07-31 14:40:51 +02:00
t-bltg 36a9e3b386 Merge pull request #3729 from t-bltg/minor_ticks
Gaston: add minor ticks
2021-07-31 14:35:51 +02:00
t-bltg 03fbc25b33 Gaston: add minor ticks 2021-07-31 14:35:30 +02:00
t-bltg cbb83e9820 Merge pull request #3728 from t-bltg/markers
Gaston: rework markers (filled, empty) - cleanup
2021-07-31 13:57:04 +02:00
t-bltg 4420e2009f Gaston: rework markers (filled, empty) - cleanup 2021-07-31 13:54:32 +02:00
t-bltg f394698458 Typo, cleanup 2021-07-31 13:05:23 +02:00
t-bltg 3ec86e983a Gaston: swap pos and size 2021-07-30 13:29:59 +02:00
t-bltg 5dab21e9ba Gaston: multiplot, add position 2021-07-30 13:28:22 +02:00
t-bltg 330d3231f6 Gaston: gaston_init_subplot should accept nothing 2021-07-30 13:11:39 +02:00
t-bltg 74b70fc039 Gaston: handle multiple colors 2021-07-30 13:09:59 +02:00
t-bltg 01d5ff1580 Gaston: move update ratios to _update_plot_object 2021-07-30 00:20:38 +02:00
t-bltg 95804039f4 Gaston: Recursive update width/heights from layout 2021-07-29 23:57:58 +02:00
t-bltg 2dec8c1192 Gaston: typo 2021-07-29 22:18:31 +02:00
t-bltg d3380f4ee8 Gaston: try to respect the layout ratio 2021-07-29 22:16:37 +02:00
t-bltg cbc0417cea Gaston: disable legends in contour plots 2021-07-29 21:49:10 +02:00
t-bltg 914ea027d0 Gaston: Unset set size for subplots - Follow Plots.DPI - Cleanup 2021-07-29 19:13:00 +02:00
t-bltg 434656def6 Merge pull request #3727 from t-bltg/debug_mplot
Gaston: debug mplot
2021-07-29 18:42:06 +02:00
t-bltg e16e46db47 Gaston: debug mplot 2021-07-29 18:40:55 +02:00
t-bltg 9e66eb5737 Merge pull request #3725 from johnnychen94/patch-1
add "no changelog" label for SnoopCompile CI
2021-07-29 15:48:56 +02:00
Johnny Chen 5c02648cdd add "no changelog" label for SnoopCompile CI
This excludes PRs created by SnoopCompile CI from the release notes.
2021-07-29 13:57:04 +08:00
t-bltg fd46fd4add 1.19.4 [skip ci] 2021-07-28 19:21:13 +02:00
t-bltg ac6c7fda11 Merge pull request #3724 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles [skip ci]
2021-07-28 19:09:51 +02:00
t-bltg 4755635fbd Update precompile_*.jl file [skip ci] 2021-07-28 17:08:56 +00:00
t-bltg c82a3c35ee Update SnoopCompile.yml 2021-07-28 18:47:15 +02:00
t-bltg 83bce6e895 Update SnoopCompile.yml 2021-07-28 18:45:27 +02:00
t-bltg 07c5019259 Merge pull request #3719 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-28 18:36:57 +02:00
t-bltg 093181d358 Merge pull request #3721 from t-bltg/inset2
Gaston: update inset_subplots
2021-07-28 18:18:14 +02:00
t-bltg 0085706f6f Gaston: update inset_subplots 2021-07-28 18:17:43 +02:00
t-bltg 31359610bd Merge pull request #3720 from t-bltg/inset
Gaston: fix lens example
2021-07-28 18:03:45 +02:00
t-bltg 34c6c76cb8 Gaston: fix lens example 2021-07-28 18:02:41 +02:00
t-bltg 734b301abc Update precompile_*.jl file 2021-07-28 15:38:02 +00:00
t-bltg e2907b973f Gaston: change @assert to @error 2021-07-28 17:17:44 +02:00
t-bltg 9e3ff15522 Skip more Gaston unsupported examples 2021-07-28 17:01:05 +02:00
t-bltg ce8255a462 Merge pull request #3717 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-28 16:53:53 +02:00
t-bltg aedb1fbb0b Update precompile_*.jl file 2021-07-28 13:12:51 +00:00
t-bltg cebcdfb3b5 Merge pull request #3711 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-28 14:52:57 +02:00
t-bltg b4632c8b48 Update precompile_*.jl file 2021-07-28 11:37:41 +00:00
t-bltg cd4ab0fd24 Merge pull request #3713 from bernd1995/bernd1995-gr-cbarfont
Added colorbar font attributes for GR
2021-07-28 13:18:25 +02:00
BerndR e3cd14944c Also added supported args 2021-07-28 12:54:11 +02:00
t-bltg cac0c8d8d3 Gaston: Remove missing debug leftovers 2021-07-28 12:38:49 +02:00
t-bltg 4f352d8e7c Update docs.yml 2021-07-28 12:07:52 +02:00
t-bltg aa32561fe8 Merge pull request #3709 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-28 11:59:22 +02:00
t-bltg f470e6a590 Update precompile_*.jl file 2021-07-28 09:53:56 +00:00
t-bltg ca0041639b Merge pull request #3699 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-28 11:32:17 +02:00
t-bltg 73208ac3bd Update precompile_*.jl file 2021-07-28 09:24:48 +00:00
t-bltg 0112e78c50 Merge pull request #3667 from t-bltg/gaston_add
Add new gnuplot capabilities (surface, contour, contour3d, scatter3d, wireframe, heatmap, ...)
2021-07-28 11:04:57 +02:00
t-bltg a3f209b100 Merge pull request #3681 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-27 21:19:02 +02:00
t-bltg 573c0cf1bf Merge pull request #3695 from bernd1995/bernd1995-gr-cbarfont
Added colorbar font settings for GR
2021-07-27 21:18:36 +02:00
t-bltg a0c939b7c7 Update gr.jl 2021-07-27 20:42:29 +02:00
BerndR f4ed35c3b6 Added colorbar font settings 2021-07-27 13:10:25 +02:00
t-bltg 50b0ae3b0f Update precompile_*.jl file 2021-07-27 10:04:34 +00:00
t-bltg f049f56e0b Fix building docs (JuliaPlots/Plots.jl/issues/3664) 2021-07-27 11:41:07 +02:00
t-bltg 6a459d4c22 Update docs.yml 2021-07-27 11:27:19 +02:00
t-bltg f576456617 Update docs.yml 2021-07-27 11:21:02 +02:00
t-bltg a7c4eef861 Update docs.yml 2021-07-26 22:28:25 +02:00
t-bltg 741684251c Update docs.yml 2021-07-26 22:11:21 +02:00
t-bltg 75bd9dbd01 Update docs.yml 2021-07-26 21:54:16 +02:00
t-bltg 42c60a4fd6 Update docs.yml 2021-07-26 21:47:57 +02:00
t-bltg 92caf2e15f Update docs.yml 2021-07-26 21:25:14 +02:00
t-bltg 99188f7a7e Update docs.yml 2021-07-26 20:56:29 +02:00
t-bltg a9d11cb2be Update docs.yml 2021-07-26 19:15:26 +02:00
t-bltg 8a3fd8f77e Merge pull request #3679 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-26 12:02:39 +02:00
t-bltg bb29abb8a4 Update precompile_*.jl file 2021-07-26 09:52:21 +00:00
t-bltg b801b2b272 explicit function return, decl - add comments - cleanup 2021-07-26 11:52:13 +02:00
t-bltg 371d9f8dd7 Merge pull request #3677 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-26 11:27:34 +02:00
t-bltg 49f8e48399 Update precompile_*.jl file 2021-07-25 22:25:42 +00:00
t-bltg dbc14ac761 Merge pull request #3675 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-26 00:04:03 +02:00
t-bltg c0e35f2358 add new gnuplot capabilities (surface, scatter3d, wireframe, ...) 2021-07-25 23:21:09 +02:00
t-bltg 3ce4e23f1b Update precompile_*.jl file 2021-07-25 19:05:52 +00:00
t-bltg d4d4d5732b Update docs.yml 2021-07-25 20:45:59 +02:00
t-bltg 86a732e9c5 Update docs.yml 2021-07-25 19:42:38 +02:00
t-bltg 530768064e Update docs.yml 2021-07-25 19:33:20 +02:00
t-bltg 89f59532c1 Merge pull request #3672 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-25 18:53:50 +02:00
t-bltg c41e586385 revert tol changes [skip ci] 2021-07-25 18:52:45 +02:00
t-bltg 8cbfe74863 Update docs.yml 2021-07-25 18:51:46 +02:00
t-bltg 27032a3e21 Update precompile_*.jl file 2021-07-25 16:33:31 +00:00
t-bltg 07a786a946 Update runtests.jl 2021-07-25 18:05:54 +02:00
t-bltg ad0f623386 Merge pull request #3670 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-25 17:59:45 +02:00
t-bltg 4fa567aa5b Update docs.yml 2021-07-25 17:58:39 +02:00
t-bltg c5ae99374a Update runtests.jl 2021-07-25 17:54:52 +02:00
t-bltg e79740d3bd Update precompile_*.jl file 2021-07-25 15:49:34 +00:00
t-bltg dbe6c8dc57 Update docs.yml 2021-07-25 17:47:15 +02:00
t-bltg 9bd075d06b Merge pull request #3655 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-25 17:29:39 +02:00
t-bltg 1c68a13d2c Update docs.yml 2021-07-25 17:24:17 +02:00
t-bltg 86d3097123 Merge pull request #3659 from daschw/gr-tickmarks
Make tickmarks the same length for both axes in GR 2D plots
2021-07-25 17:16:12 +02:00
t-bltg 6c2d969316 Update precompile_*.jl file 2021-07-25 12:54:31 +00:00
t-bltg 30e728213e Merge pull request #3666 from t-bltg/gaston_sp
Fix Gaston layout
2021-07-25 14:32:25 +02:00
t-bltg 4c9fd4e4b7 fix gaston layout 2021-07-25 10:52:20 +02:00
Daniel Schwabeneder 458d72b36b reduce tickmark length 2021-07-24 10:59:06 +02:00
t-bltg 26ddb62a27 Merge pull request #3665 from t-bltg/tol2
More strict on tolerance - drop tol for obsolete version
2021-07-23 20:41:49 +02:00
t-bltg 4aa9d74a28 more strict on tolerance - drop tol for obsolete version 2021-07-23 20:07:08 +02:00
t-bltg 878e590125 Merge pull request #3661 from t-bltg/rng
Use a stable RNG in fakedata
2021-07-23 19:50:54 +02:00
Daniel Schwabeneder 4e5b490b6c make tick length independent of plot size 2021-07-23 19:38:04 +02:00
t-bltg f3aa61293b change fakedata rng in tests 2021-07-23 14:47:16 +02:00
t-bltg cd06648ec3 Switch to Random.GLOBAL_RNG
Co-authored-by: Simon Christ <SimonChrist@gmx.de>
2021-07-23 11:02:22 +02:00
t-bltg fe7d957be3 use a stable RNG in fakedata 2021-07-22 13:00:07 +02:00
t-bltg a39582c06e Preempt IMG_TOL with env variable 2021-07-22 02:05:48 +02:00
t-bltg 86fbb39723 Merge pull request #3660 from t-bltg/anim_glob
Avoid using global variables in animation.jl
2021-07-22 01:57:03 +02:00
t-bltg 6f49ec112c avoid using global variables 2021-07-22 01:31:59 +02:00
t-bltg 6594b93126 Add gnuplot to the system dependencies (docs) 2021-07-21 23:49:13 +02:00
Daniel Schwabeneder f9a1e2e92d Merge pull request #3656 from JuliaPlots/compathelper/new_version/2021-07-21-00-05-42-160-4162151670
CompatHelper: bump compat for "GeometryBasics" to "0.4"
2021-07-21 22:52:16 +02:00
Daniel Schwabeneder e58bd8b657 fix minor tick length 2021-07-21 22:39:48 +02:00
Daniel Schwabeneder 5f31ab7169 fix gr tickmarks lengths for 2D plots 2021-07-21 22:30:56 +02:00
t-bltg b85468162c Skip failing gaston examples 2021-07-21 21:38:33 +02:00
t-bltg 0550033065 Prepare gaston (gnuplot) for PlotDocs.jl 2021-07-21 19:34:32 +02:00
t-bltg 89db9025c5 1.19.3 [skip ci] 2021-07-21 12:10:19 +02:00
t-bltg 10ed39cf83 Merge pull request #3644 from t-bltg/marker_log
Fix computing marker coordinates with log scales
2021-07-21 12:09:34 +02:00
github-actions[bot] 237fa4be7b CompatHelper: bump compat for "GeometryBasics" to "0.4" 2021-07-21 00:05:47 +00:00
t-bltg 192eb6ffbb Debugging docs generation 2021-07-20 15:20:03 +02:00
t-bltg 4fc0f85cf1 Merge pull request #3654 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-20 09:59:27 +02:00
t-bltg aecce08a0f Update precompile_*.jl file 2021-07-19 21:37:24 +00:00
t-bltg 3ad2e0674a Merge pull request #3653 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/master
[AUTO] Update precompiles
2021-07-19 23:17:39 +02:00
t-bltg 05f585f8ad Update precompile_*.jl file 2021-07-19 19:25:33 +00:00
t-bltg c6ba6dd899 Merge pull request #3652 from JuliaPlots/Test_SnoopCompile_AutoPR_refs/heads/t-bltg-patch-1
[AUTO] Update precompiles
2021-07-19 21:05:57 +02:00
t-bltg fcf75a195a Merge pull request #3651 from JuliaPlots/t-bltg-patch-1
Take series alpha into account when drawing contours
2021-07-19 14:31:02 +02:00
t-bltg 3195a67915 Update precompile_*.jl file 2021-07-19 12:22:01 +00:00
t-bltg aa837bb027 Take series alpha into account when drawing contours 2021-07-19 14:04:01 +02:00
Simon Christ 7bb2a29356 1.19.2 [skip ci] 2021-07-16 12:18:26 +02:00
github-actions[bot] f0afadf43a Update precompile_*.jl file (#3648)
Co-authored-by: BeastyBlacksmith <BeastyBlacksmith@users.noreply.github.com>
2021-07-16 12:02:04 +02:00
github-actions[bot] 0525ade990 Update precompile_*.jl file (#3647)
Co-authored-by: BeastyBlacksmith <BeastyBlacksmith@users.noreply.github.com>
2021-07-16 10:52:29 +02:00
Daniel Schwabeneder 6b22f45dd5 fix dpi for GR (#3646) 2021-07-16 10:30:41 +02:00
Jks Liu fc0e9a1245 Fix #3440 subplots attributes out of order (#3645) 2021-07-16 10:29:50 +02:00
Josef Heinen 2ec78be9a1 Update Project.toml
bump GR version
2021-07-16 10:28:49 +02:00
github-actions[bot] 4449398614 Update precompile_*.jl file (#3627)
Co-authored-by: BeastyBlacksmith <BeastyBlacksmith@users.noreply.github.com>
2021-07-16 10:26:39 +02:00
t-bltg 946f90d3e4 fix computing marker coordinates with log scales 2021-07-15 12:08:53 +02:00
Simon Christ c046e6461f 1.19.1 [skip ci] 2021-07-15 09:33:28 +02:00
t-bltg 411de69765 fix plot_title for pyplot (#3642)
Co-authored-by: t-bltg <t-bltg@users.noreply.github.com>
2021-07-14 22:01:20 +02:00
t-bltg bb493a872a allow relative positioning of annotations 2021-07-08 18:50:46 +02:00
60 changed files with 6589 additions and 4421 deletions
+12
View File
@@ -0,0 +1,12 @@
always_for_in = true
import_to_using = false
align_pair_arrow = true
align_assignment = true
align_conditional = true
always_use_return = false
conditional_to_if = false
whitespace_in_kwargs = true
remove_extra_newlines = true
whitespace_ops_in_indices = true
short_to_long_function_def = false
annotate_untyped_fields_with_any = false
+5 -3
View File
@@ -75,9 +75,11 @@ jobs:
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update precompile_*.jl file
title: "[AUTO] Update precompiles"
labels: SnoopCompile
commit-message: Update precompile_*.jl file [skip ci]
title: "[AUTO] Update precompiles [skip ci]"
labels: |
SnoopCompile
no changelog
branch: "Test_SnoopCompile_AutoPR_${{ github.ref }}"
+2 -5
View File
@@ -1,4 +1,4 @@
name: Run benchmarks
name: benchmarks
on:
pull_request:
@@ -6,9 +6,6 @@ on:
jobs:
Benchmark:
if: "!contains(github.event.head_commit.message, '[skip ci]')"
env:
GKS_ENCODING: "utf8"
GKSwstype: "100"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -16,7 +13,7 @@ jobs:
with:
version: 1
## Setup
# Setup
- name: Ubuntu TESTCMD
run: echo "TESTCMD=xvfb-run --auto-servernum julia" >> $GITHUB_ENV
- name: Install Plots dependencies
+8 -7
View File
@@ -72,14 +72,15 @@ jobs:
# Run tests
- name: Run Graphical test
run: |
$TESTCMD --project -e 'using Pkg; Pkg.test(coverage=true);'
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("StatsPlots"); Pkg.test("StatsPlots");'
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("GraphRecipes"); Pkg.test("GraphRecipes");'
$TESTCMD --project -e 'using Pkg; Pkg.test(coverage=true)'
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("StatsPlots"); Pkg.test("StatsPlots")'
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("GraphRecipes"); Pkg.test("GraphRecipes")'
- name: Codecov
uses: julia-actions/julia-uploadcodecov@latest
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# Codecov
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
with:
file: lcov.info
Skip:
if: "contains(github.event.head_commit.message, '[skip ci]')"
+8 -2
View File
@@ -38,7 +38,8 @@ jobs:
texlive-latex-extra \
texlive-luatex \
ghostscript-x \
libgconf2-4
libgconf2-4 \
gnuplot
sudo fc-cache -vr
- name: build documentation
@@ -46,5 +47,10 @@ jobs:
PYTHON: ""
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
run: |
xvfb-run julia --color=yes --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.add(PackageSpec(name="Plots", rev=split(ENV["GITHUB_REF"], "/", limit=3)[3])); Pkg.instantiate()'
export JULIA_DEBUG=Documenter
export PLOTDOCS_ANSICOLOR=true
export GKSwstype=nul # Plots.jl/issues/3664
xvfb-run julia --color=yes --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.add(PackageSpec(name="Plots", rev=split(ENV["GITHUB_REF"], "/", limit=3)[3])); Pkg.instantiate()'
# xvfb-run julia --color=yes --project=docs/ -e 'using Pkg; pkg"add Documenter#master"'
xvfb-run julia --color=yes --project=docs/ -e 'using Pkg; pkg"st -m"'
xvfb-run julia --color=yes --project=docs/ -e 'withenv("GITHUB_REPOSITORY" => "JuliaPlots/PlotDocs.jl") do; include("docs/make.jl"); end'
+22
View File
@@ -0,0 +1,22 @@
name: format
on:
pull_request:
jobs:
JuliaFormatter:
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@latest
with:
version: 1
# Check format
- name: Install JuliaFormatter and format
run: |
git diff --name-only --exit-code
julia -e 'using Pkg; pkg"add JuliaFormatter CSTParser#master"'
julia -e 'using JuliaFormatter; [format(["src", "test"]) for _ in 1:2]'
git diff --exit-code
+35
View File
@@ -0,0 +1,35 @@
name: format-pr
on:
schedule:
- cron: '0 0 * * SUN'
jobs:
build:
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install JuliaFormatter and format
run: |
julia -e 'using Pkg; pkg"add JuliaFormatter CSTParser#master"'
julia -e 'using JuliaFormatter; [format(["src", "test"]) for _ in 1:2]'
git diff --exit-code
- name: Create Pull Request
if: ${{ failure() }}
id: cpr
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Format .jl files [skip ci]"
title: 'Automatic JuliaFormatter.jl run'
branch: auto-juliaformatter-pr
delete-branch: true
labels: formatting, automated pr, no changelog
- name: Check outputs
if: ${{ failure() }}
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
+1 -1
View File
@@ -688,7 +688,7 @@
{
"name": "@t-bltg",
"type": "Other"
}
},
{
"name": "Fred Callaway",
"type": "Other"
+10 -8
View File
@@ -1,12 +1,13 @@
name = "Plots"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
author = ["Tom Breloff (@tbreloff)"]
version = "1.19.0"
version = "1.22.0"
[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
FFMPEG = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
@@ -34,23 +35,24 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[compat]
Contour = "0.5"
FFMPEG = "0.2, 0.3, 0.4"
FixedPointNumbers = "0.6, 0.7, 0.8"
GR = "0.53, 0.54, 0.55, 0.57"
GeometryBasics = "0.2, 0.3.1"
FFMPEG = "0.2 - 0.4"
FixedPointNumbers = "0.6 - 0.8"
GR = "0.53 - 0.59"
GeometryBasics = "0.2, 0.3.1, 0.4"
JSON = "0.21, 1"
Latexify = "0.14, 0.15"
Latexify = "0.14 - 0.15"
Measures = "0.3"
NaNMath = "0.3"
PlotThemes = "2"
PlotUtils = "1"
RecipesBase = "1"
RecipesPipeline = "0.3"
RecipesPipeline = "0.3.5, 0.4"
Reexport = "0.2, 1.0"
Requires = "1"
Scratch = "1"
Showoff = "0.3.1, 1.0"
StatsBase = "0.32, 0.33"
StatsBase = "0.32 - 0.33"
UnicodePlots = "2.4"
julia = "1.5"
[extras]
+3
View File
@@ -1,3 +1,6 @@
github_checks:
annotations: false
ignore:
- "src/backends/inspectdr.jl"
- "src/backends/orca.jl"
+29 -54
View File
@@ -68,15 +68,10 @@ function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:label, :blank), Tuple{Symbol, Bool}},Type{EmptyLayout}})
Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:label, :width, :height), Tuple{Symbol, Symbol, Length{:pct, Float64}}},Type{EmptyLayout}})
Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,), Tuple{GridLayout}},Type{Subplot},GRBackend})
Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,), Tuple{GridLayout}},Type{Subplot},PlotlyBackend})
Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,), Tuple{Subplot{GRBackend}}},Type{Subplot},GRBackend})
Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,), Tuple{Subplot{PlotlyBackend}}},Type{Subplot},PlotlyBackend})
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights), Tuple{Bool, Nothing}},typeof(_make_hist),Tuple{Vector{Float64}, Vector{Float64}},Int64})
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights), Tuple{Bool, Nothing}},typeof(_make_hist),Tuple{Vector{Float64}, Vector{Float64}},Tuple{Int64, Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights), Tuple{Bool, Nothing}},typeof(_make_hist),Tuple{Vector{Float64}},Symbol})
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights), Tuple{Bool, Vector{Int64}}},typeof(_make_hist),Tuple{Vector{Float64}},Symbol})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:formatter,), Tuple{Symbol}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:formatter,), Tuple{typeof(datetimeformatter)}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:grid, :flip, :minorgrid, :guide), Tuple{Bool, Bool, Bool, String}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:grid, :lims), Tuple{Bool, Tuple{Float64, Float64}}},typeof(attr!),Axis})
@@ -88,12 +83,8 @@ function _precompile_()
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:guide_position, :guidefontvalign, :mirror, :guide), Tuple{Symbol, Symbol, Bool, String}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:guidefonthalign, :guide_position, :mirror, :guide), Tuple{Symbol, Symbol, Bool, String}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims, :flip, :ticks, :guide), Tuple{Tuple{Int64, Int64}, Bool, StepRange{Int64, Int64}, String}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims,), Tuple{Tuple{Float64, Float64}}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims,), Tuple{Tuple{Int64, Float64}}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims,), Tuple{Tuple{Int64, Int64}}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:minorgrid, :scale, :guide), Tuple{Bool, Symbol, String}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:rotation,), Tuple{Int64}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:ticks,), Tuple{Nothing}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:ticks,), Tuple{UnitRange{Int64}}},typeof(attr!),Axis})
Base.precompile(Tuple{Core.kwftype(typeof(default)),NamedTuple{(:shape,), Tuple{Symbol}},typeof(default)})
Base.precompile(Tuple{Core.kwftype(typeof(default)),NamedTuple{(:titlefont, :legendfontsize, :guidefont, :tickfont, :guide, :framestyle, :yminorgrid), Tuple{Tuple{Int64, String}, Int64, Tuple{Int64, Symbol}, Tuple{Int64, Symbol}, String, Symbol, Bool}},typeof(default)})
@@ -108,7 +99,7 @@ function _precompile_()
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:alpha, :seriestype), Tuple{Float64, Symbol}},typeof(plot!),Plot{GRBackend},Vector{GeometryBasics.Point2{Float64}}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:alpha, :seriestype), Tuple{Float64, Symbol}},typeof(plot!),Plot{PlotlyBackend},Vector{GeometryBasics.Point2{Float64}}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:alpha, :seriestype), Tuple{Float64, Symbol}},typeof(plot!),Vector{GeometryBasics.Point2{Float64}}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:annotation,), Tuple{Vector{Tuple{Int64, Float64, PlotText}}}},typeof(plot!)})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:annotation,), Tuple{Vector{Tuple{Int64, Float64, Tuple{String, Any, Any, Any}}}}},typeof(plot!)})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:layout, :margin), Tuple{GridLayout, AbsoluteLength}},typeof(plot!),Plot{GRBackend},Plot{GRBackend},Plot{GRBackend},Vararg{Plot{GRBackend}, N} where N})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:legend,), Tuple{Bool}},typeof(plot!),Plot{GRBackend},Plot{GRBackend},Plot{GRBackend},Vararg{Plot{GRBackend}, N} where N})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:legend,), Tuple{Bool}},typeof(plot!),Plot{PlotlyBackend},Plot{PlotlyBackend},Plot{PlotlyBackend},Vararg{Plot{PlotlyBackend}, N} where N})
@@ -122,31 +113,22 @@ function _precompile_()
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:marker, :series_annotations, :seriestype), Tuple{Tuple{Int64, Float64, Symbol}, Vector{Any}, Symbol}},typeof(plot!),Plot{GRBackend},StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:marker, :series_annotations, :seriestype), Tuple{Tuple{Int64, Float64, Symbol}, Vector{Any}, Symbol}},typeof(plot!),Plot{PlotlyBackend},StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:marker, :series_annotations, :seriestype), Tuple{Tuple{Int64, Float64, Symbol}, Vector{Any}, Symbol}},typeof(plot!),StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:markersize, :c, :seriestype), Tuple{Int64, Symbol, Symbol}},typeof(plot!),Plot{GRBackend},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:markersize, :c, :seriestype), Tuple{Int64, Symbol, Symbol}},typeof(plot!),Plot{PlotlyBackend},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:markersize, :c, :seriestype), Tuple{Int64, Symbol, Symbol}},typeof(plot!),Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype, :inset), Tuple{Symbol, Tuple{Int64, BoundingBox{Tuple{Length{:w, Float64}, Length{:h, Float64}}, Tuple{Length{:w, Float64}, Length{:h, Float64}}}}}},typeof(plot!),Plot{GRBackend},Vector{Int64},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype, :inset), Tuple{Symbol, Tuple{Int64, BoundingBox{Tuple{Length{:w, Float64}, Length{:h, Float64}}, Tuple{Length{:w, Float64}, Length{:h, Float64}}}}}},typeof(plot!),Plot{PlotlyBackend},Vector{Int64},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype, :inset), Tuple{Symbol, Tuple{Int64, BoundingBox{Tuple{Length{:w, Float64}, Length{:h, Float64}}, Tuple{Length{:w, Float64}, Length{:h, Float64}}}}}},typeof(plot!),Vector{Int64},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype,), Tuple{Symbol}},typeof(plot!),Plot{GRBackend},Vector{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype,), Tuple{Symbol}},typeof(plot!),Vector{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:title,), Tuple{String}},typeof(plot!),Plot{GRBackend}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:title,), Tuple{String}},typeof(plot!),Plot{PlotlyBackend}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:title,), Tuple{String}},typeof(plot!)})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:w,), Tuple{Int64}},typeof(plot!),Plot{GRBackend},Vector{Float64},Vector{Float64},Vararg{Any, N} where N})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:xgrid,), Tuple{Tuple{Symbol, Symbol, Int64, Symbol, Float64}}},typeof(plot!),Plot{GRBackend}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:yaxis, :minorgrid), Tuple{Tuple{String, Symbol}, Bool}},typeof(plot!),Plot{GRBackend}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:yaxis, :minorgrid), Tuple{Tuple{String, Symbol}, Bool}},typeof(plot!),Plot{PlotlyBackend}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:yaxis, :minorgrid), Tuple{Tuple{String, Symbol}, Bool}},typeof(plot!)})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:zcolor, :m, :ms, :lab, :seriestype), Tuple{Vector{Float64}, Tuple{Symbol, Float64, Stroke}, Vector{Float64}, String, Symbol}},typeof(plot!),Plot{GRBackend},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:zcolor, :m, :ms, :lab, :seriestype), Tuple{Vector{Float64}, Tuple{Symbol, Float64, Stroke}, Vector{Float64}, String, Symbol}},typeof(plot!),Plot{PlotlyBackend},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:zcolor, :m, :ms, :lab, :seriestype), Tuple{Vector{Float64}, Tuple{Symbol, Float64, Stroke}, Vector{Float64}, String, Symbol}},typeof(plot!),Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:annotations, :leg), Tuple{Tuple{Int64, Float64, PlotText}, Bool}},typeof(plot),Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:arrow,), Tuple{Int64}},typeof(plot),Vector{Float64},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:aspect_ratio, :seriestype), Tuple{Int64, Symbol}},typeof(plot),Vector{String},Vector{String},Matrix{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:bins, :weights, :seriestype), Tuple{Symbol, Vector{Int64}, Symbol}},typeof(plot),Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:color, :line, :marker), Tuple{Matrix{Symbol}, Tuple{Symbol, Int64}, Tuple{Matrix{Symbol}, Int64, Float64, Stroke}}},typeof(plot),Vector{Vector{T} where T}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:connections, :seriestype), Tuple{Tuple{Vector{Int64}, Vector{Int64}, Vector{Int64}}, Symbol}},typeof(plot),Vector{Int64},Vector{Int64},Vector{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:connections, :title, :xlabel, :ylabel, :zlabel, :legend, :margin, :seriestype), Tuple{Tuple{Vector{Int64}, Vector{Int64}, Vector{Int64}}, String, String, String, String, Symbol, AbsoluteLength, Symbol}},typeof(plot),Vector{Int64},Vector{Int64},Vector{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:fill, :seriestype), Tuple{Bool, Symbol}},typeof(plot),StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}},StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}},Function})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:framestyle, :title, :color, :layout, :label, :markerstrokewidth, :ticks, :seriestype), Tuple{Matrix{Symbol}, Matrix{String}, Base.ReshapedArray{Int64, 2, UnitRange{Int64}, Tuple{}}, Int64, String, Int64, UnitRange{Int64}, Symbol}},typeof(plot),Vector{Vector{Float64}},Vector{Vector{Float64}}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:grid, :title), Tuple{Tuple{Symbol, Symbol, Symbol, Int64, Float64}, String}},typeof(plot),Vector{Float64}})
@@ -188,7 +170,6 @@ function _precompile_()
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:quiver, :seriestype), Tuple{Tuple{Vector{Float64}, Vector{Float64}, Vector{Float64}}, Symbol}},typeof(plot),Vector{Float64},Vector{Float64},Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:reg, :fill), Tuple{Bool, Tuple{Int64, Symbol}}},typeof(plot),Vector{Float64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,), Tuple{Int64}},typeof(plot),UnitRange{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,), Tuple{StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}}}},typeof(plot),UnitRange{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,), Tuple{Tuple{LinRange{Float64}, LinRange{Float64}}}},typeof(plot),UnitRange{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,), Tuple{typeof(sqrt)}},typeof(plot),UnitRange{Int64}})
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:seriestype, :markershape, :markersize, :color), Tuple{Matrix{Symbol}, Vector{Symbol}, Int64, Vector{Symbol}}},typeof(plot),Matrix{Float64}})
@@ -209,10 +190,8 @@ function _precompile_()
Base.precompile(Tuple{Core.kwftype(typeof(scatter!)),Any,typeof(scatter!),Any})
Base.precompile(Tuple{Core.kwftype(typeof(test_examples)),NamedTuple{(:skip, :disp), Tuple{Vector{Int64}, Bool}},typeof(test_examples),Symbol})
Base.precompile(Tuple{Core.kwftype(typeof(test_examples)),NamedTuple{(:skip,), Tuple{Vector{Int64}}},typeof(test_examples),Symbol})
Base.precompile(Tuple{Type{GridLayout},Int64,Vararg{Int64, N} where N})
Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol, Any},AbstractVector{OHLC}})
Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol, Any},PortfolioComposition})
Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol, Any},Type{Val{:barbins}},Any,Any,Any})
Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol, Any},Type{Val{:barhist}},Any,Any,Any})
Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol, Any},Type{Val{:bar}},Any,Any,Any})
Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol, Any},Type{Val{:bins2d}},Any,Any,Any})
@@ -243,8 +222,6 @@ function _precompile_()
Base.precompile(Tuple{typeof(_cbar_unique),Vector{PlotUtils.ContinuousColorGradient},String})
Base.precompile(Tuple{typeof(_cbar_unique),Vector{StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}}},String})
Base.precompile(Tuple{typeof(_cbar_unique),Vector{Symbol},String})
Base.precompile(Tuple{typeof(_cycle),Base.OneTo{Int64},Vector{Int64}})
Base.precompile(Tuple{typeof(_cycle),Vector{Float64},StepRange{Int64, Int64}})
Base.precompile(Tuple{typeof(_do_plot_show),Plot{GRBackend},Bool})
Base.precompile(Tuple{typeof(_do_plot_show),Plot{PlotlyBackend},Bool})
Base.precompile(Tuple{typeof(_heatmap_edges),Vector{Float64},Bool,Bool})
@@ -264,13 +241,12 @@ function _precompile_()
Base.precompile(Tuple{typeof(build_layout),GridLayout,Int64,Vector{Plot}})
Base.precompile(Tuple{typeof(convert_to_polar),StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}},Vector{Float64},Tuple{Int64, Float64}})
Base.precompile(Tuple{typeof(create_grid),Expr})
Base.precompile(Tuple{typeof(error_coords),Vector{Float64},Vector{Float64},Vector{Float64},Vararg{Vector{Float64}, N} where N})
Base.precompile(Tuple{typeof(discrete_value!),Axis,Vector{String}})
Base.precompile(Tuple{typeof(error_coords),Vector{Float64},Vector{Float64},Vector{Float64}})
Base.precompile(Tuple{typeof(error_zipit),Tuple{Vector{Float64}, Vector{Float64}, Vector{Float64}}})
Base.precompile(Tuple{typeof(fakedata),Int64,Int64})
Base.precompile(Tuple{typeof(get_minor_ticks),Subplot{GRBackend},Axis,Tuple{Vector{Float64}, Vector{String}}})
Base.precompile(Tuple{typeof(get_minor_ticks),Subplot{GRBackend},Axis,Tuple{Vector{Int64}, Vector{String}}})
Base.precompile(Tuple{typeof(get_series_color),Vector{Symbol},Subplot{GRBackend},Int64,Symbol})
Base.precompile(Tuple{typeof(get_series_color),Vector{Symbol},Subplot{PlotlyBackend},Int64,Symbol})
Base.precompile(Tuple{typeof(get_ticks),StepRange{Int64, Int64},Vector{Float64},Vector{Any},Tuple{Int64, Int64},Vararg{Any, N} where N})
Base.precompile(Tuple{typeof(get_ticks),Symbol,Vector{Float64},Vector{Any},Tuple{Float64, Float64},Vararg{Any, N} where N})
Base.precompile(Tuple{typeof(get_ticks),Symbol,Vector{Float64},Vector{Any},Tuple{Int64, Float64},Vararg{Any, N} where N})
@@ -289,18 +265,20 @@ function _precompile_()
Base.precompile(Tuple{typeof(gr_draw_marker),Series,Int64,Float64,Tuple{Float64, Float64},Int64,Float64,Int64,Symbol})
Base.precompile(Tuple{typeof(gr_draw_marker),Series,Int64,Int64,Tuple{Float64, Float64},Int64,Int64,Int64,Symbol})
Base.precompile(Tuple{typeof(gr_draw_markers),Series,Base.OneTo{Int64},Vector{Float64},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_markers),Series,StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}},Vector{Float64},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_markers),Series,UnitRange{Int64},Vector{Float64},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_markers),Series,Vector{Int64},Vector{Float64},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},UnitRange{Int64},Tuple{Vector{Float64}, Vector{Float64}},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},Vector{Float64},Int64,Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_segments),Series,StepRange{Int64, Int64},Vector{Float64},Int64,Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Vector{Float64},Vector{Float64},Int64,Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_segments_3d),Series,Vector{Float64},Vector{Float64},Vector{Float64},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_shapes),Series,Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_surface),Series,Vector{Float64},Vector{Float64},Matrix{Float64},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_draw_surface),Series,Vector{Float64},Vector{Float64},Vector{Float64},Tuple{Float64, Float64}})
Base.precompile(Tuple{typeof(gr_fill_viewport),Vector{Float64},RGBA{Float64}})
Base.precompile(Tuple{typeof(gr_get_ticks_size),Tuple{Vector{Float64}, Vector{String}},Int64})
Base.precompile(Tuple{typeof(gr_get_ticks_size),Tuple{Vector{Int64}, Vector{String}},Int64})
Base.precompile(Tuple{typeof(gr_label_ticks),Subplot{GRBackend},Symbol,Tuple{Vector{Float64}, Vector{String}}})
Base.precompile(Tuple{typeof(gr_label_ticks),Subplot{GRBackend},Symbol,Tuple{Vector{Int64}, Vector{String}}})
Base.precompile(Tuple{typeof(gr_label_ticks_3d),Subplot{GRBackend},Symbol,Tuple{Vector{Float64}, Vector{String}}})
Base.precompile(Tuple{typeof(gr_polaraxes),Int64,Float64,Subplot{GRBackend}})
Base.precompile(Tuple{typeof(gr_set_gradient),PlotUtils.ContinuousColorGradient})
@@ -314,10 +292,6 @@ function _precompile_()
Base.precompile(Tuple{typeof(heatmap_edges),Vector{Float64},Symbol,UnitRange{Int64},Symbol,Tuple{Int64, Int64},Bool})
Base.precompile(Tuple{typeof(heatmap_edges),Vector{Float64},Symbol,Vector{Float64},Symbol,Tuple{Int64, Int64},Bool})
Base.precompile(Tuple{typeof(heatmap_edges),Vector{Float64},Symbol})
Base.precompile(Tuple{typeof(ignorenan_minimum),Vector{Int64}})
Base.precompile(Tuple{typeof(isvertical),Dict{Symbol, Any}})
Base.precompile(Tuple{typeof(layout_args),Int64})
Base.precompile(Tuple{typeof(make_fillrange_side),UnitRange{Int64},LinRange{Float64}})
Base.precompile(Tuple{typeof(optimal_ticks_and_labels),Nothing,Tuple{Float64, Float64},Symbol,Function})
Base.precompile(Tuple{typeof(optimal_ticks_and_labels),Nothing,Tuple{Float64, Float64},Symbol,Symbol})
Base.precompile(Tuple{typeof(optimal_ticks_and_labels),Nothing,Tuple{Int64, Float64},Symbol,Symbol})
@@ -337,34 +311,40 @@ function _precompile_()
Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol, Any},Matrix{Symbol}})
Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol, Any},RGBA{Float64}})
Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol, Any},Shape{Float64, Float64}})
Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol, Any},Stroke})
Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol, Any},Symbol})
Base.precompile(Tuple{typeof(processMinorGridArg!),Dict{Symbol, Any},Bool,Symbol})
Base.precompile(Tuple{typeof(process_annotation),Subplot{GRBackend},Int64,Float64,PlotText})
Base.precompile(Tuple{typeof(process_annotation),Subplot{GRBackend},Int64,Float64,Tuple{String, Int64, Symbol, Symbol}})
Base.precompile(Tuple{typeof(process_annotation),Subplot{GRBackend},Int64,Float64,Tuple{String, Symbol, Int64, String}})
Base.precompile(Tuple{typeof(process_annotation),Subplot{PlotlyBackend},Int64,Float64,PlotText})
Base.precompile(Tuple{typeof(process_annotation),Subplot{PlotlyBackend},Int64,Float64,Tuple{String, Int64, Symbol, Symbol}})
Base.precompile(Tuple{typeof(process_annotation),Subplot{PlotlyBackend},Int64,Float64,Tuple{String, Symbol, Int64, String}})
Base.precompile(Tuple{typeof(process_axis_arg!),Dict{Symbol, Any},StepRange{Int64, Int64},Symbol})
Base.precompile(Tuple{typeof(process_axis_arg!),Dict{Symbol, Any},Symbol,Symbol})
Base.precompile(Tuple{typeof(push!),Plot{GRBackend},Float64,Vector{Float64}})
Base.precompile(Tuple{typeof(resetfontsizes)})
Base.precompile(Tuple{typeof(scalefontsizes),Float64})
Base.precompile(Tuple{typeof(slice_arg),Base.ReshapedArray{Int64, 2, UnitRange{Int64}, Tuple{}},Int64})
Base.precompile(Tuple{typeof(slice_arg),Matrix{AbsoluteLength},Int64})
Base.precompile(Tuple{typeof(slice_arg),Matrix{Int64},Int64})
Base.precompile(Tuple{typeof(slice_arg),Matrix{PlotUtils.ContinuousColorGradient},Int64})
Base.precompile(Tuple{typeof(slice_arg),Matrix{RGBA{Float64}},Int64})
Base.precompile(Tuple{typeof(series_annotations),Vector{Any}})
Base.precompile(Tuple{typeof(slice_arg),Matrix{String},Int64})
Base.precompile(Tuple{typeof(spy),Any})
Base.precompile(Tuple{typeof(straightline_data),Tuple{Float64, Float64},Tuple{Float64, Float64},Vector{Float64},Vector{Float64},Int64})
Base.precompile(Tuple{typeof(stroke),Int64,Vararg{Any, N} where N})
Base.precompile(Tuple{typeof(text),String,Int64,Symbol,Vararg{Symbol, N} where N})
Base.precompile(Tuple{typeof(text),String,Symbol,Int64,Vararg{Any, N} where N})
Base.precompile(Tuple{typeof(title!),AbstractString})
Base.precompile(Tuple{typeof(unzip),Vector{GeometryBasics.Point2{Float64}}})
Base.precompile(Tuple{typeof(unzip),Vector{Tuple{Float64, Float64, Float64}}})
Base.precompile(Tuple{typeof(unzip),Vector{Tuple{Float64, Float64}}})
Base.precompile(Tuple{typeof(unzip),Vector{Tuple{Int64, Int64}}})
Base.precompile(Tuple{typeof(unzip),Vector{Tuple{Vector{Float64}, Vector{Float64}}}})
Base.precompile(Tuple{typeof(vline!),Any})
Base.precompile(Tuple{typeof(xgrid!),Plot{GRBackend},Symbol,Vararg{Any, N} where N})
Base.precompile(Tuple{typeof(xlims),Subplot{PlotlyBackend}})
isdefined(Plots, Symbol("#add_major_or_minor_segments#126")) && Base.precompile(Tuple{getfield(Plots, Symbol("#add_major_or_minor_segments#126")),Vector{Float64},Bool,Segments{Tuple{Float64, Float64}},Float64,Bool})
isdefined(Plots, Symbol("#add_major_or_minor_segments#127")) && Base.precompile(Tuple{getfield(Plots, Symbol("#add_major_or_minor_segments#127")),Vector{Float64},Bool,Segments{Tuple{Float64, Float64, Float64}},Float64,Bool})
isdefined(Plots, Symbol("#add_major_or_minor_segments#120")) && Base.precompile(Tuple{getfield(Plots, Symbol("#add_major_or_minor_segments#120")),Vector{Float64},Bool,Segments{Tuple{Float64, Float64}},Float64,Bool})
isdefined(Plots, Symbol("#add_major_or_minor_segments#121")) && Base.precompile(Tuple{getfield(Plots, Symbol("#add_major_or_minor_segments#121")),Vector{Float64},Bool,Segments{Tuple{Float64, Float64, Float64}},Float64,Bool})
let fbody = try __lookup_kwbody__(which(font, (Font,Vararg{Any, N} where N,))) catch missing end
if !ismissing(fbody)
precompile(fbody, (Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}},typeof(font),Font,Vararg{Any, N} where N,))
end
end
let fbody = try __lookup_kwbody__(which(plot!, (Any,))) catch missing end
if !ismissing(fbody)
precompile(fbody, (Any,typeof(plot!),Any,))
@@ -385,19 +365,14 @@ function _precompile_()
precompile(fbody, (Any,typeof(plot),Any,Vararg{Any, N} where N,))
end
end
let fbody = try __lookup_kwbody__(which(text, (String,Int64,Vararg{Any, N} where N,))) catch missing end
if !ismissing(fbody)
precompile(fbody, (Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}},typeof(text),String,Int64,Vararg{Any, N} where N,))
end
end
let fbody = try __lookup_kwbody__(which(text, (String,Symbol,Vararg{Any, N} where N,))) catch missing end
if !ismissing(fbody)
precompile(fbody, (Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}},typeof(text),String,Symbol,Vararg{Any, N} where N,))
end
end
let fbody = try __lookup_kwbody__(which(title!, (AbstractString,))) catch missing end
if !ismissing(fbody)
precompile(fbody, (Any,typeof(title!),AbstractString,))
end
end
let fbody = try __lookup_kwbody__(which(wireframe, (Any,Vararg{Any, N} where N,))) catch missing end
if !ismissing(fbody)
precompile(fbody, (Any,typeof(wireframe),Any,Vararg{Any, N} where N,))
end
end
end
+42 -32
View File
@@ -4,7 +4,17 @@ if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@optle
@eval Base.Experimental.@optlevel 1
end
const _current_plots_version = VersionNumber(split(first(filter(line -> occursin("version", line), readlines(normpath(@__DIR__, "..", "Project.toml")))), "\"")[2])
const _current_plots_version = VersionNumber(
split(
first(
filter(
line -> occursin("version", line),
readlines(normpath(@__DIR__, "..", "Project.toml")),
),
),
"\"",
)[2],
)
using Reexport
@@ -21,10 +31,12 @@ using Base.Meta
@reexport using PlotThemes
import Showoff
import StatsBase
import Downloads
import JSON
using Requires
#! format: off
export
grid,
bbox,
@@ -117,8 +129,7 @@ export
scalefontsize,
scalefontsizes,
resetfontsizes
#! format: on
# ---------------------------------------------------------
import NaNMath # define functions that ignores NaNs. To overcome the destructive effects of https://github.com/JuliaLang/julia/pull/12563
@@ -137,7 +148,6 @@ ignorenan_extrema(x) = Base.extrema(x)
# This makes it impossible to create row vectors of String and Symbol with the transpose operator.
# This solves this issue, internally in Plots at least.
# commented out on the insistence of the METADATA maintainers
#Base.transpose(x::Symbol) = x
@@ -148,12 +158,13 @@ ignorenan_extrema(x) = Base.extrema(x)
import Measures
module PlotMeasures
import Measures
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
import Measures:
Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
const BBox = Measures.Absolute2DBox
# allow pixels and percentages
const px = AbsoluteLength(0.254)
const pct = Length{:pct, Float64}(1.0)
const pct = Length{:pct,Float64}(1.0)
Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
@@ -168,7 +179,8 @@ import .PlotMeasures: Length, AbsoluteLength, Measure, width, height
# ---------------------------------------------------------
import RecipesPipeline
import RecipesPipeline: SliceIt,
import RecipesPipeline:
SliceIt,
DefaultsDict,
Formatted,
AbstractSurface,
@@ -220,36 +232,34 @@ include("backends/web.jl")
include("shorthands.jl")
let PlotOrSubplot = Union{Plot, Subplot}
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
global xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
global ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
global xlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; xlims = lims, kw...)
global ylims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; ylims = lims, kw...)
global zlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; zlims = lims, kw...)
global xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
global ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
global zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
global xticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) = plot!(plt; xticks = ticks, kw...)
global yticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) = plot!(plt; yticks = ticks, kw...)
global xticks!(plt::PlotOrSubplot,
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; xticks = (ticks,labels), kw...)
global yticks!(plt::PlotOrSubplot,
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; yticks = (ticks,labels), kw...)
global xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
global ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
global annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
global annotate!(plt::PlotOrSubplot, anns::AVec{T}; kw...) where {T<:Tuple} = plot!(plt; annotation = anns, kw...)
global xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
global yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
global xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
global yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
let PlotOrSubplot = Union{Plot,Subplot}
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
global xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
global ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
global xlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; xlims = lims, kw...)
global ylims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; ylims = lims, kw...)
global zlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; zlims = lims, kw...)
global xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin, xmax), kw...)
global ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin, ymax), kw...)
global zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin, zmax), kw...)
global xticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) = plot!(plt; xticks = ticks, kw...)
global yticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) = plot!(plt; yticks = ticks, kw...)
global xticks!(plt::PlotOrSubplot, ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; xticks = (ticks, labels), kw...)
global yticks!(plt::PlotOrSubplot, ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; yticks = (ticks, labels), kw...)
global xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
global ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
global annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
global annotate!(plt::PlotOrSubplot, anns::AVec{T}; kw...) where {T<:Tuple} = plot!(plt; annotation = anns, kw...)
global xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
global yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
global xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
global yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
end
# ---------------------------------------------------------
const CURRENT_BACKEND = CurrentBackend(:none)
const PLOTS_SEED = 1234
include("precompile_includer.jl")
+85 -70
View File
@@ -14,24 +14,24 @@ end
Add a plot (the current plot if not specified) to an existing animation
"""
function frame(anim::Animation, plt::P=current()) where P<:AbstractPlot
function frame(anim::Animation, plt::P = current()) where {P<:AbstractPlot}
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
end
giffn() = (isijulia() ? "tmp.gif" : tempname()*".gif")
movfn() = (isijulia() ? "tmp.mov" : tempname()*".mov")
mp4fn() = (isijulia() ? "tmp.mp4" : tempname()*".mp4")
webmfn() = (isijulia() ? "tmp.webm" : tempname()*".webm")
giffn() = (isijulia() ? "tmp.gif" : tempname() * ".gif")
movfn() = (isijulia() ? "tmp.mov" : tempname() * ".mov")
mp4fn() = (isijulia() ? "tmp.mp4" : tempname() * ".mp4")
webmfn() = (isijulia() ? "tmp.webm" : tempname() * ".webm")
mutable struct FrameIterator
itr
every::Int
kw
end
FrameIterator(itr; every=1, kw...) = FrameIterator(itr, every, kw)
FrameIterator(itr; every = 1, kw...) = FrameIterator(itr, every, kw)
"""
Animate from an iterator which returns the plot args each iteration.
@@ -48,8 +48,8 @@ function animate(fitr::FrameIterator, fn = giffn(); kw...)
end
# most things will implement this
function animate(obj, fn = giffn(); every=1, fps=20, loop=0, kw...)
animate(FrameIterator(obj, every, kw), fn; fps=fps, loop=loop)
function animate(obj, fn = giffn(); every = 1, fps = 20, loop = 0, kw...)
animate(FrameIterator(obj, every, kw), fn; fps = fps, loop = loop)
end
# -----------------------------------------------
@@ -69,12 +69,16 @@ webm(anim::Animation, fn = webmfn(); kw...) = buildanimation(anim, fn, false; kw
ffmpeg_framerate(fps) = "$fps"
ffmpeg_framerate(fps::Rational) = "$(fps.num)/$(fps.den)"
function buildanimation(anim::Animation, fn::AbstractString,
is_animated_gif::Bool=true;
fps::Real = 20, loop::Integer = 0,
variable_palette::Bool=false,
verbose=false,
show_msg::Bool=true)
function buildanimation(
anim::Animation,
fn::AbstractString,
is_animated_gif::Bool = true;
fps::Real = 20,
loop::Integer = 0,
variable_palette::Bool = false,
verbose = false,
show_msg::Bool = true,
)
if length(anim.frames) == 0
throw(ArgumentError("Cannot build empty animations"))
end
@@ -82,41 +86,49 @@ function buildanimation(anim::Animation, fn::AbstractString,
fn = abspath(expanduser(fn))
animdir = anim.dir
framerate = ffmpeg_framerate(fps)
verbose_level = (verbose isa Int ? verbose :
verbose ? 32 # "info"
: 16) # "error"
verbose_level = (verbose isa Int ? verbose : verbose ? 32 : 16) # "error"
if is_animated_gif
if variable_palette
# generate a colorpalette for each frame for highest quality, but larger filesize
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
ffmpeg_exe(`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -lavfi "$palette" -loop $loop -y $fn`)
palette = "palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
ffmpeg_exe(
`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -lavfi "$palette" -loop $loop -y $fn`,
)
else
# generate a colorpalette first so ffmpeg does not have to guess it
ffmpeg_exe(`-v $verbose_level -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
ffmpeg_exe(
`-v $verbose_level -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`,
)
# then apply the palette to get better results
ffmpeg_exe(`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -loop $loop -y $fn`)
ffmpeg_exe(
`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -loop $loop -y $fn`,
)
end
else
ffmpeg_exe(`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -vf format=yuv420p -loop $loop -y $fn`)
ffmpeg_exe(
`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -vf format=yuv420p -loop $loop -y $fn`,
)
end
show_msg && @info("Saved animation to ", fn)
AnimatedGif(fn)
end
# write out html to view the gif
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
ext = file_extension(agif.filename)
if ext == "gif"
html = "<img src=\"data:image/gif;base64," * base64encode(read(agif.filename)) * "\" />"
elseif ext in ("mov", "mp4","webm")
html =
"<img src=\"data:image/gif;base64," *
base64encode(read(agif.filename)) *
"\" />"
elseif ext in ("mov", "mp4", "webm")
mimetype = ext == "mov" ? "video/quicktime" : "video/$ext"
html = "<video controls><source src=\"data:$mimetype;base64," *
base64encode(read(agif.filename)) *
"\" type = \"$mimetype\"></video>"
html =
"<video controls><source src=\"data:$mimetype;base64," *
base64encode(read(agif.filename)) *
"\" type = \"$mimetype\"></video>"
else
error("Cannot show animation with extension $ext: $agif")
end
@@ -125,64 +137,67 @@ function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
return nothing
end
# Only gifs can be shown via image/gif
Base.showable(::MIME"image/gif", agif::AnimatedGif) = file_extension(agif.filename) == "gif"
function Base.show(io::IO, ::MIME"image/gif", agif::AnimatedGif)
open(fio-> write(io, fio), agif.filename)
open(fio -> write(io, fio), agif.filename)
end
# -----------------------------------------------
function _animate(forloop::Expr, args...; callgif = false)
if forloop.head (:for, :while)
error("@animate macro expects a for- or while-block. got: $(forloop.head)")
end
if forloop.head (:for, :while)
error("@animate macro expects a for- or while-block. got: $(forloop.head)")
end
# add the call to frame to the end of each iteration
animsym = gensym("anim")
countersym = gensym("counter")
freqassert = :()
block = forloop.args[2]
# add the call to frame to the end of each iteration
animsym = gensym("anim")
countersym = gensym("counter")
freqassert = :()
block = forloop.args[2]
# create filter
n = length(args)
filterexpr = if n == 0
# no filter... every iteration gets a frame
true
# create filter
n = length(args)
filterexpr = if n == 0
# no filter... every iteration gets a frame
true
elseif args[1] == :every
# filter every `freq` frames (starting with the first frame)
@assert n == 2
freq = args[2]
freqassert = :(@assert isa($freq, Integer) && $freq > 0)
:(mod1($countersym, $freq) == 1)
elseif args[1] == :every
# filter every `freq` frames (starting with the first frame)
@assert n == 2
freq = args[2]
freqassert = :(@assert isa($freq, Integer) && $freq > 0)
:(mod1($countersym, $freq) == 1)
elseif args[1] == :when
# filter on custom expression
@assert n == 2
args[2]
elseif args[1] == :when
# filter on custom expression
@assert n == 2
args[2]
else
error("Unsupported animate filter: $args")
end
else
error("Unsupported animate filter: $args")
end
push!(block.args, :(if $filterexpr; Plots.frame($animsym); end))
push!(block.args, :(global $countersym += 1))
push!(block.args, :(
if $filterexpr
Plots.frame($animsym)
end
))
push!(block.args, :($countersym += 1))
# add a final call to `gif(anim)`?
retval = callgif ? :(Plots.gif($animsym)) : animsym
# add a final call to `gif(anim)`?
retval = callgif ? :(Plots.gif($animsym)) : animsym
# full expression:
esc(quote
$freqassert # if filtering, check frequency is an Integer > 0
$animsym = Plots.Animation() # init animation object
global $countersym = 1 # init iteration counter
$forloop # for loop, saving a frame after each iteration
$retval # return the animation object, or the gif
end)
# full expression:
esc(quote
$freqassert # if filtering, check frequency is an Integer > 0
$animsym = Plots.Animation() # init animation object
let $countersym = 1 # init iteration counter
$forloop # for loop, saving a frame after each iteration
end
$retval # return the animation object, or the gif
end)
end
"""
+181 -180
View File
@@ -1,187 +1,188 @@
const _arg_desc = KW(
# series args
:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
:seriescolor => "Color Type. The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot",
:seriesalpha => "Number in [0,1]. The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color.",
:seriestype => "Symbol. This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined.",
:linestyle => "Symbol. Style of the line (for path and bar stroke). Choose from $(_allStyles)",
:linewidth => "Number. Width of the line (in pixels)",
:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).",
:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.",
:fillrange => "Number or AbstractVector. Fills area between fillrange and y for line-types, sets the base for bar/stick types, and similar for other types.",
:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.",
:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.",
:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).",
:markercolor => "Color Type. Color of the interior of the marker or shape. `:match` will take the value from `:seriescolor`.",
:markeralpha => "Number in [0,1]. The alpha/opacity override for the marker interior. `nothing` (the default) means it will take the alpha value of markercolor.",
:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.",
:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)",
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(minimum(x), stop = maximum(x), length = 25)`",
:smooth => "Bool. Add a regression line?",
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
:x => "Various. Input data. First Dimension",
:y => "Various. Input data. Second Dimension",
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
:levels => "Integer, NTuple{2,Integer}, or AbstractVector. Levels or number of levels (or x-levels/y-levels) for a contour type.",
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?",
:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)",
:yerror => "AbstractVector or 2-Tuple of Vectors. y (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)",
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.",
:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes).",
:weights => "AbstractVector. Used in histogram types for weighted counts.",
:show_empty_bins => "Bool. Whether empty bins in a 2D histogram are colored as 0 (true), or transparent (the default).",
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
:contour_labels => "Bool. Show labels at the contour lines?",
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
:colorbar_entry => "Bool. Include this series in the color bar? Set to `false` to exclude.",
# series args
:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
:seriescolor => "Color Type. The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot",
:seriesalpha => "Number in [0,1]. The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color.",
:seriestype => "Symbol. This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined.",
:linestyle => "Symbol. Style of the line (for path and bar stroke). Choose from $(_allStyles)",
:linewidth => "Number. Width of the line (in pixels)",
:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).",
:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.",
:fillrange => "Number or AbstractVector. Fills area between fillrange and y for line-types, sets the base for bar/stick types, and similar for other types.",
:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.",
:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.",
:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).",
:fillstyle => "Symbol. Style of the fill area. `nothing` (the default) means solid fill. Choose from :/, :\\, :|, :-, :+, :x",
:markercolor => "Color Type. Color of the interior of the marker or shape. `:match` will take the value from `:seriescolor`.",
:markeralpha => "Number in [0,1]. The alpha/opacity override for the marker interior. `nothing` (the default) means it will take the alpha value of markercolor.",
:markersize => "Number or AbstractVector. Size (radius pixels) of the markers",
:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)",
:markerstrokewidth => "Number. Width of the marker stroke (border) in pixels",
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(minimum(x), stop = maximum(x), length = 25)`",
:smooth => "Bool. Add a regression line?",
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`",
:x => "Various. Input data. First Dimension",
:y => "Various. Input data. Second Dimension",
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
:levels => "Integer (number of contours) or AbstractVector (contour values). Determines contour levels for a contour type.",
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?",
:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)",
:yerror => "AbstractVector or 2-Tuple of Vectors. y (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)",
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.",
:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes).",
:weights => "AbstractVector. Used in histogram types for weighted counts.",
:show_empty_bins => "Bool. Whether empty bins in a 2D histogram are colored as 0 (true), or transparent (the default)",
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
:contour_labels => "Bool. Show labels at the contour lines?",
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
:colorbar_entry => "Bool. Include this series in the color bar? Set to `false` to exclude.",
# plot args
:plot_title => "String. Title for the whole plot (not the subplots)",
:plot_titlevspan => "Number in [0,1]. Vertical span of the whole plot title (fraction of the plot height)",
:background_color => "Color Type. Base color for all backgrounds.",
:background_color_outside => "Color Type or `:match` (matches `:background_color`). Color outside the plot area(s)",
:foreground_color => "Color Type. Base color for all foregrounds.",
:size => "NTuple{2,Int}. (width_px, height_px) of the whole Plot",
:pos => "NTuple{2,Int}. (left_px, top_px) position of the GUI window (note: currently unimplemented)",
:window_title => "String. Title of the standalone gui-window.",
:show => "Bool. Should this command open/refresh a GUI/display? This allows displaying in scripts or functions without explicitly calling `display`",
:layout => "Integer (number of subplots), NTuple{2,Integer} (grid dimensions), AbstractLayout (for example `grid(2,2)`), or the return from the `@layout` macro. This builds the layout of subplots.",
:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).",
: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.",
:tex_output_standalone => "Bool. When writing tex output, should the source include a preamble for a standalone document class.",
: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",
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
:extra_kwargs => "Either one of (`:plot`, `:subplot`, `:series`) to specify for which element extra keyword args are collected or a KW (Dict{Symbol,Any}) to pass a map of extra keyword args which may be specific to a backend. Default: `:series`.\n Example: `pgfplotsx(); scatter(1:5, extra_kwargs=Dict(:subplot=>Dict(\"axis line shift\" => \"10pt\"))`",
:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides",
:warn_on_unsupported => "Bool. Warn on unsupported attributes, series types and marker shapes",
# plot args
:plot_title => "String. Title for the whole plot (not the subplots)",
:plot_titlevspan => "Number in [0,1]. Vertical span of the whole plot title (fraction of the plot height)",
:background_color => "Color Type. Base color for all backgrounds.",
:background_color_outside => "Color Type or `:match` (matches `:background_color`). Color outside the plot area(s)",
:foreground_color => "Color Type. Base color for all foregrounds.",
:size => "NTuple{2,Int}. (width_px, height_px) of the whole Plot",
:pos => "NTuple{2,Int}. (left_px, top_px) position of the GUI window (note: currently unimplemented)",
:window_title => "String. Title of the standalone gui-window.",
:show => "Bool. Should this command open/refresh a GUI/display? This allows displaying in scripts or functions without explicitly calling `display`",
:layout => "Integer (number of subplots), NTuple{2,Integer} (grid dimensions), AbstractLayout (for example `grid(2,2)`), or the return from the `@layout` macro. This builds the layout of subplots.",
:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).",
: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.",
:tex_output_standalone => "Bool. When writing tex output, should the source include a preamble for a standalone document class.",
: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",
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
:extra_kwargs => "Either one of (`:plot`, `:subplot`, `:series`) to specify for which element extra keyword args are collected or a KW (Dict{Symbol,Any}) to pass a map of extra keyword args which may be specific to a backend. Default: `:series`.\n Example: `pgfplotsx(); scatter(1:5, extra_kwargs=Dict(:subplot=>Dict(\"axis line shift\" => \"10pt\"))`",
:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides",
:warn_on_unsupported => "Bool. Warn on unsupported attributes, series types and marker shapes",
# subplot args
:title => "String. Subplot title.",
:titlelocation => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`",
:titlefontfamily => "String or Symbol. Font family of subplot title.",
:titlefontsize => "Integer. Font pointsize of subplot title.",
:titlefonthalign => "Symbol. Font horizontal alignment of subplot title: :hcenter, :left, :right or :center",
:titlefontvalign => "Symbol. Font vertical alignment of subplot title: :vcenter, :top, :bottom or :center",
:titlefontrotation => "Real. Font rotation of subplot title",
:titlefontcolor => "Color Type. Font color of subplot title",
:background_color_subplot => "Color Type or `:match` (matches `:background_color`). Base background color of the subplot.",
:background_color_legend => "Color Type or `:match` (matches `:background_color_subplot`). Background color of the legend.",
:background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).",
:foreground_color_subplot => "Color Type or `:match` (matches `:foreground_color`). Base foreground color of the subplot.",
:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.",
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
:legend => "Bool (show the legend?) or (x,y) tuple or Symbol (legend position) or angle or (angle,inout) tuple. Bottom left corner of legend is placed at (x,y). Symbol values: `:none`; `:best`; `:inline`; `:inside`; `:legend`; any valid combination of `:(outer ?)(top/bottom ?)(right/left ?)`, i.e.: `:top`, `:topright`, `:outerleft`, `:outerbottomright` ... (note: only some may be supported in each backend). Legend is positioned at (angle degrees) (so (90,:outer) is roughly equivalent to :outertop), close to the inside of the axes or the outside if inout=:outer.",
:legendfontfamily => "String or Symbol. Font family of legend entries.",
:legendfontsize => "Integer. Font pointsize of legend entries.",
:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center",
:legendfontvalign => "Symbol. Font vertical alignment of legend entries: :vcenter, :top, :bottom or :center",
:legendfontrotation => "Real. Font rotation of legend entries",
:legendfontcolor => "Color Type. Font color of legend entries",
:legendtitle => "String. Legend title.",
:legendtitlefontfamily => "String or Symbol. Font family of the legend title.",
:legendtitlefontsize => "Integer. Font pointsize the legend title.",
:legendtitlefonthalign => "Symbol. Font horizontal alignment of the legend title: :hcenter, :left, :right or :center",
:legendtitlefontvalign => "Symbol. Font vertical alignment of the legend title: :vcenter, :top, :bottom or :center",
:legendtitlefontrotation => "Real. Font rotation of the legend title",
:legendtitlefontcolor => "Color Type. Font color of the legend title",
:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)",
:clims => "`:auto`, NTuple{2,Number}, or a function that takes series data in and returns NTuple{2,Number}. Fixes the limits of the colorbar.",
:colorbar_fontfamily => "String or Symbol. Font family of colobar entries.",
:colorbar_ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
:colorbar_tickfontfamily => "String or Symbol. Font family of colorbar tick labels.",
:colorbar_tickfontsize => "Integer. Font pointsize of colorbar tick entries.",
:colorbar_tickfontcolor => "Color Type. Font color of colorbar tick entries",
:colorbar_scale => "Symbol. Scale of the colorbar axis: `:none`, `:ln`, `:log2`, `:log10`",
:colorbar_formatter => "Function, :scientific, :plain or :auto. A method which converts a number to a string for tick labeling.",
:legendfont => "Font. Font of legend items.",
:legendtitlefont => "Font. Font of the legend title.",
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String, PlotText (created with `text(args...)`), or a tuple of arguments to `text` (e.g., `(\"Label\", 8, :red, :top)`). Add one-off text annotations at the x,y coordinates.",
:annotationfontfamily => "String or Symbol. Font family of annotations.",
:annotationfontsize => "Integer. Font pointsize of annotations.",
:annotationhalign => "Symbol. horizontal alignment of annotations, :hcenter, :left, :right or :center.",
:annotationvalign => "Symbol. Vertical alignment of annotations, :vcenter, :top, :bottom or :center.",
:annotationrotation => "Float. Rotation of annotations in degrees.",
:annotationcolor => "Colorant or :match. Color of annotations.",
:projection => "Symbol or String. '3d' or 'polar'",
:aspect_ratio => "Symbol (:equal or :none) or Number. Plot area is resized so that 1 y-unit is the same size as `aspect_ratio` x-units. With `:none`, images inherit aspect ratio of the plot area.",
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
: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.",
:framestyle => "Symbol. Style of the axes frame. Choose from $(_allFramestyles)",
:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots",
# subplot args
:title => "String. Subplot title.",
:titlelocation => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`",
:titlefontfamily => "String or Symbol. Font family of subplot title.",
:titlefontsize => "Integer. Font pointsize of subplot title.",
:titlefonthalign => "Symbol. Font horizontal alignment of subplot title: :hcenter, :left, :right or :center",
:titlefontvalign => "Symbol. Font vertical alignment of subplot title: :vcenter, :top, :bottom or :center",
:titlefontrotation => "Real. Font rotation of subplot title",
:titlefontcolor => "Color Type. Font color of subplot title",
:background_color_subplot => "Color Type or `:match` (matches `:background_color`). Base background color of the subplot.",
:background_color_legend => "Color Type or `:match` (matches `:background_color_subplot`). Background color of the legend.",
:background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).",
:foreground_color_subplot => "Color Type or `:match` (matches `:foreground_color`). Base foreground color of the subplot.",
:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.",
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
:legend => "Bool (show the legend?) or (x,y) tuple or Symbol (legend position) or angle or (angle,inout) tuple. Bottom left corner of legend is placed at (x,y). Symbol values: `:none`; `:best`; `:inline`; `:inside`; `:legend`; any valid combination of `:(outer ?)(top/bottom ?)(right/left ?)`, i.e.: `:top`, `:topright`, `:outerleft`, `:outerbottomright` ... (note: only some may be supported in each backend). Legend is positioned at (angle degrees) (so (90,:outer) is roughly equivalent to :outertop), close to the inside of the axes or the outside if inout=:outer.",
:legendfontfamily => "String or Symbol. Font family of legend entries.",
:legendfontsize => "Integer. Font pointsize of legend entries.",
:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center",
:legendfontvalign => "Symbol. Font vertical alignment of legend entries: :vcenter, :top, :bottom or :center",
:legendfontrotation => "Real. Font rotation of legend entries",
:legendfontcolor => "Color Type. Font color of legend entries",
:legendtitle => "String. Legend title.",
:legendtitlefontfamily => "String or Symbol. Font family of the legend title.",
:legendtitlefontsize => "Integer. Font pointsize the legend title.",
:legendtitlefonthalign => "Symbol. Font horizontal alignment of the legend title: :hcenter, :left, :right or :center",
:legendtitlefontvalign => "Symbol. Font vertical alignment of the legend title: :vcenter, :top, :bottom or :center",
:legendtitlefontrotation => "Real. Font rotation of the legend title",
:legendtitlefontcolor => "Color Type. Font color of the legend title",
:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)",
:clims => "`:auto`, NTuple{2,Number}, or a function that takes series data in and returns NTuple{2,Number}. Fixes the limits of the colorbar.",
:colorbar_fontfamily => "String or Symbol. Font family of colobar entries.",
:colorbar_ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
:colorbar_tickfontfamily => "String or Symbol. Font family of colorbar tick labels.",
:colorbar_tickfontsize => "Integer. Font pointsize of colorbar tick entries.",
:colorbar_tickfontcolor => "Color Type. Font color of colorbar tick entries",
:colorbar_scale => "Symbol. Scale of the colorbar axis: `:none`, `:ln`, `:log2`, `:log10`",
:colorbar_formatter => "Function, :scientific, :plain or :auto. A method which converts a number to a string for tick labeling.",
:legendfont => "Font. Font of legend items.",
:legendtitlefont => "Font. Font of the legend title.",
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String, PlotText (created with `text(args...)`), or a tuple of arguments to `text` (e.g., `(\"Label\", 8, :red, :top)`). Add one-off text annotations at the x,y coordinates.",
:annotationfontfamily => "String or Symbol. Font family of annotations.",
:annotationfontsize => "Integer. Font pointsize of annotations.",
:annotationhalign => "Symbol. horizontal alignment of annotations, :hcenter, :left, :right or :center.",
:annotationvalign => "Symbol. Vertical alignment of annotations, :vcenter, :top, :bottom or :center.",
:annotationrotation => "Float. Rotation of annotations in degrees.",
:annotationcolor => "Colorant or :match. Color of annotations.",
:projection => "Symbol or String. '3d' or 'polar'",
:aspect_ratio => "Symbol (:equal or :none) or Number. Plot area is resized so that 1 y-unit is the same size as `aspect_ratio` x-units. With `:none`, images inherit aspect ratio of the plot area.",
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
: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.",
:framestyle => "Symbol. Style of the axes frame. Choose from $(_allFramestyles)",
:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots",
# axis args
:guide => "String. Axis guide (label).",
:guide_position => "Symbol. Position of axis guides: :top, :bottom, :left or :right",
:lims => """
NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).
`:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]
`:symmetric` sets the limits to be symmetric around zero.
Set widen=true to widen the specified limits (as occurs when lims are not specified).
""",
:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
: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, :plain or :auto. A method which converts a number to a string for tick labeling.",
:tickfontfamily => "String or Symbol. Font family of tick labels.",
:tickfontsize => "Integer. Font pointsize of tick labels.",
:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center",
:tickfontvalign => "Symbol. Font vertical alignment of tick labels: :vcenter, :top, :bottom or :center",
:tickfontrotation => "Real. Font rotation of tick labels",
:tickfontcolor => "Color Type. Font color of tick labels",
:guidefontfamily => "String or Symbol. Font family of axes guides.",
:guidefontsize => "Integer. Font pointsize of axes guides.",
:guidefonthalign => "Symbol. Font horizontal alignment of axes guides: :hcenter, :left, :right or :center",
:guidefontvalign => "Symbol. Font vertical alignment of axes guides: :vcenter, :top, :bottom or :center",
:guidefontrotation => "Real. Font rotation of axes guides",
:guidefontcolor => "Color Type. Font color of axes guides",
: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).",
:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`",
:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.",
:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.",
:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)",
:gridlinewidth => "Number. Width of the grid lines (in pixels)",
:foreground_color_minor_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of minor grid lines.",
:minorgrid => "Bool. Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines",
:minorticks => "Integer. Intervals to divide the gap between major ticks into",
:minorgridalpha => "Number in [0,1]. The alpha/opacity override for the minorgrid lines.",
:minorgridstyle => "Symbol. Style of the minor grid lines. Choose from $(_allStyles)",
:minorgridlinewidth => "Number. Width of the minor grid lines (in pixels)",
:tick_direction => "Symbol. Direction of the ticks. `:in`, `:out` or `:none`",
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
:widen => """
Bool or :auto. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders.
Defaults to `:auto`, which widens unless limits were manually set.
""",
:draw_arrow => "Bool. Draw arrow at the end of the axis.",
# axis args
:guide => "String. Axis guide (label).",
:guide_position => "Symbol. Position of axis guides: :top, :bottom, :left or :right",
:lims => """
NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).
`:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]
`:symmetric` sets the limits to be symmetric around zero.
Set widen=true to widen the specified limits (as occurs when lims are not specified).
""",
:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
: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, :plain or :auto. A method which converts a number to a string for tick labeling.",
:tickfontfamily => "String or Symbol. Font family of tick labels.",
:tickfontsize => "Integer. Font pointsize of tick labels.",
:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center",
:tickfontvalign => "Symbol. Font vertical alignment of tick labels: :vcenter, :top, :bottom or :center",
:tickfontrotation => "Real. Font rotation of tick labels",
:tickfontcolor => "Color Type. Font color of tick labels",
:guidefontfamily => "String or Symbol. Font family of axes guides.",
:guidefontsize => "Integer. Font pointsize of axes guides.",
:guidefonthalign => "Symbol. Font horizontal alignment of axes guides: :hcenter, :left, :right or :center",
:guidefontvalign => "Symbol. Font vertical alignment of axes guides: :vcenter, :top, :bottom or :center",
:guidefontrotation => "Real. Font rotation of axes guides",
:guidefontcolor => "Color Type. Font color of axes guides",
: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).",
:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`",
:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.",
:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.",
:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)",
:gridlinewidth => "Number. Width of the grid lines (in pixels)",
:foreground_color_minor_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of minor grid lines.",
:minorgrid => "Bool. Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines",
:minorticks => "Integer. Intervals to divide the gap between major ticks into",
:minorgridalpha => "Number in [0,1]. The alpha/opacity override for the minorgrid lines.",
:minorgridstyle => "Symbol. Style of the minor grid lines. Choose from $(_allStyles)",
:minorgridlinewidth => "Number. Width of the minor grid lines (in pixels)",
:tick_direction => "Symbol. Direction of the ticks. `:in`, `:out` or `:none`",
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
:widen => """
Bool or :auto. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders.
Defaults to `:auto`, which widens unless limits were manually set.
""",
:draw_arrow => "Bool. Draw arrow at the end of the axis.",
)
+877 -430
View File
File diff suppressed because it is too large Load Diff
+175 -94
View File
@@ -1,5 +1,4 @@
# xaxis(args...; kw...) = Axis(:x, args...; kw...)
# yaxis(args...; kw...) = Axis(:y, args...; kw...)
# zaxis(args...; kw...) = Axis(:z, args...; kw...)
@@ -24,7 +23,7 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
end
function get_axis(sp::Subplot, letter::Symbol)
axissym = Symbol(letter, :axis)
axissym = get_attr_symbol(letter, :axis)
if haskey(sp.attr, axissym)
sp.attr[axissym]
else
@@ -36,42 +35,45 @@ function process_axis_arg!(plotattributes::AKW, arg, letter = "")
T = typeof(arg)
arg = get(_scaleAliases, arg, arg)
if typeof(arg) <: Font
plotattributes[Symbol(letter,:tickfont)] = arg
plotattributes[Symbol(letter,:guidefont)] = arg
plotattributes[get_attr_symbol(letter, :tickfont)] = arg
plotattributes[get_attr_symbol(letter, :guidefont)] = arg
elseif arg in _allScales
plotattributes[Symbol(letter,:scale)] = arg
plotattributes[get_attr_symbol(letter, :scale)] = arg
elseif arg in (:flip, :invert, :inverted)
plotattributes[Symbol(letter,:flip)] = true
plotattributes[get_attr_symbol(letter, :flip)] = true
elseif T <: AbstractString
plotattributes[Symbol(letter,:guide)] = arg
plotattributes[get_attr_symbol(letter, :guide)] = arg
# xlims/ylims
# xlims/ylims
elseif (T <: Tuple || T <: AVec) && length(arg) == 2
sym = typeof(arg[1]) <: Number ? :lims : :ticks
plotattributes[Symbol(letter,sym)] = arg
plotattributes[get_attr_symbol(letter, sym)] = arg
# xticks/yticks
# xticks/yticks
elseif T <: AVec
plotattributes[Symbol(letter,:ticks)] = arg
plotattributes[get_attr_symbol(letter, :ticks)] = arg
elseif arg === nothing
plotattributes[Symbol(letter,:ticks)] = []
plotattributes[get_attr_symbol(letter, :ticks)] = []
elseif T <: Bool || arg in _allShowaxisArgs
plotattributes[Symbol(letter,:showaxis)] = showaxis(arg, letter)
plotattributes[get_attr_symbol(letter, :showaxis)] = showaxis(arg, letter)
elseif typeof(arg) <: Number
plotattributes[Symbol(letter,:rotation)] = arg
plotattributes[get_attr_symbol(letter, :rotation)] = arg
elseif typeof(arg) <: Function
plotattributes[Symbol(letter,:formatter)] = arg
plotattributes[get_attr_symbol(letter, :formatter)] = arg
elseif !handleColors!(plotattributes, arg, Symbol(letter, :foreground_color_axis))
elseif !handleColors!(
plotattributes,
arg,
get_attr_symbol(letter, :foreground_color_axis),
)
@warn("Skipped $(letter)axis arg $arg")
end
end
@@ -87,15 +89,15 @@ function attr!(axis::Axis, args...; kw...)
RecipesPipeline.preprocess_attributes!(KW(kw))
# then override for any keywords... only those keywords that already exists in plotattributes
for (k,v) in kw
for (k, v) in kw
if haskey(plotattributes, k)
if k == :discrete_values
# add these discrete values to the axis
for vi in v
discrete_value!(axis, vi)
end
#could perhaps use TimeType here, as Date and DateTime are both subtypes of TimeType
# or could perhaps check if dateformatter or datetimeformatter is in use
#could perhaps use TimeType here, as Date and DateTime are both subtypes of TimeType
# or could perhaps check if dateformatter or datetimeformatter is in use
elseif k == :lims && isa(v, Tuple{Date,Date})
plotattributes[k] = (v[1].instant.periods.value, v[2].instant.periods.value)
elseif k == :lims && isa(v, Tuple{DateTime,DateTime})
@@ -122,11 +124,8 @@ Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.plotattributes, v,
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.plotattributes, k)
ignorenan_extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
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")
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
const _label_func_tex = Dict{Symbol,Function}(
@@ -136,7 +135,6 @@ const _label_func_tex = Dict{Symbol,Function}(
)
labelfunc_tex(scale::Symbol) = get(_label_func_tex, scale, convert_sci_unicode)
function optimal_ticks_and_labels(ticks, alims, scale, formatter)
amin, amax = alims
@@ -158,8 +156,8 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter)
# are converted to 'DateTime integers' (actually floats) before
# being passed to optimize_datetime_ticks.
# (convert(Int, convert(DateTime, convert(Date, i))) == 87600000*i)
ticks, labels = optimize_datetime_ticks(864e5 * amin, 864e5 * amax;
k_min = 2, k_max = 4)
ticks, labels =
optimize_datetime_ticks(864e5 * amin, 864e5 * amax; k_min = 2, k_max = 4)
# Now the ticks are converted back to floats corresponding to Dates.
return ticks / 864e5, labels
elseif formatter == RecipesPipeline.datetimeformatter
@@ -198,7 +196,10 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter)
if formatter in (:auto, :plain, :scientific, :engineering)
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, formatter))
elseif formatter == :latex
map(x -> string("\$", replace(convert_sci_unicode(x), '×' => "\\times"), "\$"), Showoff.showoff(unscaled_ticks, :auto))
map(
x -> string("\$", replace(convert_sci_unicode(x), '×' => "\\times"), "\$"),
Showoff.showoff(unscaled_ticks, :auto),
)
else
# there was an override for the formatter... use that on the unscaled ticks
map(formatter, unscaled_ticks)
@@ -223,16 +224,22 @@ function get_ticks(sp::Subplot, axis::Axis; update = true)
if update || !haskey(axis.plotattributes, :optimized_ticks)
dvals = axis[:discrete_values]
ticks = _transform_ticks(axis[:ticks])
axis.plotattributes[:optimized_ticks] = if ticks isa Symbol && ticks !== :none &&
ispolar(sp) && axis[:letter] === :x && !isempty(dvals)
collect(0:pi/4:7pi/4), string.(0:45:315)
else
cvals = axis[:continuous_values]
alims = axis_limits(sp, axis[:letter])
scale = axis[:scale]
formatter = axis[:formatter]
get_ticks(ticks, cvals, dvals, alims, scale, formatter)
end
axis.plotattributes[:optimized_ticks] =
if (
ticks isa Symbol &&
ticks !== :none &&
ispolar(sp) &&
axis[:letter] === :x &&
!isempty(dvals)
)
collect(0:(pi / 4):(7pi / 4)), string.(0:45:315)
else
cvals = axis[:continuous_values]
alims = axis_limits(sp, axis[:letter])
scale = axis[:scale]
formatter = axis[:formatter]
get_ticks(ticks, cvals, dvals, alims, scale, formatter)
end
end
return axis.plotattributes[:optimized_ticks]
end
@@ -294,10 +301,10 @@ for l in (:x, :y, :z)
end
end
# get_ticks from axis symbol :x, :y, or :z
get_ticks(sp::Subplot, s::Symbol) = get_ticks(sp, sp[Symbol(s, :axis)])
get_ticks(sp::Subplot, s::Symbol) = get_ticks(sp, sp[get_attr_symbol(s, :axis)])
get_ticks(p::Plot, s::Symbol) = [get_ticks(sp, s) for sp in p.subplots]
function get_ticks(ticks::Symbol, cvals::T, dvals, args...) where T
function get_ticks(ticks::Symbol, cvals::T, dvals, args...) where {T}
if ticks === :none
return T[], String[]
elseif !isempty(dvals)
@@ -316,21 +323,21 @@ end
get_ticks(ticks::AVec, cvals, dvals, args...) = optimal_ticks_and_labels(ticks, args...)
function get_ticks(ticks::Int, dvals, cvals, args...)
if !isempty(dvals)
rng = round.(Int, range(1, stop=length(dvals), length=ticks))
rng = round.(Int, range(1, stop = length(dvals), length = ticks))
cvals[rng], string.(dvals[rng])
else
optimal_ticks_and_labels(ticks, args...)
end
end
get_ticks(ticks::NTuple{2, Any}, args...) = ticks
get_ticks(::Nothing, cvals::T, args...) where T = T[], String[]
get_ticks(ticks::NTuple{2,Any}, args...) = ticks
get_ticks(::Nothing, cvals::T, args...) where {T} = T[], String[]
get_ticks(ticks::Bool, args...) =
ticks ? get_ticks(:auto, args...) : get_ticks(nothing, args...)
get_ticks(::T, args...) where T = error("Unknown ticks type in get_ticks: $T")
get_ticks(::T, args...) where {T} = error("Unknown ticks type in get_ticks: $T")
_transform_ticks(ticks) = ticks
_transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks)
_transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2])
_transform_ticks(ticks::AbstractArray{T}) where {T<:Dates.TimeType} = Dates.value.(ticks)
_transform_ticks(ticks::NTuple{2,Any}) = (_transform_ticks(ticks[1]), ticks[2])
function get_minor_ticks(sp, axis, ticks)
axis[:minorticks] (:none, nothing, false) && !axis[:minorgrid] && return nothing
@@ -350,30 +357,36 @@ function get_minor_ticks(sp, axis, ticks)
sub = 1 # unused
ratio = length(ticks) > 2 ? (ticks[3] - ticks[2]) / (ticks[2] - ticks[1]) : 1
first_step = ticks[2] - ticks[1]
last_step = ticks[end] - ticks[end-1]
last_step = ticks[end] - ticks[end - 1]
ticks = [ticks[1] - first_step / ratio; ticks; ticks[end] + last_step * ratio]
end
# default to 9 intervals between major ticks for log10 scale and 5 intervals otherwise
n_default = (scale == :log10) ? 9 : 5
n = typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] : n_default
n =
typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] :
n_default
minorticks = typeof(ticks[1])[]
for (i, hi) enumerate(ticks[2:end])
for (i, hi) in enumerate(ticks[2:end])
lo = ticks[i]
if isfinite(lo) && isfinite(hi) && hi > lo
if log_scaled
for e 1:sub
for e in 1:sub
lo_ = lo * base^(e - 1)
hi_ = lo_ * base
step = (hi_ - lo_) / n
append!(minorticks, collect(
lo_ + (e > 1 ? 0 : step) : step : hi_ - (e < sub ? 0 : step / 2)
))
append!(
minorticks,
collect(
(lo_ + (e > 1 ? 0 : step)):step:(hi_ - (e < sub ? 0 :
step / 2)),
),
)
end
else
step = (hi - lo) / n
append!(minorticks, collect(lo + step : step : hi - step / 2))
append!(minorticks, collect((lo + step):step:(hi - step / 2)))
end
end
end
@@ -382,17 +395,15 @@ end
# -------------------------------------------------------------------------
function reset_extrema!(sp::Subplot)
for asym in (:x,:y,:z)
sp[Symbol(asym,:axis)][:extrema] = Extrema()
for asym in (:x, :y, :z)
sp[get_attr_symbol(asym, :axis)][:extrema] = Extrema()
end
for series in sp.series_list
expand_extrema!(sp, series.plotattributes)
end
end
function expand_extrema!(ex::Extrema, v::Number)
ex.emin = isfinite(v) ? min(v, ex.emin) : ex.emin
ex.emax = isfinite(v) ? max(v, ex.emax) : ex.emax
@@ -407,14 +418,13 @@ end
expand_extrema!(axis::Axis, ::Nothing) = axis[:extrema]
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
function expand_extrema!(axis::Axis, v::Tuple{MIN,MAX}) where {MIN<:Number,MAX<:Number}
ex = axis[:extrema]
ex.emin = isfinite(v[1]) ? min(v[1], ex.emin) : ex.emin
ex.emax = isfinite(v[2]) ? max(v[2], ex.emax) : ex.emax
ex
end
function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
function expand_extrema!(axis::Axis, v::AVec{N}) where {N<:Number}
ex = axis[:extrema]
for vi in v
expand_extrema!(ex, vi)
@@ -422,7 +432,6 @@ function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
ex
end
function expand_extrema!(sp::Subplot, plotattributes::AKW)
vert = isvertical(plotattributes)
@@ -433,16 +442,22 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
else
letter == :x ? :y : letter == :y ? :x : :z
end]
if letter != :z && plotattributes[:seriestype] == :straightline && any(series[:seriestype] != :straightline for series in series_list(sp)) && data[1] != data[2]
if (
letter != :z &&
plotattributes[:seriestype] == :straightline &&
any(series[:seriestype] != :straightline for series in series_list(sp)) &&
data[1] != data[2]
)
data = [NaN]
end
axis = sp[Symbol(letter, "axis")]
axis = sp[get_attr_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))
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 = plotattributes[letter] = Surface(Matrix{Float64}(data.surf))
@@ -452,7 +467,9 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
# 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)
plotattributes[letter], plotattributes[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
plotattributes[letter],
plotattributes[get_attr_symbol(letter, :(_discrete_indices))] =
discrete_value!(axis, data)
expand_extrema!(axis, plotattributes[letter])
end
end
@@ -486,9 +503,11 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
bw = plotattributes[:bar_width]
if bw === nothing
bw = plotattributes[:bar_width] = _bar_width * ignorenan_minimum(filter(x->x>0,diff(sort(data))))
bw =
plotattributes[:bar_width] =
_bar_width * ignorenan_minimum(filter(x -> x > 0, diff(sort(data))))
end
axis = sp.attr[Symbol(dsym, :axis)]
axis = sp.attr[get_attr_symbol(dsym, :axis)]
expand_extrema!(axis, ignorenan_maximum(data) + 0.5maximum(bw))
expand_extrema!(axis, ignorenan_minimum(data) - 0.5minimum(bw))
end
@@ -497,8 +516,8 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
if plotattributes[:seriestype] == :heatmap
for letter in (:x, :y)
data = plotattributes[letter]
axis = sp[Symbol(letter, "axis")]
scale = get(plotattributes, Symbol(letter, "scale"), :identity)
axis = sp[get_attr_symbol(letter, :axis)]
scale = get(plotattributes, get_attr_symbol(letter, :scale), :identity)
expand_extrema!(axis, heatmap_edges(data, scale))
end
end
@@ -512,7 +531,7 @@ end
# -------------------------------------------------------------------------
# push the limits out slightly
function widen(lmin, lmax, scale=:identity)
function widen(lmin, lmax, scale = :identity)
f, invf = RecipesPipeline.scale_func(scale), RecipesPipeline.inverse_scale_func(scale)
span = f(lmax) - f(lmin)
# eps = NaNMath.max(1e-16, min(1e-2span, 1e-10))
@@ -521,7 +540,28 @@ function widen(lmin, lmax, scale=:identity)
end
# figure out if widening is a good idea.
const _widen_seriestypes = (:line, :path, :steppre, :stepmid, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d)
const _widen_seriestypes = (
:line,
:path,
:steppre,
:stepmid,
:steppost,
:sticks,
:scatter,
:barbins,
:barhist,
:histogram,
:scatterbins,
:scatterhist,
:stepbins,
:stephist,
:bins2d,
:histogram2d,
:bar,
:shape,
:path3d,
:scatter3d,
)
function default_should_widen(axis::Axis)
if axis[:widen] isa Bool
@@ -540,7 +580,7 @@ function default_should_widen(axis::Axis)
end
function round_limits(amin, amax, scale)
base = get(_logScaleBases, scale, 10.)
base = get(_logScaleBases, scale, 10.0)
factor = base^(1 - round(log(base, amax - amin)))
amin = floor(amin * factor) / factor
amax = ceil(amax * factor) / factor
@@ -548,8 +588,13 @@ function round_limits(amin, amax, scale)
end
# using the axis extrema and limit overrides, return the min/max value for this axis
function axis_limits(sp, letter, should_widen = default_should_widen(sp[Symbol(letter, :axis)]), consider_aspect = true)
axis = sp[Symbol(letter, :axis)]
function axis_limits(
sp,
letter,
should_widen = default_should_widen(sp[get_attr_symbol(letter, :axis)]),
consider_aspect = true,
)
axis = sp[get_attr_symbol(letter, :axis)]
ex = axis[:extrema]
amin, amax = ex.emin, ex.emax
lims = axis[:lims]
@@ -594,7 +639,9 @@ function axis_limits(sp, letter, should_widen = default_should_widen(sp[Symbol(l
end
if (
!has_user_lims && consider_aspect && letter in (:x, :y) &&
!has_user_lims &&
consider_aspect &&
letter in (:x, :y) &&
!(sp[:aspect_ratio] in (:none, :auto) || RecipesPipeline.is3d(:sp))
)
aspect_ratio = isa(sp[:aspect_ratio], Number) ? sp[:aspect_ratio] : 1
@@ -664,11 +711,11 @@ end
# add the discrete value for each item. return the continuous values and the indices
function discrete_value!(axis::Axis, v::AMat)
n,m = axes(v)
n, m = axes(v)
cmat = zeros(axes(v))
discrete_indices = similar(Array{Int}, axes(v))
for i in n, j in m
cmat[i,j], discrete_indices[i,j] = discrete_value!(axis, v[i,j])
cmat[i, j], discrete_indices[i, j] = discrete_value!(axis, v[i, j])
end
cmat, discrete_indices
end
@@ -682,10 +729,10 @@ end
# compute the line segments which should be drawn for this axis
function axis_drawing_info(sp, letter)
# find out which axis we are dealing with
asym = Symbol(letter, :axis)
asym = get_attr_symbol(letter, :axis)
isy = letter === :y
oletter = isy ? :x : :y
oasym = Symbol(oletter, :axis)
oasym = get_attr_symbol(oletter, :axis)
# get axis objects, ticks and minor ticks
ax, oax = sp[asym], sp[oasym]
@@ -711,7 +758,11 @@ function axis_drawing_info(sp, letter)
if sp[:framestyle] != :grid
push!(segments, reverse_if((amin, oa1), isy), reverse_if((amax, oa1), isy))
# don't show the 0 tick label for the origin framestyle
if sp[:framestyle] == :origin && !(ticks in (:none, nothing, false)) && length(ticks) > 1
if (
sp[:framestyle] == :origin &&
!(ticks in (:none, nothing, false)) &&
length(ticks) > 1
)
i = findfirst(==(0), ticks[1])
if i !== nothing
deleteat!(ticks[1], i)
@@ -762,12 +813,26 @@ function axis_drawing_info(sp, letter)
end
end
ax_length = letter === :x ? height(sp.plotarea).value : width(sp.plotarea).value
# add major grid segments
add_major_or_minor_segments(ticks[1], ax[:grid], grid_segments, 0.012, ax[:tick_direction] !== :none)
add_major_or_minor_segments(
ticks[1],
ax[:grid],
grid_segments,
1.2 / ax_length,
ax[:tick_direction] !== :none,
)
# add minor grid segments
if ax[:minorticks] (:none, nothing, false) || ax[:minorgrid]
add_major_or_minor_segments(minor_ticks, ax[:minorgrid], minorgrid_segments, 0.006, true)
add_major_or_minor_segments(
minor_ticks,
ax[:minorgrid],
minorgrid_segments,
0.6 / ax_length,
true,
)
end
end
end
@@ -778,7 +843,7 @@ function axis_drawing_info(sp, letter)
tick_segments = tick_segments,
grid_segments = grid_segments,
minorgrid_segments = minorgrid_segments,
border_segments = border_segments
border_segments = border_segments,
)
end
@@ -796,9 +861,9 @@ function axis_drawing_info_3d(sp, letter)
near_letter = letter in (:x, :z) ? :y : :x
far_letter = letter in (:x, :y) ? :z : :x
ax = sp[Symbol(letter, :axis)]
nax = sp[Symbol(near_letter, :axis)]
fax = sp[Symbol(far_letter, :axis)]
ax = sp[get_attr_symbol(letter, :axis)]
nax = sp[get_attr_symbol(near_letter, :axis)]
fax = sp[get_attr_symbol(far_letter, :axis)]
amin, amax = axis_limits(sp, letter)
namin, namax = axis_limits(sp, near_letter)
@@ -814,8 +879,7 @@ function axis_drawing_info_3d(sp, letter)
minorgrid_segments = Segments(3)
border_segments = Segments(3)
if sp[:framestyle] != :none# && letter === :x
if sp[:framestyle] != :none # && letter === :x
na0, na1 = if sp[:framestyle] in (:origin, :zerolines)
0, 0
else
@@ -835,7 +899,11 @@ function axis_drawing_info_3d(sp, letter)
sort_3d_axes(amax, na0, fa0, letter),
)
# don't show the 0 tick label for the origin framestyle
if sp[:framestyle] == :origin && !(ticks in (:none, nothing, false)) && length(ticks) > 1
if (
sp[:framestyle] == :origin &&
!(ticks in (:none, nothing, false)) &&
length(ticks) > 1
)
i0 = findfirst(==(0), ticks[1])
if i0 !== nothing
deleteat!(ticks[1], i0)
@@ -855,7 +923,8 @@ function axis_drawing_info_3d(sp, letter)
if ax[:ticks] (:none, nothing, false)
f = RecipesPipeline.scale_func(nax[:scale])
invf = RecipesPipeline.inverse_scale_func(nax[:scale])
ga0, ga1 = sp[:framestyle] in (:origin, :zerolines) ? (namin, namax) : (na0, na1)
ga0, ga1 =
sp[:framestyle] in (:origin, :zerolines) ? (namin, namax) : (na0, na1)
add_major_or_minor_segments(ticks, grid, segments, factor, cond) = begin
ticks === nothing && return
@@ -896,11 +965,23 @@ function axis_drawing_info_3d(sp, letter)
end
# add major grid segments
add_major_or_minor_segments(ticks[1], ax[:grid], grid_segments, 0.012, ax[:tick_direction] !== :none)
add_major_or_minor_segments(
ticks[1],
ax[:grid],
grid_segments,
0.012,
ax[:tick_direction] !== :none,
)
# add minor grid segments
if ax[:minorticks] (:none, nothing, false) || ax[:minorgrid]
add_major_or_minor_segments(minor_ticks, ax[:minorgrid], minorgrid_segments, 0.006, true)
add_major_or_minor_segments(
minor_ticks,
ax[:minorgrid],
minorgrid_segments,
0.006,
true,
)
end
end
end
@@ -911,7 +992,7 @@ function axis_drawing_info_3d(sp, letter)
tick_segments = tick_segments,
grid_segments = grid_segments,
minorgrid_segments = minorgrid_segments,
border_segments = border_segments
border_segments = border_segments,
)
end
+566 -233
View File
File diff suppressed because it is too large Load Diff
+207 -65
View File
@@ -27,7 +27,7 @@ const _pgfplots_markers = KW(
:diamond => "diamond*",
:pentagon => "pentagon*",
:hline => "-",
:vline => "|"
:vline => "|",
)
const _pgfplots_legend_pos = KW(
@@ -38,7 +38,6 @@ const _pgfplots_legend_pos = KW(
:outertopright => "outer north east",
)
const _pgf_series_extrastyle = KW(
:steppre => "const plot mark right",
:stepmid => "const plot mark mid",
@@ -50,11 +49,7 @@ const _pgf_series_extrastyle = KW(
# PGFPlots uses the anchors to define orientations for example to align left
# one needs to use the right edge as anchor
const _pgf_annotation_halign = KW(
:center => "",
:left => "right",
:right => "left"
)
const _pgf_annotation_halign = KW(:center => "", :left => "right", :right => "left")
const _pgf_framestyles = [:box, :axes, :origin, :zerolines, :grid, :none]
const _pgf_framestyle_defaults = Dict(:semi => :box)
@@ -63,7 +58,9 @@ function pgf_framestyle(style::Symbol)
return style
else
default_style = get(_pgf_framestyle_defaults, style, :axes)
@warn("Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead.")
@warn(
"Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead."
)
default_style
end
end
@@ -78,15 +75,15 @@ end
function pgf_color(grad::ColorGradient)
# Can't handle ColorGradient here, fallback to defaults.
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", 0.0, 0.60560316,0.97868012)
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", 0.0, 0.60560316, 0.97868012)
cstr, 1
end
# Generates a colormap for pgfplots based on a ColorGradient
function pgf_colormap(grad::ColorGradient)
join(map(grad.colors) do c
@sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c),blue(c))
end,", ")
@sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c), blue(c))
end, ", ")
end
pgf_thickness_scaling(plt::Plot) = plt[:thickness_scaling]
@@ -94,7 +91,7 @@ pgf_thickness_scaling(sp::Subplot) = pgf_thickness_scaling(sp.plt)
pgf_thickness_scaling(series) = pgf_thickness_scaling(series[:subplot])
function pgf_fillstyle(plotattributes, i = 1)
cstr,a = pgf_color(get_fillcolor(plotattributes, i))
cstr, a = pgf_color(get_fillcolor(plotattributes, i))
fa = get_fillalpha(plotattributes, i)
if fa !== nothing
a = fa
@@ -126,8 +123,15 @@ end
function pgf_marker(plotattributes, i = 1)
shape = _cycle(plotattributes[:markershape], i)
cstr, a = pgf_color(plot_color(get_markercolor(plotattributes, i), get_markeralpha(plotattributes, i)))
cstr_stroke, a_stroke = pgf_color(plot_color(get_markerstrokecolor(plotattributes, i), get_markerstrokealpha(plotattributes, i)))
cstr, a = pgf_color(
plot_color(get_markercolor(plotattributes, i), get_markeralpha(plotattributes, i)),
)
cstr_stroke, a_stroke = pgf_color(
plot_color(
get_markerstrokecolor(plotattributes, i),
get_markerstrokealpha(plotattributes, i),
),
)
return string(
"mark = $(get(_pgfplots_markers, shape, "*")),\n",
"mark size = $(pgf_thickness_scaling(plotattributes) * 0.5 * _cycle(plotattributes[:markersize], i)),\n",
@@ -138,22 +142,28 @@ function pgf_marker(plotattributes, i = 1)
line width = $(pgf_thickness_scaling(plotattributes) * _cycle(plotattributes[:markerstrokewidth], i)),
rotate = $(shape == :dtriangle ? 180 : 0),
$(get(_pgfplots_linestyles, _cycle(plotattributes[:markerstrokestyle], i), "solid"))
}"
)
}",
)
end
function pgf_add_annotation!(o, x, y, val, thickness_scaling = 1)
# Construct the style string.
# Currently supports color and orientation
cstr,a = pgf_color(val.font.color)
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
x, y,
style="""
$(get(_pgf_annotation_halign,val.font.halign,"")),
color=$cstr, draw opacity=$(convert(Float16,a)),
rotate=$(val.font.rotation),
font=$(pgf_font(val.font.pointsize, thickness_scaling))
"""))
cstr, a = pgf_color(val.font.color)
push!(
o,
PGFPlots.Plots.Node(
val.str, # Annotation Text
x,
y,
style = """
$(get(_pgf_annotation_halign,val.font.halign,"")),
color=$cstr, draw opacity=$(convert(Float16,a)),
rotate=$(val.font.rotation),
font=$(pgf_font(val.font.pointsize, thickness_scaling))
""",
),
)
end
# --------------------------------------------------------------------------------------
@@ -241,7 +251,15 @@ function pgf_series(sp::Subplot, series::Series)
# add fillrange
if series[:fillrange] !== nothing && st != :shape
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
push!(
series_collection,
pgf_fillrange_series(
series,
i,
_cycle(series[:fillrange], rng),
seg_args...,
),
)
end
# build/return the series object
@@ -313,7 +331,7 @@ end
# ----------------------------------------------------------------
function pgf_axis(sp::Subplot, letter)
axis = sp[Symbol(letter,:axis)]
axis = sp[get_attr_symbol(letter, :axis)]
style = []
kw = KW()
@@ -324,7 +342,7 @@ function pgf_axis(sp::Subplot, letter)
framestyle = pgf_framestyle(sp[:framestyle])
# axis guide
kw[Symbol(letter,:label)] = axis[:guide]
kw[get_attr_symbol(letter, :label)] = axis[:guide]
# axis label position
labelpos = ""
@@ -336,7 +354,23 @@ function pgf_axis(sp::Subplot, letter)
# Add label font
cstr, α = pgf_color(plot_color(axis[:guidefontcolor]))
push!(style, string(letter, "label style = {", labelpos ,"font = ", pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:guidefontrotation], "}"))
push!(
style,
string(
letter,
"label style = {",
labelpos,
"font = ",
pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)),
", color = ",
cstr,
", draw opacity = ",
α,
", rotate = ",
axis[:guidefontrotation],
"}",
),
)
# flip/reverse?
axis[:flip] && push!(style, "$letter dir=reverse")
@@ -344,7 +378,7 @@ function pgf_axis(sp::Subplot, letter)
# scale
scale = axis[:scale]
if scale in (:log2, :ln, :log10)
kw[Symbol(letter,:mode)] = "log"
kw[get_attr_symbol(letter, :mode)] = "log"
scale == :ln || push!(style, "log basis $letter=$(scale == :log2 ? 2 : 10)")
end
@@ -363,16 +397,20 @@ function pgf_axis(sp::Subplot, letter)
# limits
# TODO: support zlims
if letter != :z
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) : axis_limits(sp, letter)
kw[Symbol(letter,:min)] = lims[1]
kw[Symbol(letter,:max)] = lims[2]
lims =
ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
axis_limits(sp, letter)
kw[get_attr_symbol(letter, :min)] = lims[1]
kw[get_attr_symbol(letter, :max)] = lims[2]
end
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
ticks = get_ticks(sp, axis)
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
push!(style, string(letter, "tick = {", join(tick_values,","), "}"))
tick_values =
ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] :
ticks[1]
push!(style, string(letter, "tick = {", join(tick_values, ","), "}"))
if axis[:showaxis] && axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
# wrap the power part of label with }
tick_labels = Vector{String}(undef, length(ticks[2]))
@@ -381,21 +419,59 @@ function pgf_axis(sp::Subplot, letter)
power = string("{", power, "}")
tick_labels[i] = string(base, "^", power)
end
push!(style, string(letter, "ticklabels = {\$", join(tick_labels,"\$,\$"), "\$}"))
push!(
style,
string(letter, "ticklabels = {\$", join(tick_labels, "\$,\$"), "\$}"),
)
elseif axis[:showaxis]
tick_labels = ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
tick_labels =
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
if axis[:formatter] in (:scientific, :auto)
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
end
push!(style, string(letter, "ticklabels = {", join(tick_labels,","), "}"))
push!(style, string(letter, "ticklabels = {", join(tick_labels, ","), "}"))
else
push!(style, string(letter, "ticklabels = {}"))
end
push!(style, string(letter, "tick align = ", (axis[:tick_direction] == :out ? "outside" : "inside")))
push!(
style,
string(
letter,
"tick align = ",
(axis[:tick_direction] == :out ? "outside" : "inside"),
),
)
cstr, α = pgf_color(plot_color(axis[:tickfontcolor]))
push!(style, string(letter, "ticklabel style = {font = ", pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:tickfontrotation], "}"))
push!(style, string(letter, " grid style = {", pgf_linestyle(pgf_thickness_scaling(sp) * axis[:gridlinewidth], axis[:foreground_color_grid], axis[:gridalpha], axis[:gridstyle]), "}"))
push!(
style,
string(
letter,
"ticklabel style = {font = ",
pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)),
", color = ",
cstr,
", draw opacity = ",
α,
", rotate = ",
axis[:tickfontrotation],
"}",
),
)
push!(
style,
string(
letter,
" grid style = {",
pgf_linestyle(
pgf_thickness_scaling(sp) * axis[:gridlinewidth],
axis[:foreground_color_grid],
axis[:gridalpha],
axis[:gridstyle],
),
"}",
),
)
end
# framestyle
@@ -412,7 +488,20 @@ function pgf_axis(sp::Subplot, letter)
if framestyle == :zerolines
push!(style, string("extra ", letter, " ticks = 0"))
push!(style, string("extra ", letter, " tick labels = "))
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}}"))
push!(
style,
string(
"extra ",
letter,
" tick style = {grid = major, major grid style = {",
pgf_linestyle(
pgf_thickness_scaling(sp),
axis[:foreground_color_border],
1.0,
),
"}}",
),
)
end
if !axis[:showaxis]
@@ -421,7 +510,19 @@ function pgf_axis(sp::Subplot, letter)
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
else
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}"))
push!(
style,
string(
letter,
" axis line style = {",
pgf_linestyle(
pgf_thickness_scaling(sp),
axis[:foreground_color_border],
1.0,
),
"}",
),
)
end
# return the style list and KW args
@@ -430,7 +531,6 @@ end
# ----------------------------------------------------------------
function _update_plot_object(plt::Plot{PGFPlotsBackend})
plt.o = PGFPlots.Axis[]
# Obtain the total height of the plot by extracting the maximal bottom
@@ -438,7 +538,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
total_height = bottom(bbox(plt.layout))
for sp in plt.subplots
# first build the PGFPlots.Axis object
# first build the PGFPlots.Axis object
style = ["unbounded coords=jump"]
kw = KW()
@@ -456,18 +556,34 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
# A round on 2 decimal places should be enough precision for 300 dpi
# plots.
bb = bbox(sp)
push!(style, """
xshift = $(left(bb).value)mm,
yshift = $(round((total_height - (bottom(bb))).value, digits=2))mm,
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
""")
push!(
style,
"""
xshift = $(left(bb).value)mm,
yshift = $(round((total_height - (bottom(bb))).value, digits=2))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] != ""
kw[:title] = "$(sp[:title])"
cstr, α = pgf_color(plot_color(sp[:titlefontcolor]))
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
push!(
style,
string(
"title style = {font = ",
pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)),
", color = ",
cstr,
", draw opacity = ",
α,
", rotate = ",
sp[:titlefontrotation],
"}",
),
)
end
if get_aspect_ratio(sp) in (1, :equal)
@@ -481,15 +597,25 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
cstr, bg_alpha = pgf_color(plot_color(sp[:background_color_legend]))
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
push!(style, string(
"legend style = {",
pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], fg_alpha, "solid", ), ",",
push!(
style,
string(
"legend style = {",
pgf_linestyle(
pgf_thickness_scaling(sp),
sp[:foreground_color_legend],
fg_alpha,
"solid",
),
",",
"fill = $cstr,",
"fill opacity = $bg_alpha,",
"text opacity = $(alpha(plot_color(sp[:legendfontcolor]))),",
"font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
"}",
))
"font = ",
pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
"}",
),
)
if any(s[:seriestype] == :contour for s in series_list(sp))
kw[:view] = "{0}{90}"
@@ -520,7 +646,10 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
for series in series_list(sp)
for col in (:markercolor, :fillcolor, :linecolor)
if typeof(series.plotattributes[col]) == ColorGradient
push!(style,"colormap={plots}{$(pgf_colormap(series.plotattributes[col]))}")
push!(
style,
"colormap={plots}{$(pgf_colormap(series.plotattributes[col]))}",
)
if sp[:colorbar] == :none
kw[:colorbar] = "false"
@@ -543,17 +672,26 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
# add series annotations
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt), pgf_thickness_scaling(series))
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
pgf_add_annotation!(
o,
xi,
yi,
PlotText(str, fnt),
pgf_thickness_scaling(series),
)
end
end
# add the annotations
for ann in sp[:annotations]
pgf_add_annotation!(o, locate_annotation(sp, ann...)..., pgf_thickness_scaling(sp))
pgf_add_annotation!(
o,
locate_annotation(sp, ann...)...,
pgf_thickness_scaling(sp),
)
end
# add the PGFPlots.Axis to the list
push!(plt.o, o)
end
@@ -568,7 +706,7 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
pgfplt = PGFPlots.plot(plt.o)
# save a pdf
fn = tempname()*".pdf"
fn = tempname() * ".pdf"
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
# read it into io
@@ -579,8 +717,12 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
end
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
fn = tempname()*".tex"
PGFPlots.save(fn, backend_object(plt), include_preamble=plt.attr[:tex_output_standalone])
fn = tempname() * ".tex"
PGFPlots.save(
fn,
backend_object(plt),
include_preamble = plt.attr[:tex_output_standalone],
)
write(io, read(open(fn), String))
end
+506 -230
View File
@@ -1,315 +1,596 @@
# https://github.com/mbaz/Gaston.
const G = Gaston
const GastonSubplot = G.Plot
const GASTON_MARKER_SCALING = 1.3 / 5.0
const GNUPLOT_DPI = 72 # Compensate for DPI with increased resolution
#
# --------------------------------------------
# These functions are called by Plots
# --------------------------------------------
#
# Create the window/figure for this backend.
function _create_backend_figure(plt::Plot{GastonBackend})
xsize = plt.attr[:size][1]
ysize = plt.attr[:size][2]
G.set(termopts="""size $xsize,$ysize""")
state_handle = G.nexthandle() # for now all the figures will be kept
plt.o = G.newfigure(state_handle)
state_handle = Gaston.nexthandle() # for now all the figures will be kept
plt.o = Gaston.newfigure(state_handle)
end
function _before_layout_calcs(plt::Plot{GastonBackend})
# Initialize all the subplots first
plt.o.subplots = G.SubPlot[]
grid = size(plt.layout)
plt.o.layout = grid
plt.o.subplots = Gaston.SubPlot[]
for sp in plt.subplots
gaston_init_subplot(plt, sp)
n1 = n2 = 0
if length(plt.inset_subplots) > 0
n1, sps = gaston_get_subplots(0, plt.inset_subplots, plt.layout)
gaston_init_subplots(plt, sps)
end
if length(plt.subplots) > 0
n2, sps = gaston_get_subplots(0, plt.subplots, plt.layout)
end
if (n = n1 + n2) != length(plt.subplots)
@error "Gaston: $n != $(length(plt.subplots))"
end
plt.o.layout = gaston_init_subplots(plt, sps)
# Then add the series (curves in gaston)
for series in plt.series_list
gaston_add_series(plt, series)
end
end
function _update_min_padding!(sp::Subplot{GastonBackend})
sp.minpad = (20mm, 5mm, 2mm, 10mm)
end
function _update_plot_object(plt::Plot{GastonBackend})
end
for (mime, term) in (
"application/eps" => "epscairo", # NEED fixing TODO
"image/eps" => "epslatex", # NEED fixing TODO
"application/pdf" => "pdfcairo", # NEED fixing TODO
"application/postscript" => "postscript", # NEED fixing TODO
"image/svg+xml" => "svg",
"text/latex" => "tikz", # NEED fixing TODO
"application/x-tex" => "epslatex", # NEED fixing TODO
"text/plain" => "dumb", # NEED fixing TODO
)
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GastonBackend})
xsize = plt.attr[:size][1]
ysize = plt.attr[:size][2]
termopts="""size $xsize,$ysize"""
tmpfile = G.tempname() * "." * $term
G.save(
term = $term,
output = tmpfile,
handle = plt.o.handle,
saveopts = termopts
)
while !isfile(tmpfile) end # avoid race condition with read in next line
write(io, read(tmpfile))
rm(tmpfile, force=true)
nothing
for sp in plt.subplots
sp === nothing && continue
for ann in sp[:annotations]
x, y, val = locate_annotation(sp, ann...)
sp.o.axesconf *= "\nset label '$(val.str)' at $x,$y $(gaston_font(val.font))"
end
end
end
function _show(io::IO, mime::MIME{Symbol("image/png")},
plt::Plot{GastonBackend},)
scaling = plt.attr[:dpi] / GNUPLOT_DPI
xsize = plt.attr[:size][1] * scaling
ysize = plt.attr[:size][2] * scaling
# Scale all plot elements to match Plots.jl DPI standard
termopts="""size $xsize,$ysize fontscale $scaling lw $scaling dl $scaling ps $scaling"""
tmpfile = G.tempname()
G.save(
term = "pngcairo",
output = tmpfile,
handle = plt.o.handle,
saveopts = termopts
)
while !isfile(tmpfile) end # avoid race condition with read in next line
write(io, read(tmpfile))
rm(tmpfile, force=true)
nothing
end
function _display(plt::Plot{GastonBackend})
display(plt.o)
function _update_min_padding!(sp::Subplot{GastonBackend})
sp.minpad = 0mm, 0mm, 0mm, 0mm
nothing
end
#
function _update_plot_object(plt::Plot{GastonBackend})
# respect the layout ratio
dat = gaston_multiplot_pos_size(plt.layout, (0, 0, 1, 1))
gaston_multiplot_pos_size!(dat)
nothing
end
for (mime, term) in (
"application/eps" => "epscairo",
"image/eps" => "epslatex",
"application/pdf" => "pdfcairo",
"application/postscript" => "postscript",
"image/png" => "png",
"image/svg+xml" => "svg",
"text/latex" => "tikz",
"application/x-tex" => "epslatex",
"text/plain" => "dumb",
)
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GastonBackend})
term = String($term)
tmpfile = "$(Gaston.tempname()).$term"
Gaston.save(
term = term,
output = tmpfile,
handle = plt.o.handle,
saveopts = gaston_saveopts(plt),
)
while !isfile(tmpfile)
end # avoid race condition with read in next line
write(io, read(tmpfile))
rm(tmpfile, force = true)
nothing
end
end
_display(plt::Plot{GastonBackend}) = display(plt.o)
# --------------------------------------------
# These functions are gaston specific
# --------------------------------------------
#
function gaston_init_subplot(plt::Plot{GastonBackend}, sp::Subplot{GastonBackend})
dims = RecipesPipeline.is3d(sp) ? 3 : 2
axesconf = gaston_parse_axes_args(plt, sp) # Gnuplot string
sp.o = GastonSubplot(dims=dims, axesconf = axesconf, curves = [])
push!(plt.o.subplots, sp.o)
function gaston_saveopts(plt::Plot{GastonBackend})
saveopts = String["size $(join(plt.attr[:size], ","))"]
push!(
saveopts,
gaston_font(
plottitlefont(plt),
rot = false,
align = false,
color = false,
scale = 1,
),
)
push!(saveopts, "background $(gaston_color(plt.attr[:background_color]))")
# push!(saveopts, "title '$(plt.attr[:window_title])'")
# Scale all plot elements to match Plots.jl DPI standard
scaling = plt.attr[:dpi] / Plots.DPI
push!(saveopts, "fontscale $scaling lw $scaling dl $scaling ps $scaling")
return join(saveopts, " ")
end
function gaston_get_subplots(n, plt_subplots, layout)
nr, nc = size(layout)
sps = Array{Any}(nothing, nr, nc)
for r in 1:nr, c in 1:nc # NOTE: col major
l = layout[r, c]
if l isa GridLayout
n, sub = gaston_get_subplots(n, plt_subplots, l)
sps[r, c] = size(sub) == (1, 1) ? only(sub) : sub
else
sps[r, c] = get(l.attr, :blank, false) ? nothing : plt_subplots[n += 1]
end
end
return n, sps
end
function gaston_init_subplots(plt, sps)
sz = nr, nc = size(sps)
for c in 1:nc, r in 1:nr # NOTE: row major
sp = sps[r, c]
if sp isa Subplot || sp === nothing
gaston_init_subplot(plt, sp)
else
gaston_init_subplots(plt, sp)
sz = max.(sz, size(sp))
end
end
return sz
end
function gaston_init_subplot(
plt::Plot{GastonBackend},
sp::Union{Nothing,Subplot{GastonBackend}},
)
if sp === nothing
push!(plt.o.subplots, sp)
else
dims =
RecipesPipeline.is3d(sp) ||
sp.attr[:projection] == "3d" ||
needs_any_3d_axes(sp) ? 3 : 2
any_label = false
for series in series_list(sp)
if dims == 2 && series[:seriestype] (:heatmap, :contour)
dims = 3 # we need heatmap/contour to use splot, not plot
end
any_label |= should_add_to_legend(series)
end
sp.o = Gaston.Plot(
dims = dims,
curves = [],
axesconf = gaston_parse_axes_args(plt, sp, dims, any_label),
)
push!(plt.o.subplots, sp.o)
end
nothing
end
function gaston_multiplot_pos_size(layout, parent_xy_wh)
nr, nc = size(layout)
dat = Array{Any}(nothing, nr, nc)
for r in 1:nr, c in 1:nc
l = layout[r, c]
# width and height (pct) are multiplicative (parent)
w = layout.widths[c].value * parent_xy_wh[3]
h = layout.heights[r].value * parent_xy_wh[4]
if isa(l, EmptyLayout)
dat[r, c] = (c - 1) * w, (r - 1) * h, w, h, nothing
else
# previous position (origin)
prev_r = r > 1 ? dat[r - 1, c] : nothing
prev_c = c > 1 ? dat[r, c - 1] : nothing
prev_r isa Array && (prev_r = prev_r[end, end])
prev_c isa Array && (prev_c = prev_c[end, end])
x = prev_c !== nothing ? prev_c[1] + prev_c[3] : parent_xy_wh[1]
y = prev_r !== nothing ? prev_r[2] + prev_r[4] : parent_xy_wh[2]
if l isa GridLayout
sub = gaston_multiplot_pos_size(l, (x, y, w, h))
dat[r, c] = size(sub) == (1, 1) ? only(sub) : sub
else
dat[r, c] = x, y, w, h, l
end
end
end
return dat
end
function gaston_multiplot_pos_size!(dat)
nr, nc = size(dat)
for r in 1:nr, c in 1:nc
xy_wh_sp = dat[r, c]
if xy_wh_sp isa Array
gaston_multiplot_pos_size!(xy_wh_sp)
elseif xy_wh_sp isa Tuple
x, y, w, h, sp = xy_wh_sp
sp === nothing && continue
sp.o === nothing && continue
# gnuplot screen coordinates: bottom left at 0,0 and top right at 1,1
sp.o.axesconf = "set origin $x, $(1 - y - h)\nset size $w, $h\n" * sp.o.axesconf
end
end
nothing
end
function gaston_add_series(plt::Plot{GastonBackend}, series::Series)
# Gaston.Curve = Plots.Series
sp = series[:subplot]
g_sp = sp.o # Gaston subplot object
gsp = sp.o
x, y, z = series[:x], series[:y], series[:z]
st = series[:seriestype]
curves = []
if gsp.dims == 2 && z === nothing
for (n, seg) in enumerate(series_segments(series, st; check = true))
i, rng = seg.attr_index, seg.range
fr = _cycle(series[:fillrange], 1:length(x[rng]))
for sc in gaston_seriesconf!(sp, series, i, n == 1)
push!(curves, Gaston.Curve(x[rng], y[rng], nothing, fr, sc))
end
end
else
if z isa Surface
z = z.surf
if st == :image
z = reverse(Float32.(Gray.(z)), dims = 1) # flip y axis
nr, nc = size(z)
if (ly = length(y)) == 2 && ly != nr
y = collect(range(y[1], y[2], length = nr))
end
if (lx = length(x)) == 2 && lx != nc
x = collect(range(x[1], x[2], length = nc))
end
elseif st == :heatmap
length(x) == size(z, 2) + 1 && (x = @view x[1:(end - 1)])
length(y) == size(z, 1) + 1 && (y = @view y[1:(end - 1)])
end
end
if st == :mesh3d
x, y, z = mesh3d_triangles(x, y, z, series[:connections])
end
for sc in gaston_seriesconf!(sp, series, 1, true)
push!(curves, Gaston.Curve(x, y, z, nothing, sc))
end
end
seriesconf = gaston_parse_series_args(series) # Gnuplot string
c = G.Curve(series[:x], series[:y], nothing, nothing, seriesconf )
isfirst = length(g_sp.curves) == 0 ? true : false
push!(g_sp.curves, c)
G.write_data(c, g_sp.dims, g_sp.datafile, append = isfirst ? false : true)
for c in curves
append = length(gsp.curves) > 0
push!(gsp.curves, c)
Gaston.write_data(c, gsp.dims, gsp.datafile, append = append)
end
nothing
end
function gaston_parse_series_args(series::Series)
curveconf = String[]
function gaston_seriesconf!(
sp::Subplot{GastonBackend},
series::Series,
i::Int,
add_to_legend::Bool,
)
#=
gnuplot abbreviations (see gnuplot/src/set.c)
---------------------------------------------
dl: dashlength
dt: dashtype
fc: fillcolor
fs: fillstyle
lc: linecolor
lp: linespoints
ls: linestyle
lt: linetype
lw: linewidth
pi: pointinterval
pn: pointnumber
ps: pointscale
pt: pointtype
tc: textcolor
w: with
=#
gsp = sp.o
st = series[:seriestype]
extra = []
add_to_legend &= should_add_to_legend(series)
curveconf = String[add_to_legend ? "title '$(series[:label])'" : "notitle"]
if st == :scatter
pt = gaston_marker(series[:markershape])
ps = series[:markersize] * GASTON_MARKER_SCALING
lc = gaston_color(series[:markercolor])
# alpha = series[:markeralpha] # TODO merge alpha with rgb color
push!(curveconf, """with points pt $pt ps $ps lc $lc""")
elseif st == :path
lc = gaston_color(series[:linecolor])
dt = gaston_linestyle(series[:linestyle])
lw = series[:linewidth]
# alpha = series[:linealpha] # TODO merge alpha with rgb color
if series[:markershape] == :none # simplepath
push!(curveconf, """with lines lc $lc dt $dt lw $lw""")
clims = get_clims(sp, series)
if st (:scatter, :scatter3d)
lc, dt, lw = gaston_lc_ls_lw(series, clims, i)
pt, ps, mc = gaston_mk_ms_mc(series, clims, i)
push!(curveconf, "w points pt $pt ps $ps lc $mc")
elseif st (:path, :straightline, :path3d)
fr = series[:fillrange]
fc = gaston_color(get_fillcolor(series, i), get_fillalpha(series, i))
lc, dt, lw = gaston_lc_ls_lw(series, clims, i)
if fr !== nothing # filled curves, but not filled curves with markers
push!(
curveconf,
"w filledcurves fc $fc fs solid border lc $lc lw $lw dt $dt,'' w lines lc $lc lw $lw dt $dt",
)
elseif series[:markershape] == :none # simplepath
push!(curveconf, "w lines lc $lc dt $dt lw $lw")
else
pt = gaston_marker(series[:markershape])
ps = series[:markersize] * GASTON_MARKER_SCALING
push!(curveconf, """with lp lc $lc dt $dt lw $lw pt $pt ps $ps""")
pt, ps, mc = gaston_mk_ms_mc(series, clims, i)
push!(curveconf, "w lp lc $mc dt $dt lw $lw pt $pt ps $ps")
end
elseif st == :shape
fc = gaston_color(series[:fillcolor])
fs = "solid"
lc = gaston_color(series[:linecolor])
push!(curveconf, """with filledcurves fc $fc fs $fs border lc $lc""")
elseif st == :steppre
push!(curveconf, """with steps""")
elseif st == :steppost
push!(curveconf, """with fsteps""") # Not sure if not the other way
fc = gaston_color(get_fillcolor(series, i), get_fillalpha(series, i))
lc, _ = gaston_lc_ls_lw(series, clims, i)
push!(curveconf, "w filledcurves fc $fc fs solid border lc $lc")
elseif st (:steppre, :stepmid, :steppost)
step = if st == :steppre
"fsteps"
elseif st == :stepmid
"histeps"
elseif st == :steppost
"steps"
end
push!(curveconf, "w $step")
lc, dt, lw = gaston_lc_ls_lw(series, clims, i)
push!(extra, "w points lc $lc dt $dt lw $lw notitle")
elseif st == :image
palette = gaston_palette(series[:seriescolor])
gsp.axesconf *= "\nset palette model RGB defined $palette"
push!(curveconf, "w image pixels")
elseif st (:contour, :contour3d)
push!(curveconf, "w lines")
st == :contour && (gsp.axesconf *= "\nset view map\nunset surface") # 2D
levels = join(map(string, collect(contour_levels(series, clims))), ", ")
gsp.axesconf *= "\nset contour base\nset cntrparam levels discrete $levels"
elseif st (:surface, :heatmap)
push!(curveconf, "w pm3d")
palette = gaston_palette(series[:seriescolor])
gsp.axesconf *= "\nset palette model RGB defined $palette"
st == :heatmap && (gsp.axesconf *= "\nset view map")
elseif st (:wireframe, :mesh3d)
lc, dt, lw = gaston_lc_ls_lw(series, clims, i)
push!(curveconf, "w lines lc $lc dt $dt lw $lw")
elseif st == :quiver
push!(curveconf, "w vectors filled")
else
@warn "Gaston: $st is not implemented yet"
end
# label
push!(curveconf, """title "$(series[:label])" """)
return join(curveconf, " ")
return [join(curveconf, " "), extra...]
end
function gaston_parse_axes_args(plt::Plot{GastonBackend}, sp::Subplot{GastonBackend})
function gaston_parse_axes_args(
plt::Plot{GastonBackend},
sp::Subplot{GastonBackend},
dims::Int,
any_label::Bool,
)
# axesconf = String["set margins 2, 2, 2, 2"] # left, right, bottom, top
axesconf = String[]
# Standard 2d axis
if !ispolar(sp) && !RecipesPipeline.is3d(sp)
# TODO
# configure grid, axis spines, thickness
end
polar = ispolar(sp) && dims == 2 # cannot splot in polar coordinates
for letter in (:x, :y, :z)
axis_attr = sp.attr[Symbol(letter, :axis)]
(letter == :z && dims == 2) && continue
axis = sp.attr[get_attr_symbol(letter, :axis)]
# label names
push!(axesconf, """set $(letter)label "$(axis_attr[:guide])" """)
push!(axesconf, """set $(letter)label font "$(axis_attr[:guidefontfamily]), $(axis_attr[:guidefontsize])" """)
push!(
axesconf,
"set $(letter)label '$(axis[:guide])' $(gaston_font(guidefont(axis)))",
)
mirror = axis[:mirror] ? "mirror" : "nomirror"
# Handle ticks
# ticksyle
push!(axesconf, """set $(letter)tics font "$(axis_attr[:tickfontfamily]), $(axis_attr[:tickfontsize])" """)
push!(axesconf, """set $(letter)tics textcolor rgb "#$(hex(axis_attr[:tickfontcolor], :rrggbb))" """)
push!(axesconf, """set $(letter)tics $(axis_attr[:tick_direction])""")
mirror = axis_attr[:mirror] ? "mirror" : "nomirror"
push!(axesconf, """set $(letter)tics $(mirror) """)
logscale = if axis_attr[:scale] == :identity
"nologscale"
elseif axis_attr[:scale] == :log10
"logscale"
if axis[:scale] == :identity
logscale, base = "nologscale", ""
elseif axis[:scale] == :log10
logscale, base = "logscale", "10"
elseif axis[:scale] == :log2
logscale, base = "logscale", "2"
elseif axis[:scale] == :ln
logscale, base = "logscale", "e"
end
push!(axesconf, """set $logscale $(letter)""")
push!(axesconf, "set $logscale $letter $base")
# tick locations
if axis_attr[:ticks] != :native
# axis limits
from, to = axis_limits(sp, letter)
push!(axesconf, """set $(letter)range [$from:$to]""")
# handle ticks
if polar
push!(axesconf, "set size square\nunset $(letter)tics")
else
push!(
axesconf,
"set $(letter)tics $(mirror) $(axis[:tick_direction]) $(gaston_font(tickfont(axis)))",
)
ticks = get_ticks(sp, axis_attr)
gaston_set_ticks!(axesconf, ticks, letter)
# major tick locations
if axis[:ticks] != :native
if axis[:flip]
hi, lo = axis_limits(sp, letter)
else
lo, hi = axis_limits(sp, letter)
end
push!(axesconf, "set $(letter)range [$lo:$hi]")
ticks = get_ticks(sp, axis)
gaston_set_ticks!(axesconf, ticks, letter, "", "")
if axis[:minorticks] != :native
minor_ticks = get_minor_ticks(sp, axis, ticks)
gaston_set_ticks!(axesconf, minor_ticks, letter, "m", "add")
end
end
end
if axis[:grid]
push!(axesconf, "set grid " * (polar ? "polar" : "$(letter)tics"))
axis[:minorgrid] &&
push!(axesconf, "set grid " * (polar ? "polar" : "m$(letter)tics"))
end
ratio = get_aspect_ratio(sp)
if ratio != :none
ratio == :equal && (ratio = -1)
push!(axesconf, "set size ratio $ratio")
end
# set title {"<title-text>"} {offset <offset>} {font "<font>{,<size>}"}{{textcolor | tc} {<colorspec> | default}} {{no}enhanced}1
end
gaston_set_legend!(axesconf, sp) # Set legend params
gaston_set_legend!(axesconf, sp, any_label)
if sp[:title] != nothing
push!(axesconf, """set title '$(sp[:title])' """)
push!(axesconf, """set title font '$(sp[:titlefontfamily]), $(sp[:titlefontsize])' """)
if hascolorbar(sp)
push!(axesconf, "set cbtics $(gaston_font(colorbartitlefont(sp)))")
end
if sp[:title] !== nothing
push!(axesconf, "set title '$(sp[:title])' $(gaston_font(titlefont(sp)))")
end
if polar
push!(axesconf, "unset border\nset polar\nset border polar")
tmin, tmax = axis_limits(sp, :x, false, false)
rmin, rmax = axis_limits(sp, :y, false, false)
rticks = get_ticks(sp, :y)
if (ttype = ticksType(rticks)) == :ticks
gaston_ticks = string.(rticks)
elseif ttype == :ticks_and_labels
gaston_ticks = String["'$l' $t" for (t, l) in zip(rticks...)]
end
push!(
axesconf,
"set rtics ( " *
join(gaston_ticks, ", ") *
" ) $(gaston_font(tickfont(sp.attr[:yaxis])))",
)
push!(axesconf, "set trange [$(min(0, tmin)):$(max(2π, tmax))]")
push!(axesconf, "set rrange [$rmin:$rmax]")
push!(
axesconf,
"set ttics 0,30 format \"%g\".GPVAL_DEGREE_SIGN $(gaston_font(tickfont(sp.attr[:xaxis])))",
)
push!(axesconf, "set mttics 3")
end
return join(axesconf, "\n")
end
function gaston_set_ticks!(axesconf, ticks, letter)
function gaston_set_ticks!(axesconf, ticks, letter, maj_min, add)
ticks == :auto && return
if ticks == :none || ticks === nothing || ticks == false
push!(axesconf, """unset $(letter)tics """)
if ticks (:none, nothing, false)
push!(axesconf, "unset $(maj_min)$(letter)tics")
return
end
ttype = ticksType(ticks)
if ttype == :ticks
tick_locations = @view ticks[:]
gaston_tick_string = []
for i in eachindex(tick_locations)
lac = tick_locations[i]
push!(gaston_tick_string, "$loc")
gaston_ticks = String[]
if (ttype = ticksType(ticks)) == :ticks
tick_locs = @view ticks[:]
for i in eachindex(tick_locs)
tick = if maj_min == "m"
"'' $(tick_locs[i]) 1" # see gnuplot manual 'Mxtics'
else
"$(tick_locs[i])"
end
push!(gaston_ticks, tick)
end
push!(
axesconf,
"set $(letter)tics (" * join(gaston_tick_string, ", ") * ")"
)
elseif ttype == :ticks_and_labels
tick_locations = @view ticks[1][:]
tick_locs = @view ticks[1][:]
tick_labels = @view ticks[2][:]
gaston_tick_string = []
for i in eachindex(tick_locations)
loc = tick_locations[i]
for i in eachindex(tick_locs)
lab = gaston_enclose_tick_string(tick_labels[i])
push!(gaston_tick_string, "'$lab' $loc")
push!(gaston_ticks, "'$lab' $(tick_locs[i])")
end
push!(
axesconf,
"set $(letter)tics (" * join(gaston_tick_string, ", ") * ")"
)
else
error("Invalid input for $(letter)ticks: $ticks")
gaston_ticks = nothing
@error "Gaston: invalid input for $(maj_min)$(letter)ticks: $ticks"
end
if gaston_ticks !== nothing
push!(axesconf, "set $(letter)tics $add (" * join(gaston_ticks, ", ") * ")")
end
nothing
end
function gaston_set_legend!(axesconf, sp)
function gaston_set_legend!(axesconf, sp, any_label)
leg = sp[:legend]
if !(sp[:legend] in(:none, :inline))
if leg == :best
leg = :topright
end
if sp[:legend] (:none, :inline) && any_label
leg == :best && (leg = :topright)
if occursin("outer", string(leg))
push!(axesconf, "set key outside")
else
push!(axesconf, "set key inside")
push!(
axesconf,
"set key " * (occursin("outer", string(leg)) ? "outside" : "inside"),
)
for position in ("top", "bottom", "left", "right")
occursin(position, string(leg)) && push!(axesconf, "set key $position")
end
positions = ["top", "bottom", "left", "right"]
for position in positions
if occursin(position, string(leg))
push!(axesconf, "set key $position")
end
end
if sp[:legendtitle] != nothing
push!(axesconf, "set key $(gaston_font(legendfont(sp), rot=false, align=false))")
if sp[:legendtitle] !== nothing
# NOTE: cannot use legendtitlefont(sp) as it will override legendfont
push!(axesconf, "set key title '$(sp[:legendtitle])'")
end
push!(axesconf, "set key box linewidth 1")
push!(axesconf, "set key opaque")
push!(axesconf, "set key box lw 1 opaque")
push!(axesconf, "set border back")
push!(axesconf, """set key font "$(sp[:legendfontfamily]), $(sp[:legendfontsize])" """)
else
push!(axesconf, "set key off")
end
nothing
end
function gaston_marker(marker)
marker == :none && return -1
marker == :circle && return 7
marker == :rect && return 5
marker == :diamond && return 28
marker == :utriangle && return 9
marker == :dtriangle && return 11
marker == :+ && return 1
marker == :x && return 2
marker == :star5 && return 3
marker == :pentagon && return 15
marker == :pixel && return 0
# --------------------------------------------
# Helpers
# --------------------------------------------
@warn("Unsupported marker $marker")
gaston_halign(k) = (left = :left, hcenter = :center, right = :right)[k]
gaston_valign(k) = (top = :top, vcenter = :center, bottom = :bottom)[k]
gaston_alpha(alpha) = alpha === nothing ? 0 : alpha
gaston_lc_ls_lw(series::Series, clims, i::Int) = (
gaston_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
gaston_linestyle(get_linestyle(series, i)),
get_linewidth(series, i),
)
gaston_mk_ms_mc(series::Series, clims, i::Int) = (
gaston_marker(_cycle(series[:markershape], i), get_markeralpha(series, i)),
_cycle(series[:markersize], i) * 1.3 / 5,
gaston_color(get_markercolor(series, clims, i), get_markeralpha(series, i)),
)
function gaston_font(f; rot = true, align = true, color = true, scale = 1)
font = String["font '$(f.family),$(round(Int, scale * f.pointsize))'"]
align && push!(font, "$(gaston_halign(f.halign))")
rot && push!(font, "rotate by $(f.rotation)")
color && push!(font, "textcolor $(gaston_color(f.color))")
return join(font, " ")
end
function gaston_palette(gradient)
palette = String[]
n = -1
for rgba in gradient # FIXME: naive conversion, inefficient ?
push!(palette, "$(n += 1) $(rgba.r) $(rgba.g) $(rgba.b)")
end
return '(' * join(palette, ", ") * ')'
end
function gaston_marker(marker, alpha)
# NOTE: :rtriangle, :ltriangle, :hexagon, :heptagon, :octagon seems unsupported by gnuplot
filled = gaston_alpha(alpha) == 0
marker == :none && return -1
marker == :pixel && return 0
marker (:+, :cross) && return 1
marker (:x, :xcross) && return 2
marker == :star5 && return 3
marker == :rect && return filled ? 5 : 4
marker == :circle && return filled ? 7 : 6
marker == :utriangle && return filled ? 9 : 8
marker == :dtriangle && return filled ? 11 : 10
marker == :diamond && return filled ? 13 : 12
marker == :pentagon && return filled ? 15 : 14
@warn "Gaston: unsupported marker $marker"
return 1
end
gaston_color(color) = """rgb "#$(hex(color, :rrggbb))" """
function gaston_color(col, alpha = 0)
col = single_color(col) # in case of gradients
col = alphacolor(col, gaston_alpha(alpha)) # add a default alpha if non existent
return "rgb '#$(hex(col, :aarrggbb))'"
end
function gaston_linestyle(style)
style == :solid && return "1"
@@ -320,12 +601,7 @@ function gaston_linestyle(style)
end
function gaston_enclose_tick_string(tick_string)
if findfirst("^", tick_string) == nothing
return tick_string
end
findfirst("^", tick_string) === nothing && return tick_string
base, power = split(tick_string, "^")
power = string("{", power, "}")
return string(base, "^", power)
return "$base^{$power}"
end
+332 -239
View File
File diff suppressed because it is too large Load Diff
+50 -52
View File
@@ -13,7 +13,6 @@ Read from .hdf5 file using:
display(pread)
==#
#==TODO
===============================================================================
1. Support more features.
@@ -56,7 +55,8 @@ import ..HDF5PLOT_MAP_STR2TELEM, ..HDF5PLOT_MAP_TELEM2STR
import ..HDF5Plot_PlotRef, ..HDF5PLOT_PLOTREF
import ..BoundingBox, ..Extrema, ..Length
import ..RecipesPipeline.datetimeformatter
import ..PlotUtils.ColorPalette, ..PlotUtils.CategoricalColorGradient, ..PlotUtils.ContinuousColorGradient
import ..PlotUtils.ColorPalette,
..PlotUtils.CategoricalColorGradient, ..PlotUtils.ContinuousColorGradient
import ..Surface, ..Shape, ..Arrow
import ..GridLayout, ..RootLayout
import ..Font, ..PlotText, ..SeriesAnnotations
@@ -66,14 +66,13 @@ import .._axis_defaults
import ..plot, ..plot!
#Types that already have built-in HDF5 support (just write out natively):
const HDF5_SupportedTypes = Union{Number, String}
const HDF5_SupportedTypes = Union{Number,String}
#TODO: Types_HDF5Support
#Dispatch types:
struct CplxTuple; end #Identifies a "complex" tuple structure (not merely numbers)
struct CplxTuple end #Identifies a "complex" tuple structure (not merely numbers)
#HDF5 reader will auto-detect type correctly:
struct HDF5_AutoDetect; end #See HDF5_SupportedTypes
struct HDF5_AutoDetect end #See HDF5_SupportedTypes
#==
===============================================================================#
@@ -82,13 +81,12 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
#Possible element types of high-level data types:
#(Used to add type information as an HDF5 string attribute)
#(Also used to dispatch appropriate read function through _read_typed())
_telem2str = Dict{String, Type}(
_telem2str = Dict{String,Type}(
"NOTHING" => Nothing,
"SYMBOL" => Symbol,
"RGBA" => Colorant, #Write out any Colorant to an #RRGGBBAA string
"TUPLE" => Tuple,
"CTUPLE" => CplxTuple, #Tuple of complex structures
"CTUPLE" => CplxTuple,
"EXTREMA" => Extrema,
"LENGTH" => Length,
"ARRAY" => Array, #Array{Any} (because Array{T<:Union{Number, String}} natively supported by HDF5)
@@ -115,10 +113,12 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
"SUBPLOT" => Subplot,
)
merge!(HDF5PLOT_MAP_STR2TELEM, _telem2str) #Faster to create than push!()??
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
merge!(
HDF5PLOT_MAP_TELEM2STR,
Dict{Type,String}(v => k for (k, v) in HDF5PLOT_MAP_STR2TELEM),
)
end
#==Helper functions
===============================================================================#
@@ -151,19 +151,18 @@ function _hdf5_merge!(dest::AKW, src::AKW)
end
#_type_for_map returns the type to use with HDF5PLOT_MAP_TELEM2STR[], in case it is not concrete:
_type_for_map(::Type{T}) where T = T #Catch-all
_type_for_map(::Type{T}) where T<:BoundingBox = BoundingBox
_type_for_map(::Type{T}) where T<:ColorScheme = ColorScheme
_type_for_map(::Type{T}) where T<:Surface = Surface
_type_for_map(::Type{T}) where {T} = T #Catch-all
_type_for_map(::Type{T}) where {T<:BoundingBox} = BoundingBox
_type_for_map(::Type{T}) where {T<:ColorScheme} = ColorScheme
_type_for_map(::Type{T}) where {T<:Surface} = Surface
#==Read/write things like type name in attributes
===============================================================================#
function _write_datatype_attr(ds::Union{Group, Dataset}, ::Type{T}) where T
function _write_datatype_attr(ds::Union{Group,Dataset}, ::Type{T}) where {T}
typestr = HDF5PLOT_MAP_TELEM2STR[T]
HDF5.attributes(ds)["TYPE"] = typestr
end
function _read_datatype_attr(ds::Union{Group, Dataset})
function _read_datatype_attr(ds::Union{Group,Dataset})
if !Base.haskey(HDF5.attributes(ds), "TYPE")
return HDF5_AutoDetect
end
@@ -173,7 +172,7 @@ function _read_datatype_attr(ds::Union{Group, Dataset})
end
#Type parameter attributes:
function _write_typeparam_attr(ds::Dataset, v::Length{T}) where T
function _write_typeparam_attr(ds::Dataset, v::Length{T}) where {T}
HDF5.attributes(ds)["TYPEPARAM"] = string(T) #Need to add units for Length
end
_read_typeparam_attr(ds::Dataset) = HDF5.read(HDF5.attributes(ds)["TYPEPARAM"])
@@ -186,8 +185,8 @@ _read_length_attr(::Type{Vector}, grp::Group) = HDF5.read(HDF5.attributes(grp)["
function _write_size_attr(grp::Group, v::Array) #of an array
HDF5.attributes(grp)["SIZE"] = [size(v)...]
end
_read_size_attr(::Type{Array}, grp::Group) = tuple(HDF5.read(HDF5.attributes(grp)["SIZE"])...)
_read_size_attr(::Type{Array}, grp::Group) =
tuple(HDF5.read(HDF5.attributes(grp)["SIZE"])...)
#==_write_typed(): Simple (leaf) datatypes. (Labels with type name.)
===============================================================================#
@@ -228,9 +227,9 @@ function _write_typed(grp::Group, name::String, v::Length)
end
function _write_typed(grp::Group, name::String, v::typeof(datetimeformatter))
grp[name] = string(v) #Just write something that helps reader
_write_datatype_attr(grp[name], typeof(datetimeformatter))
_write_datatype_attr(grp[name], typeof(datetimeformatter))
end
function _write_typed(grp::Group, name::String, v::Array{T}) where T<:Number #Default for arrays
function _write_typed(grp::Group, name::String, v::Array{T}) where {T<:Number} #Default for arrays
grp[name] = v
return #No need to _write_datatype_attr
end
@@ -238,8 +237,6 @@ function _write_typed(grp::Group, name::String, v::AbstractRange)
_write_typed(grp, name, collect(v)) #For now
end
#== Helper functions for writing complex data structures
===============================================================================#
@@ -270,20 +267,19 @@ function _write(grp::Group, name::String, d::AbstractDict)
end
#Write out arbitrary `struct`s:
function _writestructgeneric(grp::Group, obj::T) where T
function _writestructgeneric(grp::Group, obj::T) where {T}
for fname in fieldnames(T)
v = getfield(obj, fname)
_write_typed(grp, String(fname), v)
v = getfield(obj, fname)
_write_typed(grp, String(fname), v)
end
return
end
#==_write_typed(): More complex structures. (Labels with type name.)
===============================================================================#
#Catch-all (default behaviour for `struct`s):
function _write_typed(grp::Group, name::String, v::T) where T
function _write_typed(grp::Group, name::String, v::T) where {T}
#NOTE: need "name" parameter so that call signature is same with built-ins
MT = _type_for_map(T)
try #Check to see if type is supported
@@ -299,12 +295,12 @@ function _write_typed(grp::Group, name::String, v::T) where T
_writestructgeneric(objgrp, v)
end
function _write_typed(grp::Group, name::String, v::Array{T}) where T
function _write_typed(grp::Group, name::String, v::Array{T}) where {T}
_write_harray(grp, name, v)
_write_datatype_attr(grp[name], Array) #{Any}
end
function _write_typed(grp::Group, name::String, v::Tuple, ::Type{ELT}) where ELT<: Number #Basic Tuple
function _write_typed(grp::Group, name::String, v::Tuple, ::Type{ELT}) where {ELT<:Number} #Basic Tuple
_write_typed(grp, name, [v...])
_write_datatype_attr(grp[name], Tuple)
end
@@ -315,12 +311,12 @@ end
_write_typed(grp::Group, name::String, v::Tuple) = _write_typed(grp, name, v, eltype(v))
function _write_typed(grp::Group, name::String, v::Dict)
#=
tstr = string(Dict)
path = HDF5.name(grp) * "/" * name
@info("Type not supported: $tstr\npath: $path")
return
=#
#=
tstr = string(Dict)
path = HDF5.name(grp) * "/" * name
@info("Type not supported: $tstr\npath: $path")
return
=#
#No support for structures with Dicts yet
end
function _write_typed(grp::Group, name::String, d::DefaultsDict) #Typically for plot attributes
@@ -347,7 +343,6 @@ function _write_typed(grp::Group, name::String, v::Plot)
#Don't write plot references
end
#==_write(): Write out more complex structures
NOTE: No need to write out type information (inferred from hierarchy)
===============================================================================#
@@ -378,7 +373,11 @@ function _write(grp::Group, plt::Plot{HDF5Backend})
return
end
function hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString; name::String="_unnamed")
function hdf5plot_write(
plt::Plot{HDF5Backend},
path::AbstractString;
name::String = "_unnamed",
)
HDF5.h5open(path, "w") do file
HDF5.write_dataset(file, "VERSION_INFO", _get_Plots_versionstr())
grp = HDF5.create_group(file, h5plotpath(name))
@@ -386,7 +385,6 @@ function hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString; name::Stri
end
end
#== _read(): Read data, but not type information.
===============================================================================#
@@ -417,7 +415,6 @@ function _read(::Type{Length}, ds::Dataset)
end
_read(::Type{typeof(datetimeformatter)}, ds::Dataset) = datetimeformatter
#== Helper functions for reading in complex data structures
===============================================================================#
@@ -429,7 +426,7 @@ function _read_typed(grp::Group, name::String)
end
#_readstructgeneric: Needs object values to be written out with _write_typed():
function _readstructgeneric(::Type{T}, grp::Group) where T
function _readstructgeneric(::Type{T}, grp::Group) where {T}
vlist = Array{Any}(nothing, fieldcount(T))
for (i, fname) in enumerate(fieldnames(T))
vlist[i] = _read_typed(grp, String(fname))
@@ -454,7 +451,6 @@ function _read(::Type{KW}, grp::Group)
return d
end
#== _read(): More complex structures.
===============================================================================#
@@ -463,7 +459,9 @@ _read(T::Type, grp::Group) = _readstructgeneric(T, grp)
function _read(::Type{Array}, grp::Group) #Array{Any}
sz = _read_size_attr(Array, grp)
if tuple(0) == sz; return []; end
if tuple(0) == sz
return []
end
result = Array{Any}(undef, sz)
lidx = LinearIndices(sz)
@@ -498,7 +496,9 @@ function _read(::Type{DefaultsDict}, grp::Group)
#User should set DefaultsDict.defaults to one of:
# _plot_defaults, _subplot_defaults, _axis_defaults, _series_defaults
path = HDF5.name(ds)
@warn("Cannot yet read DefaultsDict using _read_typed():\n $path\nCannot fully reconstruct plot.")
@warn(
"Cannot yet read DefaultsDict using _read_typed():\n $path\nCannot fully reconstruct plot."
)
end
function _read(::Type{Axis}, grp::Group)
#1st arg appears to be ref to subplots. Seems to work without it.
@@ -510,7 +510,6 @@ function _read(::Type{Subplot}, grp::Group)
return HDF5PLOT_PLOTREF.ref.subplots[idx]
end
#== _read(): Main plot structures
===============================================================================#
@@ -538,7 +537,7 @@ function _read_plot(grp::Group)
n = _read_length_attr(Vector, listgrp)
#Construct new plot, +allocate subplots:
plt = plot(layout=n)
plt = plot(layout = n)
HDF5PLOT_PLOTREF.ref = plt #Used when reading "layout"
agrp = HDF5.open_group(grp, "attr")
@@ -552,17 +551,15 @@ function _read_plot(grp::Group)
return plt
end
function hdf5plot_read(path::AbstractString; name::String="_unnamed")
function hdf5plot_read(path::AbstractString; name::String = "_unnamed")
HDF5.h5open(path, "r") do file
grp = HDF5.open_group(file, h5plotpath("_unnamed"))
return _read_plot(grp)
end
end
end #module _hdf5_implementation
#==Implement Plots.jl backend interface for HDF5Backend
===============================================================================#
@@ -640,7 +637,8 @@ end
#==Interface actually required to use HDF5Backend
===============================================================================#
hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString) = _hdf5_implementation.hdf5plot_write(plt, path)
hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString) =
_hdf5_implementation.hdf5plot_write(plt, path)
hdf5plot_write(path::AbstractString) = _hdf5_implementation.hdf5plot_write(current(), path)
hdf5plot_read(path::AbstractString) = _hdf5_implementation.hdf5plot_read(path)
+146 -107
View File
@@ -44,16 +44,16 @@ _inspectdr_mapcolor(v::Colorant) = v
function _inspectdr_mapcolor(g::PlotUtils.ColorGradient)
@warn("Color gradients are currently unsupported in InspectDR.")
#Pick middle color:
_inspectdr_mapcolor(g.colors[div(1+end,2)])
_inspectdr_mapcolor(g.colors[div(1 + end, 2)])
end
function _inspectdr_mapcolor(v::AVec)
@warn("Vectors of colors are currently unsupported in InspectDR.")
#Pick middle color:
_inspectdr_mapcolor(v[div(1+end,2)])
_inspectdr_mapcolor(v[div(1 + end, 2)])
end
#Hack: suggested point size does not seem adequate relative to plot size, for some reason.
_inspectdr_mapptsize(v) = 1.5*v
_inspectdr_mapptsize(v) = 1.5 * v
function _inspectdr_add_annotations(plot, x, y, val)
#What kind of annotation is this?
@@ -61,14 +61,21 @@ end
#plot::InspectDR.Plot2D
function _inspectdr_add_annotations(plot, x, y, val::PlotText)
vmap = Dict{Symbol, Symbol}(:top=>:t, :bottom=>:b) #:vcenter
hmap = Dict{Symbol, Symbol}(:left=>:l, :right=>:r) #:hcenter
vmap = Dict{Symbol,Symbol}(:top => :t, :bottom => :b) #:vcenter
hmap = Dict{Symbol,Symbol}(:left => :l, :right => :r) #:hcenter
align = Symbol(get(vmap, val.font.valign, :c), get(hmap, val.font.halign, :c))
fnt = InspectDR.Font(val.font.family, val.font.pointsize,
color =_inspectdr_mapcolor(val.font.color)
fnt = InspectDR.Font(
val.font.family,
val.font.pointsize,
color = _inspectdr_mapcolor(val.font.color),
)
ann = InspectDR.atext(val.str, x=x, y=y,
font=fnt, angle=val.font.rotation, align=align
ann = InspectDR.atext(
val.str,
x = x,
y = y,
font = fnt,
angle = val.font.rotation,
align = align,
)
InspectDR.add(plot, ann)
return
@@ -84,7 +91,9 @@ function _inspectdr_getaxisticks(ticks, gridlines, xfrm)
if ticks == :native
#keep current
elseif ttype == :ticks_and_labels
pos = ticks[1]; labels = ticks[2]; nticks = length(ticks[1])
pos = ticks[1]
labels = ticks[2]
nticks = length(ticks[1])
newticks = TickCustom[TickCustom(_xfrm(pos[i]), labels[i]) for i in 1:nticks]
gridlines = InspectDR.GridLinesCustom(gridlines)
gridlines.major = newticks
@@ -129,8 +138,8 @@ end
# ---------------------------------------------------------------------------
function _inspectdr_getscale(s::Symbol, yaxis::Bool)
#TODO: Support :asinh, :sqrt
kwargs = yaxis ? (:tgtmajor=>8, :tgtminor=>2) : () #More grid lines on y-axis
#TODO: Support :asinh, :sqrt
kwargs = yaxis ? (:tgtmajor => 8, :tgtminor => 2) : () #More grid lines on y-axis
if :log2 == s
return InspectDR.AxisScale(:log2; kwargs...)
elseif :log10 == s
@@ -145,13 +154,12 @@ end
# ---------------------------------------------------------------------------
#Glyph used when plotting "Shape"s:
INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
)
INSPECTDR_GLYPH_SHAPE =
InspectDR.GlyphPolyline(2 * InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y)
mutable struct InspecDRPlotRef
mplot::Union{Nothing, InspectDR.Multiplot}
gui::Union{Nothing, InspectDR.GtkPlot}
mplot::Union{Nothing,InspectDR.Multiplot}
gui::Union{Nothing,InspectDR.GtkPlot}
end
_inspectdr_getmplot(::Any) = nothing
@@ -200,7 +208,9 @@ end
function _initialize_subplot(plt::Plot{InspectDRBackend}, sp::Subplot{InspectDRBackend})
plot = sp.o
#Don't do anything without a "subplot" object: Will process later.
if nothing == plot; return; end
if nothing == plot
return
end
plot.data = []
plot.userannot = [] #Clear old markers/text annotation/polyline "annotation"
return plot
@@ -219,7 +229,9 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series)
clims = get_clims(sp, series)
#Don't do anything without a "subplot" object: Will process later.
if nothing == plot; return; end
if nothing == plot
return
end
_vectorize(v) = isa(v, Vector) ? v : collect(v) #InspectDR only supports vectors
x, y = if st == :straightline
@@ -230,30 +242,33 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series)
#No support for polar grid... but can still perform polar transformation:
if ispolar(sp)
Θ = x; r = y
x = r.*cos.(Θ); y = r.*sin.(Θ)
Θ = x
r = y
x = r .* cos.(Θ)
y = r .* sin.(Θ)
end
# doesn't handle mismatched x/y - wrap data (pyplot behaviour):
nx = length(x); ny = length(y)
nx = length(x)
ny = length(y)
if nx < ny
series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
series[:x] = Float64[x[mod1(i, nx)] for i in 1:ny]
elseif ny > nx
series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
series[:y] = Float64[y[mod1(i, ny)] for i in 1:nx]
end
#= TODO: Eventually support
series[:fillcolor] #I think this is fill under line
zorder = series[:series_plotindex]
#= TODO: Eventually support
series[:fillcolor] #I think this is fill under line
zorder = series[:series_plotindex]
For st in :shape:
zorder = series[:series_plotindex],
=#
For st in :shape:
zorder = series[:series_plotindex],
=#
if st in (:shape,)
x, y = shape_data(series)
nmax = 0
for (i,rng) in enumerate(iter_segments(x, y))
for (i, rng) in enumerate(iter_segments(x, y))
nmax = i
if length(rng) > 1
linewidth = series[:linewidth]
@@ -261,11 +276,12 @@ For st in :shape:
linecolor = _inspectdr_mapcolor(_cycle(c, i))
c = plot_color(get_fillcolor(series), get_fillalpha(series))
fillcolor = _inspectdr_mapcolor(_cycle(c, i))
line = InspectDR.line(
style=:solid, width=linewidth, color=linecolor
)
line = InspectDR.line(style = :solid, width = linewidth, color = linecolor)
apline = InspectDR.PolylineAnnotation(
x[rng], y[rng], line=line, fillcolor=fillcolor
x[rng],
y[rng],
line = line,
fillcolor = fillcolor,
)
InspectDR.add(plot, apline)
end
@@ -278,21 +294,24 @@ For st in :shape:
linecolor = _inspectdr_mapcolor(_cycle(c, i))
c = plot_color(get_fillcolor(series), get_fillalpha(series))
fillcolor = _inspectdr_mapcolor(_cycle(c, i))
wfrm = InspectDR.add(plot, Float64[], Float64[], id=series[:label])
wfrm = InspectDR.add(plot, Float64[], Float64[], id = series[:label])
wfrm.line = InspectDR.line(
style=:none, width=linewidth, #linewidth affects glyph
style = :none,
width = linewidth, #linewidth affects glyph
)
wfrm.glyph = InspectDR.glyph(
shape = INSPECTDR_GLYPH_SHAPE, size = 8,
color = linecolor, fillcolor = fillcolor
shape = INSPECTDR_GLYPH_SHAPE,
size = 8,
color = linecolor,
fillcolor = fillcolor,
)
end
elseif st in (:path, :scatter, :straightline) #, :steppre, :stepmid, :steppost)
elseif st in (:path, :scatter, :straightline) #, :steppre, :stepmid, :steppost)
#NOTE: In Plots.jl, :scatter plots have 0-linewidths (I think).
linewidth = series[:linewidth]
#More efficient & allows some support for markerstrokewidth:
_style = (0==linewidth ? :none : series[:linestyle])
wfrm = InspectDR.add(plot, x, y, id=series[:label])
_style = (0 == linewidth ? :none : series[:linestyle])
wfrm = InspectDR.add(plot, x, y, id = series[:label])
wfrm.line = InspectDR.line(
style = _style,
width = series[:linewidth],
@@ -306,14 +325,18 @@ For st in :shape:
wfrm.glyph = InspectDR.glyph(
shape = _inspectdr_mapglyph(series[:markershape]),
size = _inspectdr_mapglyphsize(series[:markersize]),
color = _inspectdr_mapcolor(plot_color(get_markerstrokecolor(series), get_markerstrokealpha(series))),
fillcolor = _inspectdr_mapcolor(plot_color(get_markercolor(series, clims), get_markeralpha(series))),
color = _inspectdr_mapcolor(
plot_color(get_markerstrokecolor(series), get_markerstrokealpha(series)),
),
fillcolor = _inspectdr_mapcolor(
plot_color(get_markercolor(series, clims), get_markeralpha(series)),
),
)
end
# this is all we need to add the series_annotations text
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, x, y)
for (xi, yi, str, fnt) in EachAnn(anns, x, y)
_inspectdr_add_annotations(plot, xi, yi, PlotText(str, fnt))
end
return
@@ -333,66 +356,72 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
plot = sp.o
strip = plot.strips[1] #Only 1 strip supported with Plots.jl
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
xaxis = sp[:xaxis]
yaxis = sp[:yaxis]
xgrid_show = xaxis[:grid]
ygrid_show = yaxis[:grid]
strip.grid = InspectDR.GridRect(
vmajor=xgrid_show, # vminor=xgrid_show,
hmajor=ygrid_show, # hminor=ygrid_show,
vmajor = xgrid_show, # vminor=xgrid_show,
hmajor = ygrid_show, # hminor=ygrid_show,
)
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
xmin, xmax = axis_limits(sp, :x)
ymin, ymax = axis_limits(sp, :y)
if ispolar(sp)
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
rmax = NaNMath.max(abs(ymin), abs(ymax))
xmin, xmax = -rmax, rmax
ymin, ymax = -rmax, rmax
end
plot.xext_full = InspectDR.PExtents1D(xmin, xmax)
strip.yext_full = InspectDR.PExtents1D(ymin, ymax)
#Set current extents = full extents (needed for _eval(strip.grid,...))
plot.xext = plot.xext_full
strip.yext = strip.yext_full
_inspectdr_setticks(sp, plot, strip, xaxis, yaxis)
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
xmin, xmax = axis_limits(sp, :x)
ymin, ymax = axis_limits(sp, :y)
if ispolar(sp)
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
rmax = NaNMath.max(abs(ymin), abs(ymax))
xmin, xmax = -rmax, rmax
ymin, ymax = -rmax, rmax
end
plot.xext_full = InspectDR.PExtents1D(xmin, xmax)
strip.yext_full = InspectDR.PExtents1D(ymin, ymax)
#Set current extents = full extents (needed for _eval(strip.grid,...))
plot.xext = plot.xext_full
strip.yext = strip.yext_full
_inspectdr_setticks(sp, plot, strip, xaxis, yaxis)
a = plot.annotation
a.title = sp[:title]
a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]]
a.title = sp[:title]
a.xlabel = xaxis[:guide]
a.ylabels = [yaxis[:guide]]
#Modify base layout of new object:
l = plot.layout.defaults = deepcopy(InspectDR.defaults.plotlayout)
#IMPORTANT: Must deepcopy to ensure we don't change layouts of other plots.
#Works because plot uses defaults (not user-overwritten `layout.values`)
l.frame_canvas.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
l.frame_data.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
l.frame_data.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
l.font_title = InspectDR.Font(sp[:titlefontfamily],
_inspectdr_mapptsize(sp[:titlefontsize]),
color = _inspectdr_mapcolor(sp[:titlefontcolor])
)
#Cannot independently control fonts of axes with InspectDR:
l.font_axislabel = InspectDR.Font(xaxis[:guidefontfamily],
_inspectdr_mapptsize(xaxis[:guidefontsize]),
color = _inspectdr_mapcolor(xaxis[:guidefontcolor])
)
l.font_ticklabel = InspectDR.Font(xaxis[:tickfontfamily],
_inspectdr_mapptsize(xaxis[:tickfontsize]),
color = _inspectdr_mapcolor(xaxis[:tickfontcolor])
)
l.enable_legend = (sp[:legend] != :none)
#l.halloc_legend = 150 #TODO: compute???
l.font_legend = InspectDR.Font(sp[:legendfontfamily],
_inspectdr_mapptsize(sp[:legendfontsize]),
color = _inspectdr_mapcolor(sp[:legendfontcolor])
)
l.frame_legend.fillcolor = _inspectdr_mapcolor(sp[:background_color_legend])
#IMPORTANT: Must deepcopy to ensure we don't change layouts of other plots.
#Works because plot uses defaults (not user-overwritten `layout.values`)
l.frame_canvas.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
l.frame_data.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
l.frame_data.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
l.font_title = InspectDR.Font(
sp[:titlefontfamily],
_inspectdr_mapptsize(sp[:titlefontsize]),
color = _inspectdr_mapcolor(sp[:titlefontcolor]),
)
#Cannot independently control fonts of axes with InspectDR:
l.font_axislabel = InspectDR.Font(
xaxis[:guidefontfamily],
_inspectdr_mapptsize(xaxis[:guidefontsize]),
color = _inspectdr_mapcolor(xaxis[:guidefontcolor]),
)
l.font_ticklabel = InspectDR.Font(
xaxis[:tickfontfamily],
_inspectdr_mapptsize(xaxis[:tickfontsize]),
color = _inspectdr_mapcolor(xaxis[:tickfontcolor]),
)
l.enable_legend = (sp[:legend] != :none)
#l.halloc_legend = 150 #TODO: compute???
l.font_legend = InspectDR.Font(
sp[:legendfontfamily],
_inspectdr_mapptsize(sp[:legendfontsize]),
color = _inspectdr_mapcolor(sp[:legendfontcolor]),
)
l.frame_legend.fillcolor = _inspectdr_mapcolor(sp[:background_color_legend])
#_round!() ensures values use integer spacings (looks better on screen):
InspectDR._round!(InspectDR.autofit2font!(l, legend_width=10.0)) #10 "em"s wide
#_round!() ensures values use integer spacings (looks better on screen):
InspectDR._round!(InspectDR.autofit2font!(l, legend_width = 10.0)) #10 "em"s wide
return
end
@@ -400,7 +429,9 @@ end
# for the calcs
function _before_layout_calcs(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end
if nothing == mplot
return
end
mplot.title = plt[:plot_title]
if "" == mplot.title
@@ -409,7 +440,7 @@ function _before_layout_calcs(plt::Plot{InspectDRBackend})
end
mplot.layout[:frame].fillcolor = _inspectdr_mapcolor(plt[:background_color_outside])
mplot.layout[:frame] = mplot.layout[:frame] #register changes
mplot.layout[:frame] = mplot.layout[:frame] #register changes
resize!(mplot.subplots, length(plt.subplots))
nsubplots = length(plt.subplots)
for (i, sp) in enumerate(plt.subplots)
@@ -453,17 +484,19 @@ end
# to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot{InspectDRBackend})
plot = sp.o
if !isa(plot, InspectDR.Plot2D); return sp.minpad; end
if !isa(plot, InspectDR.Plot2D)
return sp.minpad
end
#Computing plotbounds with 0-BoundingBox returns required padding:
bb = InspectDR.plotbounds(plot.layout.values, InspectDR.BoundingBox(0,0,0,0))
bb = InspectDR.plotbounds(plot.layout.values, InspectDR.BoundingBox(0, 0, 0, 0))
#NOTE: plotbounds always pads for titles, legends, etc. even if not in use.
#TODO: possibly zero-out items not in use??
# add in the user-specified margin to InspectDR padding:
leftpad = abs(bb.xmin)*px + sp[:left_margin]
toppad = abs(bb.ymin)*px + sp[:top_margin]
rightpad = abs(bb.xmax)*px + sp[:right_margin]
bottompad = abs(bb.ymax)*px + sp[:bottom_margin]
leftpad = abs(bb.xmin) * px + sp[:left_margin]
toppad = abs(bb.ymin) * px + sp[:top_margin]
rightpad = abs(bb.xmax) * px + sp[:right_margin]
bottompad = abs(bb.ymax) * px + sp[:bottom_margin]
sp.minpad = (leftpad, toppad, rightpad, bottompad)
end
@@ -472,21 +505,25 @@ end
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
function _update_plot_object(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end
if nothing == mplot
return
end
mplot.bblist = InspectDR.BoundingBox[]
for (i, sp) in enumerate(plt.subplots)
figw, figh = sp.plt[:size]
pcts = bbox_to_pcts(sp.bbox, figw*px, figh*px)
pcts = bbox_to_pcts(sp.bbox, figw * px, figh * px)
_left, _bottom, _width, _height = pcts
ymax = 1.0-_bottom
ymax = 1.0 - _bottom
ymin = ymax - _height
bb = InspectDR.BoundingBox(_left, _left+_width, ymin, ymax)
bb = InspectDR.BoundingBox(_left, _left + _width, ymin, ymax)
push!(mplot.bblist, bb)
end
gplot = _inspectdr_getgui(plt.o)
if nothing == gplot; return; end
if nothing == gplot
return
end
gplot.src = mplot #Ensure still references current plot
InspectDR.refresh(gplot)
@@ -522,7 +559,9 @@ end
# Display/show the plot (open a GUI window, or browser page, for example).
function _display(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end
if nothing == mplot
return
end
gplot = _inspectdr_getgui(plt.o)
if nothing == gplot
+270 -260
View File
@@ -8,23 +8,11 @@ Base.@kwdef mutable struct PGFPlotsXPlot
function PGFPlotsXPlot(is_created, was_shown, the_plot)
pgfx_plot = new(is_created, was_shown, the_plot)
# tikz libraries
PGFPlotsX.push_preamble!(
pgfx_plot.the_plot,
"\\usetikzlibrary{arrows.meta}",
)
PGFPlotsX.push_preamble!(
pgfx_plot.the_plot,
"\\usetikzlibrary{backgrounds}",
)
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{arrows.meta}")
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{backgrounds}")
# pgfplots libraries
PGFPlotsX.push_preamble!(
pgfx_plot.the_plot,
"\\usepgfplotslibrary{patchplots}",
)
PGFPlotsX.push_preamble!(
pgfx_plot.the_plot,
"\\usepgfplotslibrary{fillbetween}",
)
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{patchplots}")
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{fillbetween}")
# compatibility fixes
# add background layer to standard layers
PGFPlotsX.push_preamble!(
@@ -89,7 +77,7 @@ function Base.push!(pgfx_plot::PGFPlotsXPlot, item)
end
function pgfx_split_extra_opts(extra)
return (get(extra, :add, nothing), filter( x-> first(x) != :add, extra))
return (get(extra, :add, nothing), filter(x -> first(x) != :add, extra))
end
function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
if !pgfx_plot.is_created || pgfx_plot.was_shown
@@ -101,8 +89,9 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
extra_plot = wraptuple(extra_plot)
push!(the_plot, extra_plot...)
end
bgc = plt.attr[:background_color_outside] == :match ?
plt.attr[:background_color] : plt.attr[:background_color_outside]
bgc =
plt.attr[:background_color_outside] == :match ? plt.attr[:background_color] :
plt.attr[:background_color_outside]
if bgc isa Colors.Colorant
cstr = plot_color(bgc)
a = alpha(cstr)
@@ -125,9 +114,14 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
if sp[:subplot_index] == plt[:plot_titleindex]
x = dx + sp_width / 2 - 10mm # FIXME: get rid of magic constant
y = dy + sp_height / 2
pgfx_add_annotation!(the_plot, x, y, PlotText(plt[:plot_title], plottitlefont(plt)), pgfx_thickness_scaling(plt);
pgfx_add_annotation!(
the_plot,
x,
y,
PlotText(plt[:plot_title], plottitlefont(plt)),
pgfx_thickness_scaling(plt);
cs = "",
options = PGFPlotsX.Options("anchor" => "center")
options = PGFPlotsX.Options("anchor" => "center"),
)
continue
end
@@ -152,29 +146,22 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
"point meta min" => get_clims(sp)[1],
"title" => sp[:title],
"title style" => PGFPlotsX.Options(
pgfx_get_title_pos(title_loc)...,
"font" => pgfx_font(
sp[:titlefontsize],
pgfx_thickness_scaling(sp),
),
pgfx_get_title_pos(title_loc)...,
"font" => pgfx_font(sp[:titlefontsize], pgfx_thickness_scaling(sp)),
"color" => title_cstr,
"draw opacity" => title_a,
"rotate" => sp[:titlefontrotation],
),
"legend style" => pgfx_get_legend_style(sp),
"axis background/.style" => PGFPlotsX.Options(
"fill" => bgc_inside,
"opacity" => bgc_inside_a,
),
"axis background/.style" =>
PGFPlotsX.Options("fill" => bgc_inside, "opacity" => bgc_inside_a),
# These are for layouting
"anchor" => "north west",
"xshift" => string(dx),
"yshift" => string(-dy),
)
sp_width > 0 * mm ? push!(axis_opt, "width" => string(axis_width)) :
nothing
sp_height > 0 * mm ? push!(axis_opt, "height" => string(axis_height)) :
nothing
sp_width > 0 * mm ? push!(axis_opt, "width" => string(axis_width)) : nothing
sp_height > 0 * mm ? push!(axis_opt, "height" => string(axis_height)) : nothing
for letter in (:x, :y, :z)
if letter != :z || RecipesPipeline.is3d(sp)
pgfx_axis!(axis_opt, sp, letter)
@@ -216,11 +203,10 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
if hascolorbar(sp)
cticks = get_colorbar_ticks(sp)[2]
colorbar_style = PGFPlotsX.Options(
"title" => sp[:colorbar_title],
)
colorbar_style = PGFPlotsX.Options("title" => sp[:colorbar_title])
if sp[:colorbar] === :top
push!(colorbar_style,
push!(
colorbar_style,
"at" => string((0.5, 1.05)),
"anchor" => "south",
"xtick" => string("{", join(cticks, ","), "}"),
@@ -228,7 +214,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
"xticklabel style" => pgfx_get_colorbar_ticklabel_style(sp),
)
else
push!(colorbar_style,
push!(
colorbar_style,
"ytick" => string("{", join(cticks, ","), "}"),
"yticklabel style" => pgfx_get_colorbar_ticklabel_style(sp),
)
@@ -279,15 +266,19 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
"color" => single_color(opt[:linecolor]),
"name path" => string(series_id),
)
extra_series, extra_series_opt = pgfx_split_extra_opts(series[:extra_kwargs])
extra_series, extra_series_opt =
pgfx_split_extra_opts(series[:extra_kwargs])
series_opt = merge(series_opt, PGFPlotsX.Options(extra_series_opt...))
if RecipesPipeline.is3d(series) || st in (:heatmap, :contour) || (st == :quiver && opt[:z] !== nothing)
if (
RecipesPipeline.is3d(series) ||
st in (:heatmap, :contour) ||
(st == :quiver && opt[:z] !== nothing)
)
series_func = PGFPlotsX.Plot3
else
series_func = PGFPlotsX.Plot
end
if sf !== nothing &&
!isfilledcontour(series) && series[:ribbon] === nothing
if sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing
push!(series_opt, "area legend" => nothing)
end
pgfx_add_series!(Val(st), axis, series_opt, series, series_func, opt)
@@ -337,7 +328,7 @@ end
function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, opt)
# treat segments
segments = collect(series_segments(series, series[:seriestype]; check=true))
segments = collect(series_segments(series, series[:seriestype]; check = true))
sf = opt[:fillrange]
for (k, segment) in enumerate(segments)
i, rng = segment.attr_index, segment.range
@@ -351,10 +342,7 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
scale_factor = 0.00125
mark_size = opt[:markersize] * scale_factor
path = join(
[
"($(x[i] * mark_size), $(y[i] * mark_size))"
for i in eachindex(x)
],
["($(x[i] * mark_size), $(y[i] * mark_size))" for i in eachindex(x)],
" -- ",
)
c = get_markercolor(series, i)
@@ -372,31 +360,26 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
segment_opt = merge(segment_opt, pgfx_marker(opt, i))
end
# add fillrange
if sf !== nothing &&
!isfilledcontour(series)
if sf isa Number || sf isa AVec
pgfx_fillrange_series!(
axis,
series,
series_func,
i,
_cycle(sf, rng),
rng,
)
if sf !== nothing && !isfilledcontour(series)
if sf isa Number || sf isa AVec
pgfx_fillrange_series!(axis, series, series_func, i, _cycle(sf, rng), rng)
elseif sf isa Tuple && series[:ribbon] !== nothing
for sfi in sf
pgfx_fillrange_series!(
axis,
series,
series_func,
i,
_cycle(sfi, rng),
rng,
)
pgfx_fillrange_series!(
axis,
series,
series_func,
i,
_cycle(sfi, rng),
rng,
)
end
end
if k == 1 &&
series[:subplot][:legend] != :none && pgfx_should_add_to_legend(series)
if (
k == 1 &&
series[:subplot][:legend] != :none &&
pgfx_should_add_to_legend(series)
)
pgfx_filllegend!(series_opt, opt)
end
end
@@ -405,46 +388,44 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
if arrow isa Arrow
arrow_opt = merge(
segment_opt,
PGFPlotsX.Options("quiver" => PGFPlotsX.Options(
"u" => "\\thisrow{u}",
"v" => "\\thisrow{v}",
pgfx_arrow(arrow, :head) => nothing,
)
)
PGFPlotsX.Options(
"quiver" => PGFPlotsX.Options(
"u" => "\\thisrow{u}",
"v" => "\\thisrow{v}",
pgfx_arrow(arrow, :head) => nothing,
),
),
)
if arrow.side == :head
x_arrow = opt[:x][rng][end-1:end]
y_arrow = opt[:y][rng][end-1:end]
x_path = opt[:x][rng][1:end-1]
y_path = opt[:y][rng][1:end-1]
x_arrow = opt[:x][rng][(end - 1):end]
y_arrow = opt[:y][rng][(end - 1):end]
x_path = opt[:x][rng][1:(end - 1)]
y_path = opt[:y][rng][1:(end - 1)]
elseif arrow.side == :tail
x_arrow = opt[:x][rng][2:-1:1]
y_arrow = opt[:y][rng][2:-1:1]
x_path = opt[:x][rng][2:end]
y_path = opt[:y][rng][2:end]
elseif arrow.side == :both
x_arrow = opt[:x][rng][[2,1,end-1,end]]
y_arrow = opt[:y][rng][[2,1,end-1,end]]
x_path = opt[:x][rng][2:end-1]
y_path = opt[:y][rng][2:end-1]
x_arrow = opt[:x][rng][[2, 1, end - 1, end]]
y_arrow = opt[:y][rng][[2, 1, end - 1, end]]
x_path = opt[:x][rng][2:(end - 1)]
y_path = opt[:y][rng][2:(end - 1)]
end
coordinates = PGFPlotsX.Table([
:x => x_arrow[1:2:end-1],
:y => y_arrow[1:2:end-1],
:u => [x_arrow[i] - x_arrow[i-1] for i in 2:2:lastindex(x_arrow)],
:v => [y_arrow[i] - y_arrow[i-1] for i in 2:2:lastindex(y_arrow)],
])
arrow_plot =
series_func(merge(series_opt, arrow_opt), coordinates)
:x => x_arrow[1:2:(end - 1)],
:y => y_arrow[1:2:(end - 1)],
:u => [x_arrow[i] - x_arrow[i - 1] for i in 2:2:lastindex(x_arrow)],
:v => [y_arrow[i] - y_arrow[i - 1] for i in 2:2:lastindex(y_arrow)],
])
arrow_plot = series_func(merge(series_opt, arrow_opt), coordinates)
push!(axis, arrow_plot)
coordinates = PGFPlotsX.Table(x_path, y_path)
segment_plot =
series_func(merge(series_opt, segment_opt), coordinates)
segment_plot = series_func(merge(series_opt, segment_opt), coordinates)
push!(axis, segment_plot)
else
coordinates = PGFPlotsX.Table(pgfx_series_arguments(series, opt, rng)...)
segment_plot =
series_func(merge(series_opt, segment_opt), coordinates)
segment_plot = series_func(merge(series_opt, segment_opt), coordinates)
push!(axis, segment_plot)
end
# fill between functions
@@ -466,7 +447,10 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
end # for segments
# get that last marker
if !isnothing(opt[:y]) && !any(isnan, opt[:y]) && opt[:markershape] isa AVec
additional_plot = PGFPlotsX.PlotInc(pgfx_marker(opt, length(segments) + 1), PGFPlotsX.Coordinates(tuple((last(opt[:x]), last(opt[:y])))))
additional_plot = PGFPlotsX.PlotInc(
pgfx_marker(opt, length(segments) + 1),
PGFPlotsX.Coordinates(tuple((last(opt[:x]), last(opt[:y])))),
)
push!(axis, additional_plot)
end
end
@@ -503,9 +487,7 @@ function pgfx_add_series!(::Val{:surface}, axis, series_opt, series, series_func
end
function pgfx_add_series!(::Val{:wireframe}, axis, series_opt, series, series_func, opt)
push!(series_opt, "mesh" => nothing,
"mesh/rows" => length(opt[:x])
)
push!(series_opt, "mesh" => nothing, "mesh/rows" => length(opt[:x]))
pgfx_add_series!(axis, series_opt, series, series_func, opt)
end
@@ -530,12 +512,15 @@ function pgfx_add_series!(::Val{:heatmap}, axis, series_opt, series, series_func
end
function pgfx_add_series!(::Val{:mesh3d}, axis, series_opt, series, series_func, opt)
ptable = join([ string(i, " ", j, " ", k, "\\\\") for (i, j, k) in zip(opt[:connections]...) ], "\n ")
ptable = join(
[string(i, " ", j, " ", k, "\\\\") for (i, j, k) in zip(opt[:connections]...)],
"\n ",
)
push!(
series_opt,
"patch" => nothing,
"table/row sep" => "\\\\",
"patch table" => ptable
"patch table" => ptable,
)
pgfx_add_series!(axis, series_opt, series, series_func, opt)
end
@@ -570,9 +555,10 @@ function pgfx_add_series!(::Val{:contour3d}, axis, series_opt, series, series_fu
series_opt,
"contour prepared" => PGFPlotsX.Options("labels" => opt[:contour_labels]),
)
series_opt = merge( series_opt, pgfx_linestyle(opt) )
series_opt = merge(series_opt, pgfx_linestyle(opt))
args = pgfx_series_arguments(series, opt)
series_plot = series_func(series_opt, PGFPlotsX.Table(Contour.contours(args..., opt[:levels])))
series_plot =
series_func(series_opt, PGFPlotsX.Table(Contour.contours(args..., opt[:levels])))
push!(axis, series_plot)
pgfx_add_legend!(axis, series, opt)
end
@@ -670,7 +656,8 @@ function pgfx_add_legend!(axis, series, opt, i = 1)
return nothing
end
pgfx_series_arguments(series, opt, range) = (arg[range] for arg in pgfx_series_arguments(series, opt))
pgfx_series_arguments(series, opt, range) =
(arg[range] for arg in pgfx_series_arguments(series, opt))
function pgfx_series_arguments(series, opt)
st = series[:seriestype]
return if st in (:contour, :contour3d)
@@ -708,7 +695,7 @@ pgfx_get_marker(k) = get(
none = "none",
cross = "+",
xcross = "x",
+ = "+",
(+) = "+",
x = "x",
utriangle = "triangle*",
dtriangle = "triangle*",
@@ -734,7 +721,7 @@ pgfx_get_xguide_pos(k) = get(
left = "at={(ticklabel* cs:-0.02)}, anchor=east,",
),
k,
"at={(ticklabel cs:0.5)}, anchor=near ticklabel"
"at={(ticklabel cs:0.5)}, anchor=near ticklabel",
)
pgfx_get_yguide_pos(k) = get(
@@ -744,7 +731,7 @@ pgfx_get_yguide_pos(k) = get(
bottom = "at={(ticklabel* cs:-0.02)}, anchor=north,",
),
k,
"at={(ticklabel cs:0.5)}, anchor=near ticklabel"
"at={(ticklabel cs:0.5)}, anchor=near ticklabel",
)
pgfx_get_legend_pos(k) = get(
@@ -769,25 +756,27 @@ pgfx_get_legend_pos(k) = get(
Symbol(k),
("at" => string((1.02, 1)), "anchor" => "north west"),
)
pgfx_get_legend_pos(t::Tuple{S,T}) where {S<:Real,T<:Real} = ("at" => "{$(string(t))}", "anchor" => "north west")
pgfx_get_legend_pos(nt::NamedTuple) = ("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
pgfx_get_legend_pos(theta::Real) = pgfx_get_legend_pos((theta,:inner))
function pgfx_get_legend_pos(v::Tuple{S,Symbol}) where S <: Real
(s,c) = sincosd(v[1])
pgfx_get_legend_pos(t::Tuple{S,T}) where {S<:Real,T<:Real} =
("at" => "{$(string(t))}", "anchor" => "north west")
pgfx_get_legend_pos(nt::NamedTuple) =
("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
pgfx_get_legend_pos(theta::Real) = pgfx_get_legend_pos((theta, :inner))
function pgfx_get_legend_pos(v::Tuple{S,Symbol}) where {S<:Real}
(s, c) = sincosd(v[1])
anchors = [
"south west" "south" "south east";
"west" "center" "east";
"north west" "north" "north east";
]
"south west" "south" "south east"
"west" "center" "east"
"north west" "north" "north east"
]
if v[2] === :inner
rect = (0.07,0.5,1.0,0.07,0.52,1.0)
anchor = anchors[legend_anchor_index(s),legend_anchor_index(c)]
rect = (0.07, 0.5, 1.0, 0.07, 0.52, 1.0)
anchor = anchors[legend_anchor_index(s), legend_anchor_index(c)]
else
rect = (-0.15,0.5,1.05,-0.15,0.52,1.1)
anchor = anchors[4-legend_anchor_index(s),4-legend_anchor_index(c)]
rect = (-0.15, 0.5, 1.05, -0.15, 0.52, 1.1)
anchor = anchors[4 - legend_anchor_index(s), 4 - legend_anchor_index(c)]
end
return ("at"=>"$(string(legend_pos_from_angle(v[1],rect...)))", "anchor"=>anchor)
return ("at" => "$(string(legend_pos_from_angle(v[1],rect...)))", "anchor" => anchor)
end
function pgfx_get_legend_style(sp)
@@ -805,12 +794,15 @@ function pgfx_get_legend_style(sp)
"fill" => cstr,
"fill opacity" => a,
"text opacity" => alpha(plot_color(sp[:legendfontcolor])),
"font" => pgfx_font(
sp[:legendfontsize],
pgfx_thickness_scaling(sp),
),
"font" => pgfx_font(sp[:legendfontsize], pgfx_thickness_scaling(sp)),
"text" => plot_color(sp[:legendfontcolor]),
"cells" => PGFPlotsX.Options("anchor" => get((left = "west", right = "east", hcenter = "center"), legfont.halign, "west")),
"cells" => PGFPlotsX.Options(
"anchor" => get(
(left = "west", right = "east", hcenter = "center"),
legfont.halign,
"west",
),
),
pgfx_get_legend_pos(sp[:legend])...,
)
end
@@ -819,21 +811,23 @@ pgfx_get_colorbar_pos(s) =
get((left = " left", bottom = " horizontal", top = " horizontal"), s, "")
pgfx_get_colorbar_pos(b::Bool) = ""
pgfx_get_title_pos(s) =
get((
pgfx_get_title_pos(s) = get(
(
left = ("at" => "{(0,1)}", "anchor" => "south west"),
right = ("at" => "{(1,1)}", "anchor" => "south east"),
), s,
("at" => "{(0.5,1)}", "anchor" => "south"))
),
s,
("at" => "{(0.5,1)}", "anchor" => "south"),
)
pgfx_get_title_pos(t::Tuple) = ("at" => "{$(string(t))}", "anchor" => "south")
pgfx_get_title_pos(nt::NamedTuple) = ("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
pgfx_get_title_pos(nt::NamedTuple) =
("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
function pgfx_get_ticklabel_style(sp, axis)
cstr = plot_color(axis[:tickfontcolor])
return PGFPlotsX.Options(
"font" => pgfx_font(
axis[:tickfontsize], pgfx_thickness_scaling(sp)
), "color" => cstr,
"font" => pgfx_font(axis[:tickfontsize], pgfx_thickness_scaling(sp)),
"color" => cstr,
"draw opacity" => alpha(cstr),
"rotate" => axis[:tickfontrotation],
)
@@ -842,9 +836,8 @@ end
function pgfx_get_colorbar_ticklabel_style(sp)
cstr = plot_color(sp[:colorbar_tickfontcolor])
return PGFPlotsX.Options(
"font" => pgfx_font(
sp[:colorbar_tickfontsize], pgfx_thickness_scaling(sp)
), "color" => cstr,
"font" => pgfx_font(sp[:colorbar_tickfontsize], pgfx_thickness_scaling(sp)),
"color" => cstr,
"draw opacity" => alpha(cstr),
"rotate" => sp[:colorbar_tickfontrotation],
)
@@ -888,12 +881,20 @@ function pgfx_colormap(v::Vector{<:Colorant})
end, "\n")
end
function pgfx_colormap(cg::ColorGradient)
join(map(1:length(cg)) do i
@sprintf("rgb(%.8f)=(%.8f,%.8f,%.8f)", cg.values[i], red(cg.colors[i]), green(cg.colors[i]), blue(cg.colors[i]))
end, "\n")
join(
map(1:length(cg)) do i
@sprintf(
"rgb(%.8f)=(%.8f,%.8f,%.8f)",
cg.values[i],
red(cg.colors[i]),
green(cg.colors[i]),
blue(cg.colors[i])
)
end,
"\n",
)
end
function pgfx_framestyle(style::Symbol)
if style in (:box, :axes, :origin, :zerolines, :grid, :none)
return style
@@ -950,20 +951,20 @@ end
function pgfx_should_add_to_legend(series::Series)
series.plotattributes[:primary] &&
!(
series.plotattributes[:seriestype] in (
:hexbin,
:bins2d,
:histogram2d,
:hline,
:vline,
:contour,
:contourf,
:contour3d,
:heatmap,
:image,
!(
series.plotattributes[:seriestype] in (
:hexbin,
:bins2d,
:histogram2d,
:hline,
:vline,
:contour,
:contourf,
:contour3d,
:heatmap,
:image,
)
)
)
end
function pgfx_marker(plotattributes, i = 1)
@@ -980,8 +981,9 @@ function pgfx_marker(plotattributes, i = 1)
pgfx_thickness_scaling(plotattributes) *
0.75 *
_cycle(plotattributes[:markersize], i)
mark_freq = !any(isnan, plotattributes[:y]) && plotattributes[:markershape] isa AVec ?
length(plotattributes[:markershape]) : 1
mark_freq =
!any(isnan, plotattributes[:y]) && plotattributes[:markershape] isa AVec ?
length(plotattributes[:markershape]) : 1
return PGFPlotsX.Options(
"mark" => shape isa Shape ? "PlotsShape$i" : pgfx_get_marker(shape),
"mark size" => "$mark_size pt",
@@ -1004,12 +1006,21 @@ function pgfx_marker(plotattributes, i = 1)
else
0
end,
pgfx_get_linestyle(_cycle(plotattributes[:markerstrokestyle], i)) => nothing,
pgfx_get_linestyle(_cycle(plotattributes[:markerstrokestyle], i)) =>
nothing,
),
)
end
function pgfx_add_annotation!(o, x, y, val, thickness_scaling = 1; cs = "axis cs:", options = PGFPlotsX.Options())
function pgfx_add_annotation!(
o,
x,
y,
val,
thickness_scaling = 1;
cs = "axis cs:",
options = PGFPlotsX.Options(),
)
# Construct the style string.
cstr = val.font.color
a = alpha(cstr)
@@ -1017,17 +1028,23 @@ function pgfx_add_annotation!(o, x, y, val, thickness_scaling = 1; cs = "axis cs
o,
join([
"\\node",
sprint(PGFPlotsX.print_tex, merge(
PGFPlotsX.Options(
get((hcenter = "", left = "right", right = "left"), val.font.halign, "") =>
nothing,
"color" => cstr,
"draw opacity" => convert(Float16, a),
"rotate" => val.font.rotation,
"font" => pgfx_font(val.font.pointsize, thickness_scaling),
sprint(
PGFPlotsX.print_tex,
merge(
PGFPlotsX.Options(
get(
(hcenter = "", left = "right", right = "left"),
val.font.halign,
"",
) => nothing,
"color" => cstr,
"draw opacity" => convert(Float16, a),
"rotate" => val.font.rotation,
"font" => pgfx_font(val.font.pointsize, thickness_scaling),
),
options,
),
options
)),
),
string(" at (", cs, x, ",", y, ") {", val.str, "};"),
]),
)
@@ -1097,12 +1114,10 @@ function pgfx_fillrange_series!(axis, series, series_func, i, fillrange, rng)
push!(fillrange_opt, "mark" => "none") # no markers on fillranges
push!(fillrange_opt, "forget plot" => nothing)
opt = series.plotattributes
args = RecipesPipeline.is3d(series) ? (opt[:x][rng], opt[:y][rng], opt[:z][rng]) :
args =
RecipesPipeline.is3d(series) ? (opt[:x][rng], opt[:y][rng], opt[:z][rng]) :
(opt[:x][rng], opt[:y][rng])
push!(
axis,
PGFPlotsX.PlotInc(fillrange_opt, pgfx_fillrange_args(fillrange, args...)),
)
push!(axis, PGFPlotsX.PlotInc(fillrange_opt, pgfx_fillrange_args(fillrange, args...)))
return axis
end
@@ -1132,8 +1147,8 @@ function pgfx_sanitize_string(s::AbstractString)
s = replace(s, r"\\?\{" => "\\{")
s = replace(s, r"\\?\}" => "\\}")
s = map(split(s, "")) do s
isascii(s) ? s : latexify(s)
end |> join
isascii(s) ? s : latexify(s)
end |> join
end
@require LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" begin
using .LaTeXStrings
@@ -1144,60 +1159,64 @@ end
end
end
function pgfx_sanitize_plot!(plt)
for (key, value) in plt.attr
if value isa Union{AbstractString, AbstractVector{<:AbstractString}}
plt.attr[key] = pgfx_sanitize_string.(value)
end
for (key, value) in plt.attr
if value isa Union{AbstractString,AbstractVector{<:AbstractString}}
plt.attr[key] = pgfx_sanitize_string.(value)
end
for subplot in plt.subplots
for (key, value) in subplot.attr
if key == :annotations && subplot.attr[:annotations] !== nothing
old_ann = subplot.attr[key]
for i in eachindex(old_ann)
subplot.attr[key][i] = (old_ann[i][1], old_ann[i][2], pgfx_sanitize_string(old_ann[i][3]))
end
for subplot in plt.subplots
for (key, value) in subplot.attr
if key == :annotations && subplot.attr[:annotations] !== nothing
old_ann = subplot.attr[key]
for i in eachindex(old_ann)
subplot.attr[key][i] =
(old_ann[i][1], old_ann[i][2], pgfx_sanitize_string(old_ann[i][3]))
end
elseif value isa Union{AbstractString,AbstractVector{<:AbstractString}}
subplot.attr[key] = pgfx_sanitize_string.(value)
elseif value isa Axis
for (k, v) in value.plotattributes
if v isa Union{AbstractString,AbstractVector{<:AbstractString}}
value.plotattributes[k] = pgfx_sanitize_string.(v)
end
elseif value isa Union{AbstractString, AbstractVector{<:AbstractString}}
subplot.attr[key] = pgfx_sanitize_string.(value)
end
end
end
for series in plt.series_list
for (key, value) in series.plotattributes
if key == :series_annotations && series.plotattributes[:series_annotations] !== nothing
old_ann = series.plotattributes[key].strs
for i in eachindex(old_ann)
series.plotattributes[key].strs[i] = pgfx_sanitize_string(old_ann[i])
end
elseif value isa Union{AbstractString, AbstractVector{<:AbstractString}}
series.plotattributes[key] = pgfx_sanitize_string.(value)
end
for series in plt.series_list
for (key, value) in series.plotattributes
if key == :series_annotations &&
series.plotattributes[:series_annotations] !== nothing
old_ann = series.plotattributes[key].strs
for i in eachindex(old_ann)
series.plotattributes[key].strs[i] = pgfx_sanitize_string(old_ann[i])
end
elseif value isa Union{AbstractString,AbstractVector{<:AbstractString}}
series.plotattributes[key] = pgfx_sanitize_string.(value)
end
end
##
end
##
end
# --------------------------------------------------------------------------------------
function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
axis = sp[Symbol(letter, :axis)]
axis = sp[get_attr_symbol(letter, :axis)]
# turn off scaled ticks
push!(opt, "scaled $(letter) ticks" => "false", string(letter, :label) => axis[:guide])
tick_color = plot_color(axis[:foreground_color_axis])
push!(
opt,
"scaled $(letter) ticks" => "false",
string(letter, :label) => axis[:guide],
)
tick_color = plot_color(axis[:foreground_color_axis])
push!(opt,
"$(letter) tick style" => PGFPlotsX.Options(
"color" => color(tick_color),
"opacity" => alpha(tick_color),
),
"$(letter) tick style" =>
PGFPlotsX.Options("color" => color(tick_color), "opacity" => alpha(tick_color)),
)
tick_label_color = plot_color(axis[:tickfontcolor])
push!(opt,
push!(
opt,
"$(letter) tick label style" => PGFPlotsX.Options(
"color" => color(tick_color),
"opacity" => alpha(tick_color),
"rotate" => axis[:rotation]
"rotate" => axis[:rotation],
),
)
@@ -1219,7 +1238,13 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
opt,
string(letter, "label style") => PGFPlotsX.Options(
labelpos => nothing,
"font" => pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)),
"at" => string(
"{(ticklabel cs:",
get((left = 0, right = 1), axis[:guidefonthalign], 0.5),
")}",
),
"anchor" => "near ticklabel",
"font" => pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)),
"color" => cstr,
"draw opacity" => α,
"rotate" => axis[:guidefontrotation],
@@ -1233,8 +1258,7 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
scale = axis[:scale]
if scale in (:log2, :ln, :log10)
push!(opt, string(letter, :mode) => "log")
scale == :ln ||
push!(opt, "log basis $letter" => "$(scale == :log2 ? 2 : 10)")
scale == :ln || push!(opt, "log basis $letter" => "$(scale == :log2 ? 2 : 10)")
end
# ticks on or off
@@ -1252,7 +1276,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
end
# limits
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
lims =
ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
axis_limits(sp, letter)
push!(opt, string(letter, :min) => lims[1], string(letter, :max) => lims[2])
@@ -1263,45 +1288,40 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
tick_values =
ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] :
ticks[1]
push!(
opt,
string(letter, "tick") => string("{", join(tick_values, ","), "}"),
)
if axis[:showaxis] &&
axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
push!(opt, string(letter, "tick") => string("{", join(tick_values, ","), "}"))
if axis[:showaxis] && axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
# wrap the power part of label with }
tick_labels = Vector{String}(undef, length(ticks[2]))
tick_labels = similar(ticks[2])
for (i, label) in enumerate(ticks[2])
base, power = split(label, "^")
power = string("{", power, "}")
tick_labels[i] = string(base, "^", power)
end
push!(
opt,
string(letter, "ticklabels") =>
string("{\$", join(tick_labels, "\$,\$"), "\$}"),
)
if tick_labels isa Vector{String}
push!(
opt,
string(letter, "ticklabels") =>
string("{\$", join(tick_labels, "\$,\$"), "\$}"),
)
elseif tick_labels isa Vector{LaTeXString}
push!(opt, string(letter, "ticklabels") => join(tick_labels))
end
elseif axis[:showaxis]
tick_labels =
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] :
ticks[2]
if axis[:formatter] in (:scientific, :auto)
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
if axis[:formatter] in (:scientific, :auto) && tick_labels isa Vector{String}
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
end
push!(
opt,
string(letter, "ticklabels") =>
string("{", join(tick_labels, ","), "}"),
string(letter, "ticklabels") => string("{", join(tick_labels, ","), "}"),
)
else
push!(opt, string(letter, "ticklabels") => "{}")
end
if axis[:tick_direction] === :none
push!(
opt,
string(letter, "tick style") => "draw=none",
)
push!(opt, string(letter, "tick style") => "draw=none")
else
push!(
opt,
@@ -1309,9 +1329,7 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
(axis[:tick_direction] == :out ? "outside" : "inside"),
)
end
push!(
opt, string(letter, "ticklabel style") => pgfx_get_ticklabel_style(sp, axis)
)
push!(opt, string(letter, "ticklabel style") => pgfx_get_ticklabel_style(sp, axis))
push!(
opt,
string(letter, " grid style") => pgfx_linestyle(
@@ -1335,7 +1353,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
minor_ticks
push!(
opt,
string("extra ", letter, " ticks") => string("{", join(minor_ticks, ","), "}"),
string("extra ", letter, " ticks") =>
string("{", join(minor_ticks, ","), "}"),
)
push!(opt, string("extra ", letter, " tick labels") => "")
push!(
@@ -1348,7 +1367,9 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
axis[:minorgridalpha],
axis[:minorgridstyle],
),
"major tick length" => typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 || axis[:minorticks] ? "0.1cm" : "0"
"major tick length" =>
typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ||
axis[:minorticks] ? "0.1cm" : "0",
),
)
end
@@ -1405,7 +1426,8 @@ end
# to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot{PGFPlotsXBackend})
leg = sp[:legend]
if leg in (:best, :outertopright, :outerright, :outerbottomright) || (leg isa Tuple && leg[1] >= 1)
if leg in (:best, :outertopright, :outerright, :outerbottomright) ||
(leg isa Tuple && leg[1] >= 1)
sp.minpad = (0mm, 0mm, 5mm, 0mm)
else
sp.minpad = (0mm, 0mm, 0mm, 0mm)
@@ -1425,33 +1447,21 @@ function _update_plot_object(plt::Plot{PGFPlotsXBackend})
end
for mime in ("application/pdf", "image/svg+xml")
@eval function _show(
io::IO,
mime::MIME{Symbol($mime)},
plt::Plot{PGFPlotsXBackend},
)
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{PGFPlotsXBackend})
plt.o.was_shown = true
show(io, mime, plt.o.the_plot)
end
end
function _show(
io::IO,
mime::MIME{Symbol("image/png")},
plt::Plot{PGFPlotsXBackend},
)
plt.o.was_shown = true
plt_file = tempname() * ".png"
PGFPlotsX.pgfsave(plt_file, plt.o.the_plot; dpi=plt[:dpi])
write(io, read(plt_file))
rm(plt_file; force = true)
end
function _show(io::IO, mime::MIME{Symbol("image/png")}, plt::Plot{PGFPlotsXBackend})
plt.o.was_shown = true
plt_file = tempname() * ".png"
PGFPlotsX.pgfsave(plt_file, plt.o.the_plot; dpi = plt[:dpi])
write(io, read(plt_file))
rm(plt_file; force = true)
end
function _show(
io::IO,
mime::MIME{Symbol("application/x-tex")},
plt::Plot{PGFPlotsXBackend},
)
function _show(io::IO, mime::MIME{Symbol("application/x-tex")}, plt::Plot{PGFPlotsXBackend})
plt.o.was_shown = true
PGFPlotsX.print_tex(
io,
+300 -172
View File
@@ -7,12 +7,13 @@ function _plotly_framestyle(style::Symbol)
return style
else
default_style = get((semi = :box, origin = :zerolines), style, :axes)
@warn("Framestyle :$style is not supported by Plotly and PlotlyJS. :$default_style was chosen instead.")
@warn(
"Framestyle :$style is not supported by Plotly and PlotlyJS. :$default_style was chosen instead."
)
default_style
end
end
# --------------------------------------------------------------------------------------
using UUIDs
@@ -23,7 +24,7 @@ function labelfunc(scale::Symbol, backend::PlotlyBackend)
texfunc = labelfunc_tex(scale)
function (x)
tex_x = texfunc(x)
sup_x = replace( tex_x, r"\^{(.*)}"=>s"<sup>\1</sup>" )
sup_x = replace(tex_x, r"\^{(.*)}" => s"<sup>\1</sup>")
# replace dash with \minus (U+2212)
replace(sup_x, "-" => "")
end
@@ -32,30 +33,25 @@ end
function plotly_font(font::Font, color = font.color)
KW(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
:size => round(Int, font.pointsize * 1.4),
:color => rgba_string(color),
)
end
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
KW(
:text => val,
:xref => xref,
:x => x,
:yref => yref,
:y => y,
:showarrow => false,
)
function plotly_annotation_dict(x, y, val; xref = "paper", yref = "paper")
KW(:text => val, :xref => xref, :x => x, :yref => yref, :y => y, :showarrow => false)
end
function plotly_annotation_dict(x, y, ptxt::PlotText; xref="paper", yref="paper")
merge(plotly_annotation_dict(x, y, ptxt.str; xref=xref, yref=yref), KW(
:font => plotly_font(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
:rotation => -ptxt.font.rotation,
))
function plotly_annotation_dict(x, y, ptxt::PlotText; xref = "paper", yref = "paper")
merge(
plotly_annotation_dict(x, y, ptxt.str; xref = xref, yref = yref),
KW(
:font => plotly_font(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
:rotation => -ptxt.font.rotation,
),
)
end
# function get_annotation_dict_for_arrow(plotattributes::KW, xyprev::Tuple, xy::Tuple, a::Arrow)
@@ -100,9 +96,9 @@ function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
if aspect_ratio == :equal
aspect_ratio = 1.0
end
xmin,xmax = axis_limits(sp, :x)
ymin,ymax = axis_limits(sp, :y)
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
xmin, xmax = axis_limits(sp, :x)
ymin, ymax = axis_limits(sp, :y)
want_ratio = ((xmax - xmin) / (ymax - ymin)) / aspect_ratio
parea_ratio = width(plotarea) / height(plotarea)
if want_ratio > parea_ratio
# need to shrink y
@@ -118,34 +114,34 @@ function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
pcts
end
# this method gets the start/end in percentage of the canvas for this axis direction
function plotly_domain(sp::Subplot)
figw, figh = sp.plt[:size]
pcts = bbox_to_pcts(sp.plotarea, figw*px, figh*px)
pcts = bbox_to_pcts(sp.plotarea, figw * px, figh * px)
pcts = plotly_apply_aspect_ratio(sp, sp.plotarea, pcts)
x_domain = [pcts[1], pcts[1] + pcts[3]]
y_domain = [pcts[2], pcts[2] + pcts[4]]
return x_domain, y_domain
end
function plotly_axis(axis, sp, anchor = nothing, domain = nothing)
letter = axis[:letter]
framestyle = sp[:framestyle]
ax = KW(
:visible => framestyle != :none,
:title => axis[:guide],
:showgrid => axis[:grid],
:gridcolor => rgba_string(plot_color(axis[:foreground_color_grid], axis[:gridalpha])),
:gridwidth => axis[:gridlinewidth],
:zeroline => framestyle == :zerolines,
:visible => framestyle != :none,
:title => axis[:guide],
:showgrid => axis[:grid],
:gridcolor =>
rgba_string(plot_color(axis[:foreground_color_grid], axis[:gridalpha])),
:gridwidth => axis[:gridlinewidth],
:zeroline => framestyle == :zerolines,
:zerolinecolor => rgba_string(axis[:foreground_color_axis]),
:showline => framestyle in (:box, :axes) && axis[:showaxis],
:linecolor => rgba_string(plot_color(axis[:foreground_color_axis])),
:ticks => axis[:tick_direction] === :out ? "outside" :
axis[:tick_direction] === :in ? "inside" : "",
:mirror => framestyle == :box,
:showline => framestyle in (:box, :axes) && axis[:showaxis],
:linecolor => rgba_string(plot_color(axis[:foreground_color_axis])),
:ticks =>
axis[:tick_direction] === :out ? "outside" :
axis[:tick_direction] === :in ? "inside" : "",
:mirror => framestyle == :box,
:showticklabels => axis[:showaxis],
)
if anchor !== nothing
@@ -166,7 +162,9 @@ function plotly_axis(axis, sp, anchor = nothing, domain = nothing)
if !(axis[:ticks] in (nothing, :none, false))
ax[:titlefont] = plotly_font(guidefont(axis))
ax[:tickfont] = plotly_font(tickfont(axis))
ax[:tickcolor] = framestyle in (:zerolines, :grid) || !axis[:showaxis] ? rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
ax[:tickcolor] =
framestyle in (:zerolines, :grid) || !axis[:showaxis] ?
rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
ax[:linecolor] = rgba_string(axis[:foreground_color_axis])
# ticks
@@ -195,10 +193,7 @@ function plotly_axis(axis, sp, anchor = nothing, domain = nothing)
end
function plotly_polaraxis(sp::Subplot, axis::Axis)
ax = KW(
:visible => axis[:showaxis],
:showline => axis[:grid],
)
ax = KW(:visible => axis[:showaxis], :showline => axis[:grid])
if axis[:letter] == :x
ax[:range] = rad2deg.(axis_limits(sp, :x))
@@ -216,7 +211,7 @@ function plotly_layout(plt::Plot)
w, h = plt[:size]
plotattributes_out[:width], plotattributes_out[:height] = w, h
plotattributes_out[:paper_bgcolor] = rgba_string(plt[:background_color_outside])
plotattributes_out[:margin] = KW(:l=>0, :b=>20, :r=>0, :t=>20)
plotattributes_out[:margin] = KW(:l => 0, :b => 20, :r => 0, :t => 20)
plotattributes_out[:annotations] = KW[]
@@ -237,9 +232,12 @@ function plotly_layout(plt::Plot)
else
0.5 * (left(bb) + right(bb))
end
titlex, titley = xy_mm_to_pcts(xmm, top(bbox(sp)), w*px, h*px)
titlex, titley = xy_mm_to_pcts(xmm, top(bbox(sp)), w * px, h * px)
title_font = font(titlefont(sp), :top)
push!(plotattributes_out[:annotations], plotly_annotation_dict(titlex, titley, text(sp[:title], title_font)))
push!(
plotattributes_out[:annotations],
plotly_annotation_dict(titlex, titley, text(sp[:title], title_font)),
)
end
plotattributes_out[:plot_bgcolor] = rgba_string(sp[:background_color_inside])
@@ -248,8 +246,10 @@ function plotly_layout(plt::Plot)
sp[:framestyle] = _plotly_framestyle(sp[:framestyle])
if ispolar(sp)
plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp, sp[:xaxis])
plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp, sp[:yaxis])
plotattributes_out[Symbol("angularaxis$(spidx)")] =
plotly_polaraxis(sp, sp[:xaxis])
plotattributes_out[Symbol("radialaxis$(spidx)")] =
plotly_polaraxis(sp, sp[:yaxis])
else
x_domain, y_domain = plotly_domain(sp)
if RecipesPipeline.is3d(sp)
@@ -264,15 +264,15 @@ function plotly_layout(plt::Plot)
#2.6 multiplier set camera eye such that whole plot can be seen
:camera => KW(
:eye => KW(
:x => cosd(azim)*sind(theta)*2.6,
:y => sind(azim)*sind(theta)*2.6,
:z => cosd(theta)*2.6,
:x => cosd(azim) * sind(theta) * 2.6,
:y => sind(azim) * sind(theta) * 2.6,
:z => cosd(theta) * 2.6,
),
),
)
else
plotattributes_out[Symbol("xaxis$(x_idx)")] =
plotly_axis(sp[:xaxis], sp, string("y", y_idx) , x_domain)
plotly_axis(sp[:xaxis], sp, string("y", y_idx), x_domain)
# don't allow yaxis to be reupdated/reanchored in a linked subplot
if spidx == y_idx
plotattributes_out[Symbol("yaxis$(y_idx)")] =
@@ -286,16 +286,28 @@ function plotly_layout(plt::Plot)
# annotations
for ann in sp[:annotations]
append!(plotattributes_out[:annotations], KW[plotly_annotation_dict(locate_annotation(sp, ann...)...; xref = "x$(x_idx)", yref = "y$(y_idx)")])
append!(
plotattributes_out[:annotations],
KW[plotly_annotation_dict(
locate_annotation(sp, ann...)...;
xref = "x$(x_idx)",
yref = "y$(y_idx)",
)],
)
end
# series_annotations
for series in series_list(sp)
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
push!(plotattributes_out[:annotations], plotly_annotation_dict(
xi,
yi,
PlotText(str,fnt); xref = "x$(x_idx)", yref = "y$(y_idx)")
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
push!(
plotattributes_out[:annotations],
plotly_annotation_dict(
xi,
yi,
PlotText(str, fnt);
xref = "x$(x_idx)",
yref = "y$(y_idx)",
),
)
end
end
@@ -318,26 +330,25 @@ function plotly_layout(plt::Plot)
end
# turn off hover if nothing's using it
if all(series -> series.plotattributes[:hover] in (false,:none), plt.series_list)
if all(series -> series.plotattributes[:hover] in (false, :none), plt.series_list)
plotattributes_out[:hovermode] = "none"
end
plotattributes_out = recursive_merge(plotattributes_out, plt.attr[:extra_plot_kwargs])
end
function plotly_add_legend!(plotattributes_out::KW, sp::Subplot)
plotattributes_out[:showlegend] = sp[:legend] != :none
legend_position = plotly_legend_pos(sp[:legend])
if sp[:legend] != :none
plotattributes_out[:legend] = KW(
:bgcolor => rgba_string(sp[:background_color_legend]),
:bgcolor => rgba_string(sp[:background_color_legend]),
:bordercolor => rgba_string(sp[:foreground_color_legend]),
:borderwidth => 1,
:traceorder => "normal",
:xanchor => legend_position.xanchor,
:yanchor => legend_position.yanchor,
:font => plotly_font(legendfont(sp)),
:font => plotly_font(legendfont(sp)),
:tracegroupgap => 0,
:x => legend_position.coords[1],
:y => legend_position.coords[2],
@@ -361,55 +372,71 @@ function plotly_legend_pos(pos::Symbol)
xouterright = 1.05
xouterleft = -0.15
plotly_legend_position_mapping = (
right = (coords = [1.0, ycenter], xanchor = "right", yanchor = "middle"),
left = (coords = [xleft, ycenter], xanchor = "left", yanchor = "middle"),
top = (coords = [xcenter, ytop], xanchor = "center", yanchor = "top"),
bottom = (coords = [xcenter, ybot], xanchor = "center", yanchor = "bottom"),
bottomleft = (coords = [xleft, ybot], xanchor = "left", yanchor = "bottom"),
right = (coords = [1.0, ycenter], xanchor = "right", yanchor = "middle"),
left = (coords = [xleft, ycenter], xanchor = "left", yanchor = "middle"),
top = (coords = [xcenter, ytop], xanchor = "center", yanchor = "top"),
bottom = (coords = [xcenter, ybot], xanchor = "center", yanchor = "bottom"),
bottomleft = (coords = [xleft, ybot], xanchor = "left", yanchor = "bottom"),
bottomright = (coords = [1.0, ybot], xanchor = "right", yanchor = "bottom"),
topright = (coords = [1.0, 1.0], xanchor = "right", yanchor = "top"),
topleft = (coords = [xleft, 1.0], xanchor = "left", yanchor = "top"),
outertop =(coords = [center, youtertop ], xanchor = "upper", yanchor = "middle"),
outerbottom =(coords = [center, youterbot], xanchor = "lower", yanchor = "middle"),
outerleft =(coords = [xouterleft, center], xanchor = "left", yanchor = "top"),
outerright =(coords = [xouterright, center], xanchor = "right", yanchor = "top"),
outertopleft =(coords = [xouterleft, ytop], xanchor = "upper", yanchor = "left"),
outertopright = (coords = [xouterright, ytop], xanchor = "upper", yanchor = "right"),
outerbottomleft =(coords = [xouterleft, ybot], xanchor = "lower", yanchor = "left"),
outerbottomright =(coords = [xouterright, ybot], xanchor = "lower", yanchor = "right"),
default = (coords = [1.0, 1.0], xanchor = "auto", yanchor = "auto")
topright = (coords = [1.0, 1.0], xanchor = "right", yanchor = "top"),
topleft = (coords = [xleft, 1.0], xanchor = "left", yanchor = "top"),
outertop = (coords = [center, youtertop], xanchor = "upper", yanchor = "middle"),
outerbottom = (coords = [center, youterbot], xanchor = "lower", yanchor = "middle"),
outerleft = (coords = [xouterleft, center], xanchor = "left", yanchor = "top"),
outerright = (coords = [xouterright, center], xanchor = "right", yanchor = "top"),
outertopleft = (coords = [xouterleft, ytop], xanchor = "upper", yanchor = "left"),
outertopright = (
coords = [xouterright, ytop],
xanchor = "upper",
yanchor = "right",
),
outerbottomleft = (
coords = [xouterleft, ybot],
xanchor = "lower",
yanchor = "left",
),
outerbottomright = (
coords = [xouterright, ybot],
xanchor = "lower",
yanchor = "right",
),
default = (coords = [1.0, 1.0], xanchor = "auto", yanchor = "auto"),
)
legend_position = get(plotly_legend_position_mapping, pos, plotly_legend_position_mapping.default)
legend_position =
get(plotly_legend_position_mapping, pos, plotly_legend_position_mapping.default)
end
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real, T<:Real} = (coords=v, xanchor="left", yanchor="top")
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real,T<:Real} =
(coords = v, xanchor = "left", yanchor = "top")
plotly_legend_pos(theta::Real) = plotly_legend_pos((theta, :inner))
function plotly_legend_pos(v::Tuple{S,Symbol}) where S<:Real
(s,c) = sincosd(v[1])
function plotly_legend_pos(v::Tuple{S,Symbol}) where {S<:Real}
(s, c) = sincosd(v[1])
xanchors = ["left", "center", "right"]
yanchors = ["bottom", "middle", "top"]
if v[2] === :inner
rect = (0.07,0.5,1.0,0.07,0.52,1.0)
rect = (0.07, 0.5, 1.0, 0.07, 0.52, 1.0)
xanchor = xanchors[legend_anchor_index(c)]
yanchor = yanchors[legend_anchor_index(s)]
else
rect = (-0.15,0.5,1.05,-0.15,0.52,1.1)
xanchor = xanchors[4-legend_anchor_index(c)]
yanchor = yanchors[4-legend_anchor_index(s)]
rect = (-0.15, 0.5, 1.05, -0.15, 0.52, 1.1)
xanchor = xanchors[4 - legend_anchor_index(c)]
yanchor = yanchors[4 - legend_anchor_index(s)]
end
return (coords=legend_pos_from_angle(v[1],rect...), xanchor=xanchor, yanchor=yanchor)
return (
coords = legend_pos_from_angle(v[1], rect...),
xanchor = xanchor,
yanchor = yanchor,
)
end
function plotly_layout_json(plt::Plot)
JSON.json(plotly_layout(plt), 4)
end
plotly_colorscale(cg::ColorGradient, α = nothing) =
[[v, rgba_string(plot_color(cg.colors[v], α))] for v in cg.values]
function plotly_colorscale(c::AbstractVector{<:Colorant}, α = nothing)
@@ -428,13 +455,12 @@ function plotly_colorscale(cg::PlotUtils.CategoricalColorGradient, α = nothing)
cinds = repeat(1:n, inner = 2)
vinds = vcat((i:(i + 1) for i in 1:n)...)
return [
[cg.values[vinds[i]], rgba_string(plot_color(color_list(cg)[cinds[i]], α))]
for i in eachindex(cinds)
[cg.values[vinds[i]], rgba_string(plot_color(color_list(cg)[cinds[i]], α))] for
i in eachindex(cinds)
]
end
plotly_colorscale(c, α = nothing) = plotly_colorscale(_as_gradient(c), α)
get_plotly_marker(k, def) = get(
(
rect = "square",
@@ -461,12 +487,11 @@ function plotly_link_indicies(plt::Plot, sp::Subplot)
x_idx, y_idx
end
# the Shape contructor will automatically close the shape. since we need it closed,
# we split by NaNs and then construct/destruct the shapes to get the closed coords
function plotly_close_shapes(x, y)
xs, ys = nansplit(x), nansplit(y)
for i=eachindex(xs)
for i in eachindex(xs)
shape = Shape(xs[i], ys[i])
xs[i], ys[i] = coords(shape)
end
@@ -474,12 +499,12 @@ function plotly_close_shapes(x, y)
end
function plotly_data(series::Series, letter::Symbol, data)
axis = series[:subplot][Symbol(letter, :axis)]
axis = series[:subplot][get_attr_symbol(letter, :axis)]
data = if axis[:ticks] == :native && data !== nothing
plotly_native_data(axis, data)
else
data
data
end
if series[:seriestype] in (:heatmap, :contour, :surface, :wireframe, :mesh3d)
@@ -512,7 +537,9 @@ function plotly_convert_to_datetime(x::AbstractArray, formatter::Function)
elseif formatter == timeformatter
map(xi -> string(Dates.Date(Dates.now()), " ", formatter(xi)), x)
else
error("Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter")
error(
"Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter",
)
end
end
#ensures that a gradient is called if a single color is supplied where a gradient is needed (e.g. if a series recipe defines marker_z)
@@ -550,11 +577,12 @@ function plotly_series(plt::Plot, series::Series)
x, y = straightline_data(series, 100)
z = series[:z]
else
x, y, z = series[:x], series[:y], series[:z]
x, y, z = series[:x], series[:y], series[:z]
end
x, y, z = (plotly_data(series, letter, data)
for (letter, data) in zip((:x, :y, :z), (x, y, z))
x, y, z = (
plotly_data(series, letter, data) for
(letter, data) in zip((:x, :y, :z), (x, y, z))
)
plotattributes_out[:name] = series[:label]
@@ -562,7 +590,8 @@ function plotly_series(plt::Plot, series::Series)
isscatter = st in (:scatter, :scatter3d, :scattergl)
hasmarker = isscatter || series[:markershape] != :none
hasline = st in (:path, :path3d, :straightline)
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
hasfillrange =
st in (:path, :scatter, :scattergl, :straightline) &&
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
plotattributes_out[:colorbar] = KW(:title => sp[:colorbar_title])
@@ -580,32 +609,63 @@ function plotly_series(plt::Plot, series::Series)
y = heatmap_edges(y, sp[:yaxis][:scale])
plotattributes_out[:type] = "heatmap"
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
plotattributes_out[:colorscale] =
plotly_colorscale(series[:fillcolor], series[:fillalpha])
plotattributes_out[:showscale] = hascolorbar(sp)
elseif st == :contour
filled = isfilledcontour(series)
plotattributes_out[:type] = "contour"
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
plotattributes_out[:ncontours] = series[:levels] + 2
plotattributes_out[:contours] = KW(:coloring => filled ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
plotattributes_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
plotattributes_out[:contours] = KW(
:coloring => filled ? "fill" : "lines",
:showlabels => series[:contour_labels] == true,
)
# Plotly does not support arbitrary sets of contours
# (https://github.com/plotly/plotly.js/issues/4503)
# so we distinguish AbstractRanges and AbstractVectors
let levels = series[:levels]
if levels isa AbstractRange
plotattributes_out[:contours][:start] = first(levels)
plotattributes_out[:contours][:end] = last(levels)
plotattributes_out[:contours][:size] = step(levels)
elseif levels isa AVec
levels_range =
range(first(levels), stop = last(levels), length = length(levels))
plotattributes_out[:contours][:start] = first(levels_range)
plotattributes_out[:contours][:end] = last(levels_range)
plotattributes_out[:contours][:size] = step(levels_range)
@warn(
"setting arbitrary contour levels with Plotly backend is not supported; " *
"use a range to set equally-spaced contours or an integer to set the " *
"approximate number of contours with the keyword `levels`. " *
"Setting levels to $(levels_range)"
)
elseif levels isa Integer
plotattributes_out[:ncontours] = levels + 2
end
end
plotattributes_out[:colorscale] =
plotly_colorscale(series[:linecolor], series[:linealpha])
plotattributes_out[:showscale] = hascolorbar(sp) && hascolorbar(series)
elseif st in (:surface, :wireframe)
plotattributes_out[:type] = "surface"
plotattributes_out[:type] = "surface"
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
if st == :wireframe
plotattributes_out[:hidesurface] = true
wirelines = KW(
:show => true,
:color => rgba_string(plot_color(series[:linecolor], series[:linealpha])),
:color =>
rgba_string(plot_color(series[:linecolor], series[:linealpha])),
:highlightwidth => series[:linewidth],
)
plotattributes_out[:contours] = KW(:x => wirelines, :y => wirelines, :z => wirelines)
plotattributes_out[:contours] =
KW(:x => wirelines, :y => wirelines, :z => wirelines)
plotattributes_out[:showscale] = false
else
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
plotattributes_out[:colorscale] =
plotly_colorscale(series[:fillcolor], series[:fillalpha])
plotattributes_out[:opacity] = series[:fillalpha]
if series[:fill_z] !== nothing
plotattributes_out[:surfacecolor] = handle_surface(series[:fill_z])
@@ -613,24 +673,34 @@ function plotly_series(plt::Plot, series::Series)
plotattributes_out[:showscale] = hascolorbar(sp)
end
elseif st == :mesh3d
plotattributes_out[:type] = "mesh3d"
plotattributes_out[:type] = "mesh3d"
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
if series[:connections] !== nothing
if typeof(series[:connections]) <: Tuple{Array,Array,Array}
i,j,k = series[:connections]
if !(length(i) == length(j) == length(k))
throw(ArgumentError("Argument connections must consist of equally sized arrays."))
end
plotattributes_out[:i] = i
plotattributes_out[:j] = j
plotattributes_out[:k] = k
else
throw(ArgumentError("Argument connections has to be a tuple of three arrays."))
end
end
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
plotattributes_out[:color] = rgba_string(plot_color(series[:fillcolor], series[:fillalpha]))
if series[:connections] !== nothing
if typeof(series[:connections]) <: Tuple{Array,Array,Array}
i, j, k = series[:connections]
if !(length(i) == length(j) == length(k))
throw(
ArgumentError(
"Argument connections must consist of equally sized arrays.",
),
)
end
plotattributes_out[:i] = i
plotattributes_out[:j] = j
plotattributes_out[:k] = k
else
throw(
ArgumentError(
"Argument connections has to be a tuple of three arrays.",
),
)
end
end
plotattributes_out[:colorscale] =
plotly_colorscale(series[:fillcolor], series[:fillalpha])
plotattributes_out[:color] =
rgba_string(plot_color(series[:fillcolor], series[:fillalpha]))
plotattributes_out[:opacity] = series[:fillalpha]
if series[:fill_z] !== nothing
plotattributes_out[:surfacecolor] = handle_surface(series[:fill_z])
@@ -645,12 +715,25 @@ function plotly_series(plt::Plot, series::Series)
if hasmarker
inds = eachindex(x)
plotattributes_out[:marker] = KW(
:symbol => get_plotly_marker(series[:markershape], string(series[:markershape])),
:symbol =>
get_plotly_marker(series[:markershape], string(series[:markershape])),
# :opacity => series[:markeralpha],
:size => 2 * _cycle(series[:markersize], inds),
:color => rgba_string.(plot_color.(get_markercolor.(series, inds), get_markeralpha.(series, inds))),
:color =>
rgba_string.(
plot_color.(
get_markercolor.(series, inds),
get_markeralpha.(series, inds),
),
),
:line => KW(
:color => rgba_string.(plot_color.(get_markerstrokecolor.(series, inds), get_markerstrokealpha.(series, inds))),
:color =>
rgba_string.(
plot_color.(
get_markerstrokecolor.(series, inds),
get_markerstrokealpha.(series, inds),
),
),
:width => _cycle(series[:markerstrokewidth], inds),
),
)
@@ -663,7 +746,7 @@ function plotly_series(plt::Plot, series::Series)
end
function plotly_series_shapes(plt::Plot, series::Series, clims)
segments = series_segments(series; check=true)
segments = series_segments(series; check = true)
plotattributes_outs = Vector{KW}(undef, length(segments))
# TODO: create a plotattributes_out for each polygon
@@ -678,8 +761,9 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
:legendgroup => series[:label],
)
x, y = (plotly_data(series, letter, data)
for (letter, data) in zip((:x, :y), shape_data(series, 100))
x, y = (
plotly_data(series, letter, data) for
(letter, data) in zip((:x, :y), shape_data(series, 100))
)
for (k, segment) in enumerate(segments)
@@ -687,22 +771,29 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
length(rng) < 2 && continue
# to draw polygons, we actually draw lines with fill
plotattributes_out = merge(plotattributes_base, KW(
:type => "scatter",
:mode => "lines",
:x => vcat(x[rng], x[rng[1]]),
:y => vcat(y[rng], y[rng[1]]),
:fill => "tozeroy",
:fillcolor => rgba_string(plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i))),
))
plotattributes_out = merge(
plotattributes_base,
KW(
:type => "scatter",
:mode => "lines",
:x => vcat(x[rng], x[rng[1]]),
:y => vcat(y[rng], y[rng[1]]),
:fill => "tozeroy",
:fillcolor => rgba_string(
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
),
),
)
if series[:markerstrokewidth] > 0
plotattributes_out[:line] = KW(
:color => rgba_string(plot_color(get_linecolor(series, clims, i), get_linealpha(series, i))),
:color => rgba_string(
plot_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
),
:width => get_linewidth(series, i),
:dash => string(get_linestyle(series, i)),
)
end
plotattributes_out[:showlegend] = k==1 ? should_add_to_legend(series) : false
plotattributes_out[:showlegend] = k == 1 ? should_add_to_legend(series) : false
plotly_polar!(plotattributes_out, series)
plotly_hover!(plotattributes_out, _cycle(series[:hover], i))
plotattributes_outs[k] = plotattributes_out
@@ -712,7 +803,10 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
elseif series[:line_z] !== nothing
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line))
elseif series[:marker_z] !== nothing
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
push!(
plotattributes_outs,
plotly_colorbar_hack(series, plotattributes_base, :marker),
)
end
plotattributes_outs
end
@@ -723,37 +817,46 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
isscatter = st in (:scatter, :scatter3d, :scattergl)
hasmarker = isscatter || series[:markershape] != :none
hasline = st in (:path, :path3d, :straightline)
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
hasfillrange =
st in (:path, :scatter, :scattergl, :straightline) &&
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
segments = collect(series_segments(series, st))
plotattributes_outs = fill(KW(), (hasfillrange ? 2 : 1 ) * length(segments))
plotattributes_outs = fill(KW(), (hasfillrange ? 2 : 1) * length(segments))
needs_scatter_fix = !isscatter && hasmarker && !any(isnan,y) && length(segments) > 1
needs_scatter_fix = !isscatter && hasmarker && !any(isnan, y) && length(segments) > 1
for (k, segment) in enumerate(segments)
i, rng = segment.attr_index, segment.range
plotattributes_out = deepcopy(plotattributes_base)
plotattributes_out[:showlegend] = k==1 ? should_add_to_legend(series) : false
plotattributes_out[:showlegend] = k == 1 ? should_add_to_legend(series) : false
plotattributes_out[:legendgroup] = series[:label]
# set the type
if st in (:path, :scatter, :scattergl, :straightline)
plotattributes_out[:type] = st==:scattergl ? "scattergl" : "scatter"
plotattributes_out[:type] = st == :scattergl ? "scattergl" : "scatter"
plotattributes_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
if series[:fillrange] == true || series[:fillrange] == 0 || isa(series[:fillrange], Tuple)
if series[:fillrange] == true ||
series[:fillrange] == 0 ||
isa(series[:fillrange], Tuple)
plotattributes_out[:fill] = "tozeroy"
plotattributes_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)))
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real}, Real}
plotattributes_out[:fillcolor] = rgba_string(
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
)
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real},Real}
plotattributes_out[:fill] = "tonexty"
plotattributes_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)))
plotattributes_out[:fillcolor] = rgba_string(
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
)
elseif !(series[:fillrange] in (false, nothing))
@warn("fillrange ignored... plotly only supports filling to zero and to a vector of values. fillrange: $(series[:fillrange])")
@warn(
"fillrange ignored... plotly only supports filling to zero and to a vector of values. fillrange: $(series[:fillrange])"
)
end
plotattributes_out[:x], plotattributes_out[:y] = x[rng], y[rng]
@@ -764,20 +867,32 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
else
hasline ? "lines" : "none"
end
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x[rng], y[rng], z[rng]
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] =
x[rng], y[rng], z[rng]
end
# add "marker"
if hasmarker
mcolor = rgba_string(plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i)))
lcolor = rgba_string(plot_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i)))
mcolor = rgba_string(
plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i)),
)
lcolor = rgba_string(
plot_color(
get_markerstrokecolor(series, i),
get_markerstrokealpha(series, i),
),
)
plotattributes_out[:marker] = KW(
:symbol => get_plotly_marker(_cycle(series[:markershape], i), string(_cycle(series[:markershape], i))),
:symbol => get_plotly_marker(
_cycle(series[:markershape], i),
string(_cycle(series[:markershape], i)),
),
# :opacity => needs_scatter_fix ? [1, 0] : 1,
:size => 2 * _cycle(series[:markersize], i),
:color => needs_scatter_fix ? [mcolor, "rgba(0, 0, 0, 0.000)"] : mcolor,
:line => KW(
:color => needs_scatter_fix ? [lcolor, "rgba(0, 0, 0, 0.000)"] : lcolor,
:color =>
needs_scatter_fix ? [lcolor, "rgba(0, 0, 0, 0.000)"] : lcolor,
:width => _cycle(series[:markerstrokewidth], i),
),
)
@@ -786,7 +901,9 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
# add "line"
if hasline
plotattributes_out[:line] = KW(
:color => rgba_string(plot_color(get_linecolor(series, clims, i), get_linealpha(series, i))),
:color => rgba_string(
plot_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
),
:width => get_linewidth(series, i),
:shape => if st == :steppre
"vh"
@@ -813,8 +930,12 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
if typeof(series[:fillrange]) <: Real
series[:fillrange] = fill(series[:fillrange], length(rng))
elseif typeof(series[:fillrange]) <: Tuple
f1 = typeof(series[:fillrange][1]) <: Real ? fill(series[:fillrange][1], length(rng)) : series[:fillrange][1][rng]
f2 = typeof(series[:fillrange][2]) <: Real ? fill(series[:fillrange][2], length(rng)) : series[:fillrange][2][rng]
f1 =
typeof(series[:fillrange][1]) <: Real ?
fill(series[:fillrange][1], length(rng)) : series[:fillrange][1][rng]
f2 =
typeof(series[:fillrange][2]) <: Real ?
fill(series[:fillrange][2], length(rng)) : series[:fillrange][2][rng]
series[:fillrange] = (f1, f2)
end
if isa(series[:fillrange], AbstractVector)
@@ -824,13 +945,15 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
else
# if fillrange is a tuple with upper and lower limit, plotattributes_out_fillrange
# is the series that will do the filling
plotattributes_out_fillrange[:x], plotattributes_out_fillrange[:y] = concatenate_fillrange(x[rng], series[:fillrange])
plotattributes_out_fillrange[:x], plotattributes_out_fillrange[:y] =
concatenate_fillrange(x[rng], series[:fillrange])
plotattributes_out_fillrange[:line][:width] = 0
delete!(plotattributes_out, :fill)
delete!(plotattributes_out, :fillcolor)
end
plotattributes_outs[(2k-1):(2k)] = [plotattributes_out_fillrange, plotattributes_out]
plotattributes_outs[(2k - 1):(2k)] =
[plotattributes_out_fillrange, plotattributes_out]
else
plotattributes_outs[k] = plotattributes_out
end
@@ -841,7 +964,10 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
elseif series[:fill_z] !== nothing
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill))
elseif series[:marker_z] !== nothing
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
push!(
plotattributes_outs,
plotly_colorbar_hack(series, plotattributes_base, :marker),
)
end
plotattributes_outs
@@ -871,7 +997,6 @@ function plotly_colorbar_hack(series::Series, plotattributes_base::KW, sym::Symb
return plotattributes_out
end
function plotly_polar!(plotattributes_out::KW, series::Series)
# convert polar plots x/y to theta/radius
if ispolar(series[:subplot])
@@ -910,11 +1035,15 @@ html_body(plt::Plot{PlotlyBackend}) = plotly_html_body(plt)
function plotly_html_head(plt::Plot)
plotly =
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) : "https://cdn.plot.ly/$(_plotly_min_js_filename)"
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) :
"https://cdn.plot.ly/$(_plotly_min_js_filename)"
include_mathjax = get(plt[:extra_plot_kwargs], :include_mathjax, "")
mathjax_file = include_mathjax != "cdn" ? ("file://" * include_mathjax) : "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"
mathjax_head = include_mathjax == "" ? "" : "<script src=\"$mathjax_file\"></script>\n\t\t"
mathjax_file =
include_mathjax != "cdn" ? ("file://" * include_mathjax) :
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"
mathjax_head =
include_mathjax == "" ? "" : "<script src=\"$mathjax_file\"></script>\n\t\t"
if isijulia()
mathjax_head
@@ -934,8 +1063,9 @@ function plotly_html_body(plt, style = nothing)
if isijulia()
# require.js adds .js automatically
plotly_no_ext =
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) : "https://cdn.plot.ly/$(_plotly_min_js_filename)"
plotly_no_ext = plotly_no_ext[1:end-3]
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) :
"https://cdn.plot.ly/$(_plotly_min_js_filename)"
plotly_no_ext = plotly_no_ext[1:(end - 3)]
requirejs_prefix = """
requirejs.config({
@@ -984,12 +1114,10 @@ function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{Plotly
plotly_show_js(io, plot)
end
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
write(io, embeddable_html(plt))
end
function _display(plt::Plot{PlotlyBackend})
standalone_html_window(plt)
end
+6 -3
View File
@@ -25,16 +25,19 @@ for (mime, fmt) in (
"image/svg+xml" => "svg",
"image/eps" => "eps",
)
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plotlyjs_syncplot(plt), format = $fmt)
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyJSBackend}) =
PlotlyJS.savefig(io, plotlyjs_syncplot(plt), format = $fmt)
end
# Use the Plotly implementation for json and html:
_show(io::IO, mime::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend}) = plotly_show_js(io, plt)
_show(io::IO, mime::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend}) =
plotly_show_js(io, plt)
html_head(plt::Plot{PlotlyJSBackend}) = plotly_html_head(plt)
html_body(plt::Plot{PlotlyJSBackend}) = plotly_html_body(plt)
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = write(io, embeddable_html(plt))
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) =
write(io, embeddable_html(plt))
_display(plt::Plot{PlotlyJSBackend}) = display(plotlyjs_syncplot(plt))
+451 -253
View File
File diff suppressed because it is too large Load Diff
+158 -103
View File
@@ -1,36 +1,30 @@
# https://github.com/Evizero/UnicodePlots.jl
# https://github.com/JuliaPlots/UnicodePlots.jl
# don't warn on unsupported... there's just too many warnings!!
warn_on_unsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing
# --------------------------------------------------------------------------------------
function _canvas_map()
(
braille = UnicodePlots.BrailleCanvas,
ascii = UnicodePlots.AsciiCanvas,
block = UnicodePlots.BlockCanvas,
dot = UnicodePlots.DotCanvas,
density = UnicodePlots.DensityCanvas,
)
end
_canvas_map() = (
ascii = UnicodePlots.AsciiCanvas,
block = UnicodePlots.BlockCanvas,
braille = UnicodePlots.BrailleCanvas,
density = UnicodePlots.DensityCanvas,
dot = UnicodePlots.DotCanvas,
heatmap = UnicodePlots.HeatmapCanvas,
lookup = UnicodePlots.LookupCanvas,
)
# 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, width, height)
plt.o = []
function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend})
plt.o = UnicodePlots.Plot[]
canvas_map = _canvas_map()
for sp in plt.subplots
xaxis = sp[:xaxis]
yaxis = sp[:yaxis]
xlim = axis_limits(sp, :x)
ylim = axis_limits(sp, :y)
# make vectors
xlim = [xlim[1], xlim[2]]
ylim = [ylim[1], ylim[2]]
xlim = collect(axis_limits(sp, :x))
ylim = collect(axis_limits(sp, :y))
# we set x/y to have a single point, since we need to create the plot with some data.
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
@@ -38,108 +32,124 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
ct = _canvas_type[]
canvas_type = if ct == :auto
isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
canvas_type = if (ct = _canvas_type[]) == :auto
isijulia() ? :ascii : :braille
else
_canvas_map()[ct]
ct
end
# special handling for spy
if length(sp.series_list) == 1
series = sp.series_list[1]
if series[:seriestype] == :spy
push!(plt.o, UnicodePlots.spy(
series[:z].surf,
width = width,
height = height,
title = sp[:title],
canvas = canvas_type
))
continue
end
end
# # 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,
kw = (
title = sp[:title],
xlim = xlim,
ylim = ylim,
border = isijulia() ? :ascii : :solid
border = isijulia() ? :ascii : :solid,
xlabel = xaxis[:guide],
ylabel = yaxis[:guide],
xscale = xaxis[:scale],
yscale = yaxis[:scale],
)
# set the axis labels
UnicodePlots.xlabel!(o, xaxis[:guide])
UnicodePlots.ylabel!(o, yaxis[:guide])
# now use the ! functions to add to the plot
o = UnicodePlots.Plot(x, y, canvas_map[canvas_type]; kw...)
for series in series_list(sp)
addUnicodeSeries!(o, series.plotattributes, sp[:legend] != :none, xlim, ylim)
o = addUnicodeSeries!(sp, o, kw, series, sp[:legend] != :none)
end
# save the object
push!(plt.o, o)
for ann in sp[:annotations]
x, y, val = locate_annotation(sp, ann...)
o = UnicodePlots.annotate!(
o, x, y, val.str;
color = up_color(val.font.color), halign = val.font.halign, valign = val.font.valign
)
end
push!(plt.o, o) # save the object
end
end
function up_color(col)
if typeof(col) <: UnicodePlots.UserColorType
color = col
elseif typeof(col) <: RGBA
col = convert(ARGB32, col)
color = map(Int, (red(col).i, green(col).i, blue(col).i))
else
color = :auto
end
color
end
# add a single series
function addUnicodeSeries!(o, plotattributes, addlegend::Bool, xlim, ylim)
# get the function, or special handling for step/bar/hist
st = plotattributes[:seriestype]
if st == :histogram2d
UnicodePlots.densityplot!(o, plotattributes[:x], plotattributes[:y])
return
end
if st in (:path, :straightline)
func = UnicodePlots.lineplot!
elseif st == :scatter || plotattributes[: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
function addUnicodeSeries!(
sp::Subplot{UnicodePlotsBackend},
up::UnicodePlots.Plot,
kw, series, addlegend::Bool,
)
st = series[:seriestype]
# get the series data and label
x, y = if st == :straightline
straightline_data(plotattributes)
straightline_data(series)
elseif st == :shape
shape_data(plotattributes)
shape_data(series)
else
[collect(float(plotattributes[s])) for s in (:x, :y)]
float(series[:x]), float(series[:y])
end
label = addlegend ? plotattributes[:label] : ""
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
color = plotattributes[:linecolor] in UnicodePlots.color_cycle ? plotattributes[:linecolor] : :auto
# special handling (src/interface)
if st == :histogram2d
kw[:xlim][:] .= kw[:ylim][:] .= 0
return UnicodePlots.densityplot(x, y; kw...)
elseif st == :heatmap
rng = range(0, 1, length = length(UnicodePlots.COLOR_MAP_DATA[:viridis]))
cmap = [(red(c), green(c), blue(c)) for c in get(get_colorgradient(series), rng)]
return UnicodePlots.heatmap(
series[:z].surf;
zlabel = sp[:colorbar_title],
colormap = cmap,
kw...
)
elseif st == :spy
return UnicodePlots.spy(series[:z].surf; kw...)
end
# add the series
x, y = RecipesPipeline.unzip(collect(Base.Iterators.filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
func(o, x, y; color = color, name = label)
# now use the ! functions to add to the plot
if st in (:path, :straightline, :shape)
func = UnicodePlots.lineplot!
elseif st == :scatter || series[:markershape] != :none
func = UnicodePlots.scatterplot!
else
error("Series type $st not supported by UnicodePlots")
end
label = addlegend ? series[:label] : ""
for (n, segment) in enumerate(series_segments(series, st; check = true))
i, rng = segment.attr_index, segment.range
lc = get_linecolor(series, i)
up = func(up, x[rng], y[rng]; color = up_color(lc), name = n == 1 ? label : "")
end
for (xi, yi, str, fnt) in EachAnn(series[:series_annotations], x, y)
up = UnicodePlots.annotate!(
up, xi, yi, str;
color = up_color(fnt.color), halign = fnt.halign, valign = fnt.valign
)
end
return up
end
# -------------------------------
# since this is such a hack, it's only callable using `png`... should error during normal `show`
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
function png(plt::Plot{UnicodePlotsBackend}, fn::AbstractString)
fn = addExtension(fn, "png")
# make some whitespace and show the plot
println("\n\n\n\n\n\n")
gui(plt)
# @osx_only begin
@static if Sys.isapple()
# make some whitespace and show the plot
println("\n\n\n\n\n\n")
gui(plt)
# BEGIN HACK
# wait while the plot gets drawn
@@ -151,30 +161,75 @@ function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
# END HACK (phew)
return
elseif Sys.islinux()
run(`clear`)
gui(plt)
run(`import -window $(ENV["WINDOWID"]) $fn`)
return
end
error("Can only savepng on osx with UnicodePlots (though even then I wouldn't do it)")
error(
"Can only savepng on MacOS or Linux with UnicodePlots (though even then I wouldn't do it)",
)
end
# -------------------------------
# we don't do very much for subplots... just stack them vertically
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
Base.show(plt::Plot{UnicodePlotsBackend}) = _show(stdout, MIME("text/plain"), plt)
function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
unicodeplots_rebuild(plt)
foreach(x -> show(io, x), plt.o)
nr, nc = size(plt.layout)
lines_colored = Array{Union{Nothing,Vector{String}}}(undef, nr, nc)
lines_uncolored = copy(lines_colored)
l_max = zeros(Int, nr)
buf = IOBuffer()
cbuf = IOContext(buf, :color => true)
sps = wmax = 0
for r in 1:nr
lmax = 0
for c in 1:nc
l = plt.layout[r, c]
if l isa GridLayout && size(l) != (1, 1)
@error "UnicodePlots: complex nested layout is currently unsupported !"
else
if get(l.attr, :blank, false)
lines_colored[r, c] = lines_uncolored[r, c] = nothing
else
sp = plt.o[sps += 1]
show(cbuf, sp)
colored = String(take!(buf))
uncolored = replace(colored, r"\x1B\[[0-9;]*[a-zA-Z]" => "")
lines_colored[r, c] = lc = split(colored, "\n")
lines_uncolored[r, c] = lu = split(uncolored, "\n")
lmax = max(length(lc), lmax)
wmax = max(maximum(length.(lu)), wmax)
end
end
end
l_max[r] = lmax
end
empty = ' '^wmax
for r in 1:nr
for n in 1:l_max[r]
for c in 1:nc
pre = c == 1 ? '\0' : ' '
lc = lines_colored[r, c]
if lc === nothing || length(lc) < n
print(io, pre, empty)
else
lu = lines_uncolored[r, c]
print(io, pre, lc[n], ' '^(wmax - length(lu[n])))
end
end
println(io)
end
r < nr && println(io)
end
nothing
end
function _display(plt::Plot{UnicodePlotsBackend})
unicodeplots_rebuild(plt)
map(show, plt.o)
map(display, plt.o)
nothing
end
+9 -3
View File
@@ -3,7 +3,10 @@
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr, :window_title, "Plots.jl"))
function standalone_html(
plt::AbstractPlot;
title::AbstractString = get(plt.attr, :window_title, "Plots.jl"),
)
"""
<!DOCTYPE html>
<html>
@@ -49,7 +52,8 @@ function standalone_html_window(plt::AbstractPlot)
old = use_local_dependencies[] # save state to restore afterwards
# if we open a browser ourself, we can host local files, so
# when we have a local plotly downloaded this is the way to go!
use_local_dependencies[] = plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
use_local_dependencies[] =
plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
filename = write_temp_html(plt)
open_browser_window(filename)
# restore for other backends
@@ -58,7 +62,9 @@ end
# uses wkhtmltopdf/wkhtmltoimage: http://wkhtmltopdf.org/downloads.html
function html_to_png(html_fn, png_fn, w, h)
run(`wkhtmltoimage -f png -q --width $w --height $h --disable-smart-width $html_fn $png_fn`)
run(
`wkhtmltoimage -f png -q --width $w --height $h --disable-smart-width $html_fn $png_fn`,
)
end
function show_png_from_html(io::IO, plt::AbstractPlot)
+17 -10
View File
@@ -1,10 +1,11 @@
# These functions return an operator for use in `get_clims(::Seres, op)`
process_clims(lims::Tuple{<:Number,<:Number}) = (zlims -> ifelse.(isfinite.(lims), lims, zlims)) ignorenan_extrema
process_clims(lims::Tuple{<:Number,<:Number}) =
(zlims -> ifelse.(isfinite.(lims), lims, zlims)) ignorenan_extrema
process_clims(s::Union{Symbol,Nothing,Missing}) = ignorenan_extrema
# don't specialize on ::Function otherwise python functions won't work
process_clims(f) = f
function get_clims(sp::Subplot, op=process_clims(sp[:clims]))
function get_clims(sp::Subplot, op = process_clims(sp[:clims]))
zmin, zmax = Inf, -Inf
for series in series_list(sp)
if series[:colorbar_entry]
@@ -14,7 +15,7 @@ function get_clims(sp::Subplot, op=process_clims(sp[:clims]))
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
end
function get_clims(sp::Subplot, series::Series, op=process_clims(sp[:clims]))
function get_clims(sp::Subplot, series::Series, op = process_clims(sp[:clims]))
zmin, zmax = if series[:colorbar_entry]
get_clims(sp, op)
else
@@ -30,13 +31,18 @@ Finds the limits for the colorbar by taking the "z-values" for the series and pa
which must return the tuple `(zmin, zmax)`. The default op is the extrema of the finite
values of the input.
"""
function get_clims(series::Series, op=ignorenan_extrema)
function get_clims(series::Series, op = ignorenan_extrema)
zmin, zmax = Inf, -Inf
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin)
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing, Real})
for vals in (
series[:seriestype] in z_colored_series ? series[:z] : nothing,
series[:line_z],
series[:marker_z],
series[:fill_z],
)
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing,Real})
zmin, zmax = _update_clims(zmin, zmax, op(vals.surf)...)
elseif (vals !== nothing) && (eltype(vals) <: Union{Missing, Real})
elseif (vals !== nothing) && (eltype(vals) <: Union{Missing,Real})
zmin, zmax = _update_clims(zmin, zmax, op(vals)...)
end
end
@@ -60,8 +66,8 @@ function colorbar_style(series::Series)
cbar_fill
elseif iscontour(series)
cbar_lines
elseif series[:seriestype] (:heatmap,:surface) ||
any(series[z] !== nothing for z [:marker_z,:line_z,:fill_z])
elseif series[:seriestype] (:heatmap, :surface) ||
any(series[z] !== nothing for z in [:marker_z, :line_z, :fill_z])
cbar_gradient
else
nothing
@@ -69,7 +75,8 @@ function colorbar_style(series::Series)
end
hascolorbar(series::Series) = colorbar_style(series) !== nothing
hascolorbar(sp::Subplot) = sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp))
hascolorbar(sp::Subplot) =
sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp))
function get_colorbar_ticks(sp::Subplot; update = true)
if update || !haskey(sp.attr, :colorbar_optimized_ticks)
+331 -342
View File
@@ -1,15 +1,18 @@
const P2 = GeometryBasics.Point2{Float64}
const P3 = GeometryBasics.Point3{Float64}
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
nanpush!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); push!(a, b))
nanappend!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); append!(a, b))
const _haligns = :hcenter, :left, :right
const _valigns = :vcenter, :top, :bottom
nanpush!(a::AVec{P2}, b) = (push!(a, P2(NaN, NaN)); push!(a, b))
nanappend!(a::AVec{P2}, b) = (push!(a, P2(NaN, NaN)); append!(a, b))
nanpush!(a::AVec{P3}, b) = (push!(a, P3(NaN, NaN, NaN)); push!(a, b))
nanappend!(a::AVec{P3}, b) = (push!(a, P3(NaN, NaN, NaN)); append!(a, b))
compute_angle(v::P2) = (angle = atan(v[2], v[1]); angle < 0 ? 2π - angle : angle)
# -------------------------------------------------------------
struct Shape{X<:Number, Y<:Number}
struct Shape{X<:Number,Y<:Number}
x::Vector{X}
y::Vector{Y}
# function Shape(x::AVec, y::AVec)
@@ -38,9 +41,7 @@ vertices(shape::Shape) = collect(zip(shape.x, shape.y))
@deprecate shape_coords coords
"return the vertex points from a Shape or Segments object"
function coords(shape::Shape)
shape.x, shape.y
end
coords(shape::Shape) = shape.x, shape.y
#coords(shapes::AVec{Shape}) = unzip(map(coords, shapes))
function coords(shapes::AVec{<:Shape})
@@ -51,98 +52,102 @@ function coords(shapes::AVec{<:Shape})
end
"get an array of tuples of points on a circle with radius `r`"
function partialcircle(start_θ, end_θ, n = 20, r=1)
[(r*cos(u), r*sin(u)) for u in range(start_θ, stop=end_θ, length=n)]
end
partialcircle(start_θ, end_θ, n = 20, r = 1) =
[(r * cos(u), r * sin(u)) for u in range(start_θ, stop = end_θ, length = n)]
"interleave 2 vectors into each other (like a zipper's teeth)"
function weave(x,y; ordering = Vector[x,y])
ret = eltype(x)[]
done = false
while !done
for o in ordering
try
push!(ret, popfirst!(o))
catch
end
function weave(x, y; ordering = Vector[x, y])
ret = eltype(x)[]
done = false
while !done
for o in ordering
try
push!(ret, popfirst!(o))
catch
end
end
done = isempty(x) && isempty(y)
end
done = isempty(x) && isempty(y)
end
ret
ret
end
"create a star by weaving together points from an outer and inner circle. `n` is the number of arms"
function makestar(n; offset = -0.5, radius = 1.0)
z1 = offset * π
z2 = z1 + π / (n)
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
outercircle = partialcircle(z1, z1 + 2π, n + 1, radius)
innercircle = partialcircle(z2, z2 + 2π, n + 1, 0.4radius)
Shape(weave(outercircle, innercircle))
end
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
function makeshape(n; offset = -0.5, radius = 1.0)
z = offset * π
Shape(partialcircle(z, z + 2π, n+1, radius))
end
makeshape(n; offset = -0.5, radius = 1.0) =
Shape(partialcircle(offset * π, offset * π + 2π, n + 1, radius))
function makecross(; offset = -0.5, radius = 1.0)
z2 = offset * π
z1 = z2 - π/8
z1 = z2 - π / 8
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
Shape(weave(outercircle, innercircle,
ordering=Vector[outercircle,innercircle,outercircle]))
Shape(
weave(
outercircle,
innercircle,
ordering = Vector[outercircle, innercircle, outercircle],
),
)
end
from_polar(angle, dist) = P2(dist*cos(angle), dist*sin(angle))
from_polar(angle, dist) = P2(dist * cos(angle), dist * sin(angle))
function makearrowhead(angle; h = 2.0, w = 0.4)
tip = from_polar(angle, h)
Shape(P2[(0,0), from_polar(angle - 0.5π, w) - tip,
from_polar(angle + 0.5π, w) - tip, (0,0)])
end
makearrowhead(angle; h = 2.0, w = 0.4, tip = from_polar(angle, h)) = Shape(
P2[
(0, 0),
from_polar(angle - 0.5π, w) - tip,
from_polar(angle + 0.5π, w) - tip,
(0, 0),
],
)
const _shapes = KW(
:circle => makeshape(20),
:rect => makeshape(4, offset=-0.25),
:diamond => makeshape(4),
:utriangle => makeshape(3, offset=0.5),
:dtriangle => makeshape(3, offset=-0.5),
:rtriangle => makeshape(3, offset=0.0),
:ltriangle => makeshape(3, offset=1.0),
:pentagon => makeshape(5),
:hexagon => makeshape(6),
:heptagon => makeshape(7),
:octagon => makeshape(8),
:cross => makecross(offset=-0.25),
:xcross => makecross(),
:vline => Shape([(0,1),(0,-1)]),
:hline => Shape([(1,0),(-1,0)]),
)
:rect => makeshape(4, offset = -0.25),
:diamond => makeshape(4),
:utriangle => makeshape(3, offset = 0.5),
:dtriangle => makeshape(3, offset = -0.5),
:rtriangle => makeshape(3, offset = 0.0),
:ltriangle => makeshape(3, offset = 1.0),
:pentagon => makeshape(5),
:hexagon => makeshape(6),
:heptagon => makeshape(7),
:octagon => makeshape(8),
:cross => makecross(offset = -0.25),
:xcross => makecross(),
:vline => Shape([(0, 1), (0, -1)]),
:hline => Shape([(1, 0), (-1, 0)]),
)
for n in [4,5,6,7,8]
_shapes[Symbol("star$n")] = makestar(n)
for n in 4:8
_shapes[Symbol("star$n")] = makestar(n)
end
Shape(k::Symbol) = deepcopy(_shapes[k])
# -----------------------------------------------------------------------
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
"return the centroid of a Shape"
function center(shape::Shape)
x, y = coords(shape)
n = length(x)
A, Cx, Cy = 0.0, 0.0, 0.0
for i=1:n
ip1 = i==n ? 1 : i+1
A, Cx, Cy = 0, 0, 0
for i in 1:n
ip1 = i == n ? 1 : i + 1
A += x[i] * y[ip1] - x[ip1] * y[i]
end
A *= 0.5
for i=1:n
ip1 = i==n ? 1 : i+1
for i in 1:n
ip1 = i == n ? 1 : i + 1
m = (x[i] * y[ip1] - x[ip1] * y[i])
Cx += (x[i] + x[ip1]) * m
Cy += (y[i] + y[ip1]) * m
@@ -153,52 +158,42 @@ end
function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
sx, sy = coords(shape)
cx, cy = c
for i=eachindex(sx)
for i in eachindex(sx)
sx[i] = (sx[i] - cx) * x + cx
sy[i] = (sy[i] - cy) * y + cy
end
shape
end
function scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
shapecopy = deepcopy(shape)
scale!(shapecopy, x, y, c)
end
scale(shape::Shape, x::Real, y::Real = x, c = center(shape)) =
scale!(deepcopy(shape), x, y, c)
"translate a Shape in space"
function translate!(shape::Shape, x::Real, y::Real = x)
sx, sy = coords(shape)
for i=eachindex(sx)
for i in eachindex(sx)
sx[i] += x
sy[i] += y
end
shape
end
function translate(shape::Shape, x::Real, y::Real = x)
shapecopy = deepcopy(shape)
translate!(shapecopy, x, y)
end
translate(shape::Shape, x::Real, y::Real = x) = translate!(deepcopy(shape), x, y)
function rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
(x - centerx) * cos(Θ) - (y - centery) * sin(Θ) + centerx
end
rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real) =
((x - centerx) * cos(Θ) - (y - centery) * sin(Θ) + centerx)
function rotate_y(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
(y - centery) * cos(Θ) + (x - centerx) * sin(Θ) + centery
end
rotate_y(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real) =
((y - centery) * cos(Θ) + (x - centerx) * sin(Θ) + centery)
function rotate(x::Real, y::Real, θ::Real, c = center(shape))
cx, cy = c
rotate_x(x, y, Θ, cx, cy), rotate_y(x, y, Θ, cx, cy)
end
rotate(x::Real, y::Real, θ::Real, c = center(shape)) =
(rotate_x(x, y, Θ, c...), rotate_y(x, y, Θ, c...))
function rotate!(shape::Shape, Θ::Real, c = center(shape))
x, y = coords(shape)
cx, cy = c
for i=eachindex(x)
xi = rotate_x(x[i], y[i], Θ, cx, cy)
yi = rotate_y(x[i], y[i], Θ, cx, cy)
for i in eachindex(x)
xi = rotate_x(x[i], y[i], Θ, c...)
yi = rotate_y(x[i], y[i], Θ, c...)
x[i], y[i] = xi, yi
end
shape
@@ -207,22 +202,20 @@ end
"rotate an object in space"
function rotate(shape::Shape, θ::Real, c = center(shape))
x, y = coords(shape)
cx, cy = c
x_new = rotate_x.(x, y, θ, cx, cy)
y_new = rotate_y.(x, y, θ, cx, cy)
x_new = rotate_x.(x, y, θ, c...)
y_new = rotate_y.(x, y, θ, c...)
Shape(x_new, y_new)
end
# -----------------------------------------------------------------------
mutable struct Font
family::AbstractString
pointsize::Int
halign::Symbol
valign::Symbol
rotation::Float64
color::Colorant
family::AbstractString
pointsize::Int
halign::Symbol
valign::Symbol
rotation::Float64
color::Colorant
end
"""
@@ -239,80 +232,75 @@ arguments (which are distinguished by type/value) or as keyword arguments.
# Examples
```julia-repl
julia> font(8)
julia> font(family="serif",halign=:center,rotation=45.0)
julia> font(family="serif", halign=:center, rotation=45.0)
```
"""
function font(args...;kw...)
function font(args...; kw...)
# defaults
family = "sans-serif"
pointsize = 14
halign = :hcenter
valign = :vcenter
rotation = 0
color = colorant"black"
# defaults
family = "sans-serif"
pointsize = 14
halign = :hcenter
valign = :vcenter
rotation = 0.0
color = colorant"black"
for arg in args
T = typeof(arg)
for arg in args
T = typeof(arg)
if T == Font
family = arg.family
pointsize = arg.pointsize
halign = arg.halign
valign = arg.valign
rotation = arg.rotation
color = arg.color
elseif arg == :center
halign = :hcenter
valign = :vcenter
elseif arg in (:hcenter, :left, :right)
halign = arg
elseif arg in (:vcenter, :top, :bottom)
valign = arg
elseif T <: Colorant
color = arg
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
catch
family = string(arg)
end
elseif typeof(arg) <: Integer
pointsize = arg
elseif typeof(arg) <: Real
rotation = convert(Float64, arg)
else
@warn("Unused font arg: $arg ($(typeof(arg)))")
if T == Font
family = arg.family
pointsize = arg.pointsize
halign = arg.halign
valign = arg.valign
rotation = arg.rotation
color = arg.color
elseif arg == :center
halign = :hcenter
valign = :vcenter
elseif arg _haligns
halign = arg
elseif arg _valigns
valign = arg
elseif T <: Colorant
color = arg
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
catch
family = string(arg)
end
elseif typeof(arg) <: Integer
pointsize = arg
elseif typeof(arg) <: Real
rotation = convert(Float64, arg)
else
@warn "Unused font arg: $arg ($(typeof(arg)))"
end
end
end
for symbol in keys(kw)
if symbol == :family
family = string(kw[:family])
elseif symbol == :pointsize
pointsize = kw[:pointsize]
elseif symbol == :halign
halign = kw[:halign]
if halign == :center
halign = :hcenter
end
@assert halign in (:hcenter, :left, :right)
elseif symbol == :valign
valign = kw[:valign]
if valign == :center
valign = :vcenter
end
@assert valign in (:vcenter, :top, :bottom)
elseif symbol == :rotation
rotation = kw[:rotation]
elseif symbol == :color
color = parse(Colorant, kw[:color])
else
@warn("Unused font kwarg: $symbol")
for sym in keys(kw)
if sym == :family
family = string(kw[sym])
elseif sym == :pointsize
pointsize = kw[sym]
elseif sym == :halign
halign = kw[sym]
halign == :center && (halign = :hcenter)
@assert halign _haligns
elseif sym == :valign
valign = kw[sym]
valign == :center && (valign = :vcenter)
@assert valign _valigns
elseif sym == :rotation
rotation = kw[sym]
elseif sym == :color
color = parse(Colorant, kw[sym])
else
@warn "Unused font kwarg: $sym"
end
end
end
Font(family, pointsize, halign, valign, rotation, color)
Font(family, pointsize, halign, valign, rotation, color)
end
function scalefontsize(k::Symbol, factor::Number)
@@ -327,13 +315,13 @@ end
Scales all **current** font sizes by `factor`. For example `scalefontsizes(1.1)` increases all current font sizes by 10%. To reset to initial sizes, use `scalefontsizes()`
"""
function scalefontsizes(factor::Number)
for k in (:titlefontsize, :legendfontsize, :legendtitlefontsize)
for k in keys(merge(_initial_plt_fontsizes, _initial_sp_fontsizes))
scalefontsize(k, factor)
end
for letter in (:x,:y,:z)
for k in (:guidefontsize, :tickfontsize)
scalefontsize(Symbol(letter, k), factor)
for letter in (:x, :y, :z)
for k in keys(_initial_ax_fontsizes)
scalefontsize(get_attr_symbol(letter, k), factor)
end
end
end
@@ -344,20 +332,20 @@ end
Resets font sizes to initial default values.
"""
function scalefontsizes()
for k in (:titlefontsize, :legendfontsize, :legendtitlefontsize)
for k in keys(merge(_initial_plt_fontsizes, _initial_sp_fontsizes))
f = default(k)
if k in keys(_initial_fontsizes)
factor = f / _initial_fontsizes[k]
scalefontsize(k, 1.0/factor)
scalefontsize(k, 1.0 / factor)
end
end
for letter in (:x,:y,:z)
for k in (:guidefontsize, :tickfontsize)
for letter in (:x, :y, :z)
for k in keys(_initial_ax_fontsizes)
if k in keys(_initial_fontsizes)
f = default(Symbol(letter, k))
f = default(get_attr_symbol(letter, k))
factor = f / _initial_fontsizes[k]
scalefontsize(Symbol(letter, k), 1.0/factor)
scalefontsize(get_attr_symbol(letter, k), 1.0 / factor)
end
end
end
@@ -367,8 +355,8 @@ resetfontsizes() = scalefontsizes()
"Wrap a string with font info"
struct PlotText
str::AbstractString
font::Font
str::AbstractString
font::Font
end
PlotText(str) = PlotText(string(str), font())
@@ -381,21 +369,17 @@ Create a PlotText object wrapping a string with font info, for plot annotations.
text(t::PlotText) = t
text(t::PlotText, font::Font) = PlotText(t.str, font)
text(str::AbstractString, f::Font) = PlotText(str, f)
function text(str, args...;kw...)
PlotText(string(str), font(args...;kw...))
end
text(str, args...; kw...) = PlotText(string(str), font(args...; kw...))
Base.length(t::PlotText) = length(t.str)
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------
struct Stroke
width
color
alpha
style
width
color
alpha
style
end
"""
@@ -404,137 +388,141 @@ end
Define the properties of the stroke used in plotting lines
"""
function stroke(args...; alpha = nothing)
width = 1
color = :black
style = :solid
width = 1
color = :black
style = :solid
for arg in args
T = typeof(arg)
for arg in args
T = typeof(arg)
# if arg in _allStyles
if allStyles(arg)
style = arg
elseif T <: Colorant
color = arg
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
catch
end
elseif allAlphas(arg)
alpha = arg
elseif allReals(arg)
width = arg
else
@warn("Unused stroke arg: $arg ($(typeof(arg)))")
# if arg in _allStyles
if allStyles(arg)
style = arg
elseif T <: Colorant
color = arg
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
catch
end
elseif allAlphas(arg)
alpha = arg
elseif allReals(arg)
width = arg
else
@warn "Unused stroke arg: $arg ($(typeof(arg)))"
end
end
end
Stroke(width, color, alpha, style)
Stroke(width, color, alpha, style)
end
struct Brush
size # fillrange, markersize, or any other sizey attribute
color
alpha
size # fillrange, markersize, or any other sizey attribute
color
alpha
end
function brush(args...; alpha = nothing)
size = 1
color = :black
size = 1
color = :black
for arg in args
T = typeof(arg)
for arg in args
T = typeof(arg)
if T <: Colorant
color = arg
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
catch
end
elseif allAlphas(arg)
alpha = arg
elseif allReals(arg)
size = arg
else
@warn("Unused brush arg: $arg ($(typeof(arg)))")
if T <: Colorant
color = arg
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
catch
end
elseif allAlphas(arg)
alpha = arg
elseif allReals(arg)
size = arg
else
@warn "Unused brush arg: $arg ($(typeof(arg)))"
end
end
end
Brush(size, color, alpha)
Brush(size, color, alpha)
end
# -----------------------------------------------------------------------
mutable struct SeriesAnnotations
strs::AbstractVector # the labels/names
strs::AVec # the labels/names
font::Font
baseshape::Union{Shape, AbstractVector{Shape}, Nothing}
baseshape::Union{Shape,AVec{Shape},Nothing}
scalefactor::Tuple
end
_text_label(lab::Tuple, font) = text(lab[1], font, lab[2:end]...)
_text_label(lab::PlotText, font) = lab
_text_label(lab, font) = text(lab, font)
series_annotations(anns::AMat) = map(series_annotations, anns)
series_annotations(scalar) = series_annotations([scalar])
function series_annotations(anns::AMat)
map(series_annotations, anns)
end
function series_annotations(strs::AbstractVector, args...)
series_annotations(anns::SeriesAnnotations) = anns
series_annotations(::Nothing) = nothing
function series_annotations(strs::AVec, args...)
fnt = font()
shp = nothing
scalefactor = (1,1)
scalefactor = 1, 1
for arg in args
if isa(arg, Shape) || (isa(arg, AbstractVector) && eltype(arg) == Shape)
if isa(arg, Shape) || (isa(arg, AVec) && eltype(arg) == Shape)
shp = arg
elseif isa(arg, Font)
fnt = arg
elseif isa(arg, Symbol) && haskey(_shapes, arg)
shp = _shapes[arg]
elseif isa(arg, Number)
scalefactor = (arg,arg)
scalefactor = arg, arg
elseif is_2tuple(arg)
scalefactor = arg
elseif isa(arg, AVec)
strs = collect(zip(strs, arg))
else
@warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
@warn "Unused SeriesAnnotations arg: $arg ($(typeof(arg)))"
end
end
# if scalefactor != 1
# for s in get(shp)
# scale!(s, scalefactor, scalefactor, (0,0))
# scale!(s, scalefactor, scalefactor, (0, 0))
# end
# end
SeriesAnnotations(strs, fnt, shp, scalefactor)
SeriesAnnotations([_text_label(s, fnt) for s in strs], fnt, shp, scalefactor)
end
series_annotations(anns::SeriesAnnotations) = anns
series_annotations(::Nothing) = nothing
function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
anns = series[:series_annotations]
# msw,msh = anns.scalefactor
# msw, msh = anns.scalefactor
# ms = series[:markersize]
# msw,msh = if isa(ms, AbstractVector)
# 1,1
# msw, msh = if isa(ms, AVec)
# 1, 1
# elseif is_2tuple(ms)
# ms
# else
# ms,ms
# ms, ms
# end
# @show msw msh
if anns !== nothing && anns.baseshape !== nothing
# we use baseshape to overwrite the markershape attribute
# with a list of custom shapes for each
msw,msh = anns.scalefactor
msw, msh = anns.scalefactor
msize = Float64[]
shapes = Vector{Shape}(undef, length(anns.strs))
for i in eachindex(anns.strs)
str = _cycle(anns.strs,i)
str = _cycle(anns.strs, i)
# get the width and height of the string (in mm)
sw, sh = text_size(str, anns.font.pointsize)
# how much to scale the base shape?
# note: it's a rough assumption that the shape fills the unit box [-1,-1,1,1],
# note: it's a rough assumption that the shape fills the unit box [-1, -1, 1, 1],
# so we scale the length-2 shape by 1/2 the total length
scalar = (backend() == PyPlotBackend() ? 1.7 : 1.0)
xscale = 0.5to_pixels(sw) * scalar
@@ -545,7 +533,8 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
maxscale = max(xscale, yscale)
push!(msize, maxscale)
baseshape = _cycle(anns.baseshape, i)
shapes[i] = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
shapes[i] =
scale(baseshape, msw * xscale / maxscale, msh * yscale / maxscale, (0, 0))
end
series[:markershape] = shapes
series[:markersize] = msize
@@ -561,110 +550,120 @@ end
function Base.iterate(ea::EachAnn, i = 1)
if ea.anns === nothing || isempty(ea.anns.strs) || i > length(ea.y)
return nothing
return
end
tmp = _cycle(ea.anns.strs,i)
str,fnt = if isa(tmp, PlotText)
tmp = _cycle(ea.anns.strs, i)
str, fnt = if isa(tmp, PlotText)
tmp.str, tmp.font
else
tmp, ea.anns.font
end
((_cycle(ea.x,i), _cycle(ea.y,i), str, fnt), i+1)
((_cycle(ea.x, i), _cycle(ea.y, i), str, fnt), i + 1)
end
annotations(::Nothing) = []
annotations(anns::AVec) = anns
# -----------------------------------------------------------------------
annotations(anns::AMat) = map(annotations, anns)
annotations(anns) = Any[anns]
annotations(sa::SeriesAnnotations) = sa
annotations(anns::AVec) = anns
annotations(anns) = Any[anns]
annotations(::Nothing) = []
# Expand arrays of coordinates, positions and labels into induvidual annotations
_annotationfont(sp::Subplot) = Plots.font(;
family = sp[:annotationfontfamily],
pointsize = sp[:annotationfontsize],
halign = sp[:annotationhalign],
valign = sp[:annotationvalign],
rotation = sp[:annotationrotation],
color = sp[:annotationcolor],
)
_annotation(sp::Subplot, font, lab, pos...; alphabet = "abcdefghijklmnopqrstuvwxyz") = (
pos...,
lab == :auto ? text("($(alphabet[sp[:subplot_index]]))", font) : _text_label(lab, font),
)
# Expand arrays of coordinates, positions and labels into individual annotations
# and make sure labels are of type PlotText
function process_annotation(sp::Subplot, xs, ys, labs, font = nothing)
function process_annotation(sp::Subplot, xs, ys, labs, font = _annotationfont(sp))
anns = []
labs = makevec(labs)
xlength = length(methods(length, (typeof(xs),))) == 0 ? 1 : length(xs)
ylength = length(methods(length, (typeof(ys),))) == 0 ? 1 : length(ys)
if isnothing(font)
font = Plots.font(;
family=sp[:annotationfontfamily],
pointsize=sp[:annotationfontsize],
halign=sp[:annotationhalign],
valign=sp[:annotationvalign],
rotation=sp[:annotationrotation],
color=sp[:annotationcolor],
)
end
for i in 1:max(xlength, ylength, length(labs))
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
x = typeof(x) <: TimeType ? Dates.value(x) : x
y = typeof(y) <: TimeType ? Dates.value(y) : y
if lab == :auto
alphabet = "abcdefghijklmnopqrstuvwxyz"
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
else
push!(anns, (x, y, isa(lab, PlotText) ? lab : isa(lab, Tuple) ? text(lab[1], font, lab[2:end]...) : text(lab, font)))
end
end
anns
end
function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol}, labs, font = nothing)
anns = []
positions, labs = makevec(positions), makevec(labs)
if isnothing(font)
font = Plots.font(;
family=sp[:annotationfontfamily],
pointsize=sp[:annotationfontsize],
halign=sp[:annotationhalign],
valign=sp[:annotationvalign],
rotation=sp[:annotationrotation],
color=sp[:annotationcolor],
)
end
for i in 1:max(length(positions), length(labs))
pos, lab = _cycle(positions, i), _cycle(labs, i)
pos = get(_positionAliases, pos, pos)
if lab == :auto
alphabet = "abcdefghijklmnopqrstuvwxyz"
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
else
push!(anns, (pos, isa(lab, PlotText) ? lab : isa(lab, Tuple) ? text(lab[1], font, lab[2:end]...) : text(lab, font)))
end
push!(anns, _annotation(sp, font, lab, x, y))
end
anns
end
function process_any_label(lab, font=Font())
lab isa Tuple ? text(lab...) : text( lab, font )
function process_annotation(
sp::Subplot,
positions::Union{AVec{Symbol},Symbol,Tuple},
labs,
font = _annotationfont(sp),
)
anns = []
positions, labs = makevec(positions), makevec(labs)
for i in 1:max(length(positions), length(labs))
pos, lab = _cycle(positions, i), _cycle(labs, i)
push!(anns, _annotation(sp, font, lab, get(_positionAliases, pos, pos)))
end
anns
end
_relative_position(xmin, xmax, pos::Length{:pct}) = xmin + pos.value * (xmax - xmin)
# Give each annotation coordinates based on specified position
function locate_annotation(sp::Subplot, pos::Symbol, lab::PlotText)
position_multiplier = Dict{Symbol, Tuple{Float64,Float64}}(
:topleft => (0.1, 0.9),
:topcenter => (0.5, 0.9),
:topright => (0.9, 0.9),
:bottomleft => (0.1, 0.1),
:bottomcenter => (0.5, 0.1),
:bottomright => (0.9, 0.1),
function locate_annotation(
sp::Subplot,
pos::Symbol,
label::PlotText;
position_multiplier = Dict{Symbol,Tuple{Float64,Float64}}(
:topleft => (0.1pct, 0.9pct),
:topcenter => (0.5pct, 0.9pct),
:topright => (0.9pct, 0.9pct),
:bottomleft => (0.1pct, 0.1pct),
:bottomcenter => (0.5pct, 0.1pct),
:bottomright => (0.9pct, 0.1pct),
),
)
x, y = position_multiplier[pos]
(
_relative_position(axis_limits(sp, :x)..., x),
_relative_position(axis_limits(sp, :y)..., y),
label,
)
xmin, xmax = ignorenan_extrema(sp[:xaxis])
ymin, ymax = ignorenan_extrema(sp[:yaxis])
x, y = (xmin, ymin).+ position_multiplier[pos].* (xmax - xmin, ymax - ymin)
(x, y, lab)
end
locate_annotation(sp::Subplot, x, y, label::PlotText) = (x, y, label)
locate_annotation(sp::Subplot, x, y, z, label::PlotText) = (x, y, z, label)
locate_annotation(sp::Subplot, rel::NTuple{2,<:Number}, label::PlotText) = (
_relative_position(axis_limits(sp, :x)..., rel[1] * Plots.pct),
_relative_position(axis_limits(sp, :y)..., rel[2] * Plots.pct),
label,
)
locate_annotation(sp::Subplot, rel::NTuple{3,<:Number}, label::PlotText) = (
_relative_position(axis_limits(sp, :x)..., rel[1] * Plots.pct),
_relative_position(axis_limits(sp, :y)..., rel[2] * Plots.pct),
_relative_position(axis_limits(sp, :z)..., rel[3] * Plots.pct),
label,
)
# -----------------------------------------------------------------------
"type which represents z-values for colors and sizes (and anything else that might come up)"
struct ZValues
values::Vector{Float64}
zrange::Tuple{Float64,Float64}
values::Vector{Float64}
zrange::Tuple{Float64,Float64}
end
function zvalues(values::AVec{T}, zrange::Tuple{T,T} = (ignorenan_minimum(values), ignorenan_maximum(values))) where T<:Real
ZValues(collect(float(values)), map(Float64, zrange))
function zvalues(
values::AVec{T},
zrange::Tuple{T,T} = (ignorenan_minimum(values), ignorenan_maximum(values)),
) where {T<:Real}
ZValues(collect(float(values)), map(Float64, zrange))
end
# -----------------------------------------------------------------------
@@ -682,13 +681,10 @@ struct SurfaceFunction <: AbstractSurface
f::Function
end
# -----------------------------------------------------------------------
# # I don't want to clash with ValidatedNumerics, but this would be nice:
# ..(a::T, b::T) = (a,b)
# ..(a::T, b::T) = (a, b)
# -----------------------------------------------------------------------
@@ -709,8 +705,7 @@ Define arrowheads to apply to lines - args are `style` (`:open` or `:closed`),
function arrow(args...)
style = :simple
side = :head
headlength = 0.3
headwidth = 0.3
headlength = headwidth = 0.3
setlength = false
for arg in args
T = typeof(arg)
@@ -730,21 +725,20 @@ function arrow(args...)
elseif T <: Tuple && length(arg) == 2
headlength, headwidth = Float64(arg[1]), Float64(arg[2])
else
@warn("Skipped arrow arg $arg")
@warn "Skipped arrow arg $arg"
end
end
Arrow(style, side, headlength, headwidth)
end
# allow for do-block notation which gets called on every valid start/end pair which
# we need to draw an arrow
function add_arrows(func::Function, x::AVec, y::AVec)
for i=2:length(x)
xyprev = (x[i-1], y[i-1])
for i in 2:length(x)
xyprev = (x[i - 1], y[i - 1])
xy = (x[i], y[i])
if ok(xyprev) && ok(xy)
if i==length(x) || !ok(x[i+1], y[i+1])
if i == length(x) || !ok(x[i + 1], y[i + 1])
# add the arrow from xyprev to xy
func(xyprev, xy)
end
@@ -754,32 +748,27 @@ end
# -----------------------------------------------------------------------
"create a BezierCurve for plotting"
mutable struct BezierCurve{T <: GeometryBasics.Point}
mutable struct BezierCurve{T<:GeometryBasics.Point}
control_points::Vector{T}
end
function (bc::BezierCurve)(t::Real)
p = zero(P2)
n = length(bc.control_points)-1
n = length(bc.control_points) - 1
for i in 0:n
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
p += bc.control_points[i + 1] * binomial(n, i) * (1 - t)^(n - i) * t^i
end
p
end
@deprecate curve_points coords
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, Base.range(first(range), stop=last(range), length=n))
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
# may create a loop if necessary. Assumes the view is [0,1]
function directed_curve(args...; kw...)
error("directed_curve has been moved to PlotRecipes")
end
coords(curve::BezierCurve, n::Integer = 30; range = [0, 1]) =
map(curve, Base.range(first(range), stop = last(range), length = n))
function extrema_plus_buffer(v, buffmult = 0.2)
vmin,vmax = ignorenan_extrema(v)
vdiff = vmax-vmin
vmin, vmax = ignorenan_extrema(v)
vdiff = vmax - vmin
buffer = vdiff * buffmult
vmin - buffer, vmax + buffer
end
+342 -308
View File
@@ -28,35 +28,33 @@ const _examples = PlotExample[
to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the
animation.
""",
[:(
begin
p = plot([sin, cos], zeros(0), leg = false, xlims = (0, 2π), ylims = (-1, 1))
anim = Animation()
for x in range(0, stop = 2π, length = 20)
push!(p, x, Float64[sin(x), cos(x)])
frame(anim)
[
:(
begin
p = plot(
[sin, cos],
zeros(0),
leg = false,
xlims = (0, 2π),
ylims = (-1, 1),
)
anim = Animation()
for x in range(0, stop = 2π, length = 20)
push!(p, x, Float64[sin(x), cos(x)])
frame(anim)
end
end
end
)],
),
],
),
PlotExample( # 3
"Parametric plots",
"Plot function pair (x(u), y(u)).",
[
:(
[:(
begin
plot(
sin,
x -> sin(2x),
0,
2π,
line = 4,
leg = false,
fill = (0, :orange),
)
plot(sin, x -> sin(2x), 0, 2π, line = 4, leg = false, fill = (0, :orange))
end
),
],
)],
),
PlotExample( # 4
"Colors",
@@ -120,7 +118,6 @@ const _examples = PlotExample[
),
],
),
PlotExample( # 6
"Images",
"Plot an image. y-axis is set to flipped",
@@ -128,8 +125,10 @@ const _examples = PlotExample[
:(
begin
import FileIO
path =
download("http://juliaplots.org/PlotReferenceImages.jl/Plots/pyplot/0.7.0/ref1.png")
import Downloads
path = Downloads.download(
"http://juliaplots.org/PlotReferenceImages.jl/Plots/pyplot/0.7.0/ref1.png",
)
img = FileIO.load(path)
plot(img)
end
@@ -190,12 +189,11 @@ const _examples = PlotExample[
PlotExample( # 11
"Line types",
"",
[
:(
[:(
begin
linetypes = [:path :steppre :steppost :sticks :scatter]
n = length(linetypes)
x = Vector[sort(rand(20)) for i = 1:n]
x = Vector[sort(rand(20)) for i in 1:n]
y = rand(20, n)
plot(
x,
@@ -205,8 +203,7 @@ const _examples = PlotExample[
ms = 15,
)
end
),
],
)],
),
PlotExample( # 12
"Line styles",
@@ -237,10 +234,8 @@ const _examples = PlotExample[
[
:(
begin
markers = filter(
m -> m in Plots.supported_markers(),
Plots._shape_keys,
)
markers =
filter(m -> m in Plots.supported_markers(), Plots._shape_keys)
markers = permutedims(markers)
n = length(markers)
x = range(0, stop = 10, length = n + 2)[2:(end - 1)]
@@ -271,17 +266,11 @@ const _examples = PlotExample[
PlotExample( # 15
"Histogram",
"",
[
:(
[:(
begin
histogram(
randn(1000),
bins = :scott,
weights = repeat(1:5, outer = 200),
)
histogram(randn(1000), bins = :scott, weights = repeat(1:5, outer = 200))
end
),
],
)],
),
PlotExample( # 16
"Subplots",
@@ -327,15 +316,13 @@ const _examples = PlotExample[
PlotExample( # 18
"",
"",
[
:(
begin
using Random
Random.seed!(111)
plot!(Plots.fakedata(100, 10))
end
)
]
[:(
begin
using Random
Random.seed!(111)
plot!(Plots.fakedata(100, 10))
end
)],
),
PlotExample( # 19
"Open/High/Low/Close",
@@ -358,8 +345,7 @@ const _examples = PlotExample[
bot[i] + hgt[i],
bot[i],
closepct[i] * hgt[i] + bot[i],
)
for i = 1:n
) for i in 1:n
]
ohlc(y)
end
@@ -370,10 +356,12 @@ const _examples = PlotExample[
"Annotations",
"""
The `annotations` keyword is used for text annotations in data-coordinates. Pass in a
tuple `(x, y, text)`, or a vector of annotations, each of which is a tuple of `x`, `y`
and `text`.
`text` may be a simple `String`, or a `PlotText` object, which can be
built with the method `text(string, attrs...)`.
tuple `(x, y, text)`, a vector of annotations, each of which is a tuple of `x`, `y`
and `text`. You can position annotations using relative coordinates with the syntax
`((px, py), text)`, where for example `px=.25` positions the annotation at `25%` of
the subplot's axis width.
`text` may be a simple `String`, or a `PlotText` object, which can be built with the
method `text(string, attrs...)`.
This wraps font and color attributes and allows you to set text styling.
`text` may also be a tuple `(string, attrs...)` of arguments which are passed
to `Plots.text`.
@@ -395,11 +383,7 @@ const _examples = PlotExample[
)
annotate!([
(5, y[5], ("this is #5", 16, :red, :center)),
(
10,
y[10],
("this is #10", :right, 20, "courier"),
),
(10, y[10], ("this is #10", :right, 20, "courier")),
])
scatter!(
range(2, stop = 8, length = 6),
@@ -578,8 +562,8 @@ const _examples = PlotExample[
"",
[:(
begin
xs = [string("x", i) for i = 1:10]
ys = [string("y", i) for i = 1:4]
xs = [string("x", i) for i in 1:10]
ys = [string("y", i) for i in 1:4]
z = float((1:4) * reshape(1:10, 1, :))
heatmap(xs, ys, z, aspect_ratio = 1)
end
@@ -614,12 +598,7 @@ const _examples = PlotExample[
begin
import RDatasets
singers = RDatasets.dataset("lattice", "singer")
@df singers violin(
:VoicePart,
:Height,
line = 0,
fill = (0.2, :blue),
)
@df singers violin(:VoicePart, :Height, line = 0, fill = (0.2, :blue))
@df singers boxplot!(
:VoicePart,
:Height,
@@ -647,11 +626,7 @@ const _examples = PlotExample[
anim = Animation()
for x in range(1, stop = 2π, length = 20)
plot(push!(
p,
x,
Float64[sin(x), cos(x), atan(x), cos(x), log(x)],
))
plot(push!(p, x, Float64[sin(x), cos(x), atan(x), cos(x), log(x)]))
frame(anim)
end
end
@@ -676,18 +651,8 @@ const _examples = PlotExample[
10 => ones(40),
-10 => ones(40),
)
b = spdiagm(
0 => 1:50,
1 => 1:49,
-1 => 1:49,
10 => 1:40,
-10 => 1:40,
)
plot(
spy(a),
spy(b),
title = ["Unique nonzeros" "Different nonzeros"],
)
b = spdiagm(0 => 1:50, 1 => 1:49, -1 => 1:49, 10 => 1:40, -10 => 1:40)
plot(spy(a), spy(b), title = ["Unique nonzeros" "Different nonzeros"])
end
),
],
@@ -752,51 +717,37 @@ const _examples = PlotExample[
You can use the `line_z` and `marker_z` properties to associate a color with
each line segment or marker in the plot.
""",
[
:(
begin
t = range(0, stop = 1, length = 100)
θ = 6π .* t
x = t .* cos.(θ)
y = t .* sin.(θ)
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
p2 = scatter(
x,
y,
marker_z = +,
color = :bluesreds,
legend = false,
)
plot(p1, p2)
end
),
],
[:(
begin
t = range(0, stop = 1, length = 100)
θ = 6π .* t
x = t .* cos.(θ)
y = t .* sin.(θ)
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
p2 = scatter(x, y, marker_z = +, color = :bluesreds, legend = false)
plot(p1, p2)
end
)],
),
PlotExample( # 36
"Portfolio Composition maps",
"""
see: http://stackoverflow.com/a/37732384/5075246
""",
[
:(
begin
using Random
Random.seed!(111)
tickers = ["IBM", "Google", "Apple", "Intel"]
N = 10
D = length(tickers)
weights = rand(N, D)
weights ./= sum(weights, dims = 2)
returns = sort!((1:N) + D * randn(N))
[:(
begin
using Random
Random.seed!(111)
tickers = ["IBM", "Google", "Apple", "Intel"]
N = 10
D = length(tickers)
weights = rand(N, D)
weights ./= sum(weights, dims = 2)
returns = sort!((1:N) + D * randn(N))
portfoliocomposition(
weights,
returns,
labels = permutedims(tickers),
)
end
),
],
portfoliocomposition(weights, returns, labels = permutedims(tickers))
end
)],
),
PlotExample( # 37
"Ribbons",
@@ -809,10 +760,7 @@ const _examples = PlotExample[
:(
begin
plot(
plot(
0:10;
ribbon = (LinRange(0, 2, 11), LinRange(0, 1, 11)),
),
plot(0:10; ribbon = (LinRange(0, 2, 11), LinRange(0, 1, 11))),
plot(0:10; ribbon = 0:0.5:5),
plot(0:10; ribbon = sqrt),
plot(0:10; ribbon = 1),
@@ -877,11 +825,7 @@ const _examples = PlotExample[
plot!([(0, 0), (0, 0.9), (2, 0.9), (3, 1), (4, 0.9), (80, 0)])
plot!([(0, 0), (0, 0.9), (3, 0.9), (4, 1), (5, 0.9), (80, 0)])
plot!([(0, 0), (0, 0.9), (4, 0.9), (5, 1), (6, 0.9), (80, 0)])
lens!(
[1, 6],
[0.9, 1.1],
inset = (1, bbox(0.5, 0.0, 0.4, 0.4)),
)
lens!([1, 6], [0.9, 1.1], inset = (1, bbox(0.5, 0.0, 0.4, 0.4)))
end
end,
],
@@ -889,17 +833,15 @@ const _examples = PlotExample[
PlotExample( # 41
"Array Types",
"Plots supports different `Array` types that follow the `AbstractArray` interface, like `StaticArrays` and `OffsetArrays.`",
[
quote
begin
using StaticArrays, OffsetArrays
sv = SVector{10}(rand(10))
ov = OffsetVector(rand(10), -2)
plot([sv, ov], label = ["StaticArray" "OffsetArray"])
plot!(3ov, ribbon=ov, label="OffsetArray ribbon")
end
end,
],
[quote
begin
using StaticArrays, OffsetArrays
sv = SVector{10}(rand(10))
ov = OffsetVector(rand(10), -2)
plot([sv, ov], label = ["StaticArray" "OffsetArray"])
plot!(3ov, ribbon = ov, label = "OffsetArray ribbon")
end
end],
),
PlotExample( # 42
"Setting defaults and font arguments",
@@ -915,7 +857,7 @@ const _examples = PlotExample[
tickfont = (12, :orange),
guide = "x",
framestyle = :zerolines,
yminorgrid = true
yminorgrid = true,
)
plot(
[sin, cos],
@@ -934,29 +876,25 @@ const _examples = PlotExample[
PlotExample( # 43
"Heatmap with DateTime axis",
"",
[
quote
begin
using Dates
z = rand(5, 5)
x = DateTime.(2016:2020)
y = 1:5
heatmap(x, y, z)
end
end,
],
[quote
begin
using Dates
z = rand(5, 5)
x = DateTime.(2016:2020)
y = 1:5
heatmap(x, y, z)
end
end],
),
PlotExample( # 44
"Linked axes",
"",
[
quote
begin
x = -5:0.1:5
plot(plot(x, x->x^2), plot(x, x->sin(x)), layout = 2, link = :y)
end
end,
],
[quote
begin
x = -5:0.1:5
plot(plot(x, x -> x^2), plot(x, x -> sin(x)), layout = 2, link = :y)
end
end],
),
PlotExample( # 45
"Error bars and array type recipes",
@@ -971,8 +909,21 @@ const _examples = PlotExample[
value(m::Measurement) = m.val
uncertainty(m::Measurement) = m.err
@recipe function f(::Type{T}, m::T) where T <: AbstractArray{<:Measurement}
if !(get(plotattributes, :seriestype, :path) in [:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image])
@recipe function f(
::Type{T},
m::T,
) where {T<:AbstractArray{<:Measurement}}
if !(
get(plotattributes, :seriestype, :path) in [
:contour,
:contourf,
:contour3d,
:heatmap,
:surface,
:wireframe,
:image,
]
)
error_sym = Symbol(plotattributes[:letter], :error)
plotattributes[error_sym] = uncertainty.(m)
end
@@ -982,14 +933,14 @@ const _examples = PlotExample[
x = Measurement.(10sort(rand(10)), rand(10))
y = Measurement.(10sort(rand(10)), rand(10))
z = Measurement.(10sort(rand(10)), rand(10))
surf = Measurement.((1:10) .* (1:10)', rand(10,10))
surf = Measurement.((1:10) .* (1:10)', rand(10, 10))
plot(
scatter(x, [x y]),
scatter(x, y, z),
heatmap(x, y, surf),
wireframe(x, y, surf),
legend = :topleft
legend = :topleft,
)
end
end,
@@ -1002,9 +953,9 @@ const _examples = PlotExample[
using GeometryBasics
using Distributions
d = MvNormal([1.0 0.75; 0.75 2.0])
plot([(1,2),(3,2),(2,1),(2,3)])
scatter!(Point2.(eachcol(rand(d,1000))), alpha=0.25)
end]
plot([(1, 2), (3, 2), (2, 1), (2, 3)])
scatter!(Point2.(eachcol(rand(d, 1000))), alpha = 0.25)
end],
),
PlotExample( # 47
"Mesh3d",
@@ -1018,22 +969,29 @@ const _examples = PlotExample[
:(
begin
# specify the vertices
x=[0, 1, 2, 0]
y=[0, 0, 1, 2]
z=[0, 2, 0, 1]
x = [0, 1, 2, 0]
y = [0, 0, 1, 2]
z = [0, 2, 0, 1]
# specify the triangles
# every column is one triangle,
# where the values denote the indices of the vertices of the triangle
i=[0, 0, 0, 1]
j=[1, 2, 3, 2]
k=[2, 3, 1, 3]
i = [0, 0, 0, 1]
j = [1, 2, 3, 2]
k = [2, 3, 1, 3]
# the four triangles gives above give a tetrahedron
mesh3d(
x, y, z; connections=(i, j, k),
title="triangles", xlabel="x", ylabel="y", zlabel="z",
legend=:none, margin=2Plots.mm
x,
y,
z;
connections = (i, j, k),
title = "triangles",
xlabel = "x",
ylabel = "y",
zlabel = "z",
legend = :none,
margin = 2Plots.mm,
)
end
),
@@ -1042,85 +1000,100 @@ const _examples = PlotExample[
PlotExample( # 48
"Vectors of markershapes and segments",
"",
[quote
using Base.Iterators: cycle, take
[
quote
using Base.Iterators: cycle, take
yv = ones(9)
ys = [1; 1; NaN; ones(6)]
y = 5 .- [yv 2ys 3yv 4ys]
yv = ones(9)
ys = [1; 1; NaN; ones(6)]
y = 5 .- [yv 2ys 3yv 4ys]
plt_color_rows = plot(
y,
seriestype = [:path :path :scatter :scatter],
markershape = collect(take(cycle((:utriangle, :rect)), 9)),
markersize = 8,
color = collect(take(cycle((:red, :black)), 9))
)
plt_color_rows = plot(
y,
seriestype = [:path :path :scatter :scatter],
markershape = collect(take(cycle((:utriangle, :rect)), 9)),
markersize = 8,
color = collect(take(cycle((:red, :black)), 9)),
)
plt_z_cols = plot(
y,
markershape = [:utriangle :x :circle :square],
markersize = [5 10 10 5],
marker_z = [5 4 3 2],
line_z = [1 3 3 1],
linewidth = [1 10 5 1]
)
plt_z_cols = plot(
y,
markershape = [:utriangle :x :circle :square],
markersize = [5 10 10 5],
marker_z = [5 4 3 2],
line_z = [1 3 3 1],
linewidth = [1 10 5 1],
)
plot(plt_color_rows, plt_z_cols)
end]
plot(plt_color_rows, plt_z_cols)
end,
],
),
PlotExample( # 49
"Polar heatmaps",
"",
[quote
x = range(0, 2π, length=9)
y = 0:4
z = (1:4) .+ (1:8)'
heatmap(x, y, z, projection = :polar)
end]
x = range(0, 2π, length = 9)
y = 0:4
z = (1:4) .+ (1:8)'
heatmap(x, y, z, projection = :polar)
end],
),
PlotExample( # 50
"3D surface with axis guides",
"",
[quote
f(x,a) = 1/x + a*x^2
xs = collect(0.1:0.05:2.0);
as = collect(0.2:0.1:2.0);
[
quote
f(x, a) = 1 / x + a * x^2
xs = collect(0.1:0.05:2.0)
as = collect(0.2:0.1:2.0)
x_grid = [x for x in xs for y in as];
a_grid = [y for x in xs for y in as];
x_grid = [x for x in xs for y in as]
a_grid = [y for x in xs for y in as]
plot(x_grid, a_grid, f.(x_grid,a_grid),
st = :surface,
xlabel = "longer xlabel",
ylabel = "longer ylabel",
zlabel = "longer zlabel",
)
end]
plot(
x_grid,
a_grid,
f.(x_grid, a_grid),
st = :surface,
xlabel = "longer xlabel",
ylabel = "longer ylabel",
zlabel = "longer zlabel",
)
end,
],
),
PlotExample( # 51
"Images with custom axes",
"",
[quote
using Plots
using TestImages
img = testimage("lighthouse")
[
quote
using Plots
using TestImages
img = testimage("lighthouse")
# plot the image reversing the first dimension and setting yflip = false
plot([-π, π], [-1, 1], reverse(img, dims=1), yflip=false, aspect_ratio=:none)
# plot other data
plot!(sin, -π, π, lw=3, color=:red)
end]
# plot the image reversing the first dimension and setting yflip = false
plot(
[-π, π],
[-1, 1],
reverse(img, dims = 1),
yflip = false,
aspect_ratio = :none,
)
# plot other data
plot!(sin, -π, π, lw = 3, color = :red)
end,
],
),
PlotExample(
PlotExample( # 52
"3d quiver",
"",
[quote
using Plots
ϕs = range(-π, π, length=50)
θs = range(0, π, length=25)
θqs = range(1, π-1, length=25)
ϕs = range(-π, π, length = 50)
θs = range(0, π, length = 25)
θqs = range(1, π - 1, length = 25)
x = vec([sin(θ) * cos(ϕ) for (ϕ, θ) in Iterators.product(ϕs, θs)])
y = vec([sin(θ) * sin(ϕ) for (ϕ, θ) in Iterators.product(ϕs, θs)])
@@ -1130,25 +1103,43 @@ const _examples = PlotExample[
v = 0.1 * vec([sin(θ) * sin(ϕ) for (ϕ, θ) in Iterators.product(ϕs, θqs)])
w = 0.1 * vec([cos(θ) for (ϕ, θ) in Iterators.product(ϕs, θqs)])
quiver(x,y,z, quiver=(u,v,w))
end]
quiver(x, y, z, quiver = (u, v, w))
end],
),
PlotExample( # 53
"Step Types",
"A comparison of the various step-like `seriestype`s",
[
:(
begin
x = 1:5
y = [1, 2, 3, 2, 1]
default(shape=:circle)
plot(
plot(x, y, markershape=:circle, seriestype=:steppre, label="steppre"),
plot(x, y, markershape=:circle, seriestype=:stepmid, label="stepmid"),
plot(x, y, markershape=:circle, seriestype=:steppost, label="steppost"),
layout=(3,1)
)
end
begin
x = 1:5
y = [1, 2, 3, 2, 1]
default(shape = :circle)
plot(
plot(
x,
y,
markershape = :circle,
seriestype = :steppre,
label = "steppre",
),
plot(
x,
y,
markershape = :circle,
seriestype = :stepmid,
label = "stepmid",
),
plot(
x,
y,
markershape = :circle,
seriestype = :steppost,
label = "steppost",
),
layout = (3, 1),
)
end
),
],
),
@@ -1157,22 +1148,22 @@ const _examples = PlotExample[
"",
[
:(
begin
plot(
rand(10, 4),
layout=4,
xguide="x guide",
yguide="y guide",
xguidefonthalign=[:left :right :right :left],
yguidefontvalign=[:top :bottom :bottom :top],
xguideposition=:top,
yguideposition=[:right :left :right :left],
ymirror=[false true true false],
xmirror=[false false true true],
legend=false,
seriestype=[:bar :scatter :path :stepmid]
)
end
begin
plot(
rand(10, 4),
layout = 4,
xguide = "x guide",
yguide = "y guide",
xguidefonthalign = [:left :right :right :left],
yguidefontvalign = [:top :bottom :bottom :top],
xguideposition = :top,
yguideposition = [:right :left :right :left],
ymirror = [false true true false],
xmirror = [false false true true],
legend = false,
seriestype = [:bar :scatter :path :stepmid],
)
end
),
],
),
@@ -1181,44 +1172,60 @@ const _examples = PlotExample[
"",
[
:(
begin
using LinearAlgebra
scalefontsizes(.4)
begin
using LinearAlgebra
scalefontsizes(0.4)
x, y = collect(-6:0.5:10), collect(-8:0.5:8)
x, y = collect(-6:0.5:10), collect(-8:0.5:8)
args = x, y, (x, y) -> sinc(norm([x, y]) / π)
kwargs = Dict(:xlabel=>"x", :ylabel=>"y", :zlabel=>"z", :grid=>true, :minorgrid=>true)
args = x, y, (x, y) -> sinc(norm([x, y]) / π)
kwargs = Dict(
:xlabel => "x",
:ylabel => "y",
:zlabel => "z",
:grid => true,
:minorgrid => true,
)
plots = [wireframe(args..., title = "wire"; kwargs...)]
plots = [wireframe(args..., title = "wire"; kwargs...)]
for ax (:x, :y, :z)
push!(plots, wireframe(
args...,
title = "wire-flip-$ax",
xflip = ax == :x,
yflip = ax == :y,
zflip = ax == :z;
kwargs...,
))
for ax in (:x, :y, :z)
push!(
plots,
wireframe(
args...,
title = "wire-flip-$ax",
xflip = ax == :x,
yflip = ax == :y,
zflip = ax == :z;
kwargs...,
),
)
end
for ax in (:x, :y, :z)
push!(
plots,
wireframe(
args...,
title = "wire-mirror-$ax",
xmirror = ax == :x,
ymirror = ax == :y,
zmirror = ax == :z;
kwargs...,
),
)
end
plt = plot(
plots...,
layout = (@layout [_ ° _; ° ° °; ° ° °]),
margin = 0Plots.px,
)
resetfontsizes()
plt
end
for ax (:x, :y, :z)
push!(plots, wireframe(
args...,
title = "wire-mirror-$ax",
xmirror = ax == :x,
ymirror = ax == :y,
zmirror = ax == :z;
kwargs...,
))
end
plt = plot(plots..., layout=(@layout [_ ° _; ° ° °; ° ° °]), margin=0Plots.px)
resetfontsizes()
plt
end
),
],
),
@@ -1232,21 +1239,48 @@ _backend_skips = Dict(
:plotlyjs => [2, 21, 24, 25, 30, 31, 49, 51, 55],
:plotly => [2, 21, 24, 25, 30, 31, 49, 50, 51, 55],
:pgfplotsx => [
2, # animation
6, # images
16, # pgfplots thinks the upper panel is too small
30, # @df
31, # animation
32, # spy
49, # polar heatmap
51, # image with custom axes
2, # animation
6, # images
16, # pgfplots thinks the upper panel is too small
30, # @df
31, # animation
32, # spy
49, # polar heatmap
51, # image with custom axes
],
:inspectdr => [4, 6, 10, 22, 24, 28, 30, 38, 43, 45, 47, 48, 49, 50, 51, 55],
:unicodeplots => [6, 10, 22, 24, 28, 38, 43, 45, 47, 49, 50, 51, 55],
:unicodeplots => [
5, # limits issue
6, # embedded images unsupported
13, # markers unsupported
16, # nested layout unsupported
21, # custom markers unsupported
22, # contours unsupported
24, # 3D unsupported
26, # nested layout unsupported
27, # polar plots unsupported
29, # nested layout unsupported
33, # grid lines unsupported
34, # framestyle unsupported
37, # ribbons / filled unsupported
43, # heatmap with DateTime
45, # error bars
47, # mesh3D unsupported
48, # markershapes unsupported
49, # polar heatmap
50, # 3D surface unsupported
51, # embedded images unsupported
52, # 3D quiver unsupported
55, # 3D unsupported
],
:gaston => [
2, # animations
31, # animations
49, # TODO: support polar
50, # TODO: 1D data not supported for pm3d
],
)
# ---------------------------------------------------------------------------------
# make and display one plot
+6 -3
View File
@@ -1,8 +1,10 @@
# ---------------------------------------------------------
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
_fileio_load(@nospecialize(filename::AbstractString)) = FileIO.load(filename::AbstractString)
_fileio_save(@nospecialize(filename::AbstractString), @nospecialize(x)) = FileIO.save(filename::AbstractString, x)
_fileio_load(@nospecialize(filename::AbstractString)) =
FileIO.load(filename::AbstractString)
_fileio_save(@nospecialize(filename::AbstractString), @nospecialize(x)) =
FileIO.save(filename::AbstractString, x)
function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
fn = tempname()
@@ -21,4 +23,5 @@ function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
write(io, read(open(pngfn), String))
end
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
const PDFBackends =
Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
+6 -12
View File
@@ -1,15 +1,14 @@
const use_local_dependencies = Ref(false)
const use_local_plotlyjs = Ref(false)
function _init_ijulia_plotting()
# IJulia is more stable with local file
use_local_plotlyjs[] = plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
use_local_plotlyjs[] =
plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
ENV["MPLBACKEND"] = "Agg"
end
"""
Add extra jupyter mimetypes to display_dict based on the plot backed.
@@ -20,22 +19,17 @@ frontends like jupyterlab and nteract.
_ijulia__extra_mime_info!(plt::Plot, out::Dict) = out
function _ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
out["application/vnd.plotly.v1+json"] = Dict(
:data => plotly_series(plt),
:layout => plotly_layout(plt)
)
out["application/vnd.plotly.v1+json"] =
Dict(:data => plotly_series(plt), :layout => plotly_layout(plt))
out
end
function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
out["application/vnd.plotly.v1+json"] = Dict(
:data => plotly_series(plt),
:layout => plotly_layout(plt)
)
out["application/vnd.plotly.v1+json"] =
Dict(:data => plotly_series(plt), :layout => plotly_layout(plt))
out
end
function _ijulia_display_dict(plt::Plot)
output_type = Symbol(plt.attr[:html_output_format])
if output_type == :auto
+29 -14
View File
@@ -1,8 +1,7 @@
using REPL
using Scratch
const plotly_local_file_path = Ref{Union{Nothing, String}}(nothing)
const plotly_local_file_path = Ref{Union{Nothing,String}}(nothing)
function _plots_defaults()
if isdefined(Main, :PLOTS_DEFAULTS)
@@ -12,7 +11,6 @@ function _plots_defaults()
end
end
function __init__()
user_defaults = _plots_defaults()
if haskey(user_defaults, :theme)
@@ -21,14 +19,27 @@ function __init__()
default(; user_defaults...)
end
insert!(Base.Multimedia.displays, findlast(x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
insert!(
Base.Multimedia.displays,
findlast(
x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay,
Base.Multimedia.displays,
) + 1,
PlotsDisplay(),
)
atreplinit(i -> begin
while PlotsDisplay() in Base.Multimedia.displays
popdisplay(PlotsDisplay())
end
insert!(Base.Multimedia.displays, findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
end)
atreplinit(
i -> begin
while PlotsDisplay() in Base.Multimedia.displays
popdisplay(PlotsDisplay())
end
insert!(
Base.Multimedia.displays,
findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1,
PlotsDisplay(),
)
end,
)
@require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" begin
fn = joinpath(@__DIR__, "backends", "hdf5.jl")
@@ -84,9 +95,13 @@ function __init__()
end
if get(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL", "false") == "true"
global plotly_local_file_path[] = joinpath(@get_scratch!("plotly"), _plotly_min_js_filename)
global plotly_local_file_path[] =
joinpath(@get_scratch!("plotly"), _plotly_min_js_filename)
if !isfile(plotly_local_file_path[])
download("https://cdn.plot.ly/$(_plotly_min_js_filename)", plotly_local_file_path[])
Downloads.download(
"https://cdn.plot.ly/$(_plotly_min_js_filename)",
plotly_local_file_path[],
)
end
use_local_plotlyjs[] = true
@@ -94,8 +109,8 @@ function __init__()
use_local_dependencies[] = use_local_plotlyjs[]
@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin
_show(io::IO, mime::MIME"image/png", plt::Plot{<:PDFBackends}) = _show_pdfbackends(io, mime, plt)
_show(io::IO, mime::MIME"image/png", plt::Plot{<:PDFBackends}) =
_show_pdfbackends(io, mime, plt)
end
end
+139 -105
View File
@@ -18,16 +18,16 @@ ispositive(m::Measure) = m.value > 0
# union together bounding boxes
function Base.:+(bb1::BoundingBox, bb2::BoundingBox)
# empty boxes don't change the union
ispositive(width(bb1)) || return bb2
ispositive(width(bb1)) || return bb2
ispositive(height(bb1)) || return bb2
ispositive(width(bb2)) || return bb1
ispositive(width(bb2)) || return bb1
ispositive(height(bb2)) || return bb1
l = min(left(bb1), left(bb2))
t = min(top(bb1), top(bb2))
r = max(right(bb1), right(bb2))
b = max(bottom(bb1), bottom(bb2))
BoundingBox(l, t, r-l, b-t)
BoundingBox(l, t, r - l, b - t)
end
# this creates a bounding box in the parent's scope, where the child bounding box
@@ -53,7 +53,7 @@ end
# convert a bounding box from absolute coords to percentages...
# returns an array of percentages of figure size: [left, bottom, width, height]
function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true)
mms = Float64[f(bb).value for f in (left,bottom,width,height)]
mms = Float64[f(bb).value for f in (left, bottom, width, height)]
if flipy
mms[2] = figh.value - mms[2] # flip y when origin in bottom-left
end
@@ -61,7 +61,10 @@ function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true)
end
function Base.show(io::IO, bbox::BoundingBox)
print(io, "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}")
print(
io,
"BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}",
)
end
# -----------------------------------------------------------
@@ -83,12 +86,11 @@ function resolve_mixed(mix::MixedMeasures, sp::Subplot, letter::Symbol)
end
if pct != 0
amin, amax = axis_limits(sp, letter)
xy += pct * (amax-amin)
xy += pct * (amax - amin)
end
xy
end
# -----------------------------------------------------------
# AbstractLayout
@@ -160,7 +162,8 @@ parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
# padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
update_position!(layout::AbstractLayout) = nothing
update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm,0mm,0mm,0mm]) = nothing
update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm, 0mm, 0mm, 0mm]) =
nothing
left(layout::AbstractLayout) = left(bbox(layout))
top(layout::AbstractLayout) = top(bbox(layout))
@@ -206,7 +209,7 @@ mutable struct EmptyLayout <: AbstractLayout
end
EmptyLayout(parent = RootLayout(); kw...) = EmptyLayout(parent, defaultbox, KW(kw))
Base.size(layout::EmptyLayout) = (0,0)
Base.size(layout::EmptyLayout) = (0, 0)
Base.length(layout::EmptyLayout) = 0
Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing
@@ -235,22 +238,25 @@ columns of different width.
"""
grid(args...; kw...) = GridLayout(args...; kw...)
function GridLayout(dims...;
parent = RootLayout(),
widths = zeros(dims[2]),
heights = zeros(dims[1]),
kw...)
function GridLayout(
dims...;
parent = RootLayout(),
widths = zeros(dims[2]),
heights = zeros(dims[1]),
kw...,
)
grid = Matrix{AbstractLayout}(undef, dims...)
layout = GridLayout(
parent,
(20mm, 5mm, 2mm, 10mm),
defaultbox,
grid,
Measure[w*pct for w in widths],
Measure[h*pct for h in heights],
Measure[w * pct for w in widths],
Measure[h * pct for h in heights],
# convert(Vector{Float64}, widths),
# convert(Vector{Float64}, heights),
KW(kw))
KW(kw),
)
for i in eachindex(grid)
grid[i] = EmptyLayout(layout)
end
@@ -259,9 +265,9 @@ end
Base.size(layout::GridLayout) = size(layout.grid)
Base.length(layout::GridLayout) = length(layout.grid)
Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r,c]
Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r, c]
function Base.setindex!(layout::GridLayout, v, r::Int, c::Int)
layout.grid[r,c] = v
layout.grid[r, c] = v
end
leftpad(layout::GridLayout) = layout.minpad[1]
@@ -269,7 +275,6 @@ toppad(layout::GridLayout) = layout.minpad[2]
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
# 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
@@ -281,14 +286,13 @@ bottompad(layout::GridLayout) = layout.minpad[4]
function _update_min_padding!(layout::GridLayout)
map(_update_min_padding!, layout.grid)
layout.minpad = (
maximum(map(leftpad, layout.grid[:,1])),
maximum(map(toppad, layout.grid[1,:])),
maximum(map(rightpad, layout.grid[:,end])),
maximum(map(bottompad, layout.grid[end,:]))
maximum(map(leftpad, layout.grid[:, 1])),
maximum(map(toppad, layout.grid[1, :])),
maximum(map(rightpad, layout.grid[:, end])),
maximum(map(bottompad, layout.grid[end, :])),
)
end
function update_position!(layout::GridLayout)
map(update_position!, layout.grid)
end
@@ -307,7 +311,9 @@ function recompute_lengths(v)
end
leftover = 1.0pct - tot
if cnt > 1 && leftover.value <= 0
error("Not enough length left over in layout! v = $v, cnt = $cnt, leftover = $leftover")
error(
"Not enough length left over in layout! v = $v, cnt = $cnt, leftover = $leftover",
)
end
# now fill in the blanks
@@ -315,21 +321,21 @@ function recompute_lengths(v)
end
# recursively compute the bounding boxes for the layout and plotarea (relative to canvas!)
function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0mm,0mm])
function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm, 0mm, 0mm, 0mm])
nr, nc = size(layout)
# # create a matrix for each minimum padding direction
# _update_min_padding!(layout)
minpad_left = map(leftpad, layout.grid)
minpad_top = map(toppad, layout.grid)
minpad_right = map(rightpad, layout.grid)
minpad_left = map(leftpad, layout.grid)
minpad_top = map(toppad, layout.grid)
minpad_right = map(rightpad, layout.grid)
minpad_bottom = map(bottompad, layout.grid)
# get the max horizontal (left and right) padding over columns,
# and max vertical (bottom and top) padding over rows
# TODO: add extra padding here
pad_left = maximum(minpad_left, dims = 1)
pad_top = maximum(minpad_top, dims = 2)
pad_right = maximum(minpad_right, dims = 1)
pad_left = maximum(minpad_left, dims = 1)
pad_top = maximum(minpad_top, dims = 2)
pad_right = maximum(minpad_right, dims = 1)
pad_bottom = maximum(minpad_bottom, dims = 2)
# make sure the perimeter match the parent
@@ -343,7 +349,7 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
total_pad_vertical = sum(pad_top + pad_bottom)
# now we can compute the total plot area in each direction
total_plotarea_horizontal = width(layout) - total_pad_horizontal
total_plotarea_horizontal = width(layout) - total_pad_horizontal
total_plotarea_vertical = height(layout) - total_pad_vertical
# recompute widths/heights
@@ -355,19 +361,22 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
# denom_h = sum(layout.heights)
# we have all the data we need... lets compute the plot areas and set the bounding boxes
for r=1:nr, c=1:nc
child = layout[r,c]
for r in 1:nr, c in 1:nc
child = layout[r, c]
# get the top-left corner of this child... the first one is top-left of the parent (i.e. layout)
child_left = (c == 1 ? left(layout.bbox) : right(layout[r, c-1].bbox))
child_top = (r == 1 ? top(layout.bbox) : bottom(layout[r-1, c].bbox))
child_left = (c == 1 ? left(layout.bbox) : right(layout[r, c - 1].bbox))
child_top = (r == 1 ? top(layout.bbox) : bottom(layout[r - 1, c].bbox))
# compute plot area
plotarea_left = child_left + pad_left[c]
plotarea_top = child_top + pad_top[r]
plotarea_width = total_plotarea_horizontal * layout.widths[c]
plotarea_height = total_plotarea_vertical * layout.heights[r]
plotarea!(child, BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height))
plotarea!(
child,
BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height),
)
# compute child bbox
child_width = pad_left[c] + plotarea_width + pad_right[c]
@@ -377,10 +386,10 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
# this is the minimum perimeter as decided by this child's parent, so that
# all children on this border have the same value
min_child_perimeter = [
c == 1 ? layout.minpad[1] : pad_left[c],
r == 1 ? layout.minpad[2] : pad_top[r],
c == 1 ? layout.minpad[1] : pad_left[c],
r == 1 ? layout.minpad[2] : pad_top[r],
c == nc ? layout.minpad[3] : pad_right[c],
r == nr ? layout.minpad[4] : pad_bottom[r]
r == nr ? layout.minpad[4] : pad_bottom[r],
]
# recursively update the child's children
@@ -395,12 +404,15 @@ function update_inset_bboxes!(plt::Plot)
p_area = Measures.resolve(plotarea(sp.parent), sp[:relative_bbox])
plotarea!(sp, p_area)
bbox!(sp, bbox(
left(p_area) - leftpad(sp),
top(p_area) - toppad(sp),
width(p_area) + leftpad(sp) + rightpad(sp),
height(p_area) + toppad(sp) + bottompad(sp)
))
bbox!(
sp,
bbox(
left(p_area) - leftpad(sp),
top(p_area) - toppad(sp),
width(p_area) + leftpad(sp) + rightpad(sp),
height(p_area) + toppad(sp) + bottompad(sp),
),
)
end
end
@@ -441,7 +453,9 @@ end
function layout_args(plotattributes::AKW, n_override::Integer)
layout, n = layout_args(n_override, get(plotattributes, :layout, n_override))
if n != n_override
error("When doing layout, n ($n) != n_override ($(n_override)). You're probably trying to force existing plots into a layout that doesn't fit them.")
error(
"When doing layout, n ($n) != n_override ($(n_override)). You're probably trying to force existing plots into a layout that doesn't fit them.",
)
end
layout, n
end
@@ -451,27 +465,27 @@ function layout_args(n::Integer)
GridLayout(nr, nc), n
end
function layout_args(sztup::NTuple{2, Integer})
function layout_args(sztup::NTuple{2,Integer})
nr, nc = sztup
GridLayout(nr, nc), nr*nc
GridLayout(nr, nc), nr * nc
end
layout_args(n_override::Integer, n::Integer) = layout_args(n)
layout_args(n, sztup::NTuple{2, Integer}) = layout_args(sztup)
layout_args(n, sztup::NTuple{2,Integer}) = layout_args(sztup)
function layout_args(n, sztup::Tuple{Colon, Integer})
function layout_args(n, sztup::Tuple{Colon,Integer})
nc = sztup[2]
nr = ceil(Int, n / nc)
GridLayout(nr, nc), n
end
function layout_args(n, sztup::Tuple{Integer, Colon})
function layout_args(n, sztup::Tuple{Integer,Colon})
nr = sztup[1]
nc = ceil(Int, n / nr)
GridLayout(nr, nc), n
end
function layout_args(sztup::NTuple{3, Integer})
function layout_args(sztup::NTuple{3,Integer})
n, nr, nc = sztup
nr, nc = compute_gridsize(n, nr, nc)
GridLayout(nr, nc), n
@@ -488,16 +502,13 @@ layout_args(n_override::Integer, layout::GridLayout) = layout_args(layout)
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
# ----------------------------------------------------------------------
function build_layout(args...)
layout, n = layout_args(args...)
build_layout(layout, n, Array{Plot}(undef, 0))
end
# n is the number of subplots...
function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot})
nr, nc = size(layout)
@@ -505,15 +516,15 @@ function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot})
spmap = SubplotMap()
empty = isempty(plts)
i = 0
for r = 1:nr, c = 1:nc
for r in 1:nr, c in 1:nc
l = layout[r, c]
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
if empty
# initialize the inner subplots recursively
sp = Subplot(backend(), parent=layout)
sp = Subplot(backend(), parent = layout)
layout[r, c] = sp
push!(subplots, sp)
spmap[attr(l,:label,gensym())] = sp
spmap[attr(l, :label, gensym())] = sp
inc = 1
else
# build a layout from a list of existing Plot objects
@@ -524,19 +535,19 @@ function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot})
inc = length(plt.subplots)
end
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
layout.widths[c] = attr(l, :width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
layout.heights[r] = attr(l, :height)
end
i += inc
elseif isa(l, GridLayout)
# sub-grid
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
layout.widths[c] = attr(l, :width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
layout.heights[r] = attr(l, :height)
end
l, sps, m = build_layout(l, n - i, plts)
append!(subplots, sps)
@@ -551,7 +562,6 @@ function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot})
layout, subplots, spmap
end
# ----------------------------------------------------------------------
# @layout macro
@@ -563,9 +573,9 @@ function add_layout_pct!(kw::AKW, v::Expr, idx::Integer, nidx::Integer)
if length(v.args) == 3 && isa(num, Number)
units = v.args[3]
if units == :h
return kw[:h] = num*pct
return kw[:h] = num * pct
elseif units == :w
return kw[:w] = num*pct
return kw[:w] = num * pct
elseif units in (:pct, :px, :mm, :cm, :inch)
idx == 1 && (kw[:w] = v)
(idx == 2 || nidx == 1) && (kw[:h] = v)
@@ -578,23 +588,29 @@ end
function add_layout_pct!(kw::AKW, v::Number, idx::Integer)
# kw[idx == 1 ? :w : :h] = v*pct
idx == 1 && (kw[:w] = v*pct)
(idx == 2 || nidx == 1) && (kw[:h] = v*pct)
idx == 1 && (kw[:w] = v * pct)
(idx == 2 || nidx == 1) && (kw[:h] = v * pct)
end
isrow(v) = isa(v, Expr) && v.head in (:hcat,:row)
isrow(v) = isa(v, Expr) && v.head in (:hcat, :row)
iscol(v) = isa(v, Expr) && v.head == :vcat
rowsize(v) = isrow(v) ? length(v.args) : 1
function create_grid(expr::Expr)
if iscol(expr)
create_grid_vcat(expr)
elseif isrow(expr)
:(let cell = GridLayout(1, $(length(expr.args)))
$([:(cell[1,$i] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
cell
end)
:(
let cell = GridLayout(1, $(length(expr.args)))
$(
[
:(cell[1, $i] = $(create_grid(v))) for
(i, v) in enumerate(expr.args)
]...
)
cell
end
)
elseif expr.head == :curly
create_grid_curly(expr)
@@ -613,39 +629,58 @@ function create_grid_vcat(expr::Expr)
nr = length(expr.args)
nc = rmin
body = Expr(:block)
for r=1:nr
for r in 1:nr
arg = expr.args[r]
if isrow(arg)
for (c,item) in enumerate(arg.args)
push!(body.args, :(cell[$r,$c] = $(create_grid(item))))
for (c, item) in enumerate(arg.args)
push!(body.args, :(cell[$r, $c] = $(create_grid(item))))
end
else
push!(body.args, :(cell[$r,1] = $(create_grid(arg))))
push!(body.args, :(cell[$r, 1] = $(create_grid(arg))))
end
end
:(let cell = GridLayout($nr, $nc)
$body
cell
end)
:(
let cell = GridLayout($nr, $nc)
$body
cell
end
)
else
# otherwise just build one row at a time
:(let cell = GridLayout($(length(expr.args)), 1)
$([:(cell[$i,1] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
cell
end)
:(
let cell = GridLayout($(length(expr.args)), 1)
$(
[
:(cell[$i, 1] = $(create_grid(v))) for
(i, v) in enumerate(expr.args)
]...
)
cell
end
)
end
end
function create_grid_curly(expr::Expr)
kw = KW()
for (i,arg) in enumerate(expr.args[2:end])
add_layout_pct!(kw, arg, i, length(expr.args)-1)
for (i, arg) in enumerate(expr.args[2:end])
add_layout_pct!(kw, arg, i, length(expr.args) - 1)
end
s = expr.args[1]
if isa(s, Expr) && s.head == :call && s.args[1] == :grid
create_grid(:(grid($(s.args[2:end]...), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto))))))
create_grid(
:(grid(
$(s.args[2:end]...),
width = $(get(kw, :w, QuoteNode(:auto))),
height = $(get(kw, :h, QuoteNode(:auto))),
)),
)
elseif isa(s, Symbol)
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
:(EmptyLayout(
label = $(QuoteNode(s)),
width = $(get(kw, :w, QuoteNode(:auto))),
height = $(get(kw, :h, QuoteNode(:auto))),
))
else
error("Unknown use of curly brackets: $expr")
end
@@ -659,14 +694,13 @@ macro layout(mat::Expr)
create_grid(mat)
end
# -------------------------------------------------------------------------
# make all reference the same axis extrema/values.
# merge subplot lists.
function link_axes!(axes::Axis...)
a1 = axes[1]
for i=2:length(axes)
for i in 2:length(axes)
a2 = axes[i]
expand_extrema!(a1, ignorenan_extrema(a2))
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
@@ -688,8 +722,8 @@ function link_subplots(a::AbstractArray{AbstractLayout}, axissym::Symbol)
for l in a
if isa(l, Subplot)
push!(subplots, l)
elseif isa(l, GridLayout) && size(l) == (1,1)
push!(subplots, l[1,1])
elseif isa(l, GridLayout) && size(l) == (1, 1)
push!(subplots, l[1, 1])
end
end
subplots
@@ -705,20 +739,19 @@ function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
end
# don't do anything for most layout types
function link_axes!(l::AbstractLayout, link::Symbol)
end
function link_axes!(l::AbstractLayout, link::Symbol) end
# process a GridLayout, recursively linking axes according to the link symbol
function link_axes!(layout::GridLayout, link::Symbol)
nr, nc = size(layout)
if link in (:x, :both)
for c=1:nc
link_axes!(layout.grid[:,c], :xaxis)
for c in 1:nc
link_axes!(layout.grid[:, c], :xaxis)
end
end
if link in (:y, :both)
for r=1:nr
link_axes!(layout.grid[r,:], :yaxis)
for r in 1:nr
link_axes!(layout.grid[r, :], :yaxis)
end
end
if link == :square
@@ -744,8 +777,9 @@ end
"Adds a new, empty subplot overlayed on top of `sp`, with a mirrored y-axis and linked x-axis."
function twinx(sp::Subplot)
plot!(sp.plt,
inset = (sp[:subplot_index], bbox(0,0,1,1)),
plot!(
sp.plt,
inset = (sp[:subplot_index], bbox(0, 0, 1, 1)),
right_margin = sp[:right_margin],
left_margin = sp[:left_margin],
top_margin = sp[:top_margin],
@@ -756,7 +790,7 @@ function twinx(sp::Subplot)
twinsp[:yaxis][:grid] = false
twinsp[:xaxis][:showaxis] = false
twinsp[:yaxis][:mirror] = true
twinsp[:background_color_inside] = RGBA{Float64}(0,0,0,0)
twinsp[:background_color_inside] = RGBA{Float64}(0, 0, 0, 0)
link_axes!(sp[:xaxis], twinsp[:xaxis])
twinsp
end
+30 -31
View File
@@ -8,20 +8,19 @@ Return `(x,y)` at an angle `theta` degrees from
`xmax`, `ymin`, `ymax`).
"""
function legend_pos_from_angle(theta, xmin, xcenter, xmax, ymin, ycenter, ymax)
(s,c) = sincosd(theta)
x = c < 0 ? (xmin-xcenter)/c : (xmax-xcenter)/c
y = s < 0 ? (ymin-ycenter)/s : (ymax-ycenter)/s
A = min(x,y)
return (xcenter + A*c, ycenter + A*s)
(s, c) = sincosd(theta)
x = c < 0 ? (xmin - xcenter) / c : (xmax - xcenter) / c
y = s < 0 ? (ymin - ycenter) / s : (ymax - ycenter) / s
A = min(x, y)
return (xcenter + A * c, ycenter + A * s)
end
"""
Split continuous range `[-1,1]` evenly into an integer `[1,2,3]`
"""
function legend_anchor_index(x)
x<-1//3 && return 1
x<1//3 && return 2
x < -1 // 3 && return 1
x < 1 // 3 && return 2
return 3
end
@@ -32,28 +31,28 @@ so :topleft exactly corresponds to (45, :inner) etc.
If `leg` is a (::Real,::Real) tuple, keep it as is.
"""
legend_angle(leg::Real) = (leg,:inner)
legend_angle(leg::Real) = (leg, :inner)
legend_angle(leg::Tuple{S,T}) where {S<:Real,T<:Real} = leg
legend_angle(leg::Tuple{S,Symbol}) where S<:Real = leg
legend_angle(leg::Tuple{S,Symbol}) where {S<:Real} = leg
legend_angle(leg::Symbol) = get(
(
topleft = (135,:inner),
top = (90, :inner),
topright = (45, :inner),
left = (180,:inner),
right = (0, :inner),
bottomleft = (225,:inner),
bottom = (270,:inner),
bottomright = (315,:inner),
outertopleft = (135,:outer),
outertop = (90, :outer),
outertopright = (45, :outer),
outerleft = (180,:outer),
outerright = (0, :outer),
outerbottomleft = (225,:outer),
outerbottom = (270,:outer),
outerbottomright = (315,:outer),
),
leg,
(45, :inner)
)
(
topleft = (135, :inner),
top = (90, :inner),
topright = (45, :inner),
left = (180, :inner),
right = (0, :inner),
bottomleft = (225, :inner),
bottom = (270, :inner),
bottomright = (315, :inner),
outertopleft = (135, :outer),
outertop = (90, :outer),
outertopright = (45, :outer),
outerleft = (180, :outer),
outerright = (0, :outer),
outerbottomleft = (225, :outer),
outerbottom = (270, :outer),
outerbottomright = (315, :outer),
),
leg,
(45, :inner),
)
+2 -6
View File
@@ -73,7 +73,6 @@ function txt(plt::Plot, fn::AbstractString)
end
txt(fn::AbstractString) = txt(current(), fn)
# ----------------------------------------------------------------
const _savemap = Dict(
@@ -128,7 +127,6 @@ function savefig(plt::Plot, fn::AbstractString)
end
savefig(fn::AbstractString) = savefig(current(), fn)
# ---------------------------------------------------------
"""
@@ -186,8 +184,8 @@ function _show(io::IO, ::MIME"text/html", plt::Plot)
end
# delegate showable to _show instead
function Base.showable(m::M, plt::P) where {M <: MIME, P <: Plot}
return hasmethod(_show, Tuple{IO, M, P})
function Base.showable(m::M, plt::P) where {M<:MIME,P<:Plot}
return hasmethod(_show, Tuple{IO,M,P})
end
function _display(plt::Plot)
@@ -224,7 +222,6 @@ Base.show(io::IO, m::MIME"application/prs.juno.plotpane+html", plt::Plot) =
"Close all open gui windows of the current backend"
closeall() = closeall(backend())
# function html_output_format(fmt)
# if fmt == "png"
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
@@ -241,7 +238,6 @@ closeall() = closeall(backend())
#
# html_output_format("svg")
# ---------------------------------------------------------
# Atom PlotPane
# ---------------------------------------------------------
+39 -30
View File
@@ -30,7 +30,6 @@ function RecipesPipeline.warn_on_recipe_aliases!(
end
end
## Grouping
RecipesPipeline.splittable_attribute(plt::Plot, key, val::SeriesAnnotations, len) =
@@ -41,7 +40,6 @@ function RecipesPipeline.split_attribute(plt::Plot, key, val::SeriesAnnotations,
return SeriesAnnotations(split_strs, val.font, val.baseshape, val.scalefactor)
end
## Preprocessing attributes
function RecipesPipeline.preprocess_axis_args!(plt::Plot, plotattributes, letter)
# Fix letter for seriestypes that are x only but data gets passed as y
@@ -62,7 +60,6 @@ RecipesPipeline.is_axis_attribute(plt::Plot, attr) = is_axis_attr_noletter(attr)
RecipesPipeline.is_subplot_attribute(plt::Plot, attr) = is_subplot_attr(attr) # in src/args.jl
## User recipes
function RecipesPipeline.process_userrecipe!(plt::Plot, kw_list, kw)
@@ -84,13 +81,15 @@ function _preprocess_userrecipe(kw::AKW)
# 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] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) :
kw[:marker_z] =
isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) :
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] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) :
kw[:line_z] =
isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) :
map(kw[:line_z], kw[:x], kw[:y], kw[:z])
end
@@ -140,15 +139,12 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
end
end
RecipesPipeline.get_axis_limits(plt::Plot, letter) = axis_limits(plt[1], letter, false)
## Plot recipes
RecipesPipeline.type_alias(plt::Plot) = get(_typeAliases, st, st)
## Plot setup
function RecipesPipeline.plot_setup!(plt::Plot, plotattributes, kw_list)
@@ -159,18 +155,18 @@ end
function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_list)
# swap errors
err_inds = findall(kw -> get(kw, :seriestype, :path) in (:xerror, :yerror, :zerror), kw_list)
err_inds =
findall(kw -> get(kw, :seriestype, :path) in (:xerror, :yerror, :zerror), kw_list)
for ind in err_inds
if get(kw_list[ind-1],:seriestype,:path) == :scatter
if get(kw_list[ind - 1], :seriestype, :path) == :scatter
tmp = copy(kw_list[ind])
kw_list[ind] = copy(kw_list[ind-1])
kw_list[ind-1] = tmp
kw_list[ind] = copy(kw_list[ind - 1])
kw_list[ind - 1] = tmp
end
end
return nothing
end
# TODO: Should some of this logic be moved to RecipesPipeline?
function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
# merge in anything meant for the Plot
@@ -193,7 +189,6 @@ function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
plt.init = true
end
# handle inset subplots
insets = plt[:inset_subplots]
if insets !== nothing
@@ -226,7 +221,7 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
# 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}()
sp_attrs = Dict{Subplot,Any}()
for kw in kw_list
# get the Subplot object to which the series belongs.
sps = get(kw, :subplot, :auto)
@@ -243,17 +238,24 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
attr = KW()
for (k, v) in collect(kw)
if is_subplot_attr(k) || is_axis_attr(k)
attr[k] = pop!(kw, k)
v = pop!(kw, k)
if sps isa AbstractArray && v isa AbstractArray && length(v) == length(sps)
v = v[series_idx(kw_list, kw)]
end
attr[k] = v
end
if is_axis_attr_noletter(k)
v = pop!(kw, k)
if sps isa AbstractArray && v isa AbstractArray && length(v) == length(sps)
v = v[series_idx(kw_list, kw)]
end
for letter in (:x, :y, :z)
attr[Symbol(letter, k)] = v
attr[get_attr_symbol(letter, k)] = v
end
end
for k in (:scale,), letter in (:x, :y, :z)
# Series recipes may need access to this information
lk = Symbol(letter, k)
lk = get_attr_symbol(letter, k)
if haskey(attr, lk)
kw[lk] = attr[lk]
end
@@ -287,15 +289,17 @@ function _add_plot_title!(plt)
if plot_title != ""
the_layout = plt.layout
vspan = plt[:plot_titlevspan]
plt.layout = grid(2, 1, heights=(vspan, 1 - vspan))
plt.layout.grid[1, 1] = subplot = Subplot(plt.backend, parent=plt.layout[1, 1])
plt.layout = grid(2, 1, heights = (vspan, 1 - vspan))
plt.layout.grid[1, 1] = subplot = Subplot(plt.backend, parent = plt.layout[1, 1])
plt.layout.grid[2, 1] = the_layout
subplot.plt = plt
# propagate arguments plt[:plot_titleXXX] --> subplot[:titleXXX]
for sym filter(x -> startswith(string(x), "plot_title"), keys(_plot_defaults))
subplot[Symbol(string(sym)[length("plot_") + 1:end])] = plt[sym]
for sym in filter(x -> startswith(string(x), "plot_title"), keys(_plot_defaults))
subplot[Symbol(string(sym)[(length("plot_") + 1):end])] = plt[sym]
end
plt[:force_minpad] = nothing, 0px, nothing, 0px
top = plt.backend isa PyPlotBackend ? nothing : 0mm
bot = 0mm
plt[:force_minpad] = nothing, top, nothing, bot
subplot[:subplot_index] = last(plt.subplots)[:subplot_index] + 1
plt[:plot_titleindex] = subplot[:subplot_index]
subplot[:framestyle] = :none
@@ -337,7 +341,10 @@ function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T}
st = _override_seriestype_check(plotattributes, st)
# change to a 3d projection for this subplot?
if RecipesPipeline.needs_3d_axes(st) || (st == :quiver && plotattributes[:z] !== nothing)
if (
RecipesPipeline.needs_3d_axes(st) ||
(st == :quiver && plotattributes[:z] !== nothing)
)
sp.attr[:projection] = "3d"
end
@@ -353,8 +360,10 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol)
# do we want to override the series type?
if !RecipesPipeline.is3d(st) && !(st in (:contour, :contour3d, :quiver))
z = plotattributes[:z]
if z !== nothing &&
(size(plotattributes[:x]) == size(plotattributes[:y]) == size(z))
if (
z !== nothing &&
(size(plotattributes[:x]) == size(plotattributes[:y]) == size(z))
)
st = (st == :scatter ? :scatter3d : :path3d)
plotattributes[:seriestype] = st
end
@@ -365,7 +374,7 @@ end
function needs_any_3d_axes(sp::Subplot)
any(
RecipesPipeline.needs_3d_axes(
_override_seriestype_check(s.plotattributes, s.plotattributes[:seriestype])
_override_seriestype_check(s.plotattributes, s.plotattributes[:seriestype]),
) for s in series_list(sp)
)
end
@@ -390,9 +399,9 @@ end
function _add_the_series(plt, sp, plotattributes)
extra_kwargs = warn_on_unsupported_args(plt.backend, plotattributes)
if (kw = plt[:extra_kwargs]) isa AbstractDict
plt[:extra_plot_kwargs] = get(kw,:plot,KW())
sp[:extra_kwargs] = get(kw,:subplot, KW())
plotattributes[:extra_kwargs] = get(kw,:series,KW())
plt[:extra_plot_kwargs] = get(kw, :plot, KW())
sp[:extra_kwargs] = get(kw, :subplot, KW())
plotattributes[:extra_kwargs] = get(kw, :series, KW())
elseif plt[:extra_kwargs] == :plot
plt[:extra_plot_kwargs] = extra_kwargs
elseif plt[:extra_kwargs] == :subplot
+19 -18
View File
@@ -1,6 +1,6 @@
mutable struct CurrentPlot
nullableplot::Union{AbstractPlot, Nothing}
nullableplot::Union{AbstractPlot,Nothing}
end
const CURRENT_PLOT = CurrentPlot(nothing)
@@ -20,17 +20,20 @@ current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = plot)
# ---------------------------------------------------------
Base.string(plt::Plot) = "Plot{$(plt.backend) n=$(plt.n)}"
Base.print(io::IO, plt::Plot) = print(io, string(plt))
function Base.show(io::IO, plt::Plot)
print(io, string(plt))
sp_ekwargs = getindex.(plt.subplots, :extra_kwargs)
s_ekwargs = getindex.(plt.series_list, :extra_kwargs)
if isempty(plt[:extra_plot_kwargs]) && all(isempty, sp_ekwargs) && all(isempty, s_ekwargs)
if (
isempty(plt[:extra_plot_kwargs]) &&
all(isempty, sp_ekwargs) &&
all(isempty, s_ekwargs)
)
return
end
print(io,"\nCaptured extra kwargs:\n")
print(io, "\nCaptured extra kwargs:\n")
do_show = true
for (key, value) in plt[:extra_plot_kwargs]
do_show && println(io, " Plot:")
@@ -62,7 +65,6 @@ convertSeriesIndex(plt::Plot, n::Int) = n
# ---------------------------------------------------------
"""
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
@@ -73,7 +75,7 @@ The main plot command. Use `plot` to create a new plot object, and `plot!` to ad
```
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
When you pass in matrices, it splits by columns. To see the list of available attributes, use the `plotattr([attr])`
When you pass in matrices, it splits by columns. To see the list of available attributes, use the `plotattr(attr)`
function, where `attr` is the symbol `:Series`, `:Subplot`, `:Plot`, or `:Axis`. Pass any attribute to `plotattr`
as a String to look up its docstring, e.g., `plotattr("seriestype")`.
"""
@@ -91,7 +93,8 @@ end
# build a new plot from existing plots
# note: we split into plt1, plt2 and plts_tail so we can dispatch correctly
plot(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...) = plot!(deepcopy(plt1), deepcopy(plt2), deepcopy.(plts_tail)...; kw...)
plot(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...) =
plot!(deepcopy(plt1), deepcopy(plt2), deepcopy.(plts_tail)...; kw...)
function plot!(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...)
@nospecialize
plotattributes = KW(kw)
@@ -102,8 +105,8 @@ function plot!(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...)
plts = Array{Plot}(undef, n)
plts[1] = plt1
plts[2] = plt2
for (i,plt) in enumerate(plts_tail)
plts[i+2] = plt
for (i, plt) in enumerate(plts_tail)
plts[i + 2] = plt
end
# compute the layout
@@ -130,9 +133,9 @@ function plot!(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...)
plt.init = true
series_attr = KW()
for (k,v) in plotattributes
for (k, v) in plotattributes
if is_series_attr(k)
series_attr[k] = pop!(plotattributes,k)
series_attr[k] = pop!(plotattributes, k)
end
end
@@ -163,7 +166,7 @@ function plot!(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...)
_add_plot_title!(plt)
# first apply any args for the subplots
for (idx,sp) in enumerate(plt.subplots)
for (idx, sp) in enumerate(plt.subplots)
_update_subplot_args(plt, sp, plotattributes, idx, false)
end
@@ -173,8 +176,6 @@ function plot!(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...)
plt
end
# this adds to the current plot, or creates a new plot if none are current
function plot!(args...; kw...)
@nospecialize
@@ -210,14 +211,13 @@ function _plot!(plt::Plot, plotattributes, args)
return plt
end
# we're getting ready to display/output. prep for layout calcs, then update
# the plot object after
function prepare_output(plt::Plot)
_before_layout_calcs(plt)
w, h = plt.attr[:size]
plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
plt.layout.bbox = BoundingBox(0mm, 0mm, w * px, h * px)
# One pass down and back up the tree to compute the minimum padding
# of the children on the perimeter. This is an backend callback.
@@ -229,9 +229,10 @@ function prepare_output(plt::Plot)
# spedific to :plot_title see _add_plot_title!
force_minpad = get(plt, :force_minpad, ())
if !isempty(force_minpad)
for i eachindex(plt.layout.grid)
for i in eachindex(plt.layout.grid)
plt.layout.grid[i].minpad = Tuple(
i === nothing ? j : i for (i, j) zip(force_minpad, plt.layout.grid[i].minpad)
i === nothing ? j : i for
(i, j) in zip(force_minpad, plt.layout.grid[i].minpad)
)
end
end
+23 -14
View File
@@ -1,8 +1,10 @@
const _attribute_defaults = Dict(:Series => _series_defaults,
:Subplot => _subplot_defaults,
:Plot => _plot_defaults,
:Axis => _axis_defaults)
const _attribute_defaults = Dict(
:Series => _series_defaults,
:Subplot => _subplot_defaults,
:Plot => _plot_defaults,
:Axis => _axis_defaults,
)
attrtypes() = join(keys(_attribute_defaults), ", ")
attributes(attrtype::Symbol) = sort(collect(keys(_attribute_defaults[attrtype])))
@@ -21,7 +23,9 @@ Look up the properties of a Plots attribute, or specify an attribute type. Call
The information is the same as that given on https://docs.juliaplots.org/latest/attributes/.
"""
function plotattr()
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
println(
"Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())",
)
end
function plotattr(attrtype::Symbol)
@@ -44,27 +48,32 @@ printnothing(x) = x
printnothing(x::Nothing) = "nothing"
function plotattr(attrtype::Symbol, attribute::AbstractString)
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
in(attrtype, keys(_attribute_defaults)) ||
ArgumentError("`attrtype` must match one of $(attrtypes())")
attribute = Symbol(lookup_aliases(attrtype, attribute))
desc = get(_arg_desc, attribute, "")
first_period_idx = findfirst(isequal('.'), desc)
if isnothing(first_period_idx)
if isnothing(first_period_idx)
typedesc = ""
desc = strip(desc)
else
typedesc = desc[1:first_period_idx-1]
desc = strip(desc[first_period_idx+1:end])
typedesc = desc[1:(first_period_idx - 1)]
desc = strip(desc[(first_period_idx + 1):end])
end
als = keys(filter(x->x[2]==attribute, _keyAliases)) |> collect |> sort
als = join(map(string,als), ", ")
als = keys(filter(x -> x[2] == attribute, _keyAliases)) |> collect |> sort
als = join(map(string, als), ", ")
def = _attribute_defaults[attrtype][attribute]
# Looks up the different elements and plots them
println("$(printnothing(attribute)) ", typedesc == "" ? "" : "{$(printnothing(typedesc))}", "\n",
println(
"$(printnothing(attribute)) ",
typedesc == "" ? "" : "{$(printnothing(typedesc))}",
"\n",
als == "" ? "" : "$(printnothing(als))\n",
"\n$(printnothing(desc))\n",
"$(printnothing(attrtype)) attribute, ", def == "" ? "" : " default: $(printnothing(def))")
"$(printnothing(attrtype)) attribute, ",
def == "" ? "" : " default: $(printnothing(def))",
)
end
-2
View File
@@ -1,6 +1,5 @@
should_precompile = true
# Don't edit the following! Instead change the script for `snoop_bot`.
ismultios = false
ismultiversion = false
@@ -16,5 +15,4 @@ elseif !ismultios && !ismultiversion
_precompile_()
end
else
end # precompile_enclosure
+81 -111
View File
@@ -41,17 +41,16 @@ function all_seriestypes()
sort(collect(sts))
end
# ----------------------------------------------------------------------------------
num_series(x::AMat) = size(x, 2)
num_series(x) = 1
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} =
nothing
# ---------------------------------------------------------------------------
# for seriestype `line`, need to sort by x values
const POTENTIAL_VECTOR_ARGUMENTS = [
@@ -114,8 +113,8 @@ end
@recipe function f(::Type{Val{:hline}}, x, y, z)
n = length(y)
newx = repeat(Float64[1, 2, NaN], n)
newy = vec(Float64[yi for i = 1:3, yi in y])
newx = repeat(Float64[1, 2, NaN], n)
newy = vec(Float64[yi for i in 1:3, yi in y])
x := newx
y := newy
seriestype := :straightline
@@ -125,7 +124,7 @@ end
@recipe function f(::Type{Val{:vline}}, x, y, z)
n = length(y)
newx = vec(Float64[yi for i = 1:3, yi in y])
newx = vec(Float64[yi for i in 1:3, yi in y])
x := newx
y := repeat(Float64[1, 2, NaN], n)
seriestype := :straightline
@@ -136,7 +135,7 @@ end
@recipe function f(::Type{Val{:hspan}}, x, y, z)
n = div(length(y), 2)
newx = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i in 1:n]...)
linewidth --> 0
x := newx
y := newy
@@ -147,7 +146,7 @@ end
@recipe function f(::Type{Val{:vspan}}, x, y, z)
n = div(length(y), 2)
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i in 1:n]...)
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
linewidth --> 0
x := newx
@@ -179,7 +178,6 @@ end
end
@deps scatterpath path scatter
# ---------------------------------------------------------------------------
# regression line and scatter
@@ -202,10 +200,8 @@ end
()
end
@specialize
# ---------------------------------------------------------------------------
# steps
@@ -215,10 +211,10 @@ function make_steps(x::AbstractArray, st, even)
n == 0 && return zeros(0)
newx = zeros(2n - (even ? 0 : 1))
newx[1] = x[1]
for i = 2:n
for i in 2:n
idx = 2i - 1
if st == :mid
newx[idx] = newx[idx-1] = (x[i] + x[i-1]) / 2
newx[idx] = newx[idx - 1] = (x[i] + x[i - 1]) / 2
else
newx[idx] = x[i]
newx[idx - 1] = x[st == :pre ? i : i - 1]
@@ -229,7 +225,6 @@ function make_steps(x::AbstractArray, st, even)
end
make_steps(t::Tuple, st, even) = Tuple(make_steps(ti, st, even) for ti in t)
@nospecialize
# create a path from steps
@@ -307,7 +302,6 @@ end
end
@deps steppost path scatter
# ---------------------------------------------------------------------------
# sticks
@@ -326,7 +320,7 @@ end
end
newx, newy = zeros(3n), zeros(3n)
newz = z !== nothing ? zeros(3n) : nothing
for (i, (xi, yi, zi)) = enumerate(zip(x, y, z !== nothing ? z : 1:n))
for (i, (xi, yi, zi)) in enumerate(zip(x, y, z !== nothing ? z : 1:n))
rng = (3i - 2):(3i)
newx[rng] = [xi, xi, NaN]
if z !== nothing
@@ -343,7 +337,11 @@ end
end
fillrange := nothing
seriestype := :path
if plotattributes[:linecolor] == :auto && plotattributes[:marker_z] !== nothing && plotattributes[:line_z] === nothing
if (
plotattributes[:linecolor] == :auto &&
plotattributes[:marker_z] !== nothing &&
plotattributes[:line_z] === nothing
)
line_z := plotattributes[:marker_z]
end
@@ -368,7 +366,6 @@ end
@specialize
# ---------------------------------------------------------------------------
# bezier curves
@@ -438,8 +435,7 @@ end
# create a bar plot as a filled step function
@recipe function f(::Type{Val{:bar}}, x, y, z)
procx, procy, xscale, yscale, baseline =
_preprocess_barlike(plotattributes, x, y)
procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y)
nx, ny = length(procx), length(procy)
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
cv = [discrete_value!(axis, xi)[1] for xi in procx]
@@ -448,7 +444,9 @@ end
elseif nx == ny + 1
0.5 * diff(cv) + cv[1:(end - 1)]
else
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
error(
"bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))",
)
end
# compute half-width of bars
@@ -472,18 +470,8 @@ end
fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto)
end
if !isnothing(plotattributes[:series_annotations])
if isvertical(plotattributes)
annotations := (x,y,plotattributes[:series_annotations].strs,:bottom)
else
annotations := (y,x,plotattributes[:series_annotations].strs,:left)
end
series_annotations := nothing
end
# create the bar shapes by adding x/y segments
xseg, yseg = Segments(), Segments()
for i = 1:ny
for i in 1:ny
yi = procy[i]
if !isnan(yi)
center = procx[i]
@@ -507,15 +495,30 @@ end
# switch back
if !isvertical(plotattributes)
xseg, yseg = yseg, xseg
x, y = y, x
end
# reset orientation
orientation := default(:orientation)
x := xseg.pts
y := yseg.pts
seriestype := :shape
# draw the bar shapes
@series begin
seriestype := :shape
series_annotations := nothing
primary := true
x := xseg.pts
y := yseg.pts
()
end
# add empty series
primary := false
seriestype := :scatter
markersize := 0
markeralpha := 0
fillrange := nothing
x := x
y := y
()
end
@deps bar shape
@@ -527,8 +530,8 @@ end
m, n = size(z.surf)
x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n)
fz = zeros(m * n)
for i = 1:m # y
for j = 1:n # x
for i in 1:m # y
for j in 1:n # x
k = (j - 1) * m + i
inds = (6 * (k - 1) + 1):(6 * k - 1)
x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]]
@@ -575,7 +578,6 @@ function _scale_adjusted_values(
end
end
function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
if (scale in _logScales)
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
@@ -584,7 +586,6 @@ function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
end
end
function _preprocess_binbarlike_weights(
::Type{T},
w,
@@ -613,12 +614,10 @@ function _preprocess_binlike(plotattributes, x, y)
edge, weights, xscale, yscale, baseline
end
@nospecialize
@recipe function f(::Type{Val{:barbins}}, x, y, z)
edge, weights, xscale, yscale, baseline =
_preprocess_binlike(plotattributes, x, y)
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
if (plotattributes[:bar_width] === nothing)
bar_width := diff(edge)
end
@@ -629,10 +628,8 @@ end
end
@deps barbins bar
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
edge, weights, xscale, yscale, baseline =
_preprocess_binlike(plotattributes, x, y)
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
@series begin
x := _bin_centers(edge)
xerror := diff(edge) / 2
@@ -649,13 +646,7 @@ end
@specialize
function _stepbins_path(
edge,
weights,
baseline::Real,
xscale::Symbol,
yscale::Symbol,
)
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
log_scale_x = xscale in _logScales
log_scale_y = yscale in _logScales
@@ -717,11 +708,9 @@ end
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
@nospecialize
axis =
plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
axis = plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
edge, weights, xscale, yscale, baseline =
_preprocess_binlike(plotattributes, x, y)
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
if !isvertical(plotattributes)
@@ -773,12 +762,9 @@ function _auto_binning_nbins(
# The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation
nd = n_samples^(1 / (2 + N))
nd = N == 2 ?
min(
n_samples^(1 / (2 + N)),
nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8),
) :
nd # the >2-dimensional case does not have a nice solution to correlations
nd =
N == 2 ?
min(n_samples^(1 / (2 + N)), nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8)) : nd # the >2-dimensional case does not have a nice solution to correlations
v = vs[dim]
@@ -807,11 +793,8 @@ _hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {
StatsBase.histrange(vs[dim], binning, :left)
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} =
_hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
_hist_edge(
vs::NTuple{N,AbstractVector},
dim::Integer,
binning::AbstractVector,
) where {N} = binning
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) where {N} =
binning
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N,Any}) where {N} =
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...,))
@@ -841,8 +824,8 @@ function _make_hist(
edges = _hist_edges(localvs, binning)
h = float(
weights === nothing ?
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
StatsBase.fit(
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
StatsBase.fit(
StatsBase.Histogram,
localvs,
StatsBase.Weights(weights),
@@ -903,7 +886,6 @@ end
end
@deps scatterhist scatterbins
@recipe function f(h::StatsBase.Histogram{T,1,E}) where {T,E}
seriestype --> :barbins
@@ -913,8 +895,7 @@ end
:step => :stepbins,
:steppost => :stepbins, # :step can be mapped to :steppost in pre-processing
)
seriestype :=
get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
if plotattributes[:seriestype] == :scatterbins
# Workaround, error bars currently not set correctly by scatterbins
@@ -928,7 +909,6 @@ end
end
end
@recipe function f(hv::AbstractVector{H}) where {H<:StatsBase.Histogram}
for h in hv
@series begin
@@ -937,7 +917,6 @@ end
end
end
# ---------------------------------------------------------------------------
# Histogram 2D
@@ -964,7 +943,6 @@ end
end
Plots.@deps bins2d heatmap
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
h = _make_hist(
(x, y),
@@ -980,13 +958,11 @@ Plots.@deps bins2d heatmap
end
@deps histogram2d bins2d
@recipe function f(h::StatsBase.Histogram{T,2,E}) where {T,E}
seriestype --> :bins2d
(h.edges[1], h.edges[2], Surface(h.weights))
end
# ---------------------------------------------------------------------------
# pie
@recipe function f(::Type{Val{:pie}}, x, y, z)
@@ -1008,7 +984,6 @@ end
end
@deps pie shape
# ---------------------------------------------------------------------------
# mesh 3d replacement for non-plotly backends
@@ -1016,12 +991,15 @@ end
# As long as no i,j,k are supplied this should work with PyPlot and GR
seriestype := :surface
if plotattributes[:connections] !== nothing
throw(ArgumentError("Giving triangles using the connections argument is only supported on Plotly backend."))
throw(
ArgumentError(
"Giving triangles using the connections argument is only supported on Plotly backend.",
),
)
end
()
end
# ---------------------------------------------------------------------------
# scatter 3d
@@ -1039,7 +1017,7 @@ end
# ---------------------------------------------------------------------------
# lens! - magnify a region of a plot
lens!(args...;kwargs...) = plot!(args...; seriestype=:lens, kwargs...)
lens!(args...; kwargs...) = plot!(args...; seriestype = :lens, kwargs...)
export lens!
@recipe function f(::Type{Val{:lens}}, plt::AbstractPlot)
sp_index, inset_bbox = plotattributes[:inset_subplots]
@@ -1067,11 +1045,12 @@ export lens!
linecolor := :lightgray
bbx_mag = (x1 + x2) / 2
bby_mag = (y1 + y2) / 2
xi_lens, yi_lens = intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
xi_mag, yi_mag = intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
xi_lens, yi_lens =
intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
xi_mag, yi_mag =
intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
# add lines
if xl1 < xi_lens < xl2 &&
yl1 < yi_lens < yl2
if xl1 < xi_lens < xl2 && yl1 < yi_lens < yl2
@series begin
primary := false
subplot := sp_index
@@ -1116,14 +1095,14 @@ function intersection_point(xA, yA, xB, yB, h, w)
else # left
return xB - hw, yB - s * hw
end
# top or bot?
elseif -hw <= hh/s <= hw
# top or bot?
elseif -hw <= hh / s <= hw
if yA > yB
# top
return xB + hh/s, yB + hh
return xB + hh / s, yB + hh
else
# bottom
return xB - hh/s, yB - hh
return xB - hh / s, yB - hh
end
end
end
@@ -1263,15 +1242,15 @@ function quiver_using_arrows(plotattributes::AKW)
if !isa(plotattributes[:arrow], Arrow)
plotattributes[:arrow] = arrow()
end
is_3d = haskey(plotattributes,:z) && !isnothing(plotattributes[:z])
is_3d = haskey(plotattributes, :z) && !isnothing(plotattributes[:z])
velocity = error_zipit(plotattributes[:quiver])
xorig, yorig = plotattributes[:x], plotattributes[:y]
zorig = is_3d ? plotattributes[:z] : []
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
x, y = zeros(0), zeros(0)
is_3d && ( z = zeros(0))
for i = 1:max(length(xorig), length(yorig), is_3d ? 0 : length(zorig))
is_3d && (z = zeros(0))
for i in 1:max(length(xorig), length(yorig), is_3d ? 0 : length(zorig))
# get the starting position
xi = _cycle(xorig, i)
yi = _cycle(yorig, i)
@@ -1321,7 +1300,7 @@ function quiver_using_hack(plotattributes::AKW)
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
pts = P2[]
for i = 1:max(length(xorig), length(yorig))
for i in 1:max(length(xorig), length(yorig))
# get the starting position
xi = _cycle(xorig, i)
@@ -1350,10 +1329,7 @@ function quiver_using_hack(plotattributes::AKW)
U2 *= arrow_w
ppv = p + v
nanappend!(
pts,
P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1],
)
nanappend!(pts, P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1])
end
plotattributes[:x], plotattributes[:y] = RecipesPipeline.unzip(pts[2:end])
@@ -1372,7 +1348,6 @@ end
end
@deps quiver shape path
# --------------------------------------------------------------------
# 1 argument
# --------------------------------------------------------------------
@@ -1404,7 +1379,7 @@ end
@nospecialize
# images - colors
@recipe function f(mat::AMat{T}) where {T <: Colorant}
@recipe function f(mat::AMat{T}) where {T<:Colorant}
n, m = axes(mat)
if is_seriestype_supported(:image)
@@ -1434,9 +1409,9 @@ end
# interpreted as having one element per shape
for attr in union(_segmenting_array_attributes, _segmenting_vector_attributes)
v = get(plotattributes, attr, nothing)
if v isa AVec || v isa AMat && size(v,2) == 1
if v isa AVec || v isa AMat && size(v, 2) == 1
@warn "Column vector attribute `$attr` reinterpreted as row vector (one value per shape).\n" *
"Pass a row vector instead (e.g. using `permutedims`) to suppress this warning."
"Pass a row vector instead (e.g. using `permutedims`) to suppress this warning."
plotattributes[attr] = permutedims(v)
end
end
@@ -1450,13 +1425,12 @@ end
end
end
# --------------------------------------------------------------------
# 3 arguments
# --------------------------------------------------------------------
# images - grays
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T <: Gray}
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T<:Gray}
if is_seriestype_supported(:image)
seriestype := :image
yflip --> true
@@ -1471,7 +1445,7 @@ end
end
# images - colors
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T <: Colorant}
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T<:Colorant}
if is_seriestype_supported(:image)
seriestype := :image
yflip --> true
@@ -1492,7 +1466,7 @@ end
@recipe f(p::GeometryBasics.Point) = [p]
# Special case for 4-tuples in :ohlc series
@recipe f(xyuv::AVec{<:Tuple{R1, R2, R3, R4}}) where {R1, R2, R3, R4} =
@recipe f(xyuv::AVec{<:Tuple{R1,R2,R3,R4}}) where {R1,R2,R3,R4} =
get(plotattributes, :seriestype, :path) == :ohlc ? OHLC[OHLC(t...) for t in xyuv] :
RecipesPipeline.unzip(xyuv)
@@ -1549,8 +1523,7 @@ end
@recipe f(
x::AVec,
ohlc::AVec{Tuple{R1,R2,R3,R4}},
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} =
x, OHLC[OHLC(t...) for t in ohlc]
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = x, OHLC[OHLC(t...) for t in ohlc]
@recipe function f(x::AVec, v::AVec{OHLC})
seriestype := :path
@@ -1569,7 +1542,6 @@ end
# TODO: everything below here should be either changed to a
# series recipe or moved to PlotRecipes
# "Sparsity plot... heatmap of non-zero values of a matrix"
# function spy{T<:Real}(z::AMat{T}; kw...)
# mat = reshape(map(zi->float(zi!=0), z),1,:)
@@ -1617,7 +1589,6 @@ end
@specialize
Plots.findnz(A::AbstractSparseMatrix) = SparseArrays.findnz(A)
# fallback function for finding non-zero elements of non-sparse matrices
@@ -1657,7 +1628,6 @@ end
x, real.(y), imag.(y)
end
# Moved in from PlotRecipes - see: http://stackoverflow.com/a/37732384/5075246
@userplot PortfolioComposition
+21 -24
View File
@@ -241,7 +241,6 @@ julia> ohlc(y)
"""
@shorthands ohlc
"""
contour(x,y,z)
contour!(x,y,z)
@@ -263,7 +262,6 @@ julia> contour(x, y, (x, y) -> x^2 + y^2)
"An alias for `contour` with fill = true."
@shorthands contourf
@shorthands contour3d
"""
@@ -408,17 +406,17 @@ julia> curves([1,2,3,4],[1,1,2,4])
@shorthands pie
"Plot with seriestype :path3d"
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
"Add title to an existing plot"
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
"Add xlabel to an existing plot"
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
"Add ylabel to an existing plot"
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
"Set xlims for an existing plot"
xlims!(lims::Tuple; kw...) = plot!(; xlims = lims, kw...)
@@ -429,21 +427,20 @@ ylims!(lims::Tuple; kw...) = plot!(; ylims = lims, kw...)
"Set zlims for an existing plot"
zlims!(lims::Tuple; kw...) = plot!(; zlims = lims, kw...)
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin,zmax), kw...)
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin, xmax), kw...)
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin, ymax), kw...)
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin, zmax), kw...)
"Set xticks for an existing plot"
xticks!(v::TicksArgs; kw...) = plot!(; xticks = v, kw...)
xticks!(v::TicksArgs; kw...) = plot!(; xticks = v, kw...)
"Set yticks for an existing plot"
yticks!(v::TicksArgs; kw...) = plot!(; yticks = v, kw...)
yticks!(v::TicksArgs; kw...) = plot!(; yticks = v, kw...)
xticks!(
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; xticks = (ticks,labels), kw...)
yticks!(
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; yticks = (ticks,labels), kw...)
xticks!(ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} =
plot!(; xticks = (ticks, labels), kw...)
yticks!(ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} =
plot!(; yticks = (ticks, labels), kw...)
"""
annotate!(anns...)
@@ -463,22 +460,22 @@ julia> annotate!([(7,3,"(7,3)"),(3,7,text("hey", 14, :left, :top, :green))])
julia> annotate!([(4, 4, ("More text", 8, 45.0, :bottom, :red))])
```
"""
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
annotate!(anns::Tuple...; kw...) = plot!(; annotation = collect(anns), kw...)
annotate!(anns::AVec{<:Tuple}; kw...) = plot!(; annotation = anns, kw...)
"Flip the current plots' x axis"
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
"Flip the current plots' y axis"
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
"Specify x axis attributes for an existing plot"
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
"Specify y axis attributes for an existing plot"
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
xgrid!(args...; kw...) = plot!(; xgrid = args, kw...)
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
xgrid!(args...; kw...) = plot!(; xgrid = args, kw...)
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
@specialize
+19 -21
View File
@@ -1,6 +1,5 @@
function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
function Subplot(::T; parent = RootLayout()) where {T<:AbstractBackend}
Subplot{T}(
parent,
Series[],
@@ -9,7 +8,7 @@ function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
defaultbox,
DefaultsDict(KW(), _subplot_defaults),
nothing,
nothing
nothing,
)
end
@@ -21,8 +20,7 @@ Return the bounding box of a subplot
plotarea(sp::Subplot) = sp.plotarea
plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox)
Base.size(sp::Subplot) = (1,1)
Base.size(sp::Subplot) = (1, 1)
Base.length(sp::Subplot) = 1
Base.getindex(sp::Subplot, r::Int, c::Int) = sp
@@ -43,23 +41,23 @@ series_list(sp::Subplot) = sp.series_list # filter(series -> series.plotattribut
function should_add_to_legend(series::Series)
series.plotattributes[:primary] &&
series.plotattributes[:label] != "" &&
!(
series.plotattributes[:seriestype] in (
:hexbin,
:bins2d,
:histogram2d,
:hline,
:vline,
:contour,
:contourf,
:contour3d,
:surface,
:wireframe,
:heatmap,
:image,
series.plotattributes[:label] != "" &&
!(
series.plotattributes[:seriestype] in (
:hexbin,
:bins2d,
:histogram2d,
:hline,
:vline,
:contour,
:contourf,
:contour3d,
:surface,
:wireframe,
:heatmap,
:image,
)
)
)
end
# ----------------------------------------------------------------------
+6 -10
View File
@@ -34,11 +34,8 @@ end
@userplot ShowTheme
_color_functions = KW(
:protanopic => protanopic,
:deuteranopic => deuteranopic,
:tritanopic => tritanopic,
)
_color_functions =
KW(:protanopic => protanopic, :deuteranopic => deuteranopic, :tritanopic => tritanopic)
_get_showtheme_args(thm::Symbol) = thm, identity
_get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func, identity)
@@ -110,8 +107,8 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
subplot := 4
seriestype := :heatmap
seriescolor := colorgradient
xticks := (-2π:2π:2π, string.(-2:2:2, "π"))
yticks := (-2π:2π:2π, string.(-2:2:2, "π"))
xticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
yticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
x, y, z
end
@@ -119,8 +116,8 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
subplot := 5
seriestype := :surface
seriescolor := colorgradient
xticks := (-2π:2π:2π, string.(-2:2:2, "π"))
yticks := (-2π:2π:2π, string.(-2:2:2, "π"))
xticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
yticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
x, y, z
end
@@ -137,5 +134,4 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
line_z := z
x, y, z
end
end
+16 -7
View File
@@ -6,7 +6,8 @@ const AVec = AbstractVector
const AMat = AbstractMatrix
const KW = Dict{Symbol,Any}
const AKW = AbstractDict{Symbol,Any}
const TicksArgs = Union{AVec{T}, Tuple{AVec{T}, AVec{S}}, Symbol} where {T<:Real, S<:AbstractString}
const TicksArgs =
Union{AVec{T},Tuple{AVec{T},AVec{S}},Symbol} where {T<:Real,S<:AbstractString}
struct PlotsDisplay <: AbstractDisplay end
@@ -59,11 +60,10 @@ Extrema() = Extrema(Inf, -Inf)
# -----------------------------------------------------------
const SubplotMap = Dict{Any, Subplot}
const SubplotMap = Dict{Any,Subplot}
# -----------------------------------------------------------
mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
backend::T # the backend type
n::Int # number of series
@@ -78,9 +78,18 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
end
function Plot()
Plot(backend(), 0, DefaultsDict(KW(), _plot_defaults), Series[], nothing,
Subplot[], SubplotMap(), EmptyLayout(),
Subplot[], false)
Plot(
backend(),
0,
DefaultsDict(KW(), _plot_defaults),
Series[],
nothing,
Subplot[],
SubplotMap(),
EmptyLayout(),
Subplot[],
false,
)
end
# -----------------------------------------------------------------------
@@ -89,7 +98,7 @@ Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
Base.length(plt::Plot) = length(plt.subplots)
Base.lastindex(plt::Plot) = length(plt)
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r, c]
Base.size(plt::Plot) = size(plt.layout)
Base.size(plt::Plot, i::Integer) = size(plt.layout)[i]
Base.ndims(plt::Plot) = 2
+253 -207
View File
@@ -1,10 +1,12 @@
function treats_y_as_x(seriestype)
return seriestype in (:vline, :vspan, :histogram, :barhist, :stephist, :scatterhist)
end
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
# ---------------------------------------------------------------
treats_y_as_x(seriestype) =
seriestype in (:vline, :vspan, :histogram, :barhist, :stephist, :scatterhist)
function replace_image_with_heatmap(z::Array{T}) where {T<:Colorant}
n, m = size(z)
colors = palette(vec(z))
newz = reshape(1:n*m, n, m)
newz = reshape(1:(n * m), n, m)
newz, colors
end
@@ -19,8 +21,7 @@ end
Segments() = Segments(Float64)
Segments(::Type{T}) where {T} = Segments(T[])
Segments(p::Int) = Segments(NTuple{p, Float64}[])
Segments(p::Int) = Segments(NTuple{p,Float64}[])
# Segments() = Segments(zeros(0))
@@ -29,30 +30,32 @@ to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN)
to_nan(::Type{NTuple{3,Float64}}) = (NaN, 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]
coords(segs::Segments{NTuple{3,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts], Float64[p[3] for p in segs.pts]
coords(segs::Segments{NTuple{2,Float64}}) =
Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
coords(segs::Segments{NTuple{3,Float64}}) = Float64[p[1] for p in segs.pts],
Float64[p[2] for p in segs.pts],
Float64[p[3] for p in segs.pts]
function Base.push!(segments::Segments{T}, vs...) where T
function Base.push!(segments::Segments{T}, vs...) where {T}
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
for v in vs
push!(segments.pts, convert(T,v))
push!(segments.pts, convert(T, v))
end
segments
end
function Base.push!(segments::Segments{T}, vs::AVec) where T
function Base.push!(segments::Segments{T}, vs::AVec) where {T}
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
for v in vs
push!(segments.pts, convert(T,v))
push!(segments.pts, convert(T, v))
end
segments
end
struct SeriesSegment
# indexes of this segement in series data vectors
range::UnitRange
@@ -75,7 +78,7 @@ function iter_segments(args...)
NaNSegmentsIterator(tup, n1, n2)
end
function series_segments(series::Series, seriestype::Symbol=:path; check=false)
function series_segments(series::Series, seriestype::Symbol = :path; check = false)
x, y, z = series[:x], series[:y], series[:z]
(x === nothing || isempty(x)) && return UnitRange{Int}[]
@@ -84,13 +87,13 @@ function series_segments(series::Series, seriestype::Symbol=:path; check=false)
if check
scales = :xscale, :yscale, :zscale
for (n, s) enumerate(args)
for (n, s) in enumerate(args)
scale = get(series, scales[n], :identity)
if scale _logScales
for (i, v) enumerate(s)
for (i, v) in enumerate(s)
if v <= 0
@warn "Invalid negative or zero value $v found at series index $i for $(scale) based $(scales[n])"
@debug "" exception=(DomainError(v), stacktrace())
@debug "" exception = (DomainError(v), stacktrace())
break
end
end
@@ -100,12 +103,12 @@ function series_segments(series::Series, seriestype::Symbol=:path; check=false)
segments = if has_attribute_segments(series)
Iterators.flatten(map(nan_segments) do r
if seriestype in (:scatter, :scatter3d)
(SeriesSegment(i:i, i) for i in r)
else
(SeriesSegment(i:i+1, i) for i in first(r):last(r)-1)
end
end)
if seriestype in (:scatter, :scatter3d)
(SeriesSegment(i:i, i) for i in r)
else
(SeriesSegment(i:(i + 1), i) for i in first(r):(last(r) - 1))
end
end)
else
(SeriesSegment(r, 1) for r in nan_segments)
end
@@ -116,13 +119,15 @@ end
function warn_on_attr_dim_mismatch(series, x, y, z, segments)
isempty(segments) && return
seg_range = UnitRange(minimum(first(seg.range) for seg in segments),
maximum(last(seg.range) for seg in segments))
seg_range = UnitRange(
minimum(first(seg.range) for seg in segments),
maximum(last(seg.range) for seg in segments),
)
for attr in _segmenting_vector_attributes
v = get(series, attr, nothing)
if v isa AVec && eachindex(v) != seg_range
@warn "Indices $(eachindex(v)) of attribute `$attr` does not match data indices $seg_range."
if any(v -> !isnothing(v) && any(isnan, v), (x,y,z))
if any(v -> !isnothing(v) && any(isnan, v), (x, y, z))
@info """Data contains NaNs or missing values, and indices of `$attr` vector do not match data indices.
If you intend elements of `$attr` to apply to individual NaN-separated segements in the data,
pass each segment in a separate vector instead, and use a row vector for `$attr`. Legend entries
@@ -136,20 +141,24 @@ function warn_on_attr_dim_mismatch(series, x, y, z, segments)
end
# helpers to figure out if there are NaN values in a list of array types
anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, args)
anynan(args::Tuple) = i -> anynan(i,args)
anynan(i::Int, args::Tuple) = any(a -> try
isnan(_cycle(a, i))
catch MethodError
false
end, args)
anynan(args::Tuple) = i -> anynan(i, args)
anynan(istart::Int, iend::Int, args::Tuple) = any(anynan(args), istart:iend)
allnan(istart::Int, iend::Int, args::Tuple) = all(anynan(args), istart:iend)
function Base.iterate(itr::NaNSegmentsIterator, nextidx::Int = itr.n1)
i = findfirst(!anynan(itr.args), nextidx:itr.n2)
i === nothing && return nothing
i = findfirst(!anynan(itr.args), nextidx:(itr.n2))
i === nothing && return
nextval = nextidx + i - 1
j = findfirst(anynan(itr.args), nextval:itr.n2)
j = findfirst(anynan(itr.args), nextval:(itr.n2))
nextnan = j === nothing ? itr.n2 + 1 : nextval + j - 1
nextval:nextnan-1, nextnan
nextval:(nextnan - 1), nextnan
end
Base.IteratorSize(::NaNSegmentsIterator) = Base.SizeUnknown()
@@ -161,7 +170,6 @@ float_extended_type(x::AbstractArray{T}) where {T<:Real} = Float64
# ------------------------------------------------------------------------------------
nop() = nothing
notimpl() = error("This has not been implemented yet")
@@ -171,12 +179,12 @@ isnothing(x) = false
_cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
_cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
_cycle(v::AVec, idx::Int) = v[mod(idx, axes(v,1))]
_cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[end, mod(idx, axes(v,2))] : v[:, mod(idx, axes(v,2))]
_cycle(v::AVec, idx::Int) = v[mod(idx, axes(v, 1))]
_cycle(v::AMat, idx::Int) = size(v, 1) == 1 ? v[end, mod(idx, axes(v, 2))] : v[:, mod(idx, axes(v, 2))]
_cycle(v, idx::Int) = v
_cycle(v::AVec, indices::AVec{Int}) = map(i -> _cycle(v,i), indices)
_cycle(v::AMat, indices::AVec{Int}) = map(i -> _cycle(v,i), indices)
_cycle(v::AVec, indices::AVec{Int}) = map(i -> _cycle(v, i), indices)
_cycle(v::AMat, indices::AVec{Int}) = map(i -> _cycle(v, i), indices)
_cycle(v, indices::AVec{Int}) = fill(v, length(indices))
_cycle(cl::PlotUtils.AbstractColorList, idx::Int) = cl[mod1(idx, end)]
@@ -191,23 +199,22 @@ makevec(v::AVec) = v
makevec(v::T) where {T} = T[v]
"duplicate a single value, or pass the 2-tuple through"
maketuple(x::Real) = (x,x)
maketuple(x::Real) = (x, x)
maketuple(x::Tuple{T,S}) where {T,S} = x
for i in 2:4
@eval begin
RecipesPipeline.unzip(
v::Union{AVec{<:Tuple{Vararg{T,$i} where T}}, AVec{<:GeometryBasics.Point{$i}}},
) = $(Expr(:tuple, (:([t[$j] for t in v]) for j=1:i)...))
v::Union{AVec{<:NTuple{$i,T} where T},AVec{<:GeometryBasics.Point{$i}}},
) = $(Expr(:tuple, (:([t[$j] for t in v]) for j in 1:i)...))
end
end
RecipesPipeline.unzip(
::Union{AVec{<:GeometryBasics.Point{N}}, AVec{<:Tuple{Vararg{T,N} where T}}}
) where N = error("$N-dimensional unzip not implemented.")
RecipesPipeline.unzip(::Union{AVec{<:GeometryBasics.Point}, AVec{<:Tuple}}) = error(
"Can't unzip points of different dimensions."
)
RecipesPipeline.unzip(::Union{AVec{<:GeometryBasics.Point{N}},AVec{<:NTuple{N,T} where T}}) where {N} =
error("$N-dimensional unzip not implemented.")
RecipesPipeline.unzip(::Union{AVec{<:GeometryBasics.Point},AVec{<:Tuple}}) =
error("Can't unzip points of different dimensions.")
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
function _expand_limits(lims, x)
@@ -220,11 +227,11 @@ function _expand_limits(lims, x)
nothing
end
expand_data(v, n::Integer) = [_cycle(v, i) for i=1:n]
expand_data(v, n::Integer) = [_cycle(v, i) for i in 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...)
for (i,vi) in enumerate(v)
for (i, vi) in enumerate(v)
if isa(vi, t)
v[i] = t(args...; kw...)
return
@@ -252,29 +259,42 @@ function replaceAliases!(plotattributes::AKW, aliases::Dict{Symbol,Symbol})
end
end
createSegments(z) = collect(repeat(reshape(z,1,:),2,1))[2:end]
createSegments(z) = collect(repeat(reshape(z, 1, :), 2, 1))[2:end]
sortedkeys(plotattributes::Dict) = sort(collect(keys(plotattributes)))
function _heatmap_edges(v::AVec, isedges::Bool = false, ispolar::Bool = false)
length(v) == 1 && return v[1] .+ [ispolar ? max(-v[1], -0.5) : -0.5, 0.5]
if isedges return v end
if isedges
return v
end
# `isedges = true` means that v is a vector which already describes edges
# and does not need to be extended.
vmin, vmax = ignorenan_extrema(v)
extra_min = ispolar ? min(v[1], (v[2] - v[1]) / 2) : (v[2] - v[1]) / 2
extra_max = (v[end] - v[end - 1]) / 2
vcat(vmin-extra_min, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra_max)
vcat(vmin - extra_min, 0.5 * (v[1:(end - 1)] + v[2:end]), vmax + extra_max)
end
"create an (n+1) list of the outsides of heatmap rectangles"
function heatmap_edges(v::AVec, scale::Symbol = :identity, isedges::Bool = false, ispolar::Bool = false)
function heatmap_edges(
v::AVec,
scale::Symbol = :identity,
isedges::Bool = false,
ispolar::Bool = false,
)
f, invf = RecipesPipeline.scale_func(scale), RecipesPipeline.inverse_scale_func(scale)
map(invf, _heatmap_edges(map(f,v), isedges, ispolar))
map(invf, _heatmap_edges(map(f, v), isedges, ispolar))
end
function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size::Tuple{Int, Int}, ispolar::Bool = false)
function heatmap_edges(
x::AVec,
xscale::Symbol,
y::AVec,
yscale::Symbol,
z_size::Tuple{Int,Int},
ispolar::Bool = false,
)
nx, ny = length(x), length(y)
# ismidpoints = z_size == (ny, nx) # This fails some tests, but would actually be
# the correct check, since (4, 3) != (3, 4) and a missleading plot is produced.
@@ -285,12 +305,11 @@ function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size:
Must be either `size(z) == (length(y), length(x))` (x & y define midpoints)
or `size(z) == (length(y)+1, length(x)+1))` (x & y define edges).""")
end
x, y = heatmap_edges(x, xscale, isedges),
heatmap_edges(y, yscale, isedges, ispolar) # special handle for `r` in polar plots
x, y = heatmap_edges(x, xscale, isedges), heatmap_edges(y, yscale, isedges, ispolar) # special handle for `r` in polar plots
return x, y
end
function is_uniformly_spaced(v; tol=1e-6)
function is_uniformly_spaced(v; tol = 1e-6)
dv = diff(v)
maximum(dv) - minimum(dv) < tol * mean(abs.(dv))
end
@@ -298,15 +317,17 @@ end
function convert_to_polar(theta, r, r_extrema = ignorenan_extrema(r))
rmin, rmax = r_extrema
r = (r .- rmin) ./ (rmax .- rmin)
x = r.*cos.(theta)
y = r.*sin.(theta)
x = r .* cos.(theta)
y = r .* sin.(theta)
x, y
end
function fakedata(sz...)
fakedata(sz::Int...) = fakedata(Random.seed!(PLOTS_SEED), sz...)
function fakedata(rng::AbstractRNG, sz...)
y = zeros(sz...)
for r in 2:size(y,1)
y[r,:] = 0.95 * vec(y[r-1,:]) + randn(size(y,2))
for r in 2:size(y, 1)
y[r, :] = 0.95 * vec(y[r - 1, :]) + randn(rng, size(y, 2))
end
y
end
@@ -325,20 +346,19 @@ isscalar(::Any) = false
is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2
isvertical(plotattributes::AKW) = get(plotattributes, :orientation, :vertical) in (:vertical, :v, :vert)
isvertical(plotattributes::AKW) =
get(plotattributes, :orientation, :vertical) in (:vertical, :v, :vert)
isvertical(series::Series) = isvertical(series.plotattributes)
ticksType(ticks::AVec{T}) where {T<:Real} = :ticks
ticksType(ticks::AVec{T}) where {T<:AbstractString} = :labels
ticksType(ticks::Tuple{T,S}) where {T<:Union{AVec,Tuple},S<:Union{AVec,Tuple}} =
:ticks_and_labels
ticksType(ticks) = :invalid
ticksType(ticks::AVec{T}) where {T<:Real} = :ticks
ticksType(ticks::AVec{T}) where {T<:AbstractString} = :labels
ticksType(ticks::Tuple{T,S}) where {T<:Union{AVec,Tuple},S<:Union{AVec,Tuple}} = :ticks_and_labels
ticksType(ticks) = :invalid
limsType(lims::Tuple{T,S}) where {T<:Real,S<:Real} = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :invalid
limsType(lims::Tuple{T,S}) where {T<:Real,S<:Real} = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :invalid
# recursively merge kw-dicts, e.g. for merging extra_kwargs / extra_plot_kwargs in plotly)
recursive_merge(x::AbstractDict...) = merge(recursive_merge, x...)
@@ -357,9 +377,9 @@ function nansplit(v::AVec)
push!(vs, v)
break
elseif idx > 1
push!(vs, v[1:idx-1])
push!(vs, v[1:(idx - 1)])
end
v = v[idx+1:end]
v = v[(idx + 1):end]
end
vs
end
@@ -376,7 +396,7 @@ end
# returns the array of indices (znew) and a vector of unique values (vals)
function indices_and_unique_values(z::AbstractArray)
vals = sort(unique(z))
vmap = Dict([(v,i) for (i,v) in enumerate(vals)])
vmap = Dict([(v, i) for (i, v) in enumerate(vals)])
newz = map(zi -> vmap[zi], z)
newz, vals
end
@@ -384,16 +404,14 @@ end
handle_surface(z) = z
handle_surface(z::Surface) = permutedims(z.surf)
function ok(x::Number, y::Number, z::Number = 0)
isfinite(x) && isfinite(y) && isfinite(z)
end
ok(x::Number, y::Number, z::Number = 0) = isfinite(x) && isfinite(y) && isfinite(z)
ok(tup::Tuple) = ok(tup...)
# compute one side of a fill range from a ribbon
function make_fillrange_side(y::AVec, rib)
frs = zeros(axes(y))
for (i, yi) in pairs(y)
frs[i] = yi + _cycle(rib,i)
frs[i] = yi + _cycle(rib, i)
end
frs
end
@@ -409,16 +427,14 @@ function make_fillrange_from_ribbon(kw::AKW)
end
#turn tuple of fillranges to one path
function concatenate_fillrange(x,y::Tuple)
function concatenate_fillrange(x, y::Tuple)
rib1, rib2 = first(y), last(y)
yline = vcat(rib1,(rib2)[end:-1:1])
xline = vcat(x,x[end:-1:1])
yline = vcat(rib1, (rib2)[end:-1:1])
xline = vcat(x, x[end:-1:1])
return xline, yline
end
function get_sp_lims(sp::Subplot, letter::Symbol)
axis_limits(sp, letter)
end
get_sp_lims(sp::Subplot, letter::Symbol) = axis_limits(sp, letter)
"""
xlims([plt])
@@ -456,18 +472,15 @@ function contour_levels(series::Series, clims)
zmin, zmax = clims
levels = series[:levels]
if levels isa Integer
levels = range(zmin, stop=zmax, length=levels+2)
levels = range(zmin, stop = zmax, length = levels + 2)
if !isfilledcontour(series)
levels = levels[2:end-1]
levels = levels[2:(end - 1)]
end
end
levels
end
for comp in (:line, :fill, :marker)
compcolor = string(comp, :color)
get_compcolor = Symbol(:get_, compcolor)
comp_z = string(comp, :_z)
@@ -476,7 +489,6 @@ for comp in (:line, :fill, :marker)
get_compalpha = Symbol(:get_, compalpha)
@eval begin
function $get_compcolor(series, cmin::Real, cmax::Real, i::Int = 1)
c = series[$Symbol($compcolor)]
z = series[$Symbol($comp_z)]
@@ -487,7 +499,8 @@ for comp in (:line, :fill, :marker)
end
end
$get_compcolor(series, clims, i::Int = 1) = $get_compcolor(series, clims[1], clims[2], i)
$get_compcolor(series, clims, i::Int = 1) =
$get_compcolor(series, clims[1], clims[2], i)
function $get_compcolor(series, i::Int = 1)
if series[$Symbol($comp_z)] === nothing
@@ -509,7 +522,7 @@ function get_colorgradient(series::Series)
series[:linecolor]
elseif series[:marker_z] !== nothing
series[:markercolor]
elseif series[:line_z] !== nothing
elseif series[:line_z] !== nothing
series[:linecolor]
elseif series[:fill_z] !== nothing
series[:fillcolor]
@@ -523,26 +536,17 @@ get_gradient(c) = cgrad()
get_gradient(cg::ColorGradient) = cg
get_gradient(cp::ColorPalette) = cgrad(cp, categorical = true)
function get_linewidth(series, i::Int = 1)
_cycle(series[:linewidth], i)
end
function get_linestyle(series, i::Int = 1)
_cycle(series[:linestyle], i)
end
get_linewidth(series, i::Int = 1) = _cycle(series[:linewidth], i)
get_linestyle(series, i::Int = 1) = _cycle(series[:linestyle], i)
get_fillstyle(series, i::Int = 1) = _cycle(series[:fillstyle], i)
function get_markerstrokecolor(series, i::Int = 1)
msc = series[:markerstrokecolor]
isa(msc, ColorGradient) ? msc : _cycle(msc, i)
end
function get_markerstrokealpha(series, i::Int = 1)
_cycle(series[:markerstrokealpha], i)
end
function get_markerstrokewidth(series, i::Int = 1)
_cycle(series[:markerstrokewidth], i)
end
get_markerstrokealpha(series, i::Int = 1) = _cycle(series[:markerstrokealpha], i)
get_markerstrokewidth(series, i::Int = 1) = _cycle(series[:markerstrokewidth], i)
const _segmenting_vector_attributes = (
:seriescolor,
@@ -553,6 +557,7 @@ const _segmenting_vector_attributes = (
:linestyle,
:fillcolor,
:fillalpha,
:fillstyle,
:markercolor,
:markeralpha,
:markersize,
@@ -562,16 +567,17 @@ const _segmenting_vector_attributes = (
:markershape,
)
const _segmenting_array_attributes = (:line_z, :fill_z, :marker_z)
const _segmenting_array_attributes = :line_z, :fill_z, :marker_z
function has_attribute_segments(series::Series)
# we want to check if a series needs to be split into segments just because
# of its attributes
series[:seriestype] == :shape && return false
# check relevant attributes if they have multiple inputs
return any(series[attr] isa AbstractVector && length(series[attr]) > 1
for attr in _segmenting_vector_attributes
) || any(series[attr] isa AbstractArray for attr in _segmenting_array_attributes)
return any(
series[attr] isa AbstractVector && length(series[attr]) > 1 for
attr in _segmenting_vector_attributes
) || any(series[attr] isa AbstractArray for attr in _segmenting_array_attributes)
end
function get_aspect_ratio(sp)
@@ -587,10 +593,10 @@ function get_aspect_ratio(sp)
return aspect_ratio
end
get_size(series::Series) = get_size(series.plotattributes[:subplot])
get_size(kw) = get(kw, :size, default(:size))
get_size(plt::Plot) = get_size(plt.attr)
get_size(sp::Subplot) = get_size(sp.plt)
get_size(series::Series) = get_size(series.plotattributes[:subplot])
get_thickness_scaling(kw) = get(kw, :thickness_scaling, default(:thickness_scaling))
get_thickness_scaling(plt::Plot) = get_thickness_scaling(plt.attr)
@@ -599,7 +605,6 @@ get_thickness_scaling(series::Series) =
get_thickness_scaling(series.plotattributes[:subplot])
# ---------------------------------------------------------------
makekw(; kw...) = KW(kw)
wraptuple(x::Tuple) = x
@@ -608,18 +613,23 @@ wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
trueOrAllTrue(f::Function, x) = f(x)
allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
allShapes(arg) = trueOrAllTrue(a -> 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)
allReals(arg) = trueOrAllTrue(a -> typeof(a) <: Real, arg)
allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
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 -> 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,
)
allReals(arg) = trueOrAllTrue(a -> typeof(a) <: Real, arg)
allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
# ---------------------------------------------------------------
# ---------------------------------------------------------------
"""
Allows temporary setting of backend and defaults for Plots. Settings apply only for the `do` block. Example:
@@ -653,7 +663,6 @@ function with(f::Function, args...; kw...)
oldbackend = CURRENT_BACKEND.sym
for arg in args
# change backend?
if arg in backends()
backend(arg)
@@ -714,10 +723,7 @@ mutable struct DebugMode
end
const _debugMode = DebugMode(false)
function debugplots(on = true)
_debugMode.on = on
end
debugplots(on = true) = _debugMode.on = on
debugshow(io, x) = show(io, x)
debugshow(io, x::AbstractArray) = print(io, summary(x))
@@ -737,9 +743,7 @@ end
DD(io::IO, plotattributes::AKW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
DD(plotattributes::AKW, prefix = "") = DD(stdout, plotattributes, prefix)
function dumpcallstack()
error() # well... you wanted the stacktrace, didn't you?!?
end
dumpcallstack() = error() # well... you wanted the stacktrace, didn't you?!?
# -------------------------------------------------------
# NOTE: backends should implement the following methods to get/set the x/y/z data objects
@@ -771,17 +775,17 @@ function setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z}
_series_updated(plt, series)
end
function setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z<:AbstractMatrix}
setxyz!(plt, (xyz[1], xyz[2], Surface(xyz[3])), i)
end
setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z<:AbstractMatrix} =
(setxyz!(plt, (xyz[1], xyz[2], Surface(xyz[3])), i))
# -------------------------------------------------------
# indexing notation
# Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
Base.setindex!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y} = (setxy!(plt, xy, i); plt)
Base.setindex!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z} = (setxyz!(plt, xyz, i); plt)
Base.setindex!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y} =
(setxy!(plt, xy, i); plt)
Base.setindex!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z} =
(setxyz!(plt, xyz, i); plt)
# -------------------------------------------------------
# operate on individual series
@@ -812,7 +816,7 @@ end
function extend_series_data!(series::Series, v, letter)
copy_series!(series, letter)
d = extend_by_data!(series[letter], v)
expand_extrema!(series[:subplot][Symbol(letter, :axis)], d)
expand_extrema!(series[:subplot][get_attr_symbol(letter, :axis)], d)
return d
end
@@ -835,16 +839,15 @@ function extend_to_length!(v::AbstractVector, n)
extend_by_data!(v, vmax .+ (1:(n - length(v))))
end
extend_by_data!(v::AbstractVector, x) = isimmutable(v) ? vcat(v, x) : push!(v, x)
function extend_by_data!(v::AbstractVector, x::AbstractVector)
extend_by_data!(v::AbstractVector, x::AbstractVector) =
isimmutable(v) ? vcat(v, x) : append!(v, x)
end
# -------------------------------------------------------
function attr!(series::Series; kw...)
plotattributes = KW(kw)
RecipesPipeline.preprocess_attributes!(plotattributes)
for (k,v) in plotattributes
for (k, v) in plotattributes
if haskey(_series_defaults, k)
series[k] = v
else
@@ -858,7 +861,7 @@ end
function attr!(sp::Subplot; kw...)
plotattributes = KW(kw)
RecipesPipeline.preprocess_attributes!(plotattributes)
for (k,v) in plotattributes
for (k, v) in plotattributes
if haskey(_subplot_defaults, k)
sp[k] = v
else
@@ -888,24 +891,22 @@ Base.append!(plt::Plot, i::Integer, t::Tuple) = append!(plt, i, t...)
# push y[i] to the ith series
function Base.push!(plt::Plot, y::AVec)
ny = length(y)
for i in 1:plt.n
push!(plt, i, y[mod1(i,ny)])
for i in 1:(plt.n)
push!(plt, i, y[mod1(i, ny)])
end
plt
end
# push y[i] to the ith series
# same x for each series
function Base.push!(plt::Plot, x::Real, y::AVec)
push!(plt, [x], y)
end
Base.push!(plt::Plot, x::Real, y::AVec) = push!(plt, [x], y)
# push (x[i], y[i]) to the ith series
function Base.push!(plt::Plot, x::AVec, y::AVec)
nx = length(x)
ny = length(y)
for i in 1:plt.n
push!(plt, i, x[mod1(i,nx)], y[mod1(i,ny)])
for i in 1:(plt.n)
push!(plt, i, x[mod1(i, nx)], y[mod1(i, ny)])
end
plt
end
@@ -915,43 +916,41 @@ function Base.push!(plt::Plot, x::AVec, y::AVec, z::AVec)
nx = length(x)
ny = length(y)
nz = length(z)
for i in 1:plt.n
push!(plt, i, x[mod1(i,nx)], y[mod1(i,ny)], z[mod1(i,nz)])
for i in 1:(plt.n)
push!(plt, i, x[mod1(i, nx)], y[mod1(i, ny)], z[mod1(i, nz)])
end
plt
end
# ---------------------------------------------------------------
# Some conversion functions
# note: I borrowed these conversion constants from Compose.jl's Measure
const PX_PER_INCH = 100
const DPI = PX_PER_INCH
const MM_PER_INCH = 25.4
const MM_PER_PX = MM_PER_INCH / PX_PER_INCH
inch2px(inches::Real) = float(inches * PX_PER_INCH)
px2inch(px::Real) = float(px / PX_PER_INCH)
inch2mm(inches::Real) = float(inches * MM_PER_INCH)
mm2inch(mm::Real) = float(mm / MM_PER_INCH)
px2mm(px::Real) = float(px * MM_PER_PX)
mm2px(mm::Real) = float(mm / MM_PER_PX)
const PX_PER_INCH = 100
const DPI = PX_PER_INCH
const MM_PER_INCH = 25.4
const MM_PER_PX = MM_PER_INCH / PX_PER_INCH
inch2px(inches::Real) = float(inches * PX_PER_INCH)
px2inch(px::Real) = float(px / PX_PER_INCH)
inch2mm(inches::Real) = float(inches * MM_PER_INCH)
mm2inch(mm::Real) = float(mm / MM_PER_INCH)
px2mm(px::Real) = float(px * MM_PER_PX)
mm2px(mm::Real) = float(mm / MM_PER_PX)
"Smallest x in plot"
xmin(plt::Plot) = ignorenan_minimum([ignorenan_minimum(series.plotattributes[:x]) for series in plt.series_list])
xmin(plt::Plot) = ignorenan_minimum([
ignorenan_minimum(series.plotattributes[:x]) for series in plt.series_list
])
"Largest x in plot"
xmax(plt::Plot) = ignorenan_maximum([ignorenan_maximum(series.plotattributes[:x]) for series in plt.series_list])
xmax(plt::Plot) = ignorenan_maximum([
ignorenan_maximum(series.plotattributes[:x]) for series in plt.series_list
])
"Extrema of x-values in plot"
ignorenan_extrema(plt::Plot) = (xmin(plt), xmax(plt))
# ---------------------------------------------------------------
# get fonts from objects:
@@ -1024,18 +1023,18 @@ guidefont(ax::Axis) = font(;
function convert_sci_unicode(label::AbstractString)
unicode_dict = Dict(
'⁰' => "0",
'¹' => "1",
'²' => "2",
'³' => "3",
'⁴' => "4",
'⁵' => "5",
'⁶' => "6",
'⁷' => "7",
'⁸' => "8",
'⁹' => "9",
'⁻' => "-",
"×10" => "×10^{",
'⁰' => "0",
'¹' => "1",
'²' => "2",
'³' => "3",
'⁴' => "4",
'⁵' => "5",
'⁶' => "6",
'⁷' => "7",
'⁸' => "8",
'⁹' => "9",
'⁻' => "-",
"×10" => "×10^{",
)
for key in keys(unicode_dict)
label = replace(label, key => unicode_dict[key])
@@ -1070,11 +1069,14 @@ function straightline_data(series, expansion_factor = 1)
xdata, ydata = fill(NaN, n), fill(NaN, n)
for i in 1:k
inds = (3 * i - 2):(3 * i - 1)
xdata[inds], ydata[inds] = straightline_data(xl, yl, x[inds], y[inds], expansion_factor)
xdata[inds], ydata[inds] =
straightline_data(xl, yl, x[inds], y[inds], expansion_factor)
end
xdata, ydata
else
error("Misformed data. `straightline_data` either accepts vectors of length 2 or 3k. The provided series has length $n")
error(
"Misformed data. `straightline_data` either accepts vectors of length 2 or 3k. The provided series has length $n",
)
end
end
@@ -1096,7 +1098,10 @@ function straightline_data(xl, yl, x, y, expansion_factor = 1)
b = y[1] - (y[1] - y[2]) * x[1] / (x[1] - x[2])
a = (y[1] - y[2]) / (x[1] - x[2])
# get the data values
xdata = [clamp(x[1] + (x[1] - x[2]) * (ylim - y[1]) / (y[1] - y[2]), xl...) for ylim in yl]
xdata = [
clamp(x[1] + (x[1] - x[2]) * (ylim - y[1]) / (y[1] - y[2]), xl...) for
ylim in yl
]
xdata, a .* xdata .+ b
end
@@ -1137,40 +1142,81 @@ function shape_data(series, expansion_factor = 1)
return x, y
end
function construct_categorical_data(x::AbstractArray, axis::Axis)
map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x)
end
construct_categorical_data(x::AbstractArray, axis::Axis) =
(map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x))
_fmt_paragraph(paragraph::AbstractString;kwargs...) = _fmt_paragraph(IOBuffer(),paragraph,0;kwargs...)
function _fmt_paragraph(io::IOBuffer,
remaining_text::AbstractString,
column_count::Integer;
fillwidth=60,
leadingspaces=0)
_fmt_paragraph(paragraph::AbstractString; kwargs...) =
_fmt_paragraph(IOBuffer(), paragraph, 0; kwargs...)
function _fmt_paragraph(
io::IOBuffer,
remaining_text::AbstractString,
column_count::Integer;
fillwidth = 60,
leadingspaces = 0,
)
kwargs = (fillwidth = fillwidth, leadingspaces = leadingspaces)
m = match(r"(.*?) (.*)",remaining_text)
if isa(m,Nothing)
m = match(r"(.*?) (.*)", remaining_text)
if isa(m, Nothing)
if column_count + length(remaining_text) fillwidth
print(io,remaining_text)
print(io, remaining_text)
String(take!(io))
else
print(io,"\n"*" "^leadingspaces*remaining_text)
print(io, "\n" * " "^leadingspaces * remaining_text)
String(take!(io))
end
else
if column_count + length(m[1]) fillwidth
print(io,"$(m[1]) ")
_fmt_paragraph(io,m[2],column_count + length(m[1]) + 1;kwargs...)
print(io, "$(m[1]) ")
_fmt_paragraph(io, m[2], column_count + length(m[1]) + 1; kwargs...)
else
print(io,"\n"*" "^leadingspaces*"$(m[1]) ")
_fmt_paragraph(io,m[2],leadingspaces;kwargs...)
print(io, "\n" * " "^leadingspaces * "$(m[1]) ")
_fmt_paragraph(io, m[2], leadingspaces; kwargs...)
end
end
end
function _document_argument(S::AbstractString)
_fmt_paragraph("`$S`: "*_arg_desc[Symbol(S)],leadingspaces = 6 + length(S))
_document_argument(S::AbstractString) =
_fmt_paragraph("`$S`: " * _arg_desc[Symbol(S)], leadingspaces = 6 + length(S))
function mesh3d_triangles(x, y, z, cns)
if typeof(cns) <: Tuple{Array,Array,Array}
ci, cj, ck = cns
if !(length(ci) == length(cj) == length(ck))
throw(
ArgumentError("Argument connections must consist of equally sized arrays."),
)
end
else
throw(ArgumentError("Argument connections has to be a tuple of three arrays."))
end
X = zeros(eltype(x), 4length(ci))
Y = zeros(eltype(y), 4length(cj))
Z = zeros(eltype(z), 4length(ck))
@inbounds for I in 1:length(ci)
i = ci[I] + 1 # connections are 0-based
j = cj[I] + 1
k = ck[I] + 1
m = 4(I - 1) + 1
n = m + 1
o = m + 2
p = m + 3
X[m] = X[p] = x[i]
Y[m] = Y[p] = y[i]
Z[m] = Z[p] = z[i]
X[n] = x[j]
Y[n] = y[j]
Z[n] = z[j]
X[o] = x[k]
Y[o] = y[k]
Z[o] = z[k]
end
return X, Y, Z
end
# cache joined symbols so they can be looked up instead of constructed each time
const _attrsymbolcache = Dict{Symbol,Dict{Symbol,Symbol}}()
get_attr_symbol(letter::Symbol, keyword::String) = get_attr_symbol(letter, Symbol(keyword))
get_attr_symbol(letter::Symbol, keyword::Symbol) = _attrsymbolcache[letter][keyword]
+10 -9
View File
@@ -3,17 +3,17 @@ import Plots._current_plots_version
# replace `f(args...)` with `f(rng, args...)` for `f ∈ (rand, randn)`
function replace_rand!(ex) end
function replace_rand!(ex::Expr)
for arg in ex.args
replace_rand!(arg)
end
if ex.head === :call && ex.args[1] (:rand, :randn)
pushfirst!(ex.args, ex.args[1])
ex.args[2] = :rng
end
for arg in ex.args
replace_rand!(arg)
end
if ex.head === :call && ex.args[1] (:rand, :randn, :(Plots.fakedata))
pushfirst!(ex.args, ex.args[1])
ex.args[2] = :rng
end
end
function fix_rand!(ex)
replace_rand!(ex)
pushfirst!(ex.args[1].args, :(rng = StableRNG(1234)))
pushfirst!(ex.args[1].args, :(rng = StableRNG(PLOTS_SEED)))
end
function image_comparison_tests(
@@ -35,6 +35,7 @@ function image_comparison_tests(
fn = "ref$idx.png"
reffn = reference_file(pkg, idx, _current_plots_version)
newfn = joinpath(reference_path(pkg, _current_plots_version), fn)
@debug example.exprs
# test function
func = (fn, idx) -> begin
@@ -58,7 +59,7 @@ function image_comparison_facts(
sigma = [1, 1], # number of pixels to "blur"
tol = 1e-2,
) # acceptable error (percent)
for i = 1:length(Plots._examples)
for i in 1:length(Plots._examples)
i in skip && continue
if only === nothing || i in only
@test image_comparison_tests(pkg, i, debug = debug, sigma = sigma, tol = tol) |>
+15 -15
View File
@@ -1,13 +1,13 @@
using Plots, Test, Dates
@testset "Limits" begin
y=[1.0*i*i for i in 1:10]
x=[Date(2019,11,i) for i in 1:10]
y = [1.0 * i * i for i in 1:10]
x = [Date(2019, 11, i) for i in 1:10]
rx=[x[3],x[5]]
rx = [x[3], x[5]]
p = plot(x,y, widen = false)
vspan!(p, rx, label="", alpha=0.2)
p = plot(x, y, widen = false)
vspan!(p, rx, label = "", alpha = 0.2)
ref_ylims = (y[1], y[end])
ref_xlims = (x[1].instant.periods.value, x[end].instant.periods.value)
@@ -23,13 +23,13 @@ using Plots, Test, Dates
end # testset
@testset "Date xlims" begin
y=[1.0*i*i for i in 1:10]
x=[Date(2019,11,i) for i in 1:10]
span = (Date(2019,10,31), Date(2019,11,11))
y = [1.0 * i * i for i in 1:10]
x = [Date(2019, 11, i) for i in 1:10]
span = (Date(2019, 10, 31), Date(2019, 11, 11))
ref_xlims = map(date->date.instant.periods.value, span)
ref_xlims = map(date -> date.instant.periods.value, span)
p = plot(x,y, xlims=span, widen = false)
p = plot(x, y, xlims = span, widen = false)
@test Plots.xlims(p) == ref_xlims
#@static if (haskey(ENV, "APPVEYOR") || haskey(ENV, "CI"))
@@ -42,13 +42,13 @@ end # testset
end # testset
@testset "DateTime xlims" begin
y=[1.0*i*i for i in 1:10]
x=[DateTime(2019,11,i,11) for i in 1:10]
span = (DateTime(2019,10,31,11,59,59), DateTime(2019,11,11,12,01,15))
y = [1.0 * i * i for i in 1:10]
x = [DateTime(2019, 11, i, 11) for i in 1:10]
span = (DateTime(2019, 10, 31, 11, 59, 59), DateTime(2019, 11, 11, 12, 01, 15))
ref_xlims = map(date->date.instant.periods.value, span)
ref_xlims = map(date -> date.instant.periods.value, span)
p = plot(x,y, xlims=span, widen = false)
p = plot(x, y, xlims = span, widen = false)
@test Plots.xlims(p) == ref_xlims
#@static if (haskey(ENV, "APPVEYOR") || haskey(ENV, "CI"))
@static if haskey(ENV, "APPVEYOR")
+66 -41
View File
@@ -1,4 +1,4 @@
using Plots: guidefont, series_annotations
using Plots: guidefont, series_annotations, PLOTS_SEED
import ImageMagick
using VisualRegressionTests
using Plots
@@ -12,19 +12,25 @@ using LibGit2
import GeometryBasics
using Dates
using RecipesBase
using JSON
@testset "Infrastructure" begin
@test_nowarn JSON.Parser.parse(
String(read(joinpath(dirname(pathof(Plots)), "..", ".zenodo.json"))),
)
end
@testset "Plotly standalone" begin
@test_nowarn Plots._init_ijulia_plotting()
@test Plots.plotly_local_file_path[] === nothing
temp = Plots.use_local_dependencies[]
withenv("PLOTS_HOST_DEPENDENCY_LOCAL" => true) do
Plots.__init__()
@test Plots.plotly_local_file_path[] isa String
@test isfile(Plots.plotly_local_file_path[])
@test Plots.use_local_dependencies[] = true
@test_nowarn Plots._init_ijulia_plotting()
end
Plots.__init__()
@test Plots.plotly_local_file_path[] isa String
@test isfile(Plots.plotly_local_file_path[])
@test Plots.use_local_dependencies[] = true
@test_nowarn Plots._init_ijulia_plotting()
end
Plots.plotly_local_file_path[] = nothing
Plots.use_local_dependencies[] = temp
end # testset
@@ -32,6 +38,7 @@ end # testset
include("test_defaults.jl")
include("test_pipeline.jl")
include("test_axes.jl")
include("test_contours.jl")
include("test_axis_letter.jl")
include("test_components.jl")
include("test_shorthands.jl")
@@ -39,13 +46,15 @@ include("integration_dates.jl")
include("test_recipes.jl")
include("test_hdf5plots.jl")
include("test_pgfplotsx.jl")
include("test_plotly.jl")
reference_dir(args...) = joinpath(homedir(), ".julia", "dev", "PlotReferenceImages", args...)
reference_dir(args...) =
joinpath(homedir(), ".julia", "dev", "PlotReferenceImages", args...)
function reference_file(backend, i, version)
refdir = reference_dir("Plots", string(backend))
fn = "ref$i.png"
versions = sort(VersionNumber.(readdir(refdir)), rev=true)
versions = sort(VersionNumber.(readdir(refdir)), rev = true)
reffn = joinpath(refdir, string(version), fn)
for v in versions
@@ -63,35 +72,38 @@ reference_path(backend, version) = reference_dir("Plots", string(backend), strin
if !isdir(reference_dir())
mkpath(reference_dir())
LibGit2.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git", reference_dir())
LibGit2.clone(
"https://github.com/JuliaPlots/PlotReferenceImages.jl.git",
reference_dir(),
)
end
include("imgcomp.jl")
# don't actually show the plots
Random.seed!(1234)
default(show=false, reuse=true)
Random.seed!(PLOTS_SEED)
default(show = false, reuse = true)
is_ci() = get(ENV, "CI", "false") == "true"
const IMG_TOL = VERSION < v"1.4" && Sys.iswindows() ? 1e-1 : is_ci() ? 1e-2 : 1e-3
const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? "1e-4" : "1e-5"))
## Uncomment the following lines to update reference images for different backends
# @testset "GR" begin
# image_comparison_facts(:gr, tol=IMG_TOL, skip = Plots._backend_skips[:gr])
# image_comparison_facts(:gr, tol=PLOTS_IMG_TOL, skip = Plots._backend_skips[:gr])
# end
#
# plotly()
# @testset "Plotly" begin
# image_comparison_facts(:plotly, tol=IMG_TOL, skip = Plots._backend_skips[:plotlyjs])
# image_comparison_facts(:plotly, tol=PLOTS_IMG_TOL, skip = Plots._backend_skips[:plotlyjs])
# end
#
# pyplot()
# @testset "PyPlot" begin
# image_comparison_facts(:pyplot, tol=IMG_TOL, skip = Plots._backend_skips[:pyplot])
# image_comparison_facts(:pyplot, tol=PLOTS_IMG_TOL, skip = Plots._backend_skips[:pyplot])
# end
#
# pgfplotsx()
# @testset "PGFPlotsX" begin
# image_comparison_facts(:pgfplotsx, tol=IMG_TOL, skip = Plots._backend_skips[:pgfplotsx])
# image_comparison_facts(:pgfplotsx, tol=PLOTS_IMG_TOL, skip = Plots._backend_skips[:pgfplotsx])
# end
# 10 Histogram2D
@@ -99,7 +111,6 @@ const IMG_TOL = VERSION < v"1.4" && Sys.iswindows() ? 1e-1 : is_ci() ? 1e-2 : 1e
##
@testset "Backends" begin
@testset "GR" begin
ENV["PLOTS_TEST"] = "true"
ENV["GKSwstype"] = "100"
@@ -109,7 +120,11 @@ const IMG_TOL = VERSION < v"1.4" && Sys.iswindows() ? 1e-1 : is_ci() ? 1e-2 : 1e
@static if haskey(ENV, "APPVEYOR")
@info "Skipping GR image comparison tests on AppVeyor"
else
image_comparison_facts(:gr, tol=IMG_TOL, skip=Plots._backend_skips[:gr])
image_comparison_facts(
:gr,
tol = PLOTS_IMG_TOL,
skip = Plots._backend_skips[:gr],
)
end
end
@@ -149,7 +164,6 @@ const IMG_TOL = VERSION < v"1.4" && Sys.iswindows() ? 1e-1 : is_ci() ? 1e-2 : 1e
@test isa(p, Plots.Plot) == true
@test_broken isa(display(p), Nothing) == true
end
end
@testset "Axes" begin
@@ -159,10 +173,10 @@ end
@test Plots.discrete_value!(axis, "HI") == (0.5, 1)
@test Plots.discrete_value!(axis, :yo) == (1.5, 2)
@test Plots.ignorenan_extrema(axis) == (0.5, 1.5)
@test axis[:discrete_map] == Dict{Any,Any}(:yo => 2, "HI" => 1)
@test axis[:discrete_map] == Dict{Any,Any}(:yo => 2, "HI" => 1)
Plots.discrete_value!(axis, ["x$i" for i = 1:5])
Plots.discrete_value!(axis, ["x$i" for i = 0:2])
Plots.discrete_value!(axis, ["x$i" for i in 1:5])
Plots.discrete_value!(axis, ["x$i" for i in 0:2])
@test Plots.ignorenan_extrema(axis) == (0.5, 7.5)
end
@@ -172,14 +186,16 @@ end
@test backend() == Plots.UnicodePlotsBackend()
@testset "Plot" begin
plots = [histogram([1, 0, 0, 0, 0, 0]),
plot([missing]),
plot([missing, missing]),
plot(fill(missing, 10)),
plot([missing; 1:4]),
plot([fill(missing, 10); 1:4]),
plot([1 1; 1 missing]),
plot(["a" "b"; missing "d"], [1 2; 3 4])]
plots = [
histogram([1, 0, 0, 0, 0, 0]),
plot([missing]),
plot([missing, missing]),
plot(fill(missing, 10)),
plot([missing; 1:4]),
plot([fill(missing, 10); 1:4]),
plot([1 1; 1 missing]),
plot(["a" "b"; missing "d"], [1 2; 3 4]),
]
for plt in plots
display(plt)
end
@@ -187,13 +203,12 @@ end
end
@testset "Bar" begin
p = bar([3,2,1], [1,2,3]);
@test isa(p, Plots.Plot)
@test isa(display(p), Nothing) == true
p = bar([3, 2, 1], [1, 2, 3])
@test isa(p, Plots.Plot)
@test isa(display(p), Nothing) == true
end
end
@testset "EmptyAnim" begin
anim = @animate for i in []
end
@@ -209,17 +224,25 @@ end
@test segments([NaN]) == []
@test segments(nan10) == []
@test segments([nan10; 1:5]) == [11:15]
@test segments([1:5;nan10]) == [1:5]
@test segments([1:5; nan10]) == [1:5]
@test segments([nan10; 1:5; nan10; 1:5; nan10]) == [11:15, 26:30]
@test segments([NaN; 1], 1:10) == [2:2, 4:4, 6:6, 8:8, 10:10]
@test segments([nan10; 1:15], [1:15; nan10]) == [11:15]
end
@testset "Utils" begin
zipped = ([(1, 2)], [("a", "b")], [(1, "a"),(2, "b")],
[(1, 2),(3, 4)], [(1, 2, 3),(3, 4, 5)], [(1, 2, 3, 4),(3, 4, 5, 6)],
[(1, 2.0),(missing, missing)], [(1, missing),(missing, "a")],
[(missing, missing)], [(missing, missing, missing),("a", "b", "c")])
zipped = (
[(1, 2)],
[("a", "b")],
[(1, "a"), (2, "b")],
[(1, 2), (3, 4)],
[(1, 2, 3), (3, 4, 5)],
[(1, 2, 3, 4), (3, 4, 5, 6)],
[(1, 2.0), (missing, missing)],
[(1, missing), (missing, "a")],
[(missing, missing)],
[(missing, missing, missing), ("a", "b", "c")],
)
for z in zipped
@test isequal(collect(zip(Plots.unzip(z)...)), z)
@test isequal(collect(zip(Plots.unzip(GeometryBasics.Point.(z))...)), z)
@@ -228,5 +251,7 @@ end
op2 = Plots.process_clims((1, 2.0))
data = randn(100, 100)
@test op1(data) == op2(data)
@test Plots.process_clims(nothing) == Plots.process_clims(missing) == Plots.process_clims(:auto)
@test Plots.process_clims(nothing) ==
Plots.process_clims(missing) ==
Plots.process_clims(:auto)
end
+37 -12
View File
@@ -9,8 +9,8 @@ using Plots, Test
end
@testset "Magic axis" begin
@test plot(1, axis=nothing)[1][:xaxis][:ticks] == []
@test plot(1, axis=nothing)[1][:yaxis][:ticks] == []
@test plot(1, axis = nothing)[1][:xaxis][:ticks] == []
@test plot(1, axis = nothing)[1][:yaxis][:ticks] == []
end # testset
@testset "Categorical ticks" begin
@@ -23,34 +23,34 @@ end # testset
end
@testset "Ticks getter functions" begin
ticks1 = ([1,2,3], ("a","b","c"))
ticks2 = ([4,5], ("e","f"))
p1 = plot(1:5, 1:5, 1:5, xticks=ticks1, yticks=ticks1, zticks=ticks1)
p2 = plot(1:5, 1:5, 1:5, xticks=ticks2, yticks=ticks2, zticks=ticks2)
ticks1 = ([1, 2, 3], ("a", "b", "c"))
ticks2 = ([4, 5], ("e", "f"))
p1 = plot(1:5, 1:5, 1:5, xticks = ticks1, yticks = ticks1, zticks = ticks1)
p2 = plot(1:5, 1:5, 1:5, xticks = ticks2, yticks = ticks2, zticks = ticks2)
p = plot(p1, p2)
@test xticks(p) == yticks(p) == zticks(p) == [ticks1, ticks2]
@test xticks(p[1]) == yticks(p[1]) == zticks(p[1]) == ticks1
end
@testset "Axis limits" begin
pl = plot(1:5, xlims=:symmetric, widen = false)
pl = plot(1:5, xlims = :symmetric, widen = false)
@test Plots.xlims(pl) == (-5, 5)
pl = plot(1:3)
@test Plots.xlims(pl) == Plots.widen(1,3)
@test Plots.xlims(pl) == Plots.widen(1, 3)
pl = plot([1.05,2.0,2.95], ylims=:round)
pl = plot([1.05, 2.0, 2.95], ylims = :round)
@test Plots.ylims(pl) == (1, 3)
pl = plot(1:3, xlims=(1,5))
pl = plot(1:3, xlims = (1, 5))
@test Plots.xlims(pl) == (1, 5)
pl = plot(1:3, xlims=(1,5), widen=true)
pl = plot(1:3, xlims = (1, 5), widen = true)
@test Plots.xlims(pl) == Plots.widen(1, 5)
end
@testset "3D Axis" begin
ql = quiver([1, 2], [2, 1], [3, 4], quiver = ([1, -1], [0, 0], [1, -0.5]), arrow=true)
ql = quiver([1, 2], [2, 1], [3, 4], quiver = ([1, -1], [0, 0], [1, -0.5]), arrow = true)
@test ql[1][:projection] == "3d"
end
@@ -63,3 +63,28 @@ end
@test twpl[:top_margin] == 2Plots.cm
@test twpl[:bottom_margin] == 2Plots.cm
end
@testset "aliases" begin
p = plot(1:2, xl = "x label")
@test p[1][:xaxis][:guide] === "x label"
p = plot(1:2, xrange = (0, 3))
@test xlims(p) === (0, 3)
p = plot(1:2, xtick = [1.25, 1.5, 1.75])
@test p[1][:xaxis][:ticks] == [1.25, 1.5, 1.75]
p = plot(1:2, xlabelfontsize = 4)
@test p[1][:xaxis][:guidefontsize] == 4
p = plot(1:2, xgα = .07)
@test p[1][:xaxis][:gridalpha] .07
p = plot(1:2, xgridls = :dashdot)
@test p[1][:xaxis][:gridstyle] === :dashdot
p = plot(1:2, xgridcolor = :red)
@test p[1][:xaxis][:foreground_color_grid] === RGBA{Float64}(1.,0.,0.,1.)
p = plot(1:2, xminorgridcolor = :red)
@test p[1][:xaxis][:foreground_color_minor_grid] === RGBA{Float64}(1.,0.,0.,1.)
p = plot(1:2, xgrid_lw = .01)
@test p[1][:xaxis][:gridlinewidth] .01
p = plot(1:2, xminorgrid_lw = .01)
@test p[1][:xaxis][:minorgridlinewidth] .01
p = plot(1:2, xtickor = :out)
@test p[1][:xaxis][:tick_direction] === :out
end
+6 -3
View File
@@ -9,12 +9,15 @@ using Plots, Test
value(m::MyType) = m.val
data = MyType.(sort(randn(20)))
# A recipe that puts the axis letter in the title
@recipe function f(::Type{T}, m::T) where T <: AbstractArray{<:MyType}
@recipe function f(::Type{T}, m::T) where {T<:AbstractArray{<:MyType}}
title --> string(plotattributes[:letter])
value.(m)
end
@testset "$f (orientation = $o)" for f in [histogram, barhist, stephist, scatterhist], o in [:vertical, :horizontal]
@test f(data, orientation=o).subplots[1].attr[:title] == (o == :vertical ? "x" : "y")
@testset "$f (orientation = $o)" for f in [histogram, barhist, stephist, scatterhist],
o in [:vertical, :horizontal]
@test f(data, orientation = o).subplots[1].attr[:title] ==
(o == :vertical ? "x" : "y")
end
@testset "$f" for f in [hline, hspan]
@test f(data).subplots[1].attr[:title] == "y"
+19 -10
View File
@@ -52,7 +52,7 @@ using Plots, Test
@testset "Plot" begin
ang = range(0, 2π, length = 60)
ellipse(x, y, w, h) = Shape(w * sin.(ang) .+ x, h * cos.(ang) .+ y)
myshapes = [ellipse(x, rand(), rand(), rand()) for x = 1:4]
myshapes = [ellipse(x, rand(), rand(), rand()) for x in 1:4]
@test coords(myshapes) isa Tuple{Vector{Vector{S}},Vector{Vector{T}}} where {T,S}
local p
@test_nowarn p = plot(myshapes)
@@ -116,7 +116,7 @@ end
end
@testset "Series Annotations" begin
square = Shape([(0, 0), (1, 0), (1, 1), (0, 1)])
square = Shape([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)])
@test_logs (:warn, "Unused SeriesAnnotations arg: triangle (Symbol)") begin
p = plot(
[1, 2, 3],
@@ -130,22 +130,31 @@ end
),
)
sa = p.series_list[1].plotattributes[:series_annotations]
@test sa.strs == ["a"]
@test only(sa.strs).str == "a"
@test sa.font.family == "courier"
@test sa.baseshape == square
@test sa.scalefactor == (1, 4)
end
spl = scatter(
4.53 .* [1/1 1/2 1/3 1/4 1/5],
4.53 .* [1 / 1 1 / 2 1 / 3 1 / 4 1 / 5],
[0 0 0 0 0],
layout = (5, 1),
ylims = (-1.1, 1.1),
xlims = (0, 5),
series_annotations = permutedims([["1/1"],["1/2"],["1/3"],["1/4"],["1/5"]]),
series_annotations = permutedims([["1/1"], ["1/2"], ["1/3"], ["1/4"], ["1/5"]]),
)
@test spl.series_list[1].plotattributes[:series_annotations].strs == ["1/1"]
@test spl.series_list[2].plotattributes[:series_annotations].strs == ["1/2"]
@test spl.series_list[3].plotattributes[:series_annotations].strs == ["1/3"]
@test spl.series_list[4].plotattributes[:series_annotations].strs == ["1/4"]
@test spl.series_list[5].plotattributes[:series_annotations].strs == ["1/5"]
for i in 1:5
@test only(spl.series_list[i].plotattributes[:series_annotations].strs).str ==
"1/$i"
end
p = plot([1, 2], annotations = (1.5, 2, text("foo", :left)))
x, y, txt = only(p.subplots[end][:annotations])
@test (x, y) == (1.5, 2)
@test txt.str == "foo"
p = plot([1, 2], annotations = ((0.1, 0.5), :auto))
pos, txt = only(p.subplots[end][:annotations])
@test pos == (0.1, 0.5)
@test txt.str == "(a)"
end
+68
View File
@@ -0,0 +1,68 @@
using Plots, Test
import RecipesPipeline
@testset "Contours" begin
@testset "check_contour_levels" begin
@test Plots.check_contour_levels(2) === nothing
@test Plots.check_contour_levels(-1.0:0.2:10.0) === nothing
@test Plots.check_contour_levels([-100, -2, -1, 0, 1, 2, 100]) === nothing
@test_throws ArgumentError Plots.check_contour_levels(1.0)
@test_throws ArgumentError Plots.check_contour_levels((1, 2, 3))
@test_throws ArgumentError Plots.check_contour_levels(-3)
end
@testset "RecipesPipeline.preprocess_attributes!" begin
function equal_after_pipeline(kw)
kw = deepcopy(kw)
RecipesPipeline.preprocess_attributes!(kw)
kw == kw
end
@test equal_after_pipeline(KW(:levels => 1))
@test equal_after_pipeline(KW(:levels => 1:10))
@test equal_after_pipeline(KW(:levels => [1.0, 3.0, 5.0]))
@test_throws ArgumentError RecipesPipeline.preprocess_attributes!(
KW(:levels => 1.0),
)
@test_throws ArgumentError RecipesPipeline.preprocess_attributes!(
KW(:levels => (1, 2, 3)),
)
@test_throws ArgumentError RecipesPipeline.preprocess_attributes!(KW(:levels => -3))
end
@testset "contour[f]" begin
x = (-2π):0.1:(2π)
y = (-π):0.1:π
z = cos.(y) .* sin.(x')
@testset "Incorrect input" begin
@test_throws ArgumentError contour(x, y, z, levels = 1.0)
@test_throws ArgumentError contour(x, y, z, levels = (1, 2, 3))
@test_throws ArgumentError contour(x, y, z, levels = -3)
end
@testset "Default number" begin
@test contour(x, y, z)[1][1].plotattributes[:levels] ==
Plots._series_defaults[:levels]
end
@testset "Number" begin
@testset "$n contours" for n in (2, 5, 100)
p = contour(x, y, z, levels = n)
attr = p[1][1].plotattributes
@test attr[:seriestype] == :contour
@test attr[:levels] == n
end
end
@testset "Range" begin
levels = -1:0.5:1
@test contour(x, y, z, levels = levels)[1][1].plotattributes[:levels] == levels
end
@testset "Set of levels" begin
levels = [-1, 0.25, 0, 0.25, 1]
@test contour(x, y, z, levels = levels)[1][1].plotattributes[:levels] == levels
end
end
end
+12 -13
View File
@@ -1,21 +1,20 @@
using Plots, HDF5
@testset "HDF5_Plots" begin
fname = "tmpplotsave.hdf5"
hdf5()
fname = "tmpplotsave.hdf5"
hdf5()
x = 1:10
psrc=plot(x, x.*x); #Create some plot
Plots.hdf5plot_write(psrc, fname)
x = 1:10
psrc = plot(x, x .* x) #Create some plot
Plots.hdf5plot_write(psrc, fname)
#Read back file:
gr() #Choose some fast backend likely to work in test environment.
pread = Plots.hdf5plot_read(fname)
#Read back file:
gr() #Choose some fast backend likely to work in test environment.
pread = Plots.hdf5plot_read(fname)
#Make sure data made it through:
@test psrc.subplots[1].series_list[1][:x] == pread.subplots[1].series_list[1][:x]
@test psrc.subplots[1].series_list[1][:y] == pread.subplots[1].series_list[1][:y]
#Make sure data made it through:
@test psrc.subplots[1].series_list[1][:x] == pread.subplots[1].series_list[1][:x]
@test psrc.subplots[1].series_list[1][:y] == pread.subplots[1].series_list[1][:y]
#display(pread) #Don't display. Regression env might not support
#display(pread) #Don't display. Regression env might not support
end #testset
+386 -399
View File
@@ -2,420 +2,407 @@ using Plots, Test
pgfplotsx()
function create_plot(args...; kwargs...)
pgfx_plot = plot(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
pgfx_plot = plot(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
end
function create_plot!(args...; kwargs...)
pgfx_plot = plot!(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
pgfx_plot = plot!(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
end
@testset "PGFPlotsX" begin
pgfx_plot = plot(1:5)
Plots._update_plot_object(pgfx_plot)
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1
@test !haskey(axis.contents[1].options.dict, "fill")
pgfx_plot = plot(1:5)
Plots._update_plot_object(pgfx_plot)
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1
@test !haskey(axis.contents[1].options.dict, "fill")
@testset "Legends" begin
legends_plot = plot(rand(5, 2), lab = ["1" ""])
scatter!(legends_plot, rand(5))
Plots._update_plot_object(legends_plot)
axis_contents = Plots.pgfx_axes(legends_plot.o)[1].contents
leg_entries = filter(x -> x isa PGFPlotsX.LegendEntry, axis_contents)
series = filter(x -> x isa PGFPlotsX.Plot, axis_contents)
@test length(leg_entries) == 2
@test !haskey(series[1].options.dict, "forget plot")
@test haskey(series[2].options.dict, "forget plot")
@test !haskey(series[3].options.dict, "forget plot")
end # testset
@testset "Legends" begin
legends_plot = plot(rand(5, 2), lab = ["1" ""])
scatter!(legends_plot, rand(5))
Plots._update_plot_object(legends_plot)
axis_contents = Plots.pgfx_axes(legends_plot.o)[1].contents
leg_entries = filter(x -> x isa PGFPlotsX.LegendEntry, axis_contents)
series = filter(x -> x isa PGFPlotsX.Plot, axis_contents)
@test length(leg_entries) == 2
@test !haskey(series[1].options.dict, "forget plot")
@test haskey(series[2].options.dict, "forget plot")
@test !haskey(series[3].options.dict, "forget plot")
end # testset
@testset "3D docs example" begin
n = 100
ts = range(0, stop = 8π, length = n)
x = ts .* map(cos, ts)
y = (0.1ts) .* map(sin, ts)
z = 1:n
pl = plot(
x,
y,
z,
zcolor = reverse(z),
m = (10, 0.8, :blues, Plots.stroke(0)),
leg = false,
cbar = true,
w = 5,
)
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w = 10)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
end
end # testset
@testset "Color docs example" begin
y = rand(100)
plot(
0:10:100,
rand(11, 4),
lab = "lines",
w = 3,
palette = :grays,
fill = 0,
α = 0.6,
)
pl = scatter!(
y,
zcolor = abs.(y .- 0.5),
m = (:hot, 0.8, Plots.stroke(1, :green)),
ms = 10 * abs.(y .- 0.5) .+ 4,
lab = ["grad", "", "ient"],
)
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
@test count(x -> x isa PGFPlotsX.LegendEntry, axis.contents) == 6
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 108 # each marker is its own plot, fillranges create 2 plot-objects
marker = axis.contents[15]
@test marker isa PGFPlotsX.Plot
@test marker.options["mark"] == "*"
@test marker.options["mark options"]["color"] ==
RGBA{Float64}(colorant"green", 0.8)
@test marker.options["mark options"]["line width"] == 0.75 # 1px is 0.75pt
end # testset
@testset "Plot in pieces" begin
pic = plot(rand(100) / 3, reg = true, fill = (0, :green))
scatter!(pic, rand(100), markersize = 6, c = :orange)
Plots._update_plot_object(pic)
axis_contents = Plots.pgfx_axes(pic.o)[1].contents
leg_entries = filter(x -> x isa PGFPlotsX.LegendEntry, axis_contents)
series = filter(x -> x isa PGFPlotsX.Plot, axis_contents)
@test length(leg_entries) == 2
@test length(series) == 4
@test haskey(series[1].options.dict, "forget plot")
@test !haskey(series[2].options.dict, "forget plot")
@test haskey(series[3].options.dict, "forget plot")
@test !haskey(series[4].options.dict, "forget plot")
end # testset
@testset "Marker types" begin
markers = filter((m -> begin
m in Plots.supported_markers()
end), Plots._shape_keys)
markers = reshape(markers, 1, length(markers))
n = length(markers)
x = (range(0, stop = 10, length = n + 2))[2:(end-1)]
y = repeat(reshape(reverse(x), 1, :), n, 1)
scatter(
x,
y,
m = (8, :auto),
lab = map(string, markers),
bg = :linen,
xlim = (0, 10),
ylim = (0, 10),
)
end # testset
@testset "Layout" begin
plot(
Plots.fakedata(100, 10),
layout = 4,
palette = [:grays :blues :hot :rainbow],
bg_inside = [:orange :pink :darkblue :black],
)
end # testset
@testset "Polar plots" begin
Θ = range(0, stop = 1.5π, length = 100)
r = abs.(0.1 * randn(100) + sin.(3Θ))
plot(Θ, r, proj = :polar, m = 2)
end # testset
@testset "Drawing shapes" begin
verts = [
(-1.0, 1.0),
(-1.28, 0.6),
(-0.2, -1.4),
(0.2, -1.4),
(1.28, 0.6),
(1.0, 1.0),
(-1.0, 1.0),
(-0.2, -0.6),
(0.0, -0.2),
(-0.4, 0.6),
(1.28, 0.6),
(0.2, -1.4),
(-0.2, -1.4),
(0.6, 0.2),
(-0.2, 0.2),
(0.0, -0.2),
(0.2, 0.2),
(-0.2, -0.6),
]
x = 0.1:0.2:0.9
y = 0.7 * rand(5) .+ 0.15
plot(
x,
y,
line = (3, :dash, :lightblue),
marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
bg = :pink,
fg = :darkblue,
xlim = (0, 1),
ylim = (0, 1),
leg = false,
)
end # testset
@testset "Histogram 2D" begin
histogram2d(randn(10000), randn(10000), nbins = 20)
end # testset
@testset "Heatmap-like" begin
xs = [string("x", i) for i = 1:10]
ys = [string("y", i) for i = 1:4]
z = float((1:4) * reshape(1:10, 1, :))
pgfx_plot = heatmap(xs, ys, z, aspect_ratio = 1)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
end
@testset "3D docs example" begin
n = 100
ts = range(0, stop = 8π, length = n)
x = ts .* map(cos, ts)
y = (0.1ts) .* map(sin, ts)
z = 1:n
pl = plot(
x,
y,
z,
zcolor = reverse(z),
m = (10, 0.8, :blues, Plots.stroke(0)),
leg = false,
cbar = true,
w = 5,
)
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w = 10)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
end
end # testset
@testset "Color docs example" begin
y = rand(100)
plot(
0:10:100,
rand(11, 4),
lab = "lines",
w = 3,
palette = :grays,
fill = 0,
α = 0.6,
)
pl = scatter!(
y,
zcolor = abs.(y .- 0.5),
m = (:hot, 0.8, Plots.stroke(1, :green)),
ms = 10 * abs.(y .- 0.5) .+ 4,
lab = ["grad", "", "ient"],
)
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
@test count(x -> x isa PGFPlotsX.LegendEntry, axis.contents) == 6
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 108 # each marker is its own plot, fillranges create 2 plot-objects
marker = axis.contents[15]
@test marker isa PGFPlotsX.Plot
@test marker.options["mark"] == "*"
@test marker.options["mark options"]["color"] == RGBA{Float64}(colorant"green", 0.8)
@test marker.options["mark options"]["line width"] == 0.75 # 1px is 0.75pt
end # testset
@testset "Plot in pieces" begin
pic = plot(rand(100) / 3, reg = true, fill = (0, :green))
scatter!(pic, rand(100), markersize = 6, c = :orange)
Plots._update_plot_object(pic)
axis_contents = Plots.pgfx_axes(pic.o)[1].contents
leg_entries = filter(x -> x isa PGFPlotsX.LegendEntry, axis_contents)
series = filter(x -> x isa PGFPlotsX.Plot, axis_contents)
@test length(leg_entries) == 2
@test length(series) == 4
@test haskey(series[1].options.dict, "forget plot")
@test !haskey(series[2].options.dict, "forget plot")
@test haskey(series[3].options.dict, "forget plot")
@test !haskey(series[4].options.dict, "forget plot")
end # testset
@testset "Marker types" begin
markers = filter((m -> begin
m in Plots.supported_markers()
end), Plots._shape_keys)
markers = reshape(markers, 1, length(markers))
n = length(markers)
x = (range(0, stop = 10, length = n + 2))[2:(end - 1)]
y = repeat(reshape(reverse(x), 1, :), n, 1)
scatter(
x,
y,
m = (8, :auto),
lab = map(string, markers),
bg = :linen,
xlim = (0, 10),
ylim = (0, 10),
)
end # testset
@testset "Layout" begin
plot(
Plots.fakedata(100, 10),
layout = 4,
palette = [:grays :blues :hot :rainbow],
bg_inside = [:orange :pink :darkblue :black],
)
end # testset
@testset "Polar plots" begin
Θ = range(0, stop = 1.5π, length = 100)
r = abs.(0.1 * randn(100) + sin.(3Θ))
plot(Θ, r, proj = :polar, m = 2)
end # testset
@testset "Drawing shapes" begin
verts = [
(-1.0, 1.0),
(-1.28, 0.6),
(-0.2, -1.4),
(0.2, -1.4),
(1.28, 0.6),
(1.0, 1.0),
(-1.0, 1.0),
(-0.2, -0.6),
(0.0, -0.2),
(-0.4, 0.6),
(1.28, 0.6),
(0.2, -1.4),
(-0.2, -1.4),
(0.6, 0.2),
(-0.2, 0.2),
(0.0, -0.2),
(0.2, 0.2),
(-0.2, -0.6),
]
x = 0.1:0.2:0.9
y = 0.7 * rand(5) .+ 0.15
plot(
x,
y,
line = (3, :dash, :lightblue),
marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
bg = :pink,
fg = :darkblue,
xlim = (0, 1),
ylim = (0, 1),
leg = false,
)
end # testset
@testset "Histogram 2D" begin
histogram2d(randn(10000), randn(10000), nbins = 20)
end # testset
@testset "Heatmap-like" begin
xs = [string("x", i) for i in 1:10]
ys = [string("y", i) for i in 1:4]
z = float((1:4) * reshape(1:10, 1, :))
pgfx_plot = heatmap(xs, ys, z, aspect_ratio = 1)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
end
pgfx_plot = wireframe(xs, ys, z, aspect_ratio = 1)
# TODO: clims are wrong
end # testset
@testset "Contours" begin
x = 1:0.5:20
y = 1:0.5:10
f(x, y) = begin
(3x + y^2) * abs(sin(x) + cos(y))
end
X = repeat(reshape(x, 1, :), length(y), 1)
Y = repeat(y, 1, length(x))
Z = map(f, X, Y)
p2 = contour(x, y, Z)
p1 = contour(x, y, f, fill = true)
plot(p1, p2)
# TODO: colorbar for filled contours
end # testset
@testset "Varying colors" begin
t = range(0, stop = 1, length = 100)
θ = (6π) .* t
x = t .* cos.(θ)
y = t .* sin.(θ)
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
p2 = scatter(
x,
y,
marker_z = ((x, y) -> begin
pgfx_plot = wireframe(xs, ys, z, aspect_ratio = 1)
# TODO: clims are wrong
end # testset
@testset "Contours" begin
x = 1:0.5:20
y = 1:0.5:10
f(x, y) = begin
(3x + y^2) * abs(sin(x) + cos(y))
end
X = repeat(reshape(x, 1, :), length(y), 1)
Y = repeat(y, 1, length(x))
Z = map(f, X, Y)
p2 = contour(x, y, Z)
p1 = contour(x, y, f, fill = true)
plot(p1, p2)
# TODO: colorbar for filled contours
end # testset
@testset "Varying colors" begin
t = range(0, stop = 1, length = 100)
θ = (6π) .* t
x = t .* cos.(θ)
y = t .* sin.(θ)
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
p2 = scatter(x, y, marker_z = ((x, y) -> begin
x + y
end),
color = :bwr,
legend = false,
)
plot(p1, p2)
end # testset
@testset "Framestyles" begin
scatter(
fill(randn(10), 6),
fill(randn(10), 6),
framestyle = [:box :semi :origin :zerolines :grid :none],
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
color = permutedims(1:6),
layout = 6,
label = "",
markerstrokewidth = 0,
ticks = -2:2,
)
# TODO: support :semi
end # testset
@testset "Quiver" begin
x = (-2pi):0.2:(2*pi)
y = sin.(x)
end), color = :bwr, legend = false)
plot(p1, p2)
end # testset
@testset "Framestyles" begin
scatter(
fill(randn(10), 6),
fill(randn(10), 6),
framestyle = [:box :semi :origin :zerolines :grid :none],
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
color = permutedims(1:6),
layout = 6,
label = "",
markerstrokewidth = 0,
ticks = -2:2,
)
# TODO: support :semi
end # testset
@testset "Quiver" begin
x = (-2pi):0.2:(2 * pi)
y = sin.(x)
u = ones(length(x))
v = cos.(x)
arrow_plot = plot(x, y, quiver = (u, v), arrow = true)
# TODO: could adjust limits to fit arrows if too long, but how?
# TODO: get latex available on CI
# mktempdir() do path
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
# end
end # testset
@testset "Annotations" begin
y = rand(10)
pgfx_plot = plot(
y,
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
leg = false,
)
Plots._update_plot_object(pgfx_plot)
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 1
mktempdir() do path
file_path = joinpath(path, "annotations.tex")
@test_nowarn savefig(pgfx_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 1
end
end
annotate!([
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
])
Plots._update_plot_object(pgfx_plot)
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 3
mktempdir() do path
file_path = joinpath(path, "annotations.tex")
@test_nowarn savefig(pgfx_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 3
end
end
annotation_plot = scatter!(
range(2, stop = 8, length = 6),
rand(6),
marker = (50, 0.2, :orange),
series_annotations = [
"series",
"annotations",
"map",
"to",
"series",
Plots.text("data", :green),
],
)
Plots._update_plot_object(annotation_plot)
axis_content = Plots.pgfx_axes(annotation_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 9
mktempdir() do path
file_path = joinpath(path, "annotations.tex")
@test_nowarn savefig(annotation_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 9
end
# test .tikz extension
file_path = joinpath(path, "annotations.tikz")
@test_nowarn savefig(annotation_plot, file_path)
@test_nowarn open(file_path) do io
end
end
end # testset
@testset "Ribbon" begin
aa = rand(10)
bb = rand(10)
cc = rand(10)
conf = [aa - cc bb - cc]
ribbon_plot =
plot(collect(1:10), fill(1, 10), ribbon = (conf[:, 1], conf[:, 2]))
Plots._update_plot_object(ribbon_plot)
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
@test length(plots) == 3
@test haskey(plots[1].options.dict, "fill")
@test haskey(plots[2].options.dict, "fill")
@test !haskey(plots[3].options.dict, "fill")
@test ribbon_plot.o !== nothing
@test ribbon_plot.o.the_plot !== nothing
end # testset
@testset "Markers and Paths" begin
pl = plot(
5 .- ones(9),
markershape = [:utriangle, :rect],
markersize = 8,
color = [:red, :black],
)
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
@test length(plots) == 9
end # testset
@testset "Groups and Subplots" begin
group = rand(map((i->begin
"group $(i)"
end), 1:4), 100)
pl = plot(rand(100), layout = @layout([a b; c]), group = group, linetype = [:bar :scatter :steppre], linecolor = :match)
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
legend_entries = filter(x -> x isa PGFPlotsX.LegendEntry, axis.contents)
@test length(legend_entries) == 2
end
u = ones(length(x))
v = cos.(x)
arrow_plot = plot(x, y, quiver = (u, v), arrow = true)
# TODO: could adjust limits to fit arrows if too long, but how?
# TODO: get latex available on CI
# mktempdir() do path
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
# end
end # testset
@testset "Annotations" begin
y = rand(10)
pgfx_plot =
plot(y, annotations = (3, y[3], Plots.text("this is \\#3", :left)), leg = false)
Plots._update_plot_object(pgfx_plot)
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 1
mktempdir() do path
file_path = joinpath(path, "annotations.tex")
@test_nowarn savefig(pgfx_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 1
end
end
annotate!([
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
])
Plots._update_plot_object(pgfx_plot)
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 3
mktempdir() do path
file_path = joinpath(path, "annotations.tex")
@test_nowarn savefig(pgfx_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 3
end
end
annotation_plot = scatter!(
range(2, stop = 8, length = 6),
rand(6),
marker = (50, 0.2, :orange),
series_annotations = [
"series",
"annotations",
"map",
"to",
"series",
Plots.text("data", :green),
],
)
Plots._update_plot_object(annotation_plot)
axis_content = Plots.pgfx_axes(annotation_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 9
mktempdir() do path
file_path = joinpath(path, "annotations.tex")
@test_nowarn savefig(annotation_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 9
end
# test .tikz extension
file_path = joinpath(path, "annotations.tikz")
@test_nowarn savefig(annotation_plot, file_path)
@test_nowarn open(file_path) do io
end
end
end # testset
@testset "Ribbon" begin
aa = rand(10)
bb = rand(10)
cc = rand(10)
conf = [aa - cc bb - cc]
ribbon_plot = plot(collect(1:10), fill(1, 10), ribbon = (conf[:, 1], conf[:, 2]))
Plots._update_plot_object(ribbon_plot)
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
@test length(plots) == 3
@test haskey(plots[1].options.dict, "fill")
@test haskey(plots[2].options.dict, "fill")
@test !haskey(plots[3].options.dict, "fill")
@test ribbon_plot.o !== nothing
@test ribbon_plot.o.the_plot !== nothing
end # testset
@testset "Markers and Paths" begin
pl = plot(
5 .- ones(9),
markershape = [:utriangle, :rect],
markersize = 8,
color = [:red, :black],
)
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
@test length(plots) == 9
end # testset
@testset "Groups and Subplots" begin
group = rand(map((i -> begin
"group $(i)"
end), 1:4), 100)
pl = plot(
rand(100),
layout = @layout([a b; c]),
group = group,
linetype = [:bar :scatter :steppre],
linecolor = :match,
)
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
legend_entries = filter(x -> x isa PGFPlotsX.LegendEntry, axis.contents)
@test length(legend_entries) == 2
end
end # testset
@testset "Extra kwargs" begin
pl = plot(1:5, test = "me")
@test pl[1][1].plotattributes[:extra_kwargs][:test] == "me"
pl = plot(1:5, test = "me", extra_kwargs = :subplot)
@test pl[1].attr[:extra_kwargs][:test] == "me"
pl = plot(1:5, test = "me", extra_kwargs = :plot)
@test pl.attr[:extra_plot_kwargs][:test] == "me"
pl = plot(
1:5,
extra_kwargs = Dict(
:plot => Dict(:test => "me"),
:series => Dict(:and => "me too"),
),
)
@test pl.attr[:extra_plot_kwargs][:test] == "me"
@test pl[1][1].plotattributes[:extra_kwargs][:and] == "me too"
pl = plot(
plot(1:5, title = "Line"),
scatter(
1:5,
title = "Scatter",
extra_kwargs = Dict(:subplot => Dict("axis line shift" => "10pt")),
),
)
Plots._update_plot_object(pl)
axes = Plots.pgfx_axes(pl.o)
@test !haskey(axes[1].options.dict, "axis line shift")
@test haskey(axes[2].options.dict, "axis line shift")
pl = plot(
x -> x,
-1:1;
add = raw"\node at (0,0.5) {\huge hi};",
extra_kwargs = :subplot,
)
@test pl[1][:extra_kwargs] == Dict(:add => raw"\node at (0,0.5) {\huge hi};")
Plots._update_plot_object(pl)
axes = Plots.pgfx_axes(pl.o)
@test filter(x -> x isa String, axes[1].contents)[1] ==
raw"\node at (0,0.5) {\huge hi};"
plot!(pl)
@test pl[1][:extra_kwargs] == Dict(:add => raw"\node at (0,0.5) {\huge hi};")
Plots._update_plot_object(pl)
axes = Plots.pgfx_axes(pl.o)
@test filter(x -> x isa String, axes[1].contents)[1] ==
raw"\node at (0,0.5) {\huge hi};"
pl = plot(1:5, test = "me")
@test pl[1][1].plotattributes[:extra_kwargs][:test] == "me"
pl = plot(1:5, test = "me", extra_kwargs = :subplot)
@test pl[1].attr[:extra_kwargs][:test] == "me"
pl = plot(1:5, test = "me", extra_kwargs = :plot)
@test pl.attr[:extra_plot_kwargs][:test] == "me"
pl = plot(
1:5,
extra_kwargs = Dict(
:plot => Dict(:test => "me"),
:series => Dict(:and => "me too"),
),
)
@test pl.attr[:extra_plot_kwargs][:test] == "me"
@test pl[1][1].plotattributes[:extra_kwargs][:and] == "me too"
pl = plot(
plot(1:5, title = "Line"),
scatter(
1:5,
title = "Scatter",
extra_kwargs = Dict(:subplot => Dict("axis line shift" => "10pt")),
),
)
Plots._update_plot_object(pl)
axes = Plots.pgfx_axes(pl.o)
@test !haskey(axes[1].options.dict, "axis line shift")
@test haskey(axes[2].options.dict, "axis line shift")
pl =
plot(x -> x, -1:1; add = raw"\node at (0,0.5) {\huge hi};", extra_kwargs = :subplot)
@test pl[1][:extra_kwargs] == Dict(:add => raw"\node at (0,0.5) {\huge hi};")
Plots._update_plot_object(pl)
axes = Plots.pgfx_axes(pl.o)
@test filter(x -> x isa String, axes[1].contents)[1] ==
raw"\node at (0,0.5) {\huge hi};"
plot!(pl)
@test pl[1][:extra_kwargs] == Dict(:add => raw"\node at (0,0.5) {\huge hi};")
Plots._update_plot_object(pl)
axes = Plots.pgfx_axes(pl.o)
@test filter(x -> x isa String, axes[1].contents)[1] ==
raw"\node at (0,0.5) {\huge hi};"
end # testset
@testset "Titlefonts" begin
pl = plot(1:5, title = "Test me", titlefont = (2, :left))
@test pl[1][:title] == "Test me"
@test pl[1][:titlefontsize] == 2
@test pl[1][:titlefonthalign] == :left
Plots._update_plot_object(pl)
ax_opt = Plots.pgfx_axes(pl.o)[1].options
@test ax_opt["title"] == "Test me"
@test(haskey(ax_opt.dict, "title style")) isa Test.Pass
pl = plot(1:5, plot_title = "Test me", plot_titlefont = (2, :left))
@test pl[:plot_title] == "Test me"
@test pl[:plot_titlefontsize] == 2
@test pl[:plot_titlefonthalign] == :left
pl = heatmap(
rand(3, 3),
colorbar_title = "Test me",
colorbar_titlefont = (12, :right),
)
@test pl[1][:colorbar_title] == "Test me"
@test pl[1][:colorbar_titlefontsize] == 12
@test pl[1][:colorbar_titlefonthalign] == :right
pl = plot(1:5, title = "Test me", titlefont = (2, :left))
@test pl[1][:title] == "Test me"
@test pl[1][:titlefontsize] == 2
@test pl[1][:titlefonthalign] == :left
Plots._update_plot_object(pl)
ax_opt = Plots.pgfx_axes(pl.o)[1].options
@test ax_opt["title"] == "Test me"
@test(haskey(ax_opt.dict, "title style")) isa Test.Pass
pl = plot(1:5, plot_title = "Test me", plot_titlefont = (2, :left))
@test pl[:plot_title] == "Test me"
@test pl[:plot_titlefontsize] == 2
@test pl[:plot_titlefonthalign] == :left
pl = heatmap(rand(3, 3), colorbar_title = "Test me", colorbar_titlefont = (12, :right))
@test pl[1][:colorbar_title] == "Test me"
@test pl[1][:colorbar_titlefontsize] == 12
@test pl[1][:colorbar_titlefonthalign] == :right
end # testset
+2 -2
View File
@@ -11,8 +11,8 @@ using RecipesPipeline
end
@testset "get_axis_limits" begin
x = [.1, 5]
p1 = plot(x, [5, .1], yscale=:log10)
x = [0.1, 5]
p1 = plot(x, [5, 0.1], yscale = :log10)
p2 = plot!(identity)
@test all(RecipesPipeline.get_axis_limits(p1, :x) .== x)
@test all(RecipesPipeline.get_axis_limits(p2, :x) .== x)
+57
View File
@@ -0,0 +1,57 @@
using Plots, Test
@testset "Plotly" begin
@testset "Basic" begin
@test plotly() == Plots.PlotlyBackend()
@test backend() == Plots.PlotlyBackend()
p = plot(rand(10))
@test isa(p, Plots.Plot) == true
end
@testset "Contours" begin
x = (-2π):0.1:(2π)
y = (-π):0.1:π
z = cos.(y) .* sin.(x')
@testset "Contour numbers" begin
@testset "Default" begin
@test Plots.plotly_series(contour(x, y, z))[1][:ncontours] ==
Plots._series_defaults[:levels] + 2
end
@testset "Specified number" begin
@test Plots.plotly_series(contour(x, y, z, levels = 10))[1][:ncontours] ==
12
end
end
@testset "Contour values" begin
@testset "Range" begin
levels = -1:0.5:1
p = contour(x, y, z, levels = levels)
@test p[1][1].plotattributes[:levels] == levels
@test Plots.plotly_series(p)[1][:contours][:start] == first(levels)
@test Plots.plotly_series(p)[1][:contours][:end] == last(levels)
@test Plots.plotly_series(p)[1][:contours][:size] == step(levels)
end
@testset "Set of contours" begin
levels = [-1, -0.25, 0, 0.25, 1]
levels_range =
range(first(levels), stop = last(levels), length = length(levels))
p = contour(x, y, z, levels = levels)
@test p[1][1].plotattributes[:levels] == levels
series_dict = @test_logs (
:warn,
"setting arbitrary contour levels with Plotly backend " *
"is not supported; use a range to set equally-spaced contours or an " *
"integer to set the approximate number of contours with the keyword " *
"`levels`. Setting levels to -1.0:0.5:1.0",
) Plots.plotly_series(p)
@test series_dict[1][:contours][:start] == first(levels_range)
@test series_dict[1][:contours][:end] == last(levels_range)
@test series_dict[1][:contours][:size] == step(levels_range)
end
end
end
end
+15 -15
View File
@@ -3,27 +3,27 @@ using OffsetArrays
@testset "lens!" begin
pl = plot(1:5)
lens!(pl, [1,2], [1,2], inset = (1, bbox(0.0,0.0,0.2,0.2)), colorbar = false)
lens!(pl, [1, 2], [1, 2], inset = (1, bbox(0.0, 0.0, 0.2, 0.2)), colorbar = false)
@test length(pl.series_list) == 4
@test pl[2][:colorbar] == :none
end # testset
@testset "vline, vspan" begin
vl = vline([1], widen = false)
@test Plots.xlims(vl) == (1,2)
@test Plots.ylims(vl) == (1,2)
vl = vline([1], xlims=(0,2), widen = false)
@test Plots.xlims(vl) == (0,2)
vl = vline([1], ylims=(-3,5), widen = false)
@test Plots.ylims(vl) == (-3,5)
@test Plots.xlims(vl) == (1, 2)
@test Plots.ylims(vl) == (1, 2)
vl = vline([1], xlims = (0, 2), widen = false)
@test Plots.xlims(vl) == (0, 2)
vl = vline([1], ylims = (-3, 5), widen = false)
@test Plots.ylims(vl) == (-3, 5)
vsp = vspan([1,3], widen = false)
@test Plots.xlims(vsp) == (1,3)
@test Plots.ylims(vsp) == (0,1) # TODO: might be problematic on log-scales
vsp = vspan([1,3], xlims=(-2,5), widen = false)
@test Plots.xlims(vsp) == (-2,5)
vsp = vspan([1,3], ylims=(-2,5), widen = false)
@test Plots.ylims(vsp) == (-2,5)
vsp = vspan([1, 3], widen = false)
@test Plots.xlims(vsp) == (1, 3)
@test Plots.ylims(vsp) == (0, 1) # TODO: might be problematic on log-scales
vsp = vspan([1, 3], xlims = (-2, 5), widen = false)
@test Plots.xlims(vsp) == (-2, 5)
vsp = vspan([1, 3], ylims = (-2, 5), widen = false)
@test Plots.ylims(vsp) == (-2, 5)
end # testset
@testset "offset axes" begin
@@ -46,4 +46,4 @@ end
prevha = ha
end
end
end
end
+21 -21
View File
@@ -4,45 +4,45 @@ using Plots, Test
@testset "Set Lims" begin
p = plot(rand(10))
xlims!((1,20))
@test xlims(p) == (1,20)
xlims!((1, 20))
@test xlims(p) == (1, 20)
ylims!((-1,1))
@test ylims(p) == (-1,1)
ylims!((-1, 1))
@test ylims(p) == (-1, 1)
zlims!((-1,1))
@test zlims(p) == (-1,1)
zlims!((-1, 1))
@test zlims(p) == (-1, 1)
xlims!(-1,11)
@test xlims(p) == (-1,11)
xlims!(-1, 11)
@test xlims(p) == (-1, 11)
ylims!((-10,10))
@test ylims(p) == (-10,10)
ylims!((-10, 10))
@test ylims(p) == (-10, 10)
zlims!((-10,10))
@test zlims(p) == (-10,10)
zlims!((-10, 10))
@test zlims(p) == (-10, 10)
end
@testset "Set Ticks" begin
p = plot([0,2,3,4,5,6,7,8,9,10])
p = plot([0, 2, 3, 4, 5, 6, 7, 8, 9, 10])
xticks = 2:6
xticks!(xticks)
@test Plots.get_subplot(current(),1).attr[:xaxis][:ticks] == xticks
@test Plots.get_subplot(current(), 1).attr[:xaxis][:ticks] == xticks
yticks = 0.2:0.1:0.7
yticks!(yticks)
@test Plots.get_subplot(current(),1).attr[:yaxis][:ticks] == yticks
@test Plots.get_subplot(current(), 1).attr[:yaxis][:ticks] == yticks
xticks = [5,6,7.5]
xlabels = ["a","b","c"]
xticks = [5, 6, 7.5]
xlabels = ["a", "b", "c"]
xticks!(xticks, xlabels)
@test Plots.get_subplot(current(),1).attr[:xaxis][:ticks] == (xticks, xlabels)
@test Plots.get_subplot(current(), 1).attr[:xaxis][:ticks] == (xticks, xlabels)
yticks = [.5,.6,.75]
ylabels = ["z","y","x"]
yticks = [0.5, 0.6, 0.75]
ylabels = ["z", "y", "x"]
yticks!(yticks, ylabels)
@test Plots.get_subplot(current(),1).attr[:yaxis][:ticks] == (yticks, ylabels)
@test Plots.get_subplot(current(), 1).attr[:yaxis][:ticks] == (yticks, ylabels)
end
end