Compare commits

...

192 Commits

Author SHA1 Message Date
Thomas Breloff 40cbddab29 updated NEWS; export shorthands macro; new example 16 2016-06-08 23:32:03 -04:00
Thomas Breloff 853cda4b80 moved stuff to PlotDocs 2016-06-08 17:34:15 -04:00
Thomas Breloff 91ec0ad077 allow false for color; properly expand extrema for tuple fillrange; make_fillrange_from_ribbon 2016-06-08 09:51:27 -04:00
Thomas Breloff 4be2304ec1 working on arg_desc; violin fix for groups 2016-06-08 00:52:16 -04:00
Tom Breloff d0ee402939 Merge pull request #306 from diegozea/dev
Solve a bug with whisker lengths (#210)
2016-06-07 23:01:29 -04:00
Diego Javier Zea 2d095daddc Solve a bug with whisker lengths (#210) 2016-06-07 23:44:41 -03:00
Thomas Breloff cf4fcf3351 fixed boxplot grouping; cleaned up GroupBy recipe; added arg_descs 2016-06-07 21:12:59 -04:00
Thomas Breloff 42d57fb8c2 working on arg_desc 2016-06-07 18:17:27 -04:00
Thomas Breloff ba13c1528f working on arg_desc 2016-06-07 17:59:30 -04:00
Thomas Breloff 857fb3342a fakedata fix 2016-06-07 17:16:14 -04:00
Thomas Breloff 17189e3125 default_should_widen; symbol in userplot macro; cleanup in recipes 2016-06-07 16:44:15 -04:00
Thomas Breloff d0209d945d widen further; added zorder to pyplot annotations 2016-06-07 12:38:35 -04:00
Tom Breloff 1b6bfa1259 Merge pull request #305 from diegozea/dev
Add notch and outliers to boxplot
2016-06-07 12:09:55 -04:00
Thomas Breloff 4ea813c983 pyplot scatter fix 2016-06-07 12:07:16 -04:00
Diego Javier Zea 71d2af07da Add notch and outliers to boxplot 2016-06-07 13:06:13 -03:00
Thomas Breloff b36f8c701e type recipes 2016-06-07 11:59:38 -04:00
Thomas Breloff d64d3ef101 pgfplots fixes 2016-06-07 01:57:02 -04:00
Thomas Breloff 05fa4a92de fixes in plotly/plotlyjs; smooth primary false 2016-06-07 01:42:33 -04:00
Thomas Breloff 990314cefc fixes for gr markers; add cbar to example 24; turn on gr tests 2016-06-07 01:23:07 -04:00
Thomas Breloff 60d97ad9c9 added density recipe 2016-06-07 00:18:07 -04:00
Thomas Breloff 6bfa31a89f added primary kw for series counting; added sticks recipe; gr_set_font and related fixes 2016-06-06 23:43:06 -04:00
Thomas Breloff 8d8ac10429 step recipes 2016-06-06 18:19:46 -04:00
Thomas Breloff 56f398fb55 histogram2d recipe; handle smoothing generically 2016-06-06 17:55:09 -04:00
Thomas Breloff cf5aed00e2 test fix 2016-06-06 16:12:22 -04:00
Thomas Breloff de616dfdf0 update_min_padding fix; shape support in GR; GR attr fixes; image extrema and fixes; hline/vline recipes; nobigs closes #303 2016-06-06 15:34:51 -04:00
Thomas Breloff 3cf428d7ea pyplot colorbar fix; annotation fix; new contour example; travis fix 2016-06-06 14:06:55 -04:00
Thomas Breloff 29d93ba33e changed axis defaults byletter to match; cleaned up examples; pyplot extrema fixes; commented out gr tests 2016-06-06 12:34:24 -04:00
Thomas Breloff 76c84c7615 attr fixes; allow_fail for nightly travis; series_new cleanup 2016-06-06 01:36:07 -04:00
Thomas Breloff e129c29e2f postpone match logic and add color_or_nothing; update pyplot for attr changes; move some pyplot logic later 2016-06-06 01:19:25 -04:00
Thomas Breloff 52d325b446 Merge remote-tracking branch 'origin/master' into dev 2016-06-05 23:59:06 -04:00
Thomas Breloff 74efd98699 added user_attr field to Plot; fillrange fix; pyplot title fix 2016-06-05 23:58:35 -04:00
Tom Breloff 28effaea36 Merge pull request #298 from dhoegh/fix_animation
Make animation use ImageMagick.jl if it is installed.
2016-06-05 23:16:05 -04:00
Daniel Høegh 6256549f44 Make animation use ImageMagick.jl if it is installed. 2016-06-05 09:31:44 +02:00
Thomas Breloff 2adec2fa7b examples 2016-06-02 20:34:25 -05:00
Thomas Breloff 32e816b713 working on axis limit fixes 2016-06-02 20:22:58 -05:00
Thomas Breloff 6c7b113de5 working on new tests 2016-06-02 17:59:24 -05:00
Thomas Breloff 1d5cce61ac working on switch to PlotReferenceImages 2016-06-02 17:23:40 -05:00
Thomas Breloff 85f91b9075 fix for plotting function; closes #291 2016-06-01 14:45:35 -05:00
Thomas Breloff aa64a0d3ce pyplot color fix 2016-06-01 14:29:36 -05:00
Thomas Breloff e9d95a85b7 proper grid layout in macro; pyplot isx fix 2016-05-30 14:56:32 -05:00
Thomas Breloff 625c92a985 working on 0.5 changes 2016-05-28 11:17:34 -04:00
Thomas Breloff 3cc9425219 working on 0.5 changes -- String 2016-05-28 11:00:19 -04:00
Thomas Breloff 0a86ccb142 working on 0.5 changes 2016-05-28 10:42:41 -04:00
Thomas Breloff ce82e07dc9 Extrema type and link_axis; link keyword, removed old link logic 2016-05-27 22:03:56 -04:00
Thomas Breloff 048c60614c check _axis_defaults in default 2016-05-27 16:31:07 -04:00
Thomas Breloff 148fa6d151 discrete_values handling; pyplot lims/widen; cleanup 2016-05-27 16:23:46 -04:00
Thomas Breloff 088543176f recompute_lengths fix; renamed hist to histogram, hist2d to histogram2d to match methods; fix tests 2016-05-27 15:08:05 -04:00
Thomas Breloff 536746d47e switch order for Surface function constructor; closes #273 2016-05-27 14:38:23 -04:00
Thomas Breloff c15cbad396 shorthands macro; pyplot bar fill fix; userplot macro; nicer error message in convertToAnyVector 2016-05-27 14:25:14 -04:00
Thomas Breloff d3baa58c7e series ordering bug fix 2016-05-27 12:55:29 -04:00
Thomas Breloff 7c885de6c7 improved trickery with layout macro 2016-05-27 12:16:34 -04:00
Thomas Breloff 010b8a382e working on building plot from existing plots 2016-05-27 10:58:45 -04:00
Thomas Breloff 314053af54 gr hist2d fix; closes #253 2016-05-27 09:05:48 -04:00
Thomas Breloff 34a7f8a99c prepared_object; closes #267 2016-05-27 00:19:12 -04:00
Thomas Breloff 87031984d9 widen gr limits; fixes #265 2016-05-27 00:14:22 -04:00
Thomas Breloff 83687f96a5 PLOTS_USE_ATOM_PLOTPANE env var; closes #270 2016-05-26 23:56:11 -04:00
Thomas Breloff f33905ed80 fixes for images/heatmaps/surfaces 2016-05-26 23:47:09 -04:00
Thomas Breloff f91c4d586c html_output_format 2016-05-26 20:57:51 -04:00
Thomas Breloff 77ec4adcad default minpad fix; html_output_format 2016-05-26 17:04:28 -04:00
Thomas Breloff b5a8186d99 working on pgfplots; default minpad calcs; moved subplot/layout init later to allow setting in recipes 2016-05-26 16:01:46 -04:00
Thomas Breloff ed25e554e1 working on pgfplots 2016-05-26 15:02:04 -04:00
Thomas Breloff a388ec71e3 working on pgfplots 2016-05-26 14:18:57 -04:00
Thomas Breloff bde3c2e19d working on pgfplots 2016-05-26 13:47:00 -04:00
Thomas Breloff d1d8d196ba sticks recipe; fixed up unicodeplots backend 2016-05-26 11:19:47 -04:00
Thomas Breloff 7be5aebbc9 gr fixes: polar markers, empty legends, pie labels, scale and flip 2016-05-25 16:52:07 -04:00
Thomas Breloff d15fcb6b24 gr_polyline fixes 2016-05-25 16:01:18 -04:00
Thomas Breloff b3ac38a6f1 working on gr 2016-05-25 15:17:51 -04:00
Thomas Breloff dae9dad2f7 working on GR; series_list and should_add_to_legend; series recipes fix; hist and bar recipes 2016-05-25 13:06:08 -04:00
Thomas Breloff 603dc30bb1 backend callback changes; output callbacks; prepare_output; fixes for pyplot, plotly, plotlyjs; and more 2016-05-24 23:19:51 -04:00
Tom Breloff 71070da13e Merge pull request #276 from spencerlyon2/dev
Fix plotlyjs subplots
2016-05-24 20:48:25 -04:00
Spencer Lyon 8fa1d4eb9b Fix plotlyjs subplots 2016-05-24 17:21:07 -07:00
Thomas Breloff 41f91f9cfb working on GR rebuild 2016-05-24 18:01:20 -04:00
Thomas Breloff 5699f3c6bc begin GR cleanup 2016-05-24 15:22:16 -04:00
Thomas Breloff 300babd6b1 deprecated gadfly/immerse 2016-05-24 14:46:53 -04:00
Thomas Breloff c1baca181c plotly subplot layouts 2016-05-24 14:25:03 -04:00
Thomas Breloff 851fe9f72c plotlyjs fixes 2016-05-24 12:38:52 -04:00
Thomas Breloff cf635cbdd9 renamed plotargs to attr; working on plotly backend 2016-05-24 12:20:03 -04:00
Thomas Breloff abbd8635dd pyplot hist in legend fix; closes #254 2016-05-24 11:03:38 -04:00
Thomas Breloff 2c2218bcb9 layouts fixed 2016-05-24 10:34:02 -04:00
Thomas Breloff bb6c06d3f7 working on layouts 2016-05-24 08:30:46 -04:00
Thomas Breloff 90e22f179e discrete value fixes; margin args; check for fillrange in contourf; skip test 6 2016-05-23 18:15:33 -04:00
Thomas Breloff 0a00d1fbf3 suppress_warnings fix; discrete values fixes 2016-05-23 16:42:24 -04:00
Thomas Breloff 6f0b8d1d7b group fix; ohlc fix 2016-05-23 15:13:20 -04:00
Thomas Breloff ef0d99340a backend template renaming/reorg/cleanup, pyplot fixes 2016-05-23 10:29:35 -04:00
Thomas Breloff d4d388a94b pyplot colorbar labels bbox 2016-05-21 23:03:55 -04:00
Thomas Breloff 7022ce7553 fixes for push/append/set_xy 2016-05-21 10:03:47 -04:00
Thomas Breloff 4a96122067 colorbar layout fixes; process_axis_arg and related fix 2016-05-21 00:04:33 -04:00
Thomas Breloff 862ac3af8a fixes/improvements to annotations; added series_annotations keyword 2016-05-20 11:38:07 -04:00
Thomas Breloff 333c2765fe ohlc recipe and related fixes; removed ohlc-specific code from gadfly and gr 2016-05-20 10:06:15 -04:00
Thomas Breloff 89feb68b7c pyplot lims fix 2016-05-20 08:15:27 -04:00
Thomas Breloff 1b901673fc fixes for guide/dataframes; 3d/polar projections; delayed subplot init; fg_title 2016-05-19 23:45:07 -04:00
Thomas Breloff fb60c244f6 bar_width and bar_edges keywords; small fixes 2016-05-19 17:34:37 -04:00
Thomas Breloff 3cea2df459 fix get_subplot_index; fix pyplot colorbar 2016-05-19 14:00:39 -04:00
Thomas Breloff 1cf514d1e6 merged dev into tb_recipes 2016-05-19 13:03:01 -04:00
Thomas Breloff 65e18a8669 upper bound on RecipesBase in REQUIRE; added NEWS.md; closes #259 2016-05-19 12:36:34 -04:00
Thomas Breloff 3e8f325ddc reorg/cleanup; removed old layouts and subplots; created axes.jl and layouts.jl 2016-05-19 10:42:59 -04:00
Thomas Breloff 0d96c49f4a layout macro; attr rename; getindex; fixes and cleanup 2016-05-18 23:55:03 -04:00
Thomas Breloff bfc3fc1dec titles and sub-layouts 2016-05-18 15:53:00 -04:00
Thomas Breloff 327f235af9 working on subplot/axis arg processing 2016-05-18 14:08:44 -04:00
Thomas Breloff df7fc0c4df fixes; basic layout works 2016-05-17 23:37:47 -04:00
Thomas Breloff 33d9664df5 working on layouts 2016-05-17 21:24:34 -04:00
Thomas Breloff 0d237028e7 redo the bbox logic 2016-05-17 17:41:03 -04:00
Thomas Breloff 80b9115393 fixes 2016-05-17 15:55:54 -04:00
Thomas Breloff 606229a08f trying out measures 2016-05-17 15:45:05 -04:00
Thomas Breloff 2ac17afe30 fixes 2016-05-17 10:33:10 -04:00
Tom Breloff 70f23e695b Merge pull request #252 from pkofod/join
Replace string cats with join() in pgfplots (and more).
2016-05-17 09:09:04 -04:00
Thomas Breloff 3084b0c41c working on arg overhaul 2016-05-17 00:57:42 -04:00
Thomas Breloff b75db9e473 arg defaults 2016-05-16 21:56:27 -04:00
Thomas Breloff c89e9c5f73 refactored and renamed plot defaults into plot,subplot,axis defaults, plus some associated fixes 2016-05-16 20:57:50 -04:00
Thomas Breloff 61107b8577 working on layout 2016-05-16 17:12:45 -04:00
Thomas Breloff 35ff449dc3 working on layout 2016-05-16 15:56:12 -04:00
Thomas Breloff ae1f5b8b06 working on layouts 2016-05-16 14:24:45 -04:00
Thomas Breloff c5bcae1e34 working on subplots 2016-05-16 01:07:33 -04:00
Thomas Breloff e9ab6e7301 started subplots/layouts 2016-05-15 12:32:10 -04:00
Thomas Breloff a6ce2e691d changed add_series to accept Series and push to list automatically; default add_series simplifies; removed old supported.jl 2016-05-15 12:14:18 -04:00
Thomas Breloff bfe04bdd15 changed to _create_backend_figure; started subplot removal 2016-05-15 11:23:27 -04:00
Thomas Breloff 2d0f9f01d5 cleanup 2016-05-14 17:34:44 -04:00
Thomas Breloff 87a967ad9c working on Axis 2016-05-14 00:10:58 -04:00
Thomas Breloff 290b17a5f9 fixes 2016-05-13 22:53:32 -04:00
Thomas Breloff 76572b990b fix gr linetype 2016-05-13 21:00:02 -04:00
Thomas Breloff 54efb48d62 changed linetype to seriestype 2016-05-13 20:55:26 -04:00
Thomas Breloff 83bd780f2a working on Axis type 2016-05-13 16:46:15 -04:00
Thomas Breloff 2ba4ac6d20 working on Axis type 2016-05-13 14:42:11 -04:00
Thomas Breloff 98dc52f124 working on Axis type 2016-05-13 13:45:30 -04:00
Thomas Breloff fbb2f30f3f errorbars, ribbons, and marker_z 2016-05-13 11:58:29 -04:00
Thomas Breloff 8ec3d18339 boxplot, violin, quiver, and line series recipes; plus some fixes 2016-05-13 11:21:32 -04:00
Thomas Breloff 26779cbf4a series recipes 2016-05-13 08:03:28 -04:00
Thomas Breloff 4f429fb536 recipe overhaul; groups, annotations, SliceIt 2016-05-13 00:17:58 -04:00
Thomas Breloff 7e1706f700 working on core recipes 2016-05-12 21:30:52 -04:00
Thomas Breloff d491b29037 working on recipes overhaul 2016-05-12 16:35:57 -04:00
Thomas Breloff cf9b60660d working on recipes overhaul 2016-05-12 14:51:53 -04:00
Thomas Breloff a5e9ad9f19 working on recipes overhaul 2016-05-12 14:00:47 -04:00
Thomas Breloff 6049a9fa0a working on recipes overhaul 2016-05-12 12:31:47 -04:00
Patrick Kofod Mogensen 5196ce5263 Add PGFPlots support for the size kw. 2016-05-12 17:32:54 +02:00
Patrick Kofod Mogensen 9231da4610 Clean up wall of pushes in _pgfplots_get_marker, and lines starting with kwarg. 2016-05-12 17:32:54 +02:00
Patrick Kofod Mogensen 89f8030ba1 Add markerstrokewidth to pgfplots. 2016-05-12 17:32:54 +02:00
Patrick Kofod Mogensen 534f57b227 Replace string cats with join() in pgfplots. 2016-05-12 17:32:54 +02:00
Thomas Breloff 4d3ebf31d4 add series_new.jl 2016-05-12 10:28:04 -04:00
Tom Breloff d778c7922d Merge pull request #255 from jheinen/dev
Fixed some bugs in gr.jl
2016-05-12 10:12:29 -04:00
Josef Heinen 5109c6f72b Fixed some bugs in gr.jl
- Turn off interpolation for heatmaps
- Fixed bug plotting scatter after heatmap (scatter drew to the colorbar
  viewport)
- Don't draw superfluous legends
2016-05-12 15:29:43 +02:00
Thomas Breloff 5005d32afd recipes overhaul; is_installed and dataframes logic; removed requires 2016-05-11 22:12:51 -04:00
Thomas Breloff 88be655dca started recipe reorg 2016-05-11 17:56:18 -04:00
Tom Breloff fe5789d747 Merge pull request #251 from jheinen/dev
Added (native) image support
2016-05-11 10:41:39 -04:00
Josef Heinen 00201b5f9c Added (native) image support 2016-05-11 16:18:03 +02:00
Thomas Breloff 85f7acf9d5 add PyPlot.ioff to init; closes #229 2016-05-11 09:45:44 -04:00
Thomas Breloff 44d1cdc2b2 pgfplots aspect_ratio 2016-05-11 09:18:40 -04:00
Tom Breloff 90c2202f6e Merge pull request #250 from KristofferC/kc/fix_pgfplots_color
Fix bug in some linecolors in PGFPlots
2016-05-11 09:11:13 -04:00
Kristoffer Carlsson d30574de24 workaround a bug in engineering notation for colors in pgfplot 2016-05-11 15:01:17 +02:00
Tom Breloff dec3748fe5 Merge pull request #249 from KristofferC/kc/pgfplots_label
add support for legend and label in pgfplots
2016-05-11 08:51:38 -04:00
Kristoffer Carlsson e8cdff2310 add support for legend and label in pgfplots
also adds some extra kwargs for legend positions
2016-05-11 13:59:32 +02:00
Thomas Breloff a0ca9d675f add_arrows function; plotly cleanup and arrows attempt 2016-05-10 17:22:47 -04:00
Thomas Breloff baf410c712 remove pyplot dash_capstyle option 2016-05-10 14:34:28 -04:00
Thomas Breloff a81af2b449 arrow conveniences 2016-05-10 13:57:38 -04:00
Thomas Breloff 678dde710b arrows with updated quiver recipe; only pyplot so far 2016-05-10 13:40:25 -04:00
Tom Breloff 93a35d8fdc Merge pull request #244 from jheinen/dev
Improved scaling of plots with error bars
2016-05-10 12:21:18 -04:00
Josef Heinen 0878f44b09 Improved scaling of plots with error bars 2016-05-10 18:14:08 +02:00
Thomas Breloff b89ae245ec removed heatmap assert 2016-05-09 22:44:22 -04:00
Thomas Breloff 536da01714 boxplot nan fix; add match_dimensions to pyplot supportedargs 2016-05-09 22:13:53 -04:00
Thomas Breloff a1b991aaeb image fixes and support in pyplot 2016-05-09 15:53:33 -04:00
Thomas Breloff 7bee9a5829 setTicksFromStringVector fixes; travis 2016-05-09 14:36:09 -04:00
Thomas Breloff 17a90e3b00 revamped setTicksFromStringVector; dataframes label fix 2016-05-09 14:00:07 -04:00
Tom Breloff 43ba9b1172 Merge pull request #239 from jheinen/dev
Fix axes offset bug (#221)
2016-05-09 11:28:04 -04:00
Thomas Breloff fdb01046e6 plotly zaxis, tick rotation, 3d axis fix; dataframes recipe fixes 2016-05-09 11:19:20 -04:00
Josef Heinen 09421c9189 Fix axes offset bug (#221) 2016-05-09 17:10:20 +02:00
Thomas Breloff c563204b0a added RecipesBase dep 2016-05-09 09:01:05 -04:00
Thomas Breloff 33b3ee89f3 replaced dataframes with recipes 2016-05-09 08:31:50 -04:00
Tom Breloff 008b61c9f6 Merge pull request #236 from jheinen/dev
Fixed colormap problem
2016-05-09 06:12:55 -04:00
Josef Heinen 7546db4d42 Fixed colormap problem 2016-05-09 11:44:37 +02:00
Thomas Breloff 925243969c attempted colormap fix for gr 2016-05-08 11:02:20 -04:00
Tom Breloff 7bce4a963e Merge pull request #233 from jheinen/dev
gr: support for colormaps
2016-05-08 10:30:40 -04:00
Josef Heinen 42d09a6d80 Working on colormaps 2016-05-08 14:06:11 +02:00
Josef Heinen b6334176e4 Added support for :nbins keyword 2016-05-07 13:24:11 +02:00
Josef Heinen 42a175bd53 Added support for :zlims keyword 2016-05-07 12:45:09 +02:00
Josef Heinen b4fb9af8e4 Fixed a problem with Julia v0.3 2016-05-07 07:47:13 +02:00
Thomas Breloff ed6c9fe432 add is_key_supported definition 2016-05-06 15:17:56 -04:00
Thomas Breloff 7e04aa438f removed RecipesBase from REQUIRE 2016-05-06 15:09:53 -04:00
Thomas Breloff f1679f82c1 remove recipe macro; add dependency on RecipesBase 2016-05-06 14:55:42 -04:00
Thomas Breloff 1808b81d59 recipe fix for empty args in subplot 2016-05-06 11:51:23 -04:00
Thomas Breloff cd25c7acb2 markershape fix; added require option for recipes 2016-05-06 11:39:17 -04:00
Thomas Breloff cf03779c5b handle user keywords properly for recipes 2016-05-06 10:50:54 -04:00
Thomas Breloff d3439f0d7e recipe macro handle curly 2016-05-05 16:58:04 -04:00
Thomas Breloff 2df7f84939 recipe macro 2016-05-05 16:18:56 -04:00
Thomas Breloff 74c76c48d5 working on plotrecipe 2016-05-05 12:22:31 -04:00
Thomas Breloff cdc0d22b01 changed recipe to plotrecipe and improved it 2016-05-05 11:51:50 -04:00
Thomas Breloff 5a520aadc0 removed some debugging 2016-05-05 10:35:04 -04:00
Thomas Breloff ec87489dd4 working on image type; working on ijulia display_dict 2016-05-05 00:13:45 -04:00
Thomas Breloff e30affdfc2 made center calc centroid 2016-05-04 21:36:15 -04:00
Thomas Breloff 66bd2559b9 recipe macro; markershape fix; moved/fixed themes 2016-05-04 16:09:09 -04:00
Thomas Breloff c8d6b1fab0 linewidth fix; kw macro 2016-05-04 14:24:15 -04:00
Thomas Breloff 548c59719b linewidth default 2016-05-04 13:38:08 -04:00
Thomas Breloff 59c1e24371 default_kw and force_kw macros for recipes 2016-05-04 13:24:44 -04:00
Thomas Breloff 3c09921cbd x/y/z ticklabel rotation; new supported graphs using heatmap 2016-05-03 17:14:52 -04:00
42 changed files with 8092 additions and 5465 deletions
+5 -3
View File
@@ -4,9 +4,11 @@ os:
- linux
- osx
julia:
- 0.4
#- nightly
- release
- nightly
matrix:
allow_failures:
- julia: nightly
# # before install:
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
+164
View File
@@ -0,0 +1,164 @@
# Plots.jl NEWS
#### notes on release changes, ongoing development, and future planned work
- All new development should target 0.7!
---
## 0.7 (current master/dev)
#### 0.7.0
- Check out [the summary](http://plots.readthedocs.io/en/latest/plots_v0.7/)
- Revamped and simplified internals
- [Recipes, recipes, recipes](https://github.com/JuliaPlots/RecipesBase.jl/issues/6)
- [Layouts and Subplots](https://github.com/tbreloff/Plots.jl/issues/60)
- DataFrames is loaded automatically when installed
- Overhaul to GroupBy mechanic (now offloads to a recipe)
- Replaced much of the argument processing with recipes
- Added series recipes, and began to strip down un-needed backend code. Some recipes:
- line, step, sticks, bar, histogram, histogram2d, boxplot, violin, quiver, errorbars, density, ohlc
- Added `@shorthands` and `@userplot` macros for recipe convenience
- Better handling of errorbars and ribbons
- New Axis type
- Tracks extrema and discrete values
- New `link_axes` functionality
- `linetype` has been renamed `seriestype` (the alias is reversed)
- Many fixes and huge cleanup in GR
- Brand new subplot layout mechanics:
- `@layout` macro
- AbstractLayout, Subplot, GridLayout, and everything related
- Added dependency on Measures.jl
- Computations of axis/guide sizes and precise positioning
- Refactored and compartmentalized default dictionaries for attributes
- Deprecated Gadfly and Immerse backends
- Added `series_annotations` attribute (previously that functionality was merged with `annotations`, which are not series-specific)
- Removed `axis` attribute... currently not supporting twin (right) y axes
- Check for `ENV["PLOTS_USE_ATOM_PLOTPANE"]` and default to false
- Improved backend interface to reduce redundant code. Template updated.
- Added `html_output_format`, primarily for choosing between png and svg output in IJulia.
- Partial support of Julia v0.5
- Switched testing to dump reference images to JuliaPlots/PlotReferenceImages.jl
- Moved docs-specific code to new JuliaPlots/PlotDocs.jl
- Moved example list from ExamplePlots into Plots.
- Added several examples and improved others.
- Many other smaller changes and bug fixes.
---
## Version 0.6
#### 0.6.2
- `linewidth` fixes
- `markershape` fix
- converted center calc to centroid for shapes
- new dependency on [RecipesBase](https://github.com/JuliaPlots/RecipesBase.jl)
- REQUIRE upper limit for RecipesBase: 0.0.1
- GR fixes/improvements (@jheinen)
- support `zlims`, `bins`
- allow Plots colormaps
- other bug fixes
- native image support
- PGFPlots fixes/improvements (@pkofod)
- DataFrames are handled by recipes
- Plotly: zaxis, tick rotation, 3d axis fix
- Improvements in handling discrete data
- Support for image display
- `arrow` keyword and support for adding arrows to paths
- changed quiver recipe to use arrows
- Bug fixes for boxplots, heatmaps, and more
#### 0.6.1
- `rotation` keyword
- improved supported graphs
- subplot bug fix
#### 0.6.0
- `apply_series_recipe` framework for built-in recipes
- [boxplot/violin recipes](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/boxplot.ipynb)
- [errorbar/ribbon recipes](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/errorbars.ipynb)
- [quiver recipe](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/quiver.ipynb)
- `polar` coordinates
- better support for shapes and custom polygons (see [batman](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/batman.ipynb))
- z-axis keywords
- 3D indexing overhaul: `push!`, `append!` support
- matplotlib colormap constants (`:inferno` is the new default colormap for Plots)
- `typealias KW Dict{Symbol,Any}` used in place of splatting in many places
- png generation for plotly backend using wkhtmltoimage
- `normalize` and `weights` keywords
- background/foreground subcategories for fine-tuning of looks
- `add_theme`/`set_theme` and ggplot2 theme (see [this issue](https://github.com/tbreloff/Plots.jl/issues/201))
- `PLOTS_DEFAULT_BACKEND` environment variable
- `barh` linetype
- support for non-gridded surfaces with pyplot's trisurface
- pyplot surface zcolor
- internal refactor of supported.jl
- `wrap` method to bypass input processing
- `translate`, `scale` and `rotate` methods for coordinates and shapes
- and many more minor fixes and improvements
---
## Version 0.5
#### 0.5.4
- old heatmaps have been renamed to hist2d, and true heatmaps implemented (see https://github.com/tbreloff/Plots.jl/issues/147)
- lots of reorganization and redesign of the internals
- lots of renaming to keep to conventions: AbstractPlot, AbstractBackend, etc
- initial redesign of layouts
- integration with Atom PlotPane
- arc diagram and chord diagram (thanks to @diegozea: see https://github.com/tbreloff/Plots.jl/issues/163)
- work on GR, GLVisualize, and PGFPlots backends (thanks @jheinen @dlfivefifty @pkofod)
- improvements to Plotly setup (thanks @spencerlyon2)
- overhaul to series creation logic and groupby mechanic
- replace Dict with `typealias KW Dict{Symbol,Any}` in many places, also replacing keyword arg splatting
- new `shape` linetype for plotting polygons in plot-coordinates (see https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/batman.ipynb)
- many other fixes
#### 0.5.3
- `@gif` macro with `every`/`when` syntax
- bezier curves and other graph drawing helpers
- added FixedSizeArrays dependency with relevant functionality
- merged lots of improvements to GR (thanks @jheinen)
- `overwrite_figure`/`reuse` arg for reusing the same figure window
- deprecated Qwt, Winston, and Bokeh backends
- improved handling of 3D inputs (call `z=rand(10,10); surface(z)` for example)
- fix IJulia display issue
- lots of progress on PlotlyJS backend
- and many other changes and fixes...
#### 0.5.2
- Added [GR.jl](https://github.com/jheinen/GR.jl) as a backend (unfinished but functional) All credit to @jheinen
- Set defaults within backend calls (i.e. `gadfly(legend=false)`)
- `abline!`; also extrema allows plotting functions without giving x (i.e. `plot(cos, 0, 10); plot!(sin)`) @pkofod @joshday
- Integration with [PlotlyJS.jl](https://github.com/spencerlyon2/PlotlyJS.jl) for using Plotly inside a Blink window @spencerlyon2
- The Plotly backend has been split into my built-in version (`plotly()`) and @spencerlyon2's backend (`plotlyjs()`)
- Revamped backend setup code for easily adding new backends
- New docs (WIP) at http://plots.readthedocs.org/
- Overhaul to `:legend` keyword (see https://github.com/tbreloff/Plots.jl/issues/135)
- New dependency on Requires, allows auto-loading of DataFrames support
- Support for plotting lists of Tuples and FixedSizeArrays
- new `@animate` macro for super simple animations (see https://github.com/tbreloff/Plots.jl/issues/111#issuecomment-181515616)
- allow Function for `:fillrange` and `zcolor` arguments (for example: `scatter(sin, 0:10, marker=15, fill=(cos,0.4), zcolor=sin)`)
- allow vectors of PlotText without x/y coords (for example: `scatter(rand(10), m=20, ann=map(text, 1:10))`)
- Lots and lots of fixes
#### 0.5.1
#### 0.5.0
- `with` function for temporary defaults
- contours
- basic 3D plotting
- preliminary support for Bokeh
- `stroke` and `brush` for more fine-tuned control over visuals
- smarter "magic" arguments: `line`, `marker`
+2 -1
View File
@@ -1,7 +1,8 @@
julia 0.4
RecipesBase
Colors
Reexport
Compat
Requires
FixedSizeArrays
Measures
+170 -105
View File
@@ -6,20 +6,27 @@ module Plots
using Compat
using Reexport
@reexport using Colors
using Requires
# using Requires
using FixedSizeArrays
@reexport using RecipesBase
using Base.Meta
export
AbstractPlot,
Plot,
Subplot,
SubplotLayout,
AbstractLayout,
GridLayout,
RowsLayout,
FlexLayout,
grid,
EmptyLayout,
@layout,
# RowsLayout,
# FlexLayout,
AVec,
AMat,
KW,
# attr,
# attr!,
wrap,
set_theme,
@@ -27,60 +34,62 @@ export
plot,
plot!,
subplot,
subplot!,
# subplot,
# subplot!,
current,
default,
with,
scatter,
scatter!,
bar,
bar!,
barh,
barh!,
histogram,
histogram!,
histogram2d,
histogram2d!,
density,
density!,
heatmap,
heatmap!,
hexbin,
hexbin!,
sticks,
sticks!,
hline,
hline!,
vline,
vline!,
ohlc,
ohlc!,
@userplot,
@shorthands,
# scatter,
# scatter!,
# bar,
# bar!,
# barh,
# barh!,
# histogram,
# histogram!,
# histogram2d,
# histogram2d!,
# density,
# density!,
# heatmap,
# heatmap!,
# hexbin,
# hexbin!,
# sticks,
# sticks!,
# hline,
# hline!,
# vline,
# vline!,
# ohlc,
# ohlc!,
pie,
pie!,
contour,
contour!,
contour3d,
contour3d!,
surface,
surface!,
wireframe,
wireframe!,
path3d,
path3d!,
# contour,
# contour!,
# contour3d,
# contour3d!,
# surface,
# surface!,
# wireframe,
# wireframe!,
# path3d,
# path3d!,
plot3d,
plot3d!,
scatter3d,
scatter3d!,
abline!,
boxplot,
boxplot!,
violin,
violin!,
quiver,
quiver!,
# scatter3d,
# scatter3d!,
# abline!,
# boxplot,
# boxplot!,
# violin,
# violin!,
# quiver,
# quiver!,
title!,
xlabel!,
@@ -109,10 +118,15 @@ export
Shape,
text,
font,
Axis,
# xaxis,
# yaxis,
# zaxis,
stroke,
brush,
Surface,
OHLC,
arrow,
colorscheme,
ColorScheme,
@@ -144,6 +158,12 @@ export
arcdiagram,
chorddiagram,
# @kw,
# @recipe,
# @plotrecipe,
test_examples,
translate,
translate!,
rotate,
@@ -157,69 +177,117 @@ export
# ---------------------------------------------------------
import Measures
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height
typealias BBox Measures.Absolute2DBox
export BBox, BoundingBox, mm, cm, inch, pt, px, pct
# ---------------------------------------------------------
include("types.jl")
include("utils.jl")
include("colors.jl")
include("components.jl")
include("axes.jl")
include("backends.jl")
include("args.jl")
include("themes.jl")
include("plot.jl")
include("series_args.jl")
include("subplot.jl")
include("series_new.jl")
include("layouts.jl")
include("subplots.jl")
include("recipes.jl")
include("animation.jl")
include("output.jl")
include("examples.jl")
include("arg_desc.jl")
# ---------------------------------------------------------
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
barh(args...; kw...) = plot(args...; kw..., linetype = :barh, orientation = :h)
barh!(args...; kw...) = plot!(args...; kw..., linetype = :barh, orientation = :h)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
histogram2d(args...; kw...) = plot(args...; kw..., linetype = :hist2d)
histogram2d!(args...; kw...) = plot!(args...; kw..., linetype = :hist2d)
density(args...; kw...) = plot(args...; kw..., linetype = :density)
density!(args...; kw...) = plot!(args...; kw..., linetype = :density)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
hexbin(args...; kw...) = plot(args...; kw..., linetype = :hexbin)
hexbin!(args...; kw...) = plot!(args...; kw..., linetype = :hexbin)
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
pie(args...; kw...) = plot(args...; kw..., linetype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
pie!(args...; kw...) = plot!(args...; kw..., linetype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
contour(args...; kw...) = plot(args...; kw..., linetype = :contour)
contour!(args...; kw...) = plot!(args...; kw..., linetype = :contour)
contour3d(args...; kw...) = plot(args...; kw..., linetype = :contour3d)
contour3d!(args...; kw...) = plot!(args...; kw..., linetype = :contour3d)
surface(args...; kw...) = plot(args...; kw..., linetype = :surface)
surface!(args...; kw...) = plot!(args...; kw..., linetype = :surface)
wireframe(args...; kw...) = plot(args...; kw..., linetype = :wireframe)
wireframe!(args...; kw...) = plot!(args...; kw..., linetype = :wireframe)
path3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
path3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
plot3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
plot3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
scatter3d(args...; kw...) = plot(args...; kw..., linetype = :scatter3d)
scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d)
boxplot(args...; kw...) = plot(args...; kw..., linetype = :box)
boxplot!(args...; kw...) = plot!(args...; kw..., linetype = :box)
violin(args...; kw...) = plot(args...; kw..., linetype = :violin)
violin!(args...; kw...) = plot!(args...; kw..., linetype = :violin)
quiver(args...; kw...) = plot(args...; kw..., linetype = :quiver)
quiver!(args...; kw...) = plot!(args...; kw..., linetype = :quiver)
# define and export shorthand plotting method definitions
macro shorthands(funcname::Symbol)
funcname2 = Symbol(funcname, "!")
ret = esc(quote
export $funcname, $funcname2
$funcname(args...; kw...) = plot(args...; kw..., seriestype = $(quot(funcname)))
$funcname2(args...; kw...) = plot!(args...; kw..., seriestype = $(quot(funcname)))
end)
# dump(ret,20)
# @show ret
ret
end
@shorthands scatter
@shorthands bar
@shorthands barh
@shorthands histogram
@shorthands histogram2d
@shorthands density
@shorthands heatmap
@shorthands hexbin
@shorthands sticks
@shorthands hline
@shorthands vline
@shorthands ohlc
# @shorthands pie
@shorthands contour
@shorthands contour3d
@shorthands surface
@shorthands wireframe
@shorthands path3d
@shorthands scatter3d
@shorthands boxplot
@shorthands violin
@shorthands quiver
# scatter(args...; kw...) = plot(args...; kw..., seriestype = :scatter)
# scatter!(args...; kw...) = plot!(args...; kw..., seriestype = :scatter)
# bar(args...; kw...) = plot(args...; kw..., seriestype = :bar)
# bar!(args...; kw...) = plot!(args...; kw..., seriestype = :bar)
# barh(args...; kw...) = plot(args...; kw..., seriestype = :barh, orientation = :h)
# barh!(args...; kw...) = plot!(args...; kw..., seriestype = :barh, orientation = :h)
# histogram(args...; kw...) = plot(args...; kw..., seriestype = :histogram)
# histogram!(args...; kw...) = plot!(args...; kw..., seriestype = :histogram)
# histogram2d(args...; kw...) = plot(args...; kw..., seriestype = :histogram2d)
# histogram2d!(args...; kw...) = plot!(args...; kw..., seriestype = :histogram2d)
# density(args...; kw...) = plot(args...; kw..., seriestype = :density)
# density!(args...; kw...) = plot!(args...; kw..., seriestype = :density)
# heatmap(args...; kw...) = plot(args...; kw..., seriestype = :heatmap)
# heatmap!(args...; kw...) = plot!(args...; kw..., seriestype = :heatmap)
# hexbin(args...; kw...) = plot(args...; kw..., seriestype = :hexbin)
# hexbin!(args...; kw...) = plot!(args...; kw..., seriestype = :hexbin)
# sticks(args...; kw...) = plot(args...; kw..., seriestype = :sticks, marker = :ellipse)
# sticks!(args...; kw...) = plot!(args...; kw..., seriestype = :sticks, marker = :ellipse)
# hline(args...; kw...) = plot(args...; kw..., seriestype = :hline)
# hline!(args...; kw...) = plot!(args...; kw..., seriestype = :hline)
# vline(args...; kw...) = plot(args...; kw..., seriestype = :vline)
# vline!(args...; kw...) = plot!(args...; kw..., seriestype = :vline)
# ohlc(args...; kw...) = plot(args...; kw..., seriestype = :ohlc)
# ohlc!(args...; kw...) = plot!(args...; kw..., seriestype = :ohlc)
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
# contour(args...; kw...) = plot(args...; kw..., seriestype = :contour)
# contour!(args...; kw...) = plot!(args...; kw..., seriestype = :contour)
# contour3d(args...; kw...) = plot(args...; kw..., seriestype = :contour3d)
# contour3d!(args...; kw...) = plot!(args...; kw..., seriestype = :contour3d)
# surface(args...; kw...) = plot(args...; kw..., seriestype = :surface)
# surface!(args...; kw...) = plot!(args...; kw..., seriestype = :surface)
# wireframe(args...; kw...) = plot(args...; kw..., seriestype = :wireframe)
# wireframe!(args...; kw...) = plot!(args...; kw..., seriestype = :wireframe)
# path3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
# path3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
# scatter3d(args...; kw...) = plot(args...; kw..., seriestype = :scatter3d)
# scatter3d!(args...; kw...) = plot!(args...; kw..., seriestype = :scatter3d)
# boxplot(args...; kw...) = plot(args...; kw..., seriestype = :boxplot)
# boxplot!(args...; kw...) = plot!(args...; kw..., seriestype = :boxplot)
# violin(args...; kw...) = plot(args...; kw..., seriestype = :violin)
# violin!(args...; kw...) = plot!(args...; kw..., seriestype = :violin)
# quiver(args...; kw...) = plot(args...; kw..., seriestype = :quiver)
# quiver!(args...; kw...) = plot!(args...; kw..., seriestype = :quiver)
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
@@ -272,16 +340,13 @@ yaxis!(plt::Plot, args...; kw...) = plot!(pl
const CURRENT_BACKEND = CurrentBackend(:none)
# setup_dataframes()
function __init__()
# override IJulia inline display
if isijulia()
@eval import IJulia
IJulia.display_dict(plt::AbstractPlot) = Dict{ASCIIString, ByteString}("text/html" => sprint(writemime, "text/html", plt))
end
setup_dataframes()
setup_atom()
setup_ijulia()
# setup_dataframes()
setup_atom()
# add_axis_letter_defaults()
end
# ---------------------------------------------------------
+15 -8
View File
@@ -1,12 +1,12 @@
immutable Animation
dir::ASCIIString
frames::Vector{ASCIIString}
dir::Compat.ASCIIString
frames::Vector{Compat.ASCIIString}
end
function Animation()
tmpdir = convert(ASCIIString, mktempdir())
Animation(tmpdir, ASCIIString[])
tmpdir = convert(Compat.ASCIIString, mktempdir())
Animation(tmpdir, Compat.ASCIIString[])
end
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
@@ -21,20 +21,27 @@ end
"Wraps the location of an animated gif so that it can be displayed"
immutable AnimatedGif
filename::ASCIIString
filename::Compat.ASCIIString
end
function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Integer = 20)
function gif(anim::Animation, fn::Compat.ASCIIString = "tmp.gif"; fps::Integer = 20)
fn = abspath(fn)
try
# high quality
speed = round(Int, 100 / fps)
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png -alpha off $fn`)
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
include(file)
end
prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
run(`$(joinpath(prefix, "convert")) -delay $speed -loop 0 $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
catch err
warn("Tried to create gif using convert (ImageMagick), but got error: $err\nWill try ffmpeg, but it's lower quality...)")
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`
Will try ffmpeg, but it's lower quality...)""")
# low quality
run(`ffmpeg -v 0 -framerate $fps -i $(anim.dir)/%06d.png -y $fn`)
+103
View File
@@ -0,0 +1,103 @@
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 from this to 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 `:seriescolor`.",
: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. For histogram-types, defines the number of bins, or the edges, of the histogram.",
: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. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
:levels => "Integer, NTuple{2,Integer}. 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. Should normalize histogram types? Trying for area == 1.",
:weights => "AbstractVector. Used in histogram types for weighted counts.",
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
: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.",
# plot args
:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
: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 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.",
# subplot args
:title => "String. Subplot title.",
:title_location => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`",
:titlefont => "Font. Font 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_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.",
: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 Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)",
: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)",
:legendfont => "Font. Font of legend items.",
:grid => "Bool. Show the grid lines?",
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
:projection => "Symbol or String. '3d' or 'polar'",
:aspect_ratio => "Symbol (:equal) or Number (width to height ratio of 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.",
# axis args
:guide => "String. Axis guide (label).",
:lims => "NTuple{2,Number}. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).",
: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?",
:tickfont => "Font. Font of axis tick labels.",
:guidefont => "Font. Font of axis guide (label).",
:foreground_color_axis => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis ticks.",
:foreground_color_border => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of plot area border (spines).",
:foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.",
:foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).",
)
+548 -453
View File
File diff suppressed because it is too large Load Diff
+314
View File
@@ -0,0 +1,314 @@
# xaxis(args...; kw...) = Axis(:x, args...; kw...)
# yaxis(args...; kw...) = Axis(:y, args...; kw...)
# zaxis(args...; kw...) = Axis(:z, args...; kw...)
# -------------------------------------------------------------------------
function Axis(sp::Subplot, letter::Symbol, args...; kw...)
# init with values from _plot_defaults
d = KW(
:letter => letter,
# :extrema => (Inf, -Inf),
:extrema => Extrema(),
:discrete_map => Dict(), # map discrete values to discrete indices
:continuous_values => zeros(0),
:use_minor => false,
:show => true, # show or hide the axis? (useful for linked subplots)
)
# get defaults from letter version, unless match
for (k,v) in _axis_defaults
lk = Symbol(letter, k)
lv = _axis_defaults_byletter[lk]
d[k] = (lv == :match ? v : lv)
end
# merge!(d, _axis_defaults)
d[:discrete_values] = []
# update the defaults
update!(Axis(sp, d), args...; kw...)
end
function process_axis_arg!(d::KW, arg, letter = "")
T = typeof(arg)
arg = get(_scaleAliases, arg, arg)
if typeof(arg) <: Font
d[Symbol(letter,:tickfont)] = arg
d[Symbol(letter,:guidefont)] = arg
elseif arg in _allScales
d[Symbol(letter,:scale)] = arg
elseif arg in (:flip, :invert, :inverted)
d[Symbol(letter,:flip)] = true
elseif T <: AbstractString
d[Symbol(letter,:guide)] = arg
# xlims/ylims
elseif (T <: Tuple || T <: AVec) && length(arg) == 2
sym = typeof(arg[1]) <: Number ? :lims : :ticks
d[Symbol(letter,sym)] = arg
# xticks/yticks
elseif T <: AVec
d[Symbol(letter,:ticks)] = arg
elseif arg == nothing
d[Symbol(letter,:ticks)] = []
elseif typeof(arg) <: Number
d[Symbol(letter,:rotation)] = arg
else
warn("Skipped $(letter)axis arg $arg")
end
end
# update an Axis object with magic args and keywords
function update!(axis::Axis, args...; kw...)
# first process args
d = axis.d
for arg in args
process_axis_arg!(d, arg)
end
# then override for any keywords... only those keywords that already exists in d
for (k,v) in kw
if haskey(d, k)
if k == :discrete_values
# add these discrete values to the axis
for vi in v
discrete_value!(axis, vi)
end
else
d[k] = v
end
end
end
axis
end
# -------------------------------------------------------------------------
Base.show(io::IO, axis::Axis) = dumpdict(axis.d, "Axis", true)
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.d, k)
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
# get discrete ticks, or not
function get_ticks(axis::Axis)
ticks = axis[:ticks]
dvals = axis[:discrete_values]
if !isempty(dvals) && ticks == :auto
axis[:continuous_values], dvals
else
ticks
end
end
# -------------------------------------------------------------------------
function expand_extrema!(ex::Extrema, v::Number)
ex.emin = min(v, ex.emin)
ex.emax = max(v, ex.emax)
ex
end
function expand_extrema!(axis::Axis, v::Number)
expand_extrema!(axis[:extrema], v)
end
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
ex = axis[:extrema]
ex.emin = min(v[1], ex.emin)
ex.emax = max(v[2], ex.emax)
ex
end
function expand_extrema!{N<:Number}(axis::Axis, v::AVec{N})
ex = axis[:extrema]
for vi in v
expand_extrema!(ex, vi)
end
ex
end
function expand_extrema!(sp::Subplot, d::KW)
# first expand for the data
for letter in (:x, :y, :z)
data = d[letter]
axis = sp.attr[Symbol(letter, "axis")]
if eltype(data) <: Number
expand_extrema!(axis, data)
elseif isa(data, Surface) && eltype(data.surf) <: Number
expand_extrema!(axis, data)
elseif data != nothing
# TODO: need more here... gotta track the discrete reference value
# as well as any coord offset (think of boxplot shape coords... they all
# correspond to the same x-value)
# @show letter,eltype(data),typeof(data)
d[letter], d[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
end
end
# # expand for fillrange/bar_width
# fillaxis, baraxis = sp.attr[:yaxis], sp.attr[:xaxis]
# if isvertical(d)
# fillaxis, baraxis = baraxis, fillaxis
# end
# expand for fillrange
vert = isvertical(d)
fr = d[:fillrange]
if fr == nothing && d[:seriestype] == :bar
fr = 0.0
end
if fr != nothing
axis = sp.attr[vert ? :yaxis : :xaxis]
if typeof(fr) <: Tuple
for fri in fr
expand_extrema!(axis, fri)
end
else
expand_extrema!(axis, fr)
end
end
# expand for bar_width
if d[:seriestype] == :bar
dsym = vert ? :x : :y
data = d[dsym]
bw = d[:bar_width]
if bw == nothing
bw = d[:bar_width] = mean(diff(data))
end
# @show data bw
axis = sp.attr[Symbol(dsym, :axis)]
expand_extrema!(axis, maximum(data) + 0.5maximum(bw))
expand_extrema!(axis, minimum(data) - 0.5minimum(bw))
end
end
function expand_extrema!(sp::Subplot, xmin, xmax, ymin, ymax)
expand_extrema!(sp[:xaxis], (xmin, xmax))
expand_extrema!(sp[:yaxis], (ymin, ymax))
end
# -------------------------------------------------------------------------
# push the limits out slightly
function widen(lmin, lmax)
span = lmax - lmin
# eps = max(1e-16, min(1e-2span, 1e-10))
eps = max(1e-16, 0.03span)
lmin-eps, lmax+eps
end
# figure out if widening is a good idea
function default_should_widen(axis::Axis)
should_widen = false
for series in series_list(axis.sp)
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
should_widen = true
end
end
should_widen
end
# using the axis extrema and limit overrides, return the min/max value for this axis
function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis))
ex = axis[:extrema]
amin, amax = ex.emin, ex.emax
lims = axis[:lims]
if isa(lims, Tuple) && length(lims) == 2
if isfinite(lims[1])
amin = lims[1]
end
if isfinite(lims[2])
amax = lims[2]
end
end
if amax <= amin
amax = amin + 1.0
end
if should_widen
widen(amin, amax)
else
amin, amax
end
end
# -------------------------------------------------------------------------
# these methods track the discrete (categorical) values which correspond to axis continuous values (cv)
# whenever we have discrete values, we automatically set the ticks to match.
# we return (continuous_value, discrete_index)
function discrete_value!(axis::Axis, dv)
cv_idx = get(axis[:discrete_map], dv, -1)
# @show axis[:discrete_map], axis[:discrete_values], dv
if cv_idx == -1
ex = axis[:extrema]
cv = max(0.5, ex.emax + 1.0)
expand_extrema!(axis, cv)
push!(axis[:discrete_values], dv)
push!(axis[:continuous_values], cv)
cv_idx = length(axis[:discrete_values])
axis[:discrete_map][dv] = cv_idx
cv, cv_idx
else
cv = axis[:continuous_values][cv_idx]
cv, cv_idx
end
end
# continuous value... just pass back with axis negative index
function discrete_value!(axis::Axis, cv::Number)
cv, -1
end
# add the discrete value for each item. return the continuous values and the indices
function discrete_value!(axis::Axis, v::AVec)
n = length(v)
cvec = zeros(n)
discrete_indices = zeros(Int, n)
for i=1:n
cvec[i], discrete_indices[i] = discrete_value!(axis, v[i])
end
cvec, discrete_indices
end
# add the discrete value for each item. return the continuous values and the indices
function discrete_value!(axis::Axis, v::AMat)
n,m = size(v)
cmat = zeros(n,m)
discrete_indices = zeros(Int, n, m)
for i=1:n, j=1:m
cmat[i,j], discrete_indices[i,j] = discrete_value!(axis, v[i,j])
end
cmat, discrete_indices
end
function discrete_value!(axis::Axis, v::Surface)
map(Surface, discrete_value!(axis, v.surf))
end
# -------------------------------------------------------------------------
function pie_labels(sp::Subplot, series::Series)
d = series.d
if haskey(d,:x_discrete_indices)
dvals = sp.attr[:xaxis].d[:discrete_values]
[dvals[idx] for idx in d[:x_discrete_indices]]
else
d[:x]
end
end
+49 -19
View File
@@ -12,16 +12,16 @@ _backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]()
macro init_backend(s)
str = lowercase(string(s))
sym = symbol(str)
T = symbol(string(s) * "Backend")
sym = Symbol(str)
T = Symbol(string(s) * "Backend")
esc(quote
immutable $T <: AbstractBackend end
export $sym
$sym(; kw...) = (default(; kw...); backend(symbol($str)))
backend_name(::$T) = symbol($str)
push!(_backends, symbol($str))
_backendType[symbol($str)] = $T
_backendSymbol[$T] = symbol($str)
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
backend_name(::$T) = Symbol($str)
push!(_backends, Symbol($str))
_backendType[Symbol($str)] = $T
_backendSymbol[$T] = Symbol($str)
include("backends/" * $str * ".jl")
end)
end
@@ -44,13 +44,31 @@ include("backends/web.jl")
# ---------------------------------------------------------
# don't do anything as a default
_create_backend_figure(plt::Plot) = nothing
_prepare_plot_object(plt::Plot) = nothing
_initialize_subplot(plt::Plot, sp::Subplot) = nothing
plot(pkg::AbstractBackend; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::AbstractBackend, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
_update_plot(pkg::AbstractBackend, plt::Plot, d::KW) = error("_update_plot($pkg, plt, d) is not implemented")
_update_plot_pos_size{P<:AbstractBackend}(plt::AbstractPlot{P}, d::KW) = nothing
subplot(pkg::AbstractBackend; kw...) = error("subplot($pkg; kw...) is not implemented")
subplot!(pkg::AbstractBackend, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
_series_added(plt::Plot, series::Series) = nothing
_series_updated(plt::Plot, series::Series) = nothing
_before_layout_calcs(plt::Plot) = nothing
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
# Set the (left, top, right, bottom) minimum padding around the plot area
# to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot)
leftpad = 10mm + sp[:left_margin] + guide_padding(sp[:yaxis])
toppad = 2mm + sp[:top_margin] + title_padding(sp)
rightpad = 3mm + sp[:right_margin]
bottompad = 5mm + sp[:bottom_margin] + guide_padding(sp[:xaxis])
# @show (leftpad, toppad, rightpad, bottompad)
sp.minpad = (leftpad, toppad, rightpad, bottompad)
end
_update_plot_object(plt::Plot) = nothing
# ---------------------------------------------------------
@@ -68,7 +86,7 @@ function pickDefaultBackend()
if env_default != ""
try
Pkg.installed(env_default) # this will error if not installed
sym = symbol(lowercase(env_default))
sym = Symbol(lowercase(env_default))
if haskey(_backendType, sym)
return backend(sym)
else
@@ -85,7 +103,7 @@ function pickDefaultBackend()
# features, speed, and robustness
for pkgstr in ("PyPlot", "GR", "PlotlyJS", "Immerse", "Gadfly", "UnicodePlots")
if Pkg.installed(pkgstr) != nothing
return backend(symbol(lowercase(pkgstr)))
return backend(Symbol(lowercase(pkgstr)))
end
end
@@ -130,13 +148,23 @@ end
Set the plot backend.
"""
function backend(pkg::AbstractBackend)
CURRENT_BACKEND.sym = backend_name(pkg)
CURRENT_BACKEND.pkg = pkg
CURRENT_BACKEND.sym = backend_name(pkg)
warn_on_deprecated_backend(CURRENT_BACKEND.sym)
CURRENT_BACKEND.pkg = pkg
end
function backend(modname::Symbol)
CURRENT_BACKEND.sym = modname
CURRENT_BACKEND.pkg = _backend_instance(modname)
warn_on_deprecated_backend(modname)
CURRENT_BACKEND.sym = modname
CURRENT_BACKEND.pkg = _backend_instance(modname)
end
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse]
function warn_on_deprecated_backend(bsym::Symbol)
if bsym in _deprecated_backends
warn("Backend $bsym has been deprecated. It may not work as originally intended.")
end
end
# ---------------------------------------------------------
@@ -148,6 +176,7 @@ supportedMarkers(::AbstractBackend) = [:none]
supportedScales(::AbstractBackend) = [:identity]
subplotSupported(::AbstractBackend) = false
stringsSupported(::AbstractBackend) = false
nativeImagesSupported(b::AbstractBackend) = :image in supportedTypes(b)
supportedAxes() = supportedAxes(backend())
supportedTypes() = supportedTypes(backend())
@@ -156,5 +185,6 @@ supportedMarkers() = supportedMarkers(backend())
supportedScales() = supportedScales(backend())
subplotSupported() = subplotSupported(backend())
stringsSupported() = stringsSupported(backend())
nativeImagesSupported() = nativeImagesSupported(backend())
# ---------------------------------------------------------
+27 -55
View File
@@ -3,7 +3,7 @@
supportedArgs(::BokehBackend) = [
# :annotation,
# :annotations,
# :axis,
# :background_color,
:linecolor,
@@ -18,7 +18,7 @@ supportedArgs(::BokehBackend) = [
# :legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
:seriestype,
:linewidth,
# :linealpha,
:markershape,
@@ -37,13 +37,13 @@ supportedArgs(::BokehBackend) = [
# :show,
:size,
:title,
# :windowtitle,
# :window_title,
:x,
# :xlabel,
# :xguide,
# :xlims,
# :xticks,
:y,
# :ylabel,
# :yguide,
# :ylims,
# :yrightlabel,
# :yticks,
@@ -60,7 +60,7 @@ supportedArgs(::BokehBackend) = [
# :levels,
]
supportedAxes(::BokehBackend) = [:auto, :left]
supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :histogram2d, :hexbin, :histogram, :bar, :hline, :vline, :contour]
supportedStyles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::BokehBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::BokehBackend) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
@@ -79,13 +79,6 @@ function _initialize_backend(::BokehBackend; kw...)
end
end
# make255(x) = round(Int, 255 * x)
# function bokehcolor(c::Colorant)
# @sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
# end
# bokehcolor(cs::ColorScheme) = bokehcolor(getColor(cs))
const _glyphtypes = KW(
:ellipse => :Circle,
@@ -104,14 +97,14 @@ const _glyphtypes = KW(
function bokeh_glyph_type(d::KW)
lt = d[:linetype]
st = d[:seriestype]
mt = d[:markershape]
if lt == :scatter && mt == :none
if st == :scatter && mt == :none
mt = :ellipse
end
# if we have a marker, use that
if lt == :scatter || mt != :none
if st == :scatter || mt != :none
return _glyphtypes[mt]
end
@@ -133,28 +126,30 @@ end
# ---------------------------------------------------------------------------
function _create_plot(pkg::BokehBackend, d::KW)
# function _create_plot(pkg::BokehBackend, d::KW)
function _create_backend_figure(plt::Plot{BokehBackend})
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
datacolumns = Bokeh.BokehDataSet[]
tools = Bokeh.tools()
filename = tempname() * ".html"
title = d[:title]
w, h = d[:size]
xaxis_type = d[:xscale] == :log10 ? :log : :auto
yaxis_type = d[:yscale] == :log10 ? :log : :auto
# legend = d[:legend] ? xxxx : nothing
title = plt.attr[:title]
w, h = plt.attr[:size]
xaxis_type = plt.attr[:xscale] == :log10 ? :log : :auto
yaxis_type = plt.attr[:yscale] == :log10 ? :log : :auto
# legend = plt.attr[:legend] ? xxxx : nothing
legend = nothing
extra_args = KW() # TODO: we'll put extra settings (xlim, etc) here
bplt = Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
Plot(bplt, pkg, 0, d, KW[])
# Plot(bplt, pkg, 0, d, KW[])
end
function _add_series(::BokehBackend, plt::Plot, d::KW)
bdata = Dict{Symbol, Vector}(:x => collect(d[:x]), :y => collect(d[:y]))
# function _series_added(::BokehBackend, plt::Plot, d::KW)
function _series_added(plt::Plot{BokehBackend}, series::Series)
bdata = Dict{Symbol, Vector}(:x => collect(series.d[:x]), :y => collect(series.d[:y]))
glyph = Bokeh.Bokehjs.Glyph(
glyphtype = bokeh_glyph_type(d),
@@ -168,17 +163,14 @@ function _add_series(::BokehBackend, plt::Plot, d::KW)
legend = nothing # TODO
push!(plt.o.datacolumns, Bokeh.BokehDataSet(bdata, glyph, legend))
push!(plt.seriesargs, d)
plt
# push!(plt.seriesargs, d)
# plt
end
# ----------------------------------------------------------------
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{BokehBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{BokehBackend}, d::KW)
function _update_plot_object(plt::Plot{BokehBackend}, d::KW)
end
# ----------------------------------------------------------------
@@ -197,29 +189,9 @@ end
# end
# ----------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{BokehBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{BokehBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
function _expand_limits(lims, plt::Plot{BokehBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{BokehBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
@@ -232,6 +204,6 @@ function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
Bokeh.showplot(plt.o)
end
function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
# TODO: display/show the subplot
end
# function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
# # TODO: display/show the subplot
# end
+125 -121
View File
@@ -3,19 +3,19 @@
supportedArgs(::GadflyBackend) = [
:annotation,
:annotations,
:background_color, :foreground_color, :color_palette,
:group, :label, :linetype,
:group, :label, :seriestype,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:fillrange, :fillcolor, :fillalpha,
:bins, :n, :nc, :nr, :layout, :smooth,
:title, :windowtitle, :show, :size,
:x, :xlabel, :xlims, :xticks, :xscale, :xflip,
:y, :ylabel, :ylims, :yticks, :yscale, :yflip,
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
:title, :window_title, :show, :size,
:x, :xguide, :xlims, :xticks, :xscale, :xflip,
:y, :yguide, :ylims, :yticks, :yscale, :yflip,
# :z, :zguide, :zlims, :zticks, :zscale, :zflip,
:z,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
@@ -27,8 +27,8 @@ supportedArgs(::GadflyBackend) = [
supportedAxes(::GadflyBackend) = [:auto, :left]
supportedTypes(::GadflyBackend) = [
:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :hist2d, :hexbin, :hist,
:bar, :box, :violin, :quiver,
:scatter, :histogram2d, :hexbin, :histogram,
:bar, #:box, :violin, :quiver,
:hline, :vline, :contour, :shape
]
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
@@ -59,8 +59,8 @@ function createGadflyPlotObject(d::KW)
gplt.data_source = Gadfly.DataFrames.DataFrame()
# gplt.layers = gplt.layers[1:0]
gplt.layers = [Gadfly.layer(Gadfly.Geom.point(tag=:remove), x=zeros(1), y=zeros(1));] # x=MissingVec(), y=MissingVec());]
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
Gadfly.Guide.ylabel(d[:ylabel]),
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xguide]),
Gadfly.Guide.ylabel(d[:yguide]),
Gadfly.Guide.title(d[:title])]
gplt
end
@@ -69,31 +69,31 @@ end
function getLineGeom(d::KW)
lt = d[:linetype]
st = d[:seriestype]
xbins, ybins = maketuple(d[:bins])
if lt == :hexb
if st == :hexb
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
elseif lt == :hist2d
elseif st == :histogram2d
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
elseif lt == :hist
elseif st == :histogram
Gadfly.Geom.histogram(bincount = xbins,
orientation = isvertical(d) ? :vertical : :horizontal,
position = d[:bar_position] == :stack ? :stack : :dodge)
elseif lt == :path
elseif st == :path
Gadfly.Geom.path
elseif lt in (:bar, :sticks)
elseif st in (:bar, :sticks)
Gadfly.Geom.bar
elseif lt == :steppost
elseif st == :steppost
Gadfly.Geom.step
elseif lt == :steppre
elseif st == :steppre
Gadfly.Geom.step(direction = :vh)
elseif lt == :hline
elseif st == :hline
Gadfly.Geom.hline
elseif lt == :vline
elseif st == :vline
Gadfly.Geom.vline
elseif lt == :contour
elseif st == :contour
Gadfly.Geom.contour(levels = d[:levels])
# elseif lt == :shape
# elseif st == :shape
# Gadfly.Geom.polygon(fill = true, preserve_order = true)
else
nothing
@@ -116,13 +116,13 @@ function get_extra_theme_args(d::KW, k::Symbol)
end
function getGadflyLineTheme(d::KW)
lt = d[:linetype]
st = d[:seriestype]
lc = convertColor(getColor(d[:linecolor]), d[:linealpha])
fc = convertColor(getColor(d[:fillcolor]), d[:fillalpha])
Gadfly.Theme(;
default_color = (lt in (:hist,:hist2d,:hexbin,:bar,:sticks) ? fc : lc),
line_width = (lt == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
default_color = (st in (:histogram,:histogram2d,:hexbin,:bar,:sticks) ? fc : lc),
line_width = (st == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
# line_style = Gadfly.get_stroke_vector(d[:linestyle]),
lowlight_color = x->RGB(fc), # fill/ribbon
lowlight_opacity = alpha(fc), # fill/ribbon
@@ -136,10 +136,10 @@ function addGadflyLine!(plt::Plot, numlayers::Int, d::KW, geoms...)
gplt = getGadflyContext(plt)
gfargs = vcat(geoms..., getGadflyLineTheme(d))
kwargs = KW()
lt = d[:linetype]
st = d[:seriestype]
# add a fill?
if d[:fillrange] != nothing && lt != :contour
if d[:fillrange] != nothing && st != :contour
fillmin, fillmax = map(makevec, maketuple(d[:fillrange]))
nmin, nmax = length(fillmin), length(fillmax)
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
@@ -147,20 +147,20 @@ function addGadflyLine!(plt::Plot, numlayers::Int, d::KW, geoms...)
push!(gfargs, Gadfly.Geom.ribbon)
end
if lt in (:hline, :vline)
kwargs[lt == :hline ? :yintercept : :xintercept] = d[:y]
if st in (:hline, :vline)
kwargs[st == :hline ? :yintercept : :xintercept] = d[:y]
else
if lt == :sticks
if st == :sticks
w = 0.01 * mean(diff(d[:x]))
kwargs[:xmin] = d[:x] - w
kwargs[:xmax] = d[:x] + w
elseif lt == :contour
elseif st == :contour
kwargs[:z] = d[:z].surf
addGadflyContColorScale(plt, d[:linecolor])
end
kwargs[:x] = d[lt == :hist ? :y : :x]
kwargs[:x] = d[st == :histogram ? :y : :x]
kwargs[:y] = d[:y]
end
@@ -183,14 +183,14 @@ getMarkerGeom(other) = gadflyshape(get_shape(other))
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
function getMarkerGeom(d::KW)
if d[:linetype] == :shape
if d[:seriestype] == :shape
Gadfly.Geom.polygon(fill = true, preserve_order = true)
else
getMarkerGeom(d[:markershape])
end
end
function getGadflyMarkerTheme(d::KW, plotargs::KW)
function getGadflyMarkerTheme(d::KW, attr::KW)
c = getColor(d[:markercolor])
α = d[:markeralpha]
if α != nothing
@@ -216,15 +216,15 @@ function getGadflyMarkerTheme(d::KW, plotargs::KW)
end
function addGadflyContColorScale(plt::Plot{GadflyBackend}, c)
plt.plotargs[:colorbar] == :none && return
plt.attr[:colorbar] == :none && return
if !isa(c, ColorGradient)
c = default_gradient()
end
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(c, p))))
end
function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, plotargs::KW, geoms...)
gfargs = vcat(geoms..., getGadflyMarkerTheme(d, plotargs), getMarkerGeom(d))
function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, attr::KW, geoms...)
gfargs = vcat(geoms..., getGadflyMarkerTheme(d, attr), getMarkerGeom(d))
kwargs = KW()
# handle continuous color scales for the markers
@@ -241,7 +241,7 @@ end
# ---------------------------------------------------------------------------
function addToGadflyLegend(plt::Plot, d::KW)
if plt.plotargs[:legend] != :none && d[:label] != ""
if plt.attr[:legend] != :none && d[:label] != ""
gplt = getGadflyContext(plt)
# add the legend if needed
@@ -297,21 +297,21 @@ function addGadflySeries!(plt::Plot, d::KW)
end
# special handling for ohlc and scatter
lt = d[:linetype]
if lt == :ohlc
error("Haven't re-implemented after refactoring")
elseif lt in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
st = d[:seriestype]
# if st == :ohlc
# error("Haven't re-implemented after refactoring")
if st in (:histogram2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
elseif lt == :scatter && d[:markershape] == :none
elseif st == :scatter && d[:markershape] == :none
d[:markershape] = :ellipse
end
# markers
if d[:markershape] != :none || lt == :shape
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.plotargs, smooth...))
if d[:markershape] != :none || st == :shape
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.attr, smooth...))
end
lt in (:hist2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
st in (:histogram2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
# now save the layers that apply to this series
d[:gadflylayers] = layers
@@ -469,8 +469,8 @@ end
function updateGadflyGuides(plt::Plot, d::KW)
gplt = getGadflyContext(plt)
haskey(d, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(d[:title]))
haskey(d, :xlabel) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xlabel]))
haskey(d, :ylabel) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:ylabel]))
haskey(d, :xguide) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xguide]))
haskey(d, :yguide) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:yguide]))
xlims, xfunc = addGadflyLimitsScale(gplt, d, true)
ylims, yfunc = addGadflyLimitsScale(gplt, d, false)
@@ -570,28 +570,32 @@ end
# ---------------------------------------------------------------------------
# create a blank Gadfly.Plot object
function _create_plot(pkg::GadflyBackend, d::KW)
gplt = createGadflyPlotObject(d)
Plot(gplt, pkg, 0, d, KW[])
# function _create_plot(pkg::GadflyBackend, d::KW)
# gplt = createGadflyPlotObject(d)
# Plot(gplt, pkg, 0, d, KW[])
# end
function _create_backend_figure(plt::Plot{GadflyBackend})
createGadflyPlotObject(plt.attr)
end
# plot one data series
function _add_series(::GadflyBackend, plt::Plot, d::KW)
# function _series_added(::GadflyBackend, plt::Plot, d::KW)
function _series_added(plt::Plot{GadflyBackend}, series::Series)
# first clear out the temporary layer
gplt = getGadflyContext(plt)
if gplt.layers[1].geom.tag == :remove
gplt.layers = gplt.layers[2:end]
end
addGadflySeries!(plt, d)
push!(plt.seriesargs, d)
plt
addGadflySeries!(plt, series.d)
# push!(plt.seriesargs, d)
# plt
end
function _update_plot(plt::Plot{GadflyBackend}, d::KW)
function _update_plot_object(plt::Plot{GadflyBackend}, d::KW)
updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d)
end
@@ -622,12 +626,12 @@ end
# ----------------------------------------------------------------
# create the underlying object (each backend will do this differently)
function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
isbefore && return false # wait until after plotting to create the subplots
subplt.o = nothing
true
end
# # create the underlying object (each backend will do this differently)
# function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
# isbefore && return false # wait until after plotting to create the subplots
# subplt.o = nothing
# true
# end
function _remove_axis(plt::Plot{GadflyBackend}, isx::Bool)
@@ -647,31 +651,31 @@ end
getGadflyContext(plt::Plot{GadflyBackend}) = plt.o
getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
# getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
# create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
function buildGadflySubplotContext(subplt::Subplot)
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
# add the Plot object to the row
push!(row, getGadflyContext(subplt.plts[i]))
# add the row
if c == ncols(subplt.layout, r)
push!(rows, Gadfly.hstack(row...))
row = Any[]
end
end
# stack the rows
Gadfly.vstack(rows...)
end
# # create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
# function buildGadflySubplotContext(subplt::Subplot)
# rows = Any[]
# row = Any[]
# for (i,(r,c)) in enumerate(subplt.layout)
#
# # add the Plot object to the row
# push!(row, getGadflyContext(subplt.plts[i]))
#
# # add the row
# if c == ncols(subplt.layout, r)
# push!(rows, Gadfly.hstack(row...))
# row = Any[]
# end
# end
#
# # stack the rows
# Gadfly.vstack(rows...)
# end
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.plotargs[:size]...)
setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getplotargs(subplt, 1)[:size]...)
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.attr[:size]...)
# setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getattr(subplt, 1)[:size]...)
# -------------------------------------------------------------------------
@@ -699,44 +703,44 @@ end
function Base.display(::PlotsDisplay, plt::Plot{GadflyBackend})
setGadflyDisplaySize(plt.plotargs[:size]...)
setGadflyDisplaySize(plt.attr[:size]...)
display(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
setGadflyDisplaySize(getplotargs(subplt,1)[:size]...)
ctx = buildGadflySubplotContext(subplt)
# taken from Gadfly since I couldn't figure out how to do it directly
filename = string(Gadfly.tempname(), ".html")
output = open(filename, "w")
plot_output = IOBuffer()
Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
Compose.default_graphic_height, false), ctx)
plotsvg = takebuf_string(plot_output)
write(output,
"""
<!DOCTYPE html>
<html>
<head>
<title>Gadfly Plot</title>
<meta charset="utf-8">
</head>
<body>
<script charset="utf-8">
$(readall(Compose.snapsvgjs))
</script>
<script charset="utf-8">
$(readall(Gadfly.gadflyjs))
</script>
$(plotsvg)
</body>
</html>
""")
close(output)
Gadfly.open_file(filename)
end
# function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
# setGadflyDisplaySize(getattr(subplt,1)[:size]...)
# ctx = buildGadflySubplotContext(subplt)
#
# # taken from Gadfly since I couldn't figure out how to do it directly
#
# filename = string(Gadfly.tempname(), ".html")
# output = open(filename, "w")
#
# plot_output = IOBuffer()
# Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
# Compose.default_graphic_height, false), ctx)
# plotsvg = takebuf_string(plot_output)
#
# write(output,
# """
# <!DOCTYPE html>
# <html>
# <head>
# <title>Gadfly Plot</title>
# <meta charset="utf-8">
# </head>
# <body>
# <script charset="utf-8">
# $(readall(Compose.snapsvgjs))
# </script>
# <script charset="utf-8">
# $(readall(Gadfly.gadflyjs))
# </script>
# $(plotsvg)
# </body>
# </html>
# """)
# close(output)
# Gadfly.open_file(filename)
# end
+28 -63
View File
@@ -3,7 +3,7 @@
# [WEBSITE]
supportedArgs(::GLVisualizeBackend) = [
# :annotation,
# :annotations,
# :axis,
# :background_color,
# :color_palette,
@@ -17,7 +17,7 @@ supportedArgs(::GLVisualizeBackend) = [
# :legend,
# :linecolor,
# :linestyle,
:linetype
:seriestype
# :seriescolor, :seriesalpha,
# :linewidth,
# :linealpha,
@@ -37,13 +37,13 @@ supportedArgs(::GLVisualizeBackend) = [
# :show,
# :size,
# :title,
# :windowtitle,
# :window_title,
# :x,
# :xlabel,
# :xguide,
# :xlims,
# :xticks,
# :y,
# :ylabel,
# :yguide,
# :ylims,
# :yrightlabel,
# :yticks,
@@ -60,7 +60,7 @@ supportedArgs(::GLVisualizeBackend) = [
# :levels,
]
supportedAxes(::GLVisualizeBackend) = [:auto, :left]
supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :histogram, :bar, :hline, :vline, :contour]
supportedStyles(::GLVisualizeBackend) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GLVisualizeBackend) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::GLVisualizeBackend) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
@@ -82,90 +82,55 @@ immutable GLScreenWrapper
window
end
function _create_plot(pkg::GLVisualizeBackend, d::KW)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# function _create_plot(pkg::GLVisualizeBackend, d::KW)
function _create_backend_figure(plt::Plot{GLVisualizeBackend})
# TODO: create the window/canvas/context that is the plot within the backend
# TODO: initialize the plot... title, xlabel, bgcolor, etc
# TODO: this should be moved to the display method?
w=GLVisualize.glscreen()
@async GLVisualize.renderloop(w)
Plot(GLScreenWrapper(w), pkg, 0, d, KW[])
GLScreenWrapper(w)
# Plot(GLScreenWrapper(w), pkg, 0, d, KW[])
end
function _add_series(::GLVisualizeBackend, plt::Plot, d::KW)
# ----------------------------------------------------------------
function _series_added(plt::Plot{GLVisualizeBackend}, series::Series)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
# TODO: this should be moved to the display method?
x,y,z=map(Float32,d[:x]), map(Float32,d[:y]), map(Float32,d[:z].surf)
GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface),plt.o.window)
plt
x, y, z = map(Float32, series.d[:x]), map(Float32, series.d[:y]), map(Float32, series.d[:z].surf)
GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface), plt.o.window)
# plt
end
function _add_annotations{X,Y,V}(plt::Plot{GLVisualizeBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
# When series data is added/changed, this callback can do dynamic updates to the backend object.
# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
function _series_updated(plt::Plot{GLVisualizeBackend}, series::Series)
# TODO
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{GLVisualizeBackend})
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
function _update_plot_object(plt::Plot{GLVisualizeBackend})
# TODO
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{GLVisualizeBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{GLVisualizeBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
function getxy(plt::Plot{GLVisualizeBackend}, i::Int)
# TODO:
# series = plt.o.lines[i]
# series.x, series.y
nothing, nothing
end
function setxy!{X,Y}(plt::Plot{GLVisualizeBackend}, xy::Tuple{X,Y}, i::Integer)
# TODO:
# series = plt.o.lines[i]
# series.x, series.y = xy
plt
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{GLVisualizeBackend})
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
function _expand_limits(lims, plt::Plot{GLVisualizeBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{GLVisualizeBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
function _writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
# TODO: write a png to io
end
function Base.display(::PlotsDisplay, plt::Plot{GLVisualizeBackend})
function _display(plt::Plot{GLVisualizeBackend})
# TODO: display/show the plot
# NOTE: I think maybe this should be empty? We can start with the assumption that creating
# and adding to a plot will automatically open a window and draw to it, then the display
# wouldn't actually need to do anything
end
function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizeBackend})
# TODO: display/show the subplot
end
+1291 -821
View File
File diff suppressed because it is too large Load Diff
+84 -77
View File
@@ -21,7 +21,7 @@ end
function createImmerseFigure(d::KW)
w,h = d[:size]
figidx = Immerse.figure(; name = d[:windowtitle], width = w, height = h)
figidx = Immerse.figure(; name = d[:window_title], width = w, height = h)
Immerse.Figure(figidx)
end
@@ -29,24 +29,31 @@ end
# create a blank Gadfly.Plot object
function _create_plot(pkg::ImmerseBackend, d::KW)
# create the underlying Gadfly.Plot object
gplt = createGadflyPlotObject(d)
# save both the Immerse.Figure and the Gadfly.Plot
Plot((nothing,gplt), pkg, 0, d, KW[])
# function _create_plot(pkg::ImmerseBackend, d::KW)
# # create the underlying Gadfly.Plot object
# gplt = createGadflyPlotObject(d)
#
# # save both the Immerse.Figure and the Gadfly.Plot
# Plot((nothing,gplt), pkg, 0, d, KW[])
# end
function _create_backend_figure(plt::Plot{ImmerseBackend})
(nothing, createGadflyPlotObject(plt.attr))
end
# plot one data series
function _add_series(::ImmerseBackend, plt::Plot, d::KW)
addGadflySeries!(plt, d)
push!(plt.seriesargs, d)
plt
# # plot one data series
# function _series_added(::ImmerseBackend, plt::Plot, d::KW)
# addGadflySeries!(plt, d)
# push!(plt.seriesargs, d)
# plt
# end
function _series_added(plt::Plot{ImmerseBackend}, series::Series)
addGadflySeries!(plt, series.d)
end
function _update_plot(plt::Plot{ImmerseBackend}, d::KW)
function _update_plot_object(plt::Plot{ImmerseBackend}, d::KW)
updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d)
end
@@ -81,51 +88,51 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
return false
# isbefore && return false
end
function showSubplotObject(subplt::Subplot{ImmerseBackend})
# create the Gtk window with vertical box vsep
d = getplotargs(subplt,1)
w,h = d[:size]
vsep = Gtk.GtkBoxLeaf(:v)
win = Gtk.GtkWindowLeaf(vsep, d[:windowtitle], w, h)
figindices = []
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
for (i,(r,c)) in enumerate(subplt.layout)
plt = subplt.plts[i]
# get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
box, toolbar, canvas = Immerse.createPlotGuiComponents()
# add the plot's box to the row
push!(row, box)
# create the figure and store the index returned for destruction later
figidx = Immerse.figure(canvas)
push!(figindices, figidx)
fig = Immerse.figure(figidx)
plt.o = (fig, plt.o[2])
# add the row
if c == ncols(subplt.layout, r)
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
end
end
# destructor... clean up plots
Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
subplt.o = win
true
end
# function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
# return false
# # isbefore && return false
# end
#
# function showSubplotObject(subplt::Subplot{ImmerseBackend})
# # create the Gtk window with vertical box vsep
# d = getattr(subplt,1)
# w,h = d[:size]
# vsep = Gtk.GtkBoxLeaf(:v)
# win = Gtk.GtkWindowLeaf(vsep, d[:window_title], w, h)
#
# figindices = []
# row = Gtk.GtkBoxLeaf(:h)
# push!(vsep, row)
# for (i,(r,c)) in enumerate(subplt.layout)
# plt = subplt.plts[i]
#
# # get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
# box, toolbar, canvas = Immerse.createPlotGuiComponents()
#
# # add the plot's box to the row
# push!(row, box)
#
# # create the figure and store the index returned for destruction later
# figidx = Immerse.figure(canvas)
# push!(figindices, figidx)
#
# fig = Immerse.figure(figidx)
# plt.o = (fig, plt.o[2])
#
# # add the row
# if c == ncols(subplt.layout, r)
# row = Gtk.GtkBoxLeaf(:h)
# push!(vsep, row)
# end
#
# end
#
# # destructor... clean up plots
# Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
#
# subplt.o = win
# true
# end
function _remove_axis(plt::Plot{ImmerseBackend}, isx::Bool)
@@ -144,14 +151,14 @@ end
# ----------------------------------------------------------------
getGadflyContext(plt::Plot{ImmerseBackend}) = plt.o[2]
getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
# getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
fig, gplt = plt.o
if fig == nothing
fig = createImmerseFigure(plt.plotargs)
fig = createImmerseFigure(plt.attr)
Gtk.on_signal_destroy((x...) -> (Immerse.dropfig(Immerse._display, fig.figno); plt.o = (nothing,gplt)), fig.canvas)
plt.o = (fig, gplt)
end
@@ -161,20 +168,20 @@ function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
end
function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
# if we haven't created the window yet, do it
if subplt.o == nothing
showSubplotObject(subplt)
end
# display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
for plt in subplt.plts
fig, gplt = plt.o
Immerse.figure(fig.figno; displayfig = false)
display(gplt)
end
# o is the window... show it
showall(subplt.o)
end
# function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
#
# # if we haven't created the window yet, do it
# if subplt.o == nothing
# showSubplotObject(subplt)
# end
#
# # display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
# for plt in subplt.plts
# fig, gplt = plt.o
# Immerse.figure(fig.figno; displayfig = false)
# display(gplt)
# end
#
# # o is the window... show it
# showall(subplt.o)
# end
+233 -291
View File
@@ -1,68 +1,53 @@
# https://github.com/sisl/PGFPlots.jl
# significant contributions by: @pkofod
supportedArgs(::PGFPlotsBackend) = [
# :annotation,
# :axis,
:background_color,
# :color_palette,
# :fillrange,
:fillcolor,
:fillalpha,
# :foreground_color,
# :group,
# :label,
# :layout,
# :legend,
:seriescolor, :seriesalpha,
:linecolor,
:linestyle,
:linetype,
:linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
# :markerstrokewidth,
:markerstrokecolor,
:markerstrokestyle,
# :n,
# :bins,
# :nc,
# :nr,
# :pos,
# :annotations,
:background_color, :foreground_color,
:color_palette,
# :background_color_legend,
:background_color_inside,
# :background_color_outside,
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
# :foreground_color_text, :foreground_color_border,
:group,
:label,
:seriestype,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:fillrange, :fillcolor, :fillalpha,
:bins,
# :bar_width, :bar_edges,
# :n, :nc, :nr,
:layout,
# :smooth,
# :show,
# :size,
:title,
# :windowtitle,
:x,
:xlabel,
:xlims,
# :xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
# :yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z,
:zscale,
# :tickfont,
# :guidefont,
# :legendfont,
:grid,
# :surface
# :levels,
:title,
# :window_title,
:show, :size,
:x, :xguide, :xlims, :xticks, :xscale, :xflip, :xrotation,
:y, :yguide, :ylims, :yticks, :yscale, :yflip, :yrotation,
:z, :zguide, :zlims, :zticks, :zscale, :zflip, :zrotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend,
# :colorbar,
# :marker_z, :levels,
# :xerror, :yerror,
# :ribbon, :quiver, :arrow,
# :orientation,
# :overwrite_figure,
# :polar,
# :normalize, :weights, :contours,
:aspect_ratio,
# :match_dimensions,
]
supportedAxes(::PGFPlotsBackend) = [:auto, :left]
supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :line, :steppre, :stepmid, :steppost, :hist, :bar, :hist2d, :sticks, :ysticks, :xsticks, :contour] # :hexbin, :hline, :vline,]
supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
supportedStyles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::PGFPlotsBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
supportedScales(::PGFPlotsBackend) = [:identity, :log, :ln, :log2, :log10] # :asinh, :sqrt]
supportedScales(::PGFPlotsBackend) = [:identity, :ln, :log2, :log10] # :asinh, :sqrt]
subplotSupported(::PGFPlotsBackend) = false
@@ -73,282 +58,239 @@ function _initialize_backend(::PGFPlotsBackend; kw...)
@eval begin
import PGFPlots
export PGFPlots
# TODO: other initialization that needs to be eval-ed
end
# TODO: other initialization
end
# --------------------------------------------------------------------------------------
const _pgfplots_linestyles = KW(
:solid => "solid",
:dash => "dashed",
:dot => "dotted",
:dashdot => "dashdotted",
:dashdotdot => "dashdotdotted"
:dashdotdot => "dashdotdotted",
)
const _pgfplots_markers = KW(
:none => "mark = none,",
:cross => "mark = +,",
:xcross => "mark = x,",
:utriangle => "mark = triangle*,",
:dtriangle => "mark = triangle*,",
:ellipse => "mark = o*,",
:rect => "mark = square*,",
:star5 => "mark = star,",
:star6 => "mark = asterisk,",
:diamond => "mark = diamond*,",
:pentagon => "mark = pentagon*,"
:none => "none",
:cross => "+",
:xcross => "x",
:utriangle => "triangle*",
:dtriangle => "triangle*",
:ellipse => "*",
:rect => "square*",
:star5 => "star",
:star6 => "asterisk",
:diamond => "diamond*",
:pentagon => "pentagon*",
)
function _pgfplots_get_color(kwargs, symb)
c = typeof(kwargs[symb]) == Symbol ? convertColor(kwargs[symb]) : kwargs[symb].c
"{rgb,1:red,$(float(c.r));green,$(float(c.g));blue,$(float(c.b))}"
const _pgfplots_legend_pos = KW(
:bottomleft => "south west",
:bottomright => "south east",
:topright => "north east",
:topleft => "north west",
)
const _pgf_series_extrastyle = KW(
:steppre => "const plot mark right",
:stepmid => "const plot mark mid",
:steppost => "const plot",
:sticks => "ycomb",
:ysticks => "ycomb",
:xsticks => "xcomb",
)
# --------------------------------------------------------------------------------------
# takes in color,alpha, and returns color and alpha appropriate for pgf style
function pgf_color(c, a = nothing)
c = getColor(c)
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c))
a = float(a == nothing ? alpha(c) : a)
cstr, a
end
function _pgfplots_get_linestyle!(kwargs, plt)
ls = plt[:linestyle]
if haskey(_pgfplots_linestyles, ls)
kwargs[:style] *= _pgfplots_linestyles[ls]*","
function pgf_fillstyle(d::KW)
cstr,a = pgf_color(d[:fillcolor], d[:fillalpha])
"fill = $cstr, fill opacity=$a"
end
function pgf_linestyle(d::KW)
cstr,a = pgf_color(d[:linecolor], d[:linealpha])
"""
color = $cstr,
draw opacity=$a,
line width=$(d[:linewidth]),
$(get(_pgfplots_linestyles, d[:linestyle], "solid"))"""
end
function pgf_marker(d::KW)
shape = d[:markershape]
cstr, a = pgf_color(d[:markercolor], d[:markeralpha])
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor], d[:markerstrokealpha])
"""
mark = $(get(_pgfplots_markers, shape, "*")),
mark size = $(0.5 * d[:markersize]),
mark options = {
color = $cstr_stroke, draw opacity = $a_stroke,
fill = $cstr, fill opacity = $a,
line width = $(d[:markerstrokewidth]),
rotate = $(shape == :dtriangle ? 180 : 0),
$(get(_pgfplots_linestyles, d[:markerstrokestyle], "solid"))
}"""
end
# --------------------------------------------------------------------------------------
function pgf_series(sp::Subplot, series::Series)
d = series.d
st = d[:seriestype]
style = []
kw = KW()
push!(style, pgf_linestyle(d))
push!(style, pgf_marker(d))
if d[:fillrange] != nothing
push!(style, pgf_fillstyle(d))
end
kwargs[:style] *= "line width = $(plt[:linewidth]) pt"*","
end
function _pgfplots_get_marker!(kwargs, plt)
# Control marker shape
mark = plt[:markershape]
kwargs[:style] *= _pgfplots_markers[mark]
# Control marker size
kwargs[:style] *= "mark size = $(plt[:markersize]/2),"
# Control marker colors and alphas
α = plt[:markeralpha] == nothing ? 1.0 : plt[:markeralpha]
kwargs[:style] *= "mark options = {color=$(_pgfplots_get_color(plt, :markerstrokecolor)),"
kwargs[:style] *= mark == :dtriangle ? "rotate=180," : ""
kwargs[:style] *= "fill=$(_pgfplots_get_color(plt, :markercolor)),"
kwargs[:style] *= "fill opacity = $α,"
markstrokestyle = plt[:markerstrokestyle]
if haskey(_pgfplots_linestyles, markstrokestyle)
kwargs[:style] *= _pgfplots_linestyles[markstrokestyle]
# add to legend?
if sp[:legend] != :none && should_add_to_legend(series)
kw[:legendentry] = d[:label]
end
kwargs[:style] *= "},"
end
function _pgfplots_get_series_color!(kwargs, plt)
α = plt[:seriesalpha] == nothing ? 1.0 : plt[:seriesalpha]
kwargs[:style] *= "color=$(_pgfplots_get_color(plt, :seriescolor)),"
kwargs[:style] *= "draw opacity = $α,"
end
function _pgfplots_get_line_color!(kwargs, plt)
α = plt[:linealpha] == nothing ? 1.0 : plt[:linealpha]
kwargs[:style] *= "color=$(_pgfplots_get_color(plt, :linecolor)),"
kwargs[:style] *= "draw opacity = $α,"
end
function _pgfplots_get_fill_color!(kwargs, plt)
α = plt[:fillalpha] == nothing ? 1.0 : plt[:fillalpha]
kwargs[:style] *= "fill=$(_pgfplots_get_color(plt, :fillcolor)),"
kwargs[:style] *= "fill opacity = $α,"
end
function _pgfplots_get_plot_kwargs(plt)
kwargs = KW()
kwargs[:style] = ""
_pgfplots_get_linestyle!(kwargs, plt)
_pgfplots_get_marker!(kwargs, plt)
_pgfplots_get_series_color!(kwargs, plt)
kwargs
end
function _pgfplots_axis(plt_series)
line_type = plt_series[:linetype]
plt_kwargs = _pgfplots_get_plot_kwargs(plt_series)
if line_type == :path
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :path3d
PGFPlots.Linear3(plt_series[:x], plt_series[:y], plt_series[:z]; plt_kwargs...)
elseif line_type == :scatter
PGFPlots.Scatter(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :steppre
plt_kwargs[:style] *= "const plot mark right,"
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :stepmid
plt_kwargs[:style] *= "const plot mark mid,"
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :steppost
plt_kwargs[:style] *= "const plot,"
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :hist
#TODO patch this in PGFPlots.jl instead; the problem is that PGFPlots will
# save _all_ data points in the figure which can be quite heavy
plt_hist = hist(plt_series[:y])
plt_kwargs[:style] *= "ybar interval,"
_pgfplots_get_line_color!(plt_kwargs, plt_series)
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
PGFPlots.Linear(plt_hist[1][1:end-1]+plt_hist[1].step/2, plt_hist[2]; plt_kwargs...)
elseif line_type == :hist2d
PGFPlots.Histogram2(plt_series[:x], plt_series[:y])
elseif line_type == :bar
plt_kwargs[:style] *= "ybar,"
_pgfplots_get_line_color!(plt_kwargs, plt_series)
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :sticks || line_type == :ysticks
plt_kwargs[:style] *= "ycomb"
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :xsticks
plt_kwargs[:style] *= "xcomb"
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
elseif line_type == :contour
PGFPlots.Contour(plt_series[:z].surf, plt_series[:x], plt_series[:y])
# function args
args = if st == :contour
d[:z].surf, d[:x], d[:y]
elseif is3d(st)
d[:x], d[:y], d[:z]
else
d[:x], d[:y]
end
end
# ---------------------------------------------------------------------------
# include additional style, then add to the kw
if haskey(_pgf_series_extrastyle, st)
push!(style, _pgf_series_extrastyle[st])
end
kw[:style] = join(style, ',')
function _create_plot(pkg::PGFPlotsBackend, d::KW)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
Plot(nothing, pkg, 0, d, KW[])
# build/return the series object
func = if st == :path3d
PGFPlots.Linear3
elseif st == :scatter
PGFPlots.Scatter
elseif st == :histogram2d
PGFPlots.Histogram2
elseif st == :contour
PGFPlots.Contour
else
PGFPlots.Linear
end
func(args...; kw...)
end
function _add_series(::PGFPlotsBackend, plt::Plot, d::KW)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{PGFPlotsBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
# set or add to the annotation_list
if haskey(plt.plotargs, :annotation_list)
append!(plt.plotargs[:annotation_list], anns)
else
plt.plotargs[:annotation_list] = anns
end
function pgf_axis(sp::Subplot, letter)
axis = sp[Symbol(letter,:axis)]
style = []
kw = KW()
# axis guide
kw[Symbol(letter,:label)] = axis[:guide]
# flip/reverse?
axis[:flip] && push!(style, "$letter dir=reverse")
# scale
scale = axis[:scale]
if scale in (:log2, :ln, :log10)
kw[Symbol(letter,:mode)] = "log"
scale == :ln || push!(style, "log basis $letter=$(scale == :log2 ? 2 : 10)")
end
# limits
lims = axis_limits(axis)
kw[Symbol(letter,:min)] = lims[1]
kw[Symbol(letter,:max)] = lims[2]
# return the style list and KW args
style, kw
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{PGFPlotsBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PGFPlotsBackend}, d::KW)
end
function _make_pgf_plot!(plt::Plot)
plt.o = PGFPlots.Axis[]
for sp in plt.subplots
# first build the PGFPlots.Axis object
style = ["unbounded coords=jump"]
kw = KW()
function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# function getxy(plt::Plot{PGFPlotsBackend}, i::Int)
# d = plt.seriesargs[i]
# d[:x], d[:y]
# end
#
# function setxy!{X,Y}(plt::Plot{PGFPlotsBackend}, xy::Tuple{X,Y}, i::Integer)
# d = plt.seriesargs[i]
# d[:x], d[:y] = xy
# plt
# end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PGFPlotsBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
true
end
function _expand_limits(lims, plt::Plot{PGFPlotsBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PGFPlotsBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function _pgfplots_get_axis_kwargs(d)
axisargs = KW()
for arg in (:xlabel, :ylabel, :zlabel, :title)
axisargs[arg] = d[arg]
end
axisargs[:style] = ""
axisargs[:style] *= d[:xflip] == true ? "x dir=reverse," : ""
axisargs[:style] *= d[:yflip] == true ? "y dir=reverse," : ""
if d[:xscale] in (:log, :log2, :ln, :log10)
axisargs[:xmode] = "log"
if d[:xscale] == :log2
axisargs[:style] *= "log basis x=2,"
elseif d[:xscale] in (:log, :log10)
axisargs[:style] *= "log basis x=10,"
# add to style/kw for each axis
for letter in (:x, :y, :z)
if letter != :z || is3d(sp)
axisstyle, axiskw = pgf_axis(sp, letter)
merge!(kw, axiskw)
end
end
end
if d[:yscale] in (:log, :log2, :ln, :log10)
axisargs[:ymode] = "log"
if d[:yscale] == :log2
axisargs[:style] *= "log basis y=2,"
elseif d[:yscale] in (:log, :log10)
axisargs[:style] *= "log basis x=10,"
# bounding box values are in mm
# note: bb origin is top-left, pgf is bottom-left
bb = bbox(sp)
push!(style, """
xshift = $(left(bb).value)mm,
yshift = $((height(bb) - (bottom(bb))).value)mm,
width = $(width(bb).value)mm,
height = $(height(bb).value)mm,
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
""")
if sp[:title] != ""
push!(style, "title = $(sp[:title])")
end
end
if d[:zscale] in (:log, :log2, :ln, :log10)
axisargs[:zmode] = "log"
if d[:zscale] == :log2
axisargs[:style] *= "log basis z=2,"
elseif d[:zscale] in (:log, :log10)
axisargs[:style] *= "log basis x=10,"
sp[:grid] && push!(style, "grid = major")
if sp[:aspect_ratio] in (1, :equal)
kw[:axisEqual] = "true"
end
end
# Control background color
axisargs[:style] *= "axis background/.style={fill=$(_pgfplots_get_color(d, :background_color))},"
# Control x/y-limits
if d[:xlims] !== :auto
axisargs[:xmin] = d[:xlims][1]
axisargs[:xmax] = d[:xlims][2]
end
if d[:ylims] !== :auto
axisargs[:ymin] = d[:ylims][1]
axisargs[:ymax] = d[:ylims][2]
end
if d[:grid] == true
axisargs[:style] *= "grid = major"
elseif d[:grid] == false
legpos = sp[:legend]
if haskey(_pgfplots_legend_pos, legpos)
kw[:legendPos] = _pgfplots_legend_pos[legpos]
end
end
o = PGFPlots.Axis(; style = style, kw...)
axisargs
# add the series object to the PGFPlots.Axis
for series in series_list(sp)
push!(o, pgf_series(sp, series))
end
# add the PGFPlots.Axis to the list
push!(plt.o, o)
end
end
# ----------------------------------------------------------------
################# This is the important method to implement!!! #################
function _make_pgf_plot(plt::Plot{PGFPlotsBackend})
os = [_pgfplots_axis(plt_series) for plt_series in plt.seriesargs]
axisargs =_pgfplots_get_axis_kwargs(plt.plotargs)
plt.o = PGFPlots.Axis([os...]; axisargs...)
end
function Base.writemime(io::IO, mime::MIME"image/svg+xml", plt::AbstractPlot{PGFPlotsBackend})
plt.o = _make_pgf_plot(plt)
function _writemime(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
writemime(io, mime, plt.o)
end
# function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PGFPlotsBackend})
# end
function Base.display(::PlotsDisplay, plt::AbstractPlot{PGFPlotsBackend})
plt.o = _make_pgf_plot(plt)
display(plt.o)
function _writemime(io::IO, mime::MIME"image/png", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
writemime(io, mime, plt.o)
end
# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsBackend})
# # TODO: display/show the subplot
# end
function _display(plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
display(plt.o)
end
+380 -439
View File
@@ -2,71 +2,41 @@
# https://plot.ly/javascript/getting-started
supportedArgs(::PlotlyBackend) = [
:annotation,
# :axis,
:background_color,
:color_palette,
:fillrange,
:fillcolor,
:fillalpha,
:foreground_color,
:annotations,
:background_color, :foreground_color, :color_palette,
# :background_color_legend, :background_color_inside, :background_color_outside,
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
# :foreground_color_text, :foreground_color_border,
:group,
:label,
:layout,
:legend,
:seriestype,
:seriescolor, :seriesalpha,
:linecolor,
:linestyle,
:linetype,
:linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokestyle,
:n,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:fillrange, :fillcolor, :fillalpha,
:bins,
:nc,
:nr,
# :pos,
:n, :nc, :nr, :layout,
# :smooth,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:title, :window_title, :show, :size,
:x, :xguide, :xlims, :xticks, :xscale, :xflip, :xrotation,
:y, :yguide, :ylims, :yticks, :yscale, :yflip, :yrotation,
:z, :zguide, :zlims, :zticks, :zscale, :zflip, :zrotation,
:z,
:marker_z,
:tickfont,
:guidefont,
:legendfont,
:grid,
:levels,
:xerror,
:yerror,
:ribbon,
:quiver,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
:marker_z, :levels,
:xerror, :yerror,
:ribbon, :quiver,
:orientation,
# :overwrite_figure,
:polar,
# :normalize, :weights, :contours, :aspect_ratio
]
supportedAxes(::PlotlyBackend) = [:auto, :left]
supportedTypes(::PlotlyBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:histogram2d, :histogram, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
@@ -84,7 +54,7 @@ function _initialize_backend(::PlotlyBackend; kw...)
JSON._print(io::IO, state::JSON.State, dt::Union{Date,DateTime}) = print(io, '"', dt, '"')
_js_path = Pkg.dir("Plots", "deps", "plotly-latest.min.js")
_js_code = open(readall, _js_path, "r")
_js_code = open(@compat(readstring), _js_path, "r")
# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2
_js_script = """
@@ -112,230 +82,232 @@ function _initialize_backend(::PlotlyBackend; kw...)
# TODO: other initialization
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PlotlyBackend, d::KW)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
Plot(nothing, pkg, 0, d, KW[])
end
function _add_series(::PlotlyBackend, plt::Plot, d::KW)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{PlotlyBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
# set or add to the annotation_list
if haskey(plt.plotargs, :annotation_list)
append!(plt.plotargs[:annotation_list], anns)
else
plt.plotargs[:annotation_list] = anns
end
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{PlotlyBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PlotlyBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{PlotlyBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# function getxy(plt::Plot{PlotlyBackend}, i::Int)
# d = plt.seriesargs[i]
# d[:x], d[:y]
# end
#
# function setxy!{X,Y}(plt::Plot{PlotlyBackend}, xy::Tuple{X,Y}, i::Integer)
# d = plt.seriesargs[i]
# d[:x], d[:y] = xy
# plt
# end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PlotlyBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
true
end
function _expand_limits(lims, plt::Plot{PlotlyBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PlotlyBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
# TODO:
# _plotDefaults[:yrightlabel] = ""
# _plotDefaults[:xlims] = :auto
# _plotDefaults[:ylims] = :auto
# _plotDefaults[:xticks] = :auto
# _plotDefaults[:yticks] = :auto
# _plotDefaults[:xscale] = :identity
# _plotDefaults[:yscale] = :identity
# _plotDefaults[:xflip] = false
# _plotDefaults[:yflip] = false
function plotlyfont(font::Font, color = font.color)
KW(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
:color => webcolor(color),
KW(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
:color => webcolor(color),
)
end
function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
KW(
:text => val,
:xref => "x",
:x => x,
:yref => "y",
:y => y,
:showarrow => false,
function get_annotation_dict(x, y, val)
KW(
:text => val,
:xref => "x",
:x => x,
:yref => "y",
:y => y,
:showarrow => false,
)
end
function get_annotation_dict(x, y, ptxt::PlotText)
merge(get_annotation_dict(x, y, ptxt.str), KW(
:font => plotlyfont(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
:rotation => ptxt.font.rotation,
merge(get_annotation_dict(x, y, ptxt.str), KW(
:font => plotlyfont(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
:rotation => ptxt.font.rotation,
))
end
# function get_annotation_dict_for_arrow(d::KW, xyprev::Tuple, xy::Tuple, a::Arrow)
# xdiff = xyprev[1] - xy[1]
# ydiff = xyprev[2] - xy[2]
# dist = sqrt(xdiff^2 + ydiff^2)
# KW(
# :showarrow => true,
# :x => xy[1],
# :y => xy[2],
# # :ax => xyprev[1] - xy[1],
# # :ay => xy[2] - xyprev[2],
# # :ax => 0,
# # :ay => -40,
# :ax => 10xdiff / dist,
# :ay => -10ydiff / dist,
# :arrowcolor => webcolor(d[:linecolor], d[:linealpha]),
# :xref => "x",
# :yref => "y",
# :arrowsize => 10a.headwidth,
# # :arrowwidth => a.headlength,
# :arrowwidth => 0.1,
# )
# end
function plotlyscale(scale::Symbol)
if scale == :log10
"log"
else
"-"
end
if scale == :log10
"log"
else
"-"
end
end
use_axis_field(ticks) = !(ticks in (nothing, :none))
tickssym(isx::Bool) = symbol((isx ? "x" : "y") * "ticks")
limssym(isx::Bool) = symbol((isx ? "x" : "y") * "lims")
flipsym(isx::Bool) = symbol((isx ? "x" : "y") * "flip")
scalesym(isx::Bool) = symbol((isx ? "x" : "y") * "scale")
labelsym(isx::Bool) = symbol((isx ? "x" : "y") * "label")
# this method gets the start/end in percentage of the canvas for this axis direction
function plotly_domain(sp::Subplot, letter)
figw, figh = sp.plt[:size]
pcts = bbox_to_pcts(sp.plotarea, figw*px, figh*px)
i1,i2 = (letter == :x ? (1,3) : (2,4))
[pcts[i1], pcts[i1]+pcts[i2]]
end
function plotlyaxis(d::KW, isx::Bool)
ax = KW(
:title => d[labelsym(isx)],
:showgrid => d[:grid],
:zeroline => false,
function plotly_axis(axis::Axis, sp::Subplot)
letter = axis[:letter]
# d = axis.d
ax = KW(
:title => axis[:guide],
:showgrid => sp[:grid],
:zeroline => false,
)
fgcolor = webcolor(d[:foreground_color])
tsym = tickssym(isx)
# fgcolor = webcolor(axis[:foreground_color])
# tsym = tickssym(letter)
if use_axis_field(d[tsym])
ax[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
ax[:type] = plotlyscale(d[scalesym(isx)])
ax[:tickfont] = plotlyfont(d[:tickfont], fgcolor)
ax[:tickcolor] = fgcolor
ax[:linecolor] = fgcolor
# xlims
lims = d[limssym(isx)]
if lims != :auto && limsType(lims) == :limits
ax[:range] = lims
# spidx = sp[:subplot_index]
# d_out[:xaxis] = "x$spidx"
# d_out[:yaxis] = "y$spidx"
if letter in (:x,:y)
ax[:domain] = plotly_domain(sp, letter)
ax[:anchor] = "$(letter==:x ? :y : :x)$(plotly_subplot_index(sp))"
end
# xflip
if d[flipsym(isx)]
ax[:autorange] = "reversed"
rot = axis[:rotation]
if rot != 0
ax[:tickangle] = rot
end
# xticks
ticks = d[tsym]
if ticks != :auto
ttype = ticksType(ticks)
if ttype == :ticks
ax[:tickmode] = "array"
ax[:tickvals] = ticks
elseif ttype == :ticks_and_labels
ax[:tickmode] = "array"
ax[:tickvals], ax[:ticktext] = ticks
end
if use_axis_field(axis[:ticks])
ax[:titlefont] = plotlyfont(axis[:guidefont], webcolor(axis[:foreground_color_guide]))
ax[:type] = plotlyscale(axis[:scale])
ax[:tickfont] = plotlyfont(axis[:tickfont], webcolor(axis[:foreground_color_text]))
ax[:tickcolor] = webcolor(axis[:foreground_color_border])
ax[:linecolor] = webcolor(axis[:foreground_color_border])
# lims
lims = axis[:lims]
if lims != :auto && limsType(lims) == :limits
ax[:range] = lims
end
# flip
if axis[:flip]
ax[:autorange] = "reversed"
end
# ticks
ticks = axis[:ticks]
if ticks != :auto
ttype = ticksType(ticks)
if ttype == :ticks
ax[:tickmode] = "array"
ax[:tickvals] = ticks
elseif ttype == :ticks_and_labels
ax[:tickmode] = "array"
ax[:tickvals], ax[:ticktext] = ticks
end
end
else
ax[:showticklabels] = false
ax[:showgrid] = false
end
ax
else
ax[:showticklabels] = false
ax[:showgrid] = false
end
ax
end
# function get_plot_json(plt::Plot{PlotlyBackend})
# d = plt.plotargs
function plotly_layout(d::KW)
d_out = KW()
# function plotly_layout_json(plt::Plot{PlotlyBackend})
# d = plt
# function plotly_layout(d::KW, seriesargs::AVec{KW})
function plotly_layout(plt::Plot)
d_out = KW()
d_out[:width], d_out[:height] = d[:size]
# # for now, we only support 1 subplot
# if length(plt.subplots) > 1
# warn("Subplots not supported yet")
# end
# sp = plt.subplots[1]
bgcolor = webcolor(d[:background_color])
fgcolor = webcolor(d[:foreground_color])
d_out[:width], d_out[:height] = plt[:size]
d_out[:paper_bgcolor] = webcolor(plt[:background_color_outside])
# set the fields for the plot
d_out[:title] = d[:title]
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:plot_bgcolor] = bgcolor
d_out[:paper_bgcolor] = bgcolor
for sp in plt.subplots
sp_out = KW()
spidx = plotly_subplot_index(sp)
# TODO: x/y axis tick values/labels
d_out[:xaxis] = plotlyaxis(d, true)
d_out[:yaxis] = plotlyaxis(d, false)
# set the fields for the plot
d_out[:title] = sp[:title]
d_out[:titlefont] = plotlyfont(sp[:titlefont], webcolor(sp[:foreground_color_title]))
# legend
d_out[:showlegend] = d[:legend] != :none
if d[:legend] != :none
d_out[:legend] = KW(
:bgcolor => bgcolor,
:bordercolor => fgcolor,
:font => plotlyfont(d[:legendfont]),
)
end
# # TODO: use subplot positioning logic
# d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>30)
# annotations
anns = get(d, :annotation_list, [])
if !isempty(anns)
d_out[:annotations] = [get_annotation_dict(ann...) for ann in anns]
end
d_out[:plot_bgcolor] = webcolor(sp[:background_color_inside])
if get(d, :polar, false)
d_out[:direction] = "counterclockwise"
end
# TODO: x/y axis tick values/labels
d_out
# if any(is3d, seriesargs)
if is3d(sp)
d_out[:scene] = KW(
Symbol("xaxis$spidx") => plotly_axis(sp[:xaxis], sp),
Symbol("yaxis$spidx") => plotly_axis(sp[:yaxis], sp),
Symbol("zaxis$spidx") => plotly_axis(sp[:zaxis], sp),
)
else
d_out[Symbol("xaxis$spidx")] = plotly_axis(sp[:xaxis], sp)
d_out[Symbol("yaxis$spidx")] = plotly_axis(sp[:yaxis], sp)
end
# legend
d_out[:showlegend] = sp[:legend] != :none
if sp[:legend] != :none
d_out[:legend] = KW(
:bgcolor => webcolor(sp[:background_color_legend]),
:bordercolor => webcolor(sp[:foreground_color_legend]),
:font => plotlyfont(sp[:legendfont]),
)
end
# annotations
anns = sp[:annotations]
d_out[:annotations] = if isempty(anns)
KW[]
else
KW[get_annotation_dict(ann...) for ann in anns]
end
# # arrows
# for sargs in seriesargs
# a = sargs[:arrow]
# if sargs[:seriestype] in (:path, :line) && typeof(a) <: Arrow
# add_arrows(sargs[:x], sargs[:y]) do xyprev, xy
# push!(d_out[:annotations], get_annotation_dict_for_arrow(sargs, xyprev, xy, a))
# end
# end
# end
# dumpdict(d_out,"",true)
# @show d_out[:annotations]
if ispolar(sp)
d_out[:direction] = "counterclockwise"
end
d_out
end
d_out
end
function get_plot_json(plt::Plot{PlotlyBackend})
JSON.json(plotly_layout(plt.plotargs))
function plotly_layout_json(plt::Plot)
JSON.json(plotly_layout(plt))
end
function plotly_colorscale(grad::ColorGradient, alpha = nothing)
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
end
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(default_gradient(), alpha)
@@ -347,246 +319,215 @@ const _plotly_markers = KW(
:star5 => "star-triangle-up",
:vline => "line-ns",
:hline => "line-ew",
)
)
function plotly_subplot_index(sp::Subplot)
spidx = sp[:subplot_index]
spidx == 1 ? "" : spidx
end
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
function plotly_series(d::KW, plotargs::KW; plot_index = nothing)
# dumpdict(d,"series",true)
d_out = KW()
function plotly_series(plt::Plot, series::Series)
d = series.d
sp = d[:subplot]
d_out = KW()
x, y = collect(d[:x]), collect(d[:y])
d_out[:name] = d[:label]
# these are the axes that the series should be mapped to
spidx = plotly_subplot_index(sp)
d_out[:xaxis] = "x$spidx"
d_out[:yaxis] = "y$spidx"
lt = d[:linetype]
isscatter = lt in (:scatter, :scatter3d)
hasmarker = isscatter || d[:markershape] != :none
hasline = !isscatter
x, y = collect(d[:x]), collect(d[:y])
d_out[:name] = d[:label]
st = d[:seriestype]
isscatter = st in (:scatter, :scatter3d)
hasmarker = isscatter || d[:markershape] != :none
hasline = !isscatter
# set the "type"
if lt in (:line, :path, :scatter, :steppre, :steppost)
d_out[:type] = "scatter"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
if d[:fillrange] == true || d[:fillrange] == 0
d_out[:fill] = "tozeroy"
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
elseif !(d[:fillrange] in (false, nothing))
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(d[:fillrange])")
end
d_out[:x], d_out[:y] = x, y
elseif lt == :bar
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
elseif lt == :hist2d
d_out[:type] = "histogram2d"
d_out[:x], d_out[:y] = x, y
if isa(d[:bins], Tuple)
xbins, ybins = d[:bins]
else
xbins = ybins = d[:bins]
end
d_out[:nbinsx] = xbins
d_out[:nbinsy] = ybins
elseif lt in (:hist, :density)
d_out[:type] = "histogram"
isvert = isvertical(d)
d_out[isvert ? :x : :y] = y
d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
if lt == :density
d_out[:histnorm] = "probability density"
end
elseif lt == :heatmap
d_out[:type] = "heatmap"
d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
elseif lt == :contour
d_out[:type] = "contour"
d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf
# d_out[:showscale] = d[:colorbar] != :none
d_out[:ncontours] = d[:levels]
d_out[:contours] = KW(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
d_out[:colorscale] = plotly_colorscale(d[:linecolor], d[:linealpha])
elseif lt in (:surface, :wireframe)
d_out[:type] = "surface"
d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
elseif lt == :pie
d_out[:type] = "pie"
d_out[:labels] = x
d_out[:values] = y
d_out[:hoverinfo] = "label+percent+name"
elseif lt in (:path3d, :scatter3d)
d_out[:type] = "scatter3d"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
d_out[:x], d_out[:y] = x, y
d_out[:z] = collect(d[:z])
else
warn("Plotly: linetype $lt isn't supported.")
return KW()
end
# add "marker"
if hasmarker
d_out[:marker] = KW(
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
:opacity => d[:markeralpha],
:size => 2 * d[:markersize],
:color => webcolor(d[:markercolor], d[:markeralpha]),
:line => KW(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth],
),
)
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
if d[:marker_z] != nothing
# d_out[:marker][:color] = d[:marker_z]
# d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor], d[:markeralpha])
# d_out[:showscale] = true
grad = ColorGradient(d[:markercolor], alpha=d[:markeralpha])
zmin, zmax = extrema(d[:marker_z])
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:marker_z]]
end
end
# add "line"
if hasline
d_out[:line] = KW(
:color => webcolor(d[:linecolor], d[:linealpha]),
:width => d[:linewidth],
:shape => if lt == :steppre
"vh"
elseif lt == :steppost
"hv"
# set the "type"
if st in (:line, :path, :scatter, :steppre, :steppost)
d_out[:type] = "scatter"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
"linear"
end,
:dash => string(d[:linestyle]),
# :dash => "solid",
)
end
hasline ? "lines" : "none"
end
if d[:fillrange] == true || d[:fillrange] == 0
d_out[:fill] = "tozeroy"
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
elseif !(d[:fillrange] in (false, nothing))
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(d[:fillrange])")
end
d_out[:x], d_out[:y] = x, y
# convert polar plots x/y to theta/radius
if get(plotargs, :polar, false)
d_out[:t] = rad2deg(pop!(d_out, :x))
d_out[:r] = pop!(d_out, :y)
end
elseif st == :bar
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
# # for subplots, we need to add the xaxis/yaxis fields
# if plot_index != nothing
# d_out[:xaxis] = "x$(plot_index)"
# d_out[:yaxis] = "y$(plot_index)"
# end
elseif st == :histogram2d
d_out[:type] = "histogram2d"
d_out[:x], d_out[:y] = x, y
if isa(d[:bins], Tuple)
xbins, ybins = d[:bins]
else
xbins = ybins = d[:bins]
end
d_out[:nbinsx] = xbins
d_out[:nbinsy] = ybins
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
d_out
elseif st in (:histogram, :density)
d_out[:type] = "histogram"
isvert = isvertical(d)
d_out[isvert ? :x : :y] = y
d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
if st == :density
d_out[:histogramnorm] = "probability density"
end
elseif st == :heatmap
d_out[:type] = "heatmap"
# d_out[:x], d_out[:y] = x, y
# d_out[:z] = d[:z].surf
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
elseif st == :contour
d_out[:type] = "contour"
# d_out[:x], d_out[:y] = x, y
# d_out[:z] = d[:z].surf
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
# d_out[:showscale] = d[:colorbar] != :none
d_out[:ncontours] = d[:levels]
d_out[:contours] = KW(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
d_out[:colorscale] = plotly_colorscale(d[:linecolor], d[:linealpha])
elseif st in (:surface, :wireframe)
d_out[:type] = "surface"
# d_out[:x], d_out[:y] = x, y
# d_out[:z] = d[:z].surf
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
elseif st == :pie
d_out[:type] = "pie"
d_out[:labels] = pie_labels(sp, series)
d_out[:values] = y
d_out[:hoverinfo] = "label+percent+name"
elseif st in (:path3d, :scatter3d)
d_out[:type] = "scatter3d"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
hasline ? "lines" : "none"
end
d_out[:x], d_out[:y] = x, y
d_out[:z] = collect(d[:z])
else
warn("Plotly: seriestype $st isn't supported.")
return KW()
end
# add "marker"
if hasmarker
d_out[:marker] = KW(
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
:opacity => d[:markeralpha],
:size => 2 * d[:markersize],
:color => webcolor(d[:markercolor], d[:markeralpha]),
:line => KW(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth],
),
)
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
if d[:marker_z] != nothing
# d_out[:marker][:color] = d[:marker_z]
# d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor], d[:markeralpha])
# d_out[:showscale] = true
grad = ColorGradient(d[:markercolor], alpha=d[:markeralpha])
zmin, zmax = extrema(d[:marker_z])
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:marker_z]]
end
end
# add "line"
if hasline
d_out[:line] = KW(
:color => webcolor(d[:linecolor], d[:linealpha]),
:width => d[:linewidth],
:shape => if st == :steppre
"vh"
elseif st == :steppost
"hv"
else
"linear"
end,
:dash => string(d[:linestyle]),
# :dash => "solid",
)
end
# convert polar plots x/y to theta/radius
if ispolar(d[:subplot])
d_out[:t] = rad2deg(pop!(d_out, :x))
d_out[:r] = pop!(d_out, :y)
end
d_out
end
# get a list of dictionaries, each representing the series params
function get_series_json(plt::Plot{PlotlyBackend})
JSON.json(map(d -> plotly_series(d, plt.plotargs), plt.seriesargs))
end
function get_series_json(subplt::Subplot{PlotlyBackend})
ds = KW[]
for (i,plt) in enumerate(subplt.plts)
for d in plt.seriesargs
push!(ds, plotly_series(d, plt.plotargs, plot_index = i))
end
end
JSON.json(ds)
function plotly_series_json(plt::Plot)
JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
end
# ----------------------------------------------------------------
function html_head(plt::AbstractPlot{PlotlyBackend})
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
function html_head(plt::Plot{PlotlyBackend})
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
end
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
if style == nothing
w, h = plt.plotargs[:size]
style = "width:$(w)px;height:$(h)px;"
end
uuid = Base.Random.uuid4()
html = """
<div id=\"$(uuid)\" style=\"$(style)\"></div>
<script>
PLOT = document.getElementById('$(uuid)');
Plotly.plot(PLOT, $(get_series_json(plt)), $(get_plot_json(plt)));
</script>
"""
# @show html
html
if style == nothing
w, h = plt[:size]
style = "width:$(w)px;height:$(h)px;"
end
uuid = Base.Random.uuid4()
html = """
<div id=\"$(uuid)\" style=\"$(style)\"></div>
<script>
PLOT = document.getElementById('$(uuid)');
Plotly.plot(PLOT, $(plotly_series_json(plt)), $(plotly_layout_json(plt)));
</script>
"""
html
end
function js_body(plt::Plot{PlotlyBackend}, uuid)
js = """
PLOT = document.getElementById('$(uuid)');
Plotly.plot(PLOT, $(get_series_json(plt)), $(get_plot_json(plt)));
Plotly.plot(PLOT, $(plotly_series_json(plt)), $(plotly_layout_json(plt)));
"""
end
function html_body(subplt::Subplot{PlotlyBackend})
w, h = subplt.plts[1].plotargs[:size]
html = ["<div style=\"width:$(w)px;height:$(h)px;\">"]
nr = nrows(subplt.layout)
ph = h / nr
for r in 1:nr
push!(html, "<div style=\"clear:both;\">")
nc = ncols(subplt.layout, r)
pw = w / nc
for c in 1:nc
plt = subplt[r,c]
push!(html, html_body(plt, "float:left; width:$(pw)px; height:$(ph)px;"))
end
push!(html, "</div>")
end
push!(html, "</div>")
join(html)
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend})
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
writemime_png_from_html(io, plt)
end
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend})
function _writemime(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
write(io, html_head(plt) * html_body(plt))
# write(io, html_body(plt))
end
function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend})
standalone_html_window(plt)
# function Base.display(::PlotsDisplay, plt::Plot{PlotlyBackend})
function _display(plt::Plot{PlotlyBackend})
standalone_html_window(plt)
end
# function Base.display(::PlotsDisplay, plt::Subplot{PlotlyBackend})
# # TODO: display/show the subplot
# end
+57 -76
View File
@@ -17,7 +17,7 @@ supportedArgs(::PlotlyJSBackend) = [
:seriescolor, :seriesalpha,
:linecolor,
:linestyle,
:linetype,
:seriestype,
:linewidth,
:linealpha,
:markershape,
@@ -36,13 +36,13 @@ supportedArgs(::PlotlyJSBackend) = [
:show,
:size,
:title,
:windowtitle,
:window_title,
:x,
:xlabel,
:xguide,
:xlims,
:xticks,
:y,
:ylabel,
:yguide,
:ylims,
# :yrightlabel,
:yticks,
@@ -66,7 +66,7 @@ supportedArgs(::PlotlyJSBackend) = [
]
supportedAxes(::PlotlyJSBackend) = [:auto, :left]
supportedTypes(::PlotlyJSBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:histogram2d, :histogram, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyJSBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyJSBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
@@ -85,7 +85,7 @@ function _initialize_backend(::PlotlyJSBackend; kw...)
for (mime, fmt) in PlotlyJS._mimeformats
# mime == "image/png" && continue # don't use plotlyjs's writemime for png
@eval Base.writemime(io::IO, m::MIME{symbol($mime)}, p::Plot{PlotlyJSBackend}) = writemime(io, m, p.o)
@eval Base.writemime(io::IO, m::MIME{Symbol($mime)}, p::Plot{PlotlyJSBackend}) = writemime(io, m, p.o)
end
# override IJulia inline display
@@ -96,61 +96,53 @@ end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PlotlyJSBackend, d::KW)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
# o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(),
# Base.Random.uuid4(), PlotlyJS.ElectronDisplay())
# T = isijulia() ? PlotlyJS.JupyterPlot : PlotlyJS.ElectronPlot
# o = T(PlotlyJS.Plot())
o = PlotlyJS.plot()
# function _create_plot(pkg::PlotlyJSBackend, d::KW)
# # TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# # TODO: initialize the plot... title, xlabel, bgcolor, etc
# # o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(),
# # Base.Random.uuid4(), PlotlyJS.ElectronDisplay())
# # T = isijulia() ? PlotlyJS.JupyterPlot : PlotlyJS.ElectronPlot
# # o = T(PlotlyJS.Plot())
# o = PlotlyJS.plot()
#
# Plot(o, pkg, 0, d, KW[])
# end
Plot(o, pkg, 0, d, KW[])
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
PlotlyJS.plot()
end
function _add_series(::PlotlyJSBackend, plt::Plot, d::KW)
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
syncplot = plt.o
# add to the data array
pdict = plotly_series(d, plt.plotargs)
pdict = plotly_series(plt, series)
typ = pop!(pdict, :type)
gt = PlotlyJS.GenericTrace(typ; pdict...)
PlotlyJS.addtraces!(syncplot, gt)
push!(plt.seriesargs, d)
plt
end
# ---------------------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{PlotlyJSBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
# set or add to the annotation_list
if !haskey(plt.plotargs, :annotation_list)
plt.plotargs[:annotation_list] = Any[]
end
append!(plt.plotargs[:annotation_list], anns)
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
PlotlyJS.restyle!(
plt.o,
findfirst(plt.series_list, series),
KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
)
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{PlotlyJSBackend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PlotlyJSBackend}, d::KW)
pdict = plotly_layout(d)
function _update_plot_object(plt::Plot{PlotlyJSBackend})
pdict = plotly_layout(plt)
syncplot = plt.o
w,h = d[:size]
w,h = plt[:size]
# DD(pdict)
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
end
function _update_plot_pos_size(plt::AbstractPlot{PlotlyJSBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
@@ -160,47 +152,36 @@ end
# d[:x], d[:y]
# end
function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
d = plt.seriesargs[i]
ispolar = get(plt.plotargs, :polar, false)
xsym = ispolar ? :t : :x
ysym = ispolar ? :r : :y
d[xsym], d[ysym] = xy
# TODO: this is likely ineffecient... we should make a call that ONLY changes the plot data
PlotlyJS.restyle!(plt.o, i, KW(xsym=>(d[xsym],), ysym=>(d[ysym],)))
plt
end
# function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
# d = plt.seriesargs[i]
# ispolar = get(plt.attr, :polar, false)
# xsym = ispolar ? :t : :x
# ysym = ispolar ? :r : :y
# d[xsym], d[ysym] = xy
# # TODO: this is likely ineffecient... we should make a call that ONLY changes the plot data
# PlotlyJS.restyle!(plt.o, i, KW(xsym=>(d[xsym],), ysym=>(d[ysym],)))
# plt
# end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PlotlyJSBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
true
end
function _expand_limits(lims, plt::Plot{PlotlyJSBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PlotlyJSBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
# function Base.writemime(io::IO, m::MIME"text/html", plt::AbstractPlot{PlotlyJSBackend})
# Base.writemime(io, m, plt.o)
# function _update_min_padding!(sp::Subplot{PlotlyBackend})
# sp.minpad = plotly_minpad(sp)
# end
# function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyJSBackend})
# println("here!")
# writemime_png_from_html(io, plt)
# function plotlyjs_finalize(plt::Plot)
# plotly_finalize(plt)
# PlotlyJS.relayout!(plt.o, plotly_layout(plt))
# end
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend})
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
tmpfn = tempname() * "png"
PlotlyJS.savefig(plt.o, tmpfn)
write(io, read(open(tmpfn)))
end
function _display(plt::Plot{PlotlyJSBackend})
# plotlyjs_finalize(plt)
display(plt.o)
end
function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSBackend})
error()
end
+580 -456
View File
File diff suppressed because it is too large Load Diff
+66 -64
View File
@@ -3,7 +3,7 @@
supportedArgs(::QwtBackend) = [
:annotation,
:annotations,
:axis,
:background_color,
:linecolor,
@@ -17,7 +17,7 @@ supportedArgs(::QwtBackend) = [
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
:seriestype,
:linewidth,
:markershape,
:markercolor,
@@ -31,20 +31,20 @@ supportedArgs(::QwtBackend) = [
:show,
:size,
:title,
:windowtitle,
:window_title,
:x,
:xlabel,
:xguide,
:xlims,
:xticks,
:y,
:ylabel,
:yguide,
:ylims,
:yrightlabel,
:yticks,
:xscale,
:yscale,
]
supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :histogram2d, :hexbin, :histogram, :bar, :hline, :vline]
supportedMarkers(::QwtBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
supportedScales(::QwtBackend) = [:identity, :log10]
subplotSupported(::QwtBackend) = true
@@ -91,29 +91,29 @@ end
function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
d = KW(kw)
lt = d[:linetype]
if lt == :scatter
d[:linetype] = :none
st = d[:seriestype]
if st == :scatter
d[:seriestype] = :none
if d[:markershape] == :none
d[:markershape] = :ellipse
end
elseif lt in (:hline, :vline)
elseif st in (:hline, :vline)
addLineMarker(plt, d)
d[:linetype] = :none
d[:seriestype] = :none
d[:markershape] = :ellipse
d[:markersize] = 1
if lt == :vline
if st == :vline
d[:x], d[:y] = d[:y], d[:x]
end
elseif !iscreating && lt == :bar
elseif !iscreating && st == :bar
d = barHack(; kw...)
elseif !iscreating && lt == :hist
elseif !iscreating && st == :histogram
d = barHack(; histogramHack(; kw...)...)
end
replaceQwtAliases(d, :linetype)
replaceQwtAliases(d, :seriestype)
replaceQwtAliases(d, :markershape)
for k in keys(d)
@@ -128,21 +128,23 @@ function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
d
end
function _create_plot(pkg::QwtBackend, d::KW)
fixcolors(d)
dumpdict(d,"\n\n!!! plot")
o = Qwt.plot(zeros(0,0); d..., show=false)
plt = Plot(o, pkg, 0, d, KW[])
plt
# function _create_plot(pkg::QwtBackend, d::KW)
function _create_backend_figure(plt::Plot{QwtBackend})
fixcolors(plt.attr)
dumpdict(plt.attr,"\n\n!!! plot")
o = Qwt.plot(zeros(0,0); plt.attr..., show=false)
# plt = Plot(o, pkg, 0, d, KW[])
# plt
end
function _add_series(::QwtBackend, plt::Plot, d::KW)
d = adjustQwtKeywords(plt, false; d...)
# function _series_added(::QwtBackend, plt::Plot, d::KW)
function _series_added(plt::Plot{QwtBackend}, series::Series)
d = adjustQwtKeywords(plt, false; series.d...)
fixcolors(d)
dumpdict(d,"\n\n!!! plot!")
Qwt.oplot(plt.o; d...)
push!(plt.seriesargs, d)
plt
# push!(plt.seriesargs, d)
# plt
end
@@ -188,10 +190,10 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
end
function _update_plot(plt::Plot{QwtBackend}, d::KW)
function _update_plot_object(plt::Plot{QwtBackend}, d::KW)
haskey(d, :title) && Qwt.title(plt.o, d[:title])
haskey(d, :xlabel) && Qwt.xlabel(plt.o, d[:xlabel])
haskey(d, :ylabel) && Qwt.ylabel(plt.o, d[:ylabel])
haskey(d, :xguide) && Qwt.xlabel(plt.o, d[:xguide])
haskey(d, :yguide) && Qwt.ylabel(plt.o, d[:yguide])
updateLimsAndTicks(plt, d, true)
updateLimsAndTicks(plt, d, false)
end
@@ -208,7 +210,7 @@ end
function addLineMarker(plt::Plot{QwtBackend}, d::KW)
for yi in d[:y]
marker = Qwt.QWT.QwtPlotMarker()
ishorizontal = (d[:linetype] == :hline)
ishorizontal = (d[:seriestype] == :hline)
marker[:setLineStyle](ishorizontal ? 1 : 2)
marker[ishorizontal ? :setYValue : :setXValue](yi)
qcolor = Qwt.convertRGBToQColor(getColor(d[:linecolor]))
@@ -267,28 +269,28 @@ end
# -------------------------------
# create the underlying object (each backend will do this differently)
function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
isbefore && return false
i = 0
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
push!(row, subplt.plts[i].o)
if c == ncols(subplt.layout, r)
push!(rows, Qwt.hsplitter(row...))
row = Any[]
end
end
# for rowcnt in subplt.layout.rowcounts
# push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
# i += rowcnt
# end
subplt.o = Qwt.vsplitter(rows...)
# Qwt.resizewidget(subplt.o, getplotargs(subplt,1)[:size]...)
# Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
true
end
# # create the underlying object (each backend will do this differently)
# function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
# isbefore && return false
# i = 0
# rows = Any[]
# row = Any[]
# for (i,(r,c)) in enumerate(subplt.layout)
# push!(row, subplt.plts[i].o)
# if c == ncols(subplt.layout, r)
# push!(rows, Qwt.hsplitter(row...))
# row = Any[]
# end
# end
# # for rowcnt in subplt.layout.rowcounts
# # push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
# # i += rowcnt
# # end
# subplt.o = Qwt.vsplitter(rows...)
# # Qwt.resizewidget(subplt.o, getattr(subplt,1)[:size]...)
# # Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
# true
# end
function _expand_limits(lims, plt::Plot{QwtBackend}, isx::Bool)
for series in plt.o.lines
@@ -309,13 +311,13 @@ function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
write(io, readall("/tmp/dfskjdhfkh.png"))
end
function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
write(io, readall("/tmp/dfskjdhfkh.png"))
end
# function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
# for plt in subplt.plts
# Qwt.refresh(plt.o)
# end
# Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
# write(io, readall("/tmp/dfskjdhfkh.png"))
# end
function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
@@ -323,9 +325,9 @@ function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
Qwt.showwidget(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.showwidget(subplt.o)
end
# function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
# for plt in subplt.plts
# Qwt.refresh(plt.o)
# end
# Qwt.showwidget(subplt.o)
# end
-698
View File
@@ -1,698 +0,0 @@
#
# supportedAxes(::AbstractBackend) = [:left]
# supportedTypes(::AbstractBackend) = []
# supportedStyles(::AbstractBackend) = [:solid]
# supportedMarkers(::AbstractBackend) = [:none]
# supportedScales(::AbstractBackend) = [:identity]
# subplotSupported(::AbstractBackend) = false
# stringsSupported(::AbstractBackend) = false
#
# supportedAxes() = supportedAxes(backend())
# supportedTypes() = supportedTypes(backend())
# supportedStyles() = supportedStyles(backend())
# supportedMarkers() = supportedMarkers(backend())
# supportedScales() = supportedScales(backend())
# subplotSupported() = subplotSupported(backend())
# stringsSupported() = stringsSupported(backend())
# --------------------------------------------------------------------------------------
#
#
# supportedArgs(::GadflyBackend) = [
# :annotation,
# :background_color, :foreground_color, :color_palette,
# :group,
# :label,
# :linetype,
# :seriescolor, :seriesalpha,
# :linecolor, :linestyle, :linewidth, :linealpha,
# :markershape, :markercolor, :markersize, :markeralpha,
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
# :fillrange, :fillcolor, :fillalpha,
# :bins,
# :n, :nc, :nr, :layout,
# :smooth,
# :title, :windowtitle, :show, :size,
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
# # :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
# :z,
# :tickfont, :guidefont, :legendfont,
# :grid, :legend, :colorbar,
# :marker_z, :levels,
# :xerror, :yerror,
# :ribbon, :quiver,
# :orientation,
# ]
# supportedAxes(::GadflyBackend) = [:auto, :left]
# supportedTypes(::GadflyBackend) = [
# :none, :line, :path, :steppre, :steppost, :sticks,
# :scatter, :hist2d, :hexbin, :hist,
# :bar, :box, :violin, :quiver,
# :hline, :vline, :contour, :shape
# ]
# supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
# supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
# supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
# subplotSupported(::GadflyBackend) = true
#
#
# # --------------------------------------------------------------------------------------
#
# supportedArgs(::ImmerseBackend) = supportedArgs(GadflyBackend())
# supportedAxes(::ImmerseBackend) = supportedAxes(GadflyBackend())
# supportedTypes(::ImmerseBackend) = supportedTypes(GadflyBackend())
# supportedStyles(::ImmerseBackend) = supportedStyles(GadflyBackend())
# supportedMarkers(::ImmerseBackend) = supportedMarkers(GadflyBackend())
# supportedScales(::ImmerseBackend) = supportedScales(GadflyBackend())
# subplotSupported(::ImmerseBackend) = true
#
# # --------------------------------------------------------------------------------------
#
#
# supportedArgs(::PyPlotBackend) = [
# :annotation,
# :background_color, :foreground_color, :color_palette,
# :background_color_legend, :background_color_inside, :background_color_outside,
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
# :foreground_color_text, :foreground_color_border,
# :group,
# :label,
# :linetype,
# :seriescolor, :seriesalpha,
# :linecolor, :linestyle, :linewidth, :linealpha,
# :markershape, :markercolor, :markersize, :markeralpha,
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
# :fillrange, :fillcolor, :fillalpha,
# :bins,
# :n, :nc, :nr, :layout,
# :smooth,
# :title, :windowtitle, :show, :size,
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
# :axis, :yrightlabel,
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
# :z,
# :tickfont, :guidefont, :legendfont,
# :grid, :legend, :colorbar,
# :marker_z, :levels,
# :xerror, :yerror,
# :ribbon, :quiver,
# :orientation,
# :overwrite_figure,
# :polar,
# :normalize, :weights, :contours, :aspect_ratio
# ]
# supportedAxes(::PyPlotBackend) = _allAxes
# supportedTypes(::PyPlotBackend) = [
# :none, :line, :path, :steppre, :steppost, :shape,
# :scatter, :hist2d, :hexbin, :hist, :density,
# :bar, :sticks, :box, :violin, :quiver,
# :hline, :vline, :heatmap, :pie,
# :contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
# ]
# supportedStyles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::PyPlotBackend) = vcat(_allMarkers, Shape)
# supportedScales(::PyPlotBackend) = [:identity, :ln, :log2, :log10]
# subplotSupported(::PyPlotBackend) = true
#
#
# # --------------------------------------------------------------------------------------
#
#
# supportedArgs(::GRBackend) = [
# :annotation,
# :background_color, :foreground_color, :color_palette,
# :group,
# :label,
# :linetype,
# :seriescolor, :seriesalpha,
# :linecolor, :linestyle, :linewidth, :linealpha,
# :markershape, :markercolor, :markersize, :markeralpha,
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
# :fillrange, :fillcolor, :fillalpha,
# :bins,
# :n, :nc, :nr, :layout,
# :smooth,
# :title, :windowtitle, :show, :size,
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
# :axis, :yrightlabel,
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
# :z,
# :tickfont, :guidefont, :legendfont,
# :grid, :legend, :colorbar,
# :marker_z, :levels,
# :xerror, :yerror,
# :ribbon, :quiver,
# :orientation,
# :overwrite_figure,
# :polar,
# ]
# supportedAxes(::GRBackend) = _allAxes
# supportedTypes(::GRBackend) = [:none, :line, :path, :steppre, :steppost, :sticks,
# :scatter, :hist2d, :hexbin, :hist, :density, :bar,
# :hline, :vline, :contour, :heatmap, :path3d, :scatter3d, :surface,
# :wireframe, :ohlc, :pie]
# supportedStyles(::GRBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
# supportedMarkers(::GRBackend) = vcat(_allMarkers, Shape)
# supportedScales(::GRBackend) = [:identity, :log10]
# subplotSupported(::GRBackend) = true
#
#
# # --------------------------------------------------------------------------------------
#
#
# supportedArgs(::QwtBackend) = [
# :annotation,
# :axis,
# :background_color,
# :linecolor,
# :color_palette,
# :fillrange,
# :fillcolor,
# :foreground_color,
# :group,
# :label,
# :layout,
# :legend,
# :seriescolor, :seriesalpha,
# :linestyle,
# :linetype,
# :linewidth,
# :markershape,
# :markercolor,
# :markersize,
# :n,
# :bins,
# :nc,
# :nr,
# :pos,
# :smooth,
# :show,
# :size,
# :title,
# :windowtitle,
# :x,
# :xlabel,
# :xlims,
# :xticks,
# :y,
# :ylabel,
# :ylims,
# :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# ]
# supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
# supportedMarkers(::QwtBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
# supportedScales(::QwtBackend) = [:identity, :log10]
# subplotSupported(::QwtBackend) = true
#
#
# # --------------------------------------------------------------------------------------
#
# supportedArgs(::UnicodePlotsBackend) = [
# # :annotation,
# # :args,
# # :axis,
# # :background_color,
# # :linecolor,
# # :fill,
# # :foreground_color,
# :group,
# # :heatmap_c,
# # :kwargs,
# :label,
# # :layout,
# :legend,
# :seriescolor, :seriesalpha,
# :linestyle,
# :linetype,
# # :linewidth,
# :markershape,
# # :markercolor,
# # :markersize,
# # :markerstrokewidth,
# # :markerstrokecolor,
# # :markerstrokestyle,
# # :n,
# :bins,
# # :nc,
# # :nr,
# # :pos,
# # :reg,
# # :ribbon,
# :show,
# :size,
# :title,
# :windowtitle,
# :x,
# :xlabel,
# :xlims,
# # :xticks,
# :y,
# :ylabel,
# :ylims,
# # :yrightlabel,
# # :yticks,
# # :xscale,
# # :yscale,
# # :xflip,
# # :yflip,
# # :z,
# ]
# supportedAxes(::UnicodePlotsBackend) = [:auto, :left]
# supportedTypes(::UnicodePlotsBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
# supportedStyles(::UnicodePlotsBackend) = [:auto, :solid]
# supportedMarkers(::UnicodePlotsBackend) = [:none, :auto, :ellipse]
# supportedScales(::UnicodePlotsBackend) = [:identity]
# subplotSupported(::UnicodePlotsBackend) = true
#
#
#
#
# # --------------------------------------------------------------------------------------
#
# supportedArgs(::WinstonBackend) = [
# :annotation,
# # :args,
# # :axis,
# # :background_color,
# :linecolor,
# :color_palette,
# :fillrange,
# :fillcolor,
# # :foreground_color,
# :group,
# # :heatmap_c,
# # :kwargs,
# :label,
# # :layout,
# :legend,
# :seriescolor, :seriesalpha,
# :linestyle,
# :linetype,
# :linewidth,
# :markershape,
# :markercolor,
# :markersize,
# # :markerstrokewidth,
# # :markerstrokecolor,
# # :markerstrokestyle,
# # :n,
# :bins,
# # :nc,
# # :nr,
# # :pos,
# :smooth,
# # :ribbon,
# :show,
# :size,
# :title,
# :windowtitle,
# :x,
# :xlabel,
# :xlims,
# # :xticks,
# :y,
# :ylabel,
# :ylims,
# # :yrightlabel,
# # :yticks,
# :xscale,
# :yscale,
# # :xflip,
# # :yflip,
# # :z,
# ]
# supportedAxes(::WinstonBackend) = [:auto, :left]
# supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
# supportedStyles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::WinstonBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
# supportedScales(::WinstonBackend) = [:identity, :log10]
# subplotSupported(::WinstonBackend) = false
#
#
# # --------------------------------------------------------------------------------------
#
#
# supportedArgs(::BokehBackend) = [
# # :annotation,
# # :axis,
# # :background_color,
# :linecolor,
# # :color_palette,
# # :fillrange,
# # :fillcolor,
# # :fillalpha,
# # :foreground_color,
# :group,
# # :label,
# # :layout,
# # :legend,
# :seriescolor, :seriesalpha,
# :linestyle,
# :linetype,
# :linewidth,
# # :linealpha,
# :markershape,
# :markercolor,
# :markersize,
# # :markeralpha,
# # :markerstrokewidth,
# # :markerstrokecolor,
# # :markerstrokestyle,
# # :n,
# # :bins,
# # :nc,
# # :nr,
# # :pos,
# # :smooth,
# # :show,
# :size,
# :title,
# # :windowtitle,
# :x,
# # :xlabel,
# # :xlims,
# # :xticks,
# :y,
# # :ylabel,
# # :ylims,
# # :yrightlabel,
# # :yticks,
# # :xscale,
# # :yscale,
# # :xflip,
# # :yflip,
# # :z,
# # :tickfont,
# # :guidefont,
# # :legendfont,
# # :grid,
# # :surface,
# # :levels,
# ]
# supportedAxes(::BokehBackend) = [:auto, :left]
# supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
# supportedStyles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
# supportedMarkers(::BokehBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
# supportedScales(::BokehBackend) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
# subplotSupported(::BokehBackend) = false
#
#
# # --------------------------------------------------------------------------------------
#
# supportedArgs(::PlotlyBackend) = [
# :annotation,
# # :axis,
# :background_color,
# :color_palette,
# :fillrange,
# :fillcolor,
# :fillalpha,
# :foreground_color,
# :group,
# :label,
# :layout,
# :legend,
# :seriescolor, :seriesalpha,
# :linecolor,
# :linestyle,
# :linetype,
# :linewidth,
# :linealpha,
# :markershape,
# :markercolor,
# :markersize,
# :markeralpha,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
# :bins,
# :nc,
# :nr,
# # :pos,
# # :smooth,
# :show,
# :size,
# :title,
# :windowtitle,
# :x,
# :xlabel,
# :xlims,
# :xticks,
# :y,
# :ylabel,
# :ylims,
# # :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
# :marker_z,
# :tickfont,
# :guidefont,
# :legendfont,
# :grid,
# :levels,
# :xerror,
# :yerror,
# :ribbon,
# :quiver,
# :orientation,
# :polar,
# ]
# supportedAxes(::PlotlyBackend) = [:auto, :left]
# supportedTypes(::PlotlyBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
# :hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
# :pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
# supportedStyles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::PlotlyBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
# :pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
# supportedScales(::PlotlyBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
# subplotSupported(::PlotlyBackend) = true
# stringsSupported(::PlotlyBackend) = true
#
#
# # --------------------------------------------------------------------------------------
#
# supportedArgs(::PlotlyJSBackend) = [
# :annotation,
# # :axis,
# :background_color,
# :color_palette,
# :fillrange,
# :fillcolor,
# :fillalpha,
# :foreground_color,
# :group,
# :label,
# :layout,
# :legend,
# :seriescolor, :seriesalpha,
# :linecolor,
# :linestyle,
# :linetype,
# :linewidth,
# :linealpha,
# :markershape,
# :markercolor,
# :markersize,
# :markeralpha,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
# :bins,
# :nc,
# :nr,
# # :pos,
# # :smooth,
# :show,
# :size,
# :title,
# :windowtitle,
# :x,
# :xlabel,
# :xlims,
# :xticks,
# :y,
# :ylabel,
# :ylims,
# # :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
# :marker_z,
# :tickfont,
# :guidefont,
# :legendfont,
# :grid,
# :levels,
# :xerror,
# :yerror,
# :ribbon,
# :quiver,
# :orientation,
# :polar,
# ]
# supportedAxes(::PlotlyJSBackend) = [:auto, :left]
# supportedTypes(::PlotlyJSBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
# :hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
# :pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
# supportedStyles(::PlotlyJSBackend) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::PlotlyJSBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
# :pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
# supportedScales(::PlotlyJSBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
# subplotSupported(::PlotlyJSBackend) = true
# stringsSupported(::PlotlyJSBackend) = true
#
# # --------------------------------------------------------------------------------------
#
# supportedArgs(::GLVisualizeBackend) = [
# # :annotation,
# # :axis,
# # :background_color,
# # :color_palette,
# # :fillrange,
# # :fillcolor,
# # :fillalpha,
# # :foreground_color,
# # :group,
# # :label,
# # :layout,
# # :legend,
# # :linecolor,
# # :linestyle,
# :linetype
# # :seriescolor, :seriesalpha,
# # :linewidth,
# # :linealpha,
# # :markershape,
# # :markercolor,
# # :markersize,
# # :markeralpha,
# # :markerstrokewidth,
# # :markerstrokecolor,
# # :markerstrokestyle,
# # :n,
# # :bins,
# # :nc,
# # :nr,
# # :pos,
# # :smooth,
# # :show,
# # :size,
# # :title,
# # :windowtitle,
# # :x,
# # :xlabel,
# # :xlims,
# # :xticks,
# # :y,
# # :ylabel,
# # :ylims,
# # :yrightlabel,
# # :yticks,
# # :xscale,
# # :yscale,
# # :xflip,
# # :yflip,
# # :z,
# # :tickfont,
# # :guidefont,
# # :legendfont,
# # :grid,
# # :surface
# # :levels,
# ]
# supportedAxes(::GLVisualizeBackend) = [:auto, :left]
# supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
# supportedStyles(::GLVisualizeBackend) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
# supportedMarkers(::GLVisualizeBackend) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
# supportedScales(::GLVisualizeBackend) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
# subplotSupported(::GLVisualizeBackend) = false
#
# # --------------------------------------------------------------------------------------
#
# supportedArgs(::PGFPlotsBackend) = [
# # :annotation,
# # :axis,
# :background_color,
# # :color_palette,
# # :fillrange,
# :fillcolor,
# :fillalpha,
# # :foreground_color,
# # :group,
# # :label,
# # :layout,
# # :legend,
# :seriescolor, :seriesalpha,
# :linecolor,
# :linestyle,
# :linetype,
# :linewidth,
# :linealpha,
# :markershape,
# :markercolor,
# :markersize,
# :markeralpha,
# # :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# # :n,
# # :bins,
# # :nc,
# # :nr,
# # :pos,
# # :smooth,
# # :show,
# # :size,
# :title,
# # :windowtitle,
# :x,
# :xlabel,
# :xlims,
# # :xticks,
# :y,
# :ylabel,
# :ylims,
# # :yrightlabel,
# # :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
# :zscale,
# # :tickfont,
# # :guidefont,
# # :legendfont,
# :grid,
# # :surface
# # :levels,
# ]
# supportedAxes(::PGFPlotsBackend) = [:auto, :left]
# supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :line, :steppre, :stepmid, :steppost, :hist, :bar, :hist2d, :sticks, :ysticks, :xsticks, :contour] # :hexbin, :hline, :vline,]
# supportedStyles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
# supportedMarkers(::PGFPlotsBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
# supportedScales(::PGFPlotsBackend) = [:identity, :log, :ln, :log2, :log10] # :asinh, :sqrt]
# subplotSupported(::PGFPlotsBackend) = false
+57 -69
View File
@@ -1,87 +1,75 @@
# TODO: find/replace all [PkgName] with CamelCase, all [pkgname] with lowercase
# TODO: find/replace all [PkgName] with CamelCase
# [WEBSITE]
# [ADD BACKEND WEBSITE]
function _initialize_backend(::[PkgName]AbstractBackend; kw...)
@eval begin
import [PkgName]
export [PkgName]
# TODO: other initialization that needs to be eval-ed
end
# TODO: other initialization
function _initialize_backend(::[PkgName]Backend; kw...)
@eval begin
import [PkgName]
export [PkgName]
# todo: other initialization that needs to be eval-ed
end
# todo: other initialization
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::[PkgName]AbstractBackend, d::KW)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
Plot(nothing, pkg, 0, d, KW[])
# Create the window/figure for this backend.
function _create_backend_figure(plt::Plot{[PkgName]Backend})
nothing
end
# this is called early in the pipeline, use it to make the plot current or something
function _prepare_plot_object(plt::Plot{[PkgName]Backend})
end
# Set up the subplot within the backend object.
function _initialize_subplot(plt::Plot{[PkgName]Backend}, sp::Subplot{[PkgName]Backend})
end
# ---------------------------------------------------------------------------
# Add one series to the underlying backend object.
function _series_added(plt::Plot{[PkgName]Backend}, series::Series)
end
# When series data is added/changed, this callback can do dynamic updates to the backend object.
# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
function _series_updated(plt::Plot{[PkgName]Backend}, series::Series)
end
# ---------------------------------------------------------------------------
# called just before updating layout bounding boxes... in case you need to prep
# for the calcs
function _before_layout_calcs(plt::Plot{[PkgName]Backend})
end
# Set the (left, top, right, bottom) minimum padding around the plot area
# to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot{[PkgName]Backend})
sp.minpad = (20mm, 5mm, 2mm, 10mm)
end
function _add_series(::[PkgName]AbstractBackend, plt::Plot, d::KW)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
function _add_annotations{X,Y,V}(plt::Plot{[PkgName]AbstractBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
# TODO: add the annotation to the plot
end
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
function _update_plot_object(plt::Plot{[PkgName]Backend})
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{[PkgName]AbstractBackend})
# Write a png to io. You could define methods for:
# "application/eps" => "eps",
# "image/eps" => "eps",
# "application/pdf" => "pdf",
# "image/png" => "png",
# "application/postscript" => "ps",
# "image/svg+xml" => "svg"
function _writemime(io::IO, ::MIME"image/png", plt::Plot{[PkgName]Backend})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{[PkgName]AbstractBackend}, d::KW)
end
function _update_plot_pos_size(plt::AbstractPlot{[PkgName]AbstractBackend}, d::KW)
end
# ----------------------------------------------------------------
# accessors for x/y data
# function getxy(plt::Plot{[PkgName]AbstractBackend}, i::Int)
# # TODO: return a tuple of (x, y) vectors
# end
#
# function setxy!{X,Y}(plt::Plot{[PkgName]AbstractBackend}, xy::Tuple{X,Y}, i::Integer)
# # TODO: set the plot data from the (x,y) tuple
# plt
# end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{[PkgName]AbstractBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
function _expand_limits(lims, plt::Plot{[PkgName]AbstractBackend}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{[PkgName]AbstractBackend}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{[PkgName]AbstractBackend})
# TODO: write a png to io
end
function Base.display(::PlotsDisplay, plt::Plot{[PkgName]AbstractBackend})
# TODO: display/show the plot
end
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]AbstractBackend})
# TODO: display/show the subplot
# Display/show the plot (open a GUI window, or browser page, for example).
function _display(plt::Plot{[PkgName]Backend})
end
+182 -113
View File
@@ -2,7 +2,7 @@
# https://github.com/Evizero/UnicodePlots.jl
supportedArgs(::UnicodePlotsBackend) = [
# :annotation,
# :annotations,
# :args,
# :axis,
# :background_color,
@@ -17,7 +17,7 @@ supportedArgs(::UnicodePlotsBackend) = [
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
:seriestype,
# :linewidth,
:markershape,
# :markercolor,
@@ -35,13 +35,13 @@ supportedArgs(::UnicodePlotsBackend) = [
:show,
:size,
:title,
:windowtitle,
:window_title,
:x,
:xlabel,
:xguide,
:xlims,
# :xticks,
:y,
:ylabel,
:yguide,
:ylims,
# :yrightlabel,
# :yticks,
@@ -52,14 +52,18 @@ supportedArgs(::UnicodePlotsBackend) = [
# :z,
]
supportedAxes(::UnicodePlotsBackend) = [:auto, :left]
supportedTypes(::UnicodePlotsBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
supportedTypes(::UnicodePlotsBackend) = [
:path, :steppre, :steppost, :scatter,
:histogram2d, :hline, :vline
]
supportedStyles(::UnicodePlotsBackend) = [:auto, :solid]
supportedMarkers(::UnicodePlotsBackend) = [:none, :auto, :ellipse]
supportedScales(::UnicodePlotsBackend) = [:identity]
subplotSupported(::UnicodePlotsBackend) = true
# don't warn on unsupported... there's just too many warnings!!
warnOnUnsupportedArgs(pkg::UnicodePlotsBackend, d::KW) = nothing
# --------------------------------------------------------------------------------------
@@ -72,93 +76,149 @@ end
# -------------------------------
# convert_size_from_pixels(sz) =
# 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)
plt.o = []
for sp in plt.subplots
xaxis = sp[:xaxis]
yaxis = sp[:yaxis]
xlim = axis_limits(xaxis)
ylim = axis_limits(yaxis)
# figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
sargs = plt.seriesargs
iargs = plt.plotargs
# make vectors
xlim = [xlim[1], xlim[2]]
ylim = [ylim[1], ylim[2]]
# get the x/y limits
if get(iargs, :xlims, :auto) == :auto
xlim = [Inf, -Inf]
for d in sargs
_expand_limits(xlim, d[:x])
# we set x/y to have a single point, since we need to create the plot with some data.
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
x = Float64[xlim[1]]
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
width, height = plt[:size]
o = UnicodePlots.Plot(x, y;
width = width,
height = height,
title = sp[:title],
xlim = xlim,
ylim = ylim
)
# set the axis labels
UnicodePlots.xlabel!(o, xaxis[:guide])
UnicodePlots.ylabel!(o, yaxis[:guide])
# now use the ! functions to add to the plot
for series in series_list(sp)
addUnicodeSeries!(o, series.d, sp[:legend] != :none, xlim, ylim)
end
# save the object
push!(plt.o, o)
end
else
xmin, xmax = iargs[:xlims]
xlim = [xmin, xmax]
end
if get(iargs, :ylims, :auto) == :auto
ylim = [Inf, -Inf]
for d in sargs
_expand_limits(ylim, d[:y])
end
else
ymin, ymax = iargs[:ylims]
ylim = [ymin, ymax]
end
# we set x/y to have a single point, since we need to create the plot with some data.
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
x = Float64[xlim[1]]
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
width, height = iargs[:size]
o = UnicodePlots.Plot(x, y; width = width,
height = height,
title = iargs[:title],
# labels = iargs[:legend],
xlim = xlim,
ylim = ylim)
# set the axis labels
UnicodePlots.xlabel!(o, iargs[:xlabel])
UnicodePlots.ylabel!(o, iargs[:ylabel])
# now use the ! functions to add to the plot
for d in sargs
addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
end
# save the object
plt.o = o
end
# # do all the magic here... build it all at once, since we need to know about all the series at the very beginning
# function rebuildUnicodePlot!(plt::Plot)
#
# # figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
# sargs = plt.seriesargs
# iargs = plt.attr
#
# # get the x/y limits
# if get(iargs, :xlims, :auto) == :auto
# xlim = [Inf, -Inf]
# for d in sargs
# _expand_limits(xlim, d[:x])
# end
# else
# xmin, xmax = iargs[:xlims]
# xlim = [xmin, xmax]
# end
#
# if get(iargs, :ylims, :auto) == :auto
# ylim = [Inf, -Inf]
# for d in sargs
# _expand_limits(ylim, d[:y])
# end
# else
# ymin, ymax = iargs[:ylims]
# ylim = [ymin, ymax]
# end
#
# # we set x/y to have a single point, since we need to create the plot with some data.
# # since this point is at the bottom left corner of the plot, it shouldn't actually be shown
# x = Float64[xlim[1]]
# y = Float64[ylim[1]]
#
# # create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
# width, height = iargs[:size]
# o = UnicodePlots.Plot(x, y; width = width,
# height = height,
# title = iargs[:title],
# # labels = iargs[:legend],
# xlim = xlim,
# ylim = ylim)
#
# # set the axis labels
# UnicodePlots.xlabel!(o, iargs[:xguide])
# UnicodePlots.ylabel!(o, iargs[:yguide])
#
# # now use the ! functions to add to the plot
# for d in sargs
# addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
# end
#
# # save the object
# plt.o = o
# end
# add a single series
function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
# get the function, or special handling for step/bar/hist
lt = d[:linetype]
# get the function, or special handling for step/bar/hist
st = d[:seriestype]
# handle hline/vline separately
if st in (:hline,:vline)
for yi in d[:y]
if st == :hline
UnicodePlots.lineplot!(o, xlim, [yi,yi])
else
UnicodePlots.lineplot!(o, [yi,yi], ylim)
end
end
return
# elseif st == :bar
# UnicodePlots.barplot!(o, d[:x], d[:y])
# return
# elseif st == :histogram
# UnicodePlots.histogram!(o, d[:y], bins = d[:bins])
# return
elseif st == :histogram2d
UnicodePlots.densityplot!(o, d[:x], d[:y])
return
# handle hline/vline separately
if lt in (:hline,:vline)
for yi in d[:y]
if lt == :hline
UnicodePlots.lineplot!(o, xlim, [yi,yi])
else
UnicodePlots.lineplot!(o, [yi,yi], ylim)
end
end
return
end
stepstyle = :post
if lt == :path
if st == :path
func = UnicodePlots.lineplot!
elseif lt == :scatter || d[:markershape] != :none
elseif st == :scatter || d[:markershape] != :none
func = UnicodePlots.scatterplot!
elseif lt == :steppost
elseif st == :steppost
func = UnicodePlots.stairs!
elseif lt == :steppre
elseif st == :steppre
func = UnicodePlots.stairs!
stepstyle = :pre
else
error("Linestyle $lt not supported by UnicodePlots")
error("Linestyle $st not supported by UnicodePlots")
end
# get the series data and label
@@ -173,56 +233,64 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
end
function handlePlotColors(::UnicodePlotsBackend, d::KW)
# TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
d[:color_palette] = [RGB(0,0,0)]
end
# function handlePlotColors(::UnicodePlotsBackend, d::KW)
# # TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
# d[:color_palette] = [RGB(0,0,0)]
# end
# -------------------------------
function _create_plot(pkg::UnicodePlotsBackend, d::KW)
plt = Plot(nothing, pkg, 0, d, KW[])
# function _create_plot(pkg::UnicodePlotsBackend, d::KW)
# plt = Plot(nothing, pkg, 0, d, KW[])
function _create_backend_figure(plt::Plot{UnicodePlotsBackend})
# do we want to give a new default size?
if !haskey(plt.plotargs, :size) || plt.plotargs[:size] == _plotDefaults[:size]
plt.plotargs[:size] = (60,20)
end
# if !haskey(plt.attr, :size) || plt.attr[:size] == default(:size)
# plt.attr[:size] = (60,20)
# end
w, h = plt[:size]
plt.attr[:size] = div(w, 10), div(h, 20)
plt.attr[:color_palette] = [RGB(0,0,0)]
nothing
plt
# plt
end
function _add_series(::UnicodePlotsBackend, plt::Plot, d::KW)
if d[:linetype] in (:sticks, :bar)
d = barHack(; d...)
elseif d[:linetype] == :hist
d = barHack(; histogramHack(; d...)...)
end
push!(plt.seriesargs, d)
plt
end
function _update_plot(plt::Plot{UnicodePlotsBackend}, d::KW)
for k in (:title, :xlabel, :ylabel, :xlims, :ylims)
if haskey(d, k)
plt.plotargs[k] = d[k]
end
end
end
# function _series_added(plt::Plot{UnicodePlotsBackend}, series::Series)
# d = series.d
# # TODO don't need these once the "bar" series recipe is done
# if d[:seriestype] in (:sticks, :bar)
# d = barHack(; d...)
# elseif d[:seriestype] == :histogram
# d = barHack(; histogramHack(; d...)...)
# end
# # push!(plt.seriesargs, d)
# # plt
# end
#
#
# function _update_plot_object(plt::Plot{UnicodePlotsBackend}, d::KW)
# for k in (:title, :xguide, :yguide, :xlims, :ylims)
# if haskey(d, k)
# plt.attr[k] = d[k]
# end
# end
# end
# -------------------------------
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::@compat(AbstractString))
function png(plt::AbstractPlot{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
# @osx_only begin
@compat @static if is_apple()
# BEGIN HACK
# wait while the plot gets drawn
@@ -243,21 +311,22 @@ end
# we don't do very much for subplots... just stack them vertically
function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
isbefore && return false
true
end
# function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
# isbefore && return false
# true
# end
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsBackend})
function _display(plt::Plot{UnicodePlotsBackend})
rebuildUnicodePlot!(plt)
show(plt.o)
map(show, plt.o)
nothing
end
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
for plt in subplt.plts
gui(plt)
end
end
# function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
# for plt in subplt.plts
# gui(plt)
# end
# end
+12 -6
View File
@@ -4,7 +4,7 @@
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.plotargs, :window_title, "Plots.jl"))
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr, :window_title, "Plots.jl"))
"""
<!DOCTYPE html>
<html>
@@ -20,14 +20,20 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.plot
end
function open_browser_window(filename::AbstractString)
@osx_only return run(`open $(filename)`)
@linux_only return run(`xdg-open $(filename)`)
@windows_only return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
@compat @static if is_apple()
return run(`open $(filename)`)
end
@compat @static if is_linux()
return run(`xdg-open $(filename)`)
end
@compat @static if is_windows()
return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
end
warn("Unknown OS... cannot open browser window.")
end
function write_temp_html(plt::AbstractPlot)
html = standalone_html(plt; title = plt.plotargs[:title])
html = standalone_html(plt; title = plt.attr[:window_title])
filename = string(tempname(), ".html")
output = open(filename, "w")
write(output, html)
@@ -51,7 +57,7 @@ function writemime_png_from_html(io::IO, plt::AbstractPlot)
# convert that html file to a temporary png file using wkhtmltoimage
png_fn = tempname() * ".png"
w, h = plt.plotargs[:size]
w, h = plt.attr[:size]
html_to_png(html_fn, png_fn, w, h)
# now read that file data into io
+43 -41
View File
@@ -4,7 +4,7 @@
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
supportedArgs(::WinstonBackend) = [
:annotation,
:annotations,
# :args,
# :axis,
# :background_color,
@@ -21,7 +21,7 @@ supportedArgs(::WinstonBackend) = [
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:linetype,
:seriestype,
:linewidth,
:markershape,
:markercolor,
@@ -39,13 +39,13 @@ supportedArgs(::WinstonBackend) = [
:show,
:size,
:title,
:windowtitle,
:window_title,
:x,
:xlabel,
:xguide,
:xlims,
# :xticks,
:y,
:ylabel,
:yguide,
:ylims,
# :yrightlabel,
# :yticks,
@@ -56,7 +56,7 @@ supportedArgs(::WinstonBackend) = [
# :z,
]
supportedAxes(::WinstonBackend) = [:auto, :left]
supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :histogram, :bar]
supportedStyles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::WinstonBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
supportedScales(::WinstonBackend) = [:identity, :log10]
@@ -96,17 +96,18 @@ end
:star5 => "asterisk"
)
function _before_add_series(plt::Plot{WinstonBackend})
function _before_update(plt::Plot{WinstonBackend})
Winston.ghf(plt.o)
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::WinstonBackend, d::KW)
wplt = Winston.FramedPlot(title = d[:title], xlabel = d[:xlabel], ylabel = d[:ylabel])
Plot(wplt, pkg, 0, d, KW[])
function _create_backend_figure(plt::Plot{WinstonBackend})
Winston.FramedPlot(
title = plt.attr[:title],
xlabel = plt.attr[:xguide],
ylabel = plt.attr[:yguide]
)
end
copy_remove(d::KW, s::Symbol) = delete!(copy(d), s)
@@ -126,11 +127,12 @@ function getWinstonItems(plt::Plot)
window, canvas, wplt
end
function _add_series(::WinstonBackend, plt::Plot, d::KW)
function _series_added(plt::Plot{WinstonBackend}, series::Series)
d = series.d
window, canvas, wplt = getWinstonItems(plt)
# until we call it normally, do the hack
if d[:linetype] == :bar
if d[:seriestype] == :bar
d = barHack(;d...)
end
@@ -148,11 +150,11 @@ function _add_series(::WinstonBackend, plt::Plot, d::KW)
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :hist2d, :hexbin, :hist, :bar
if d[:linetype] == :none
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :histogram2d, :hexbin, :histogram, :bar
if d[:seriestype] == :none
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
elseif d[:linetype] == :path
elseif d[:seriestype] == :path
x, y = d[:x], d[:y]
Winston.add(wplt, Winston.Curve(x, y; e...))
@@ -166,38 +168,38 @@ function _add_series(::WinstonBackend, plt::Plot, d::KW)
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(d[:fillcolor])))
end
elseif d[:linetype] == :scatter
elseif d[:seriestype] == :scatter
if d[:markershape] == :none
d[:markershape] = :ellipse
end
# elseif d[:linetype] == :step
# elseif d[:seriestype] == :step
# fn = Winston.XXX
# elseif d[:linetype] == :stepinverted
# elseif d[:seriestype] == :stepinverted
# fn = Winston.XXX
elseif d[:linetype] == :sticks
elseif d[:seriestype] == :sticks
Winston.add(wplt, Winston.Stems(d[:x], d[:y]; e...))
# elseif d[:linetype] == :dots
# elseif d[:seriestype] == :dots
# fn = Winston.XXX
# elseif d[:linetype] == :hist2d
# elseif d[:seriestype] == :histogram2d
# fn = Winston.XXX
# elseif d[:linetype] == :hexbin
# elseif d[:seriestype] == :hexbin
# fn = Winston.XXX
elseif d[:linetype] == :hist
elseif d[:seriestype] == :histogram
hst = hist(d[:y], d[:bins])
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
# elseif d[:linetype] == :bar
# elseif d[:seriestype] == :bar
# # fn = Winston.XXX
else
error("linetype $(d[:linetype]) not supported by Winston.")
error("seriestype $(d[:seriestype]) not supported by Winston.")
end
@@ -209,10 +211,10 @@ function _add_series(::WinstonBackend, plt::Plot, d::KW)
# optionally add a regression line
d[:smooth] && d[:linetype] != :hist && addRegressionLineWinston(d, wplt)
d[:smooth] && d[:seriestype] != :histogram && addRegressionLineWinston(d, wplt)
push!(plt.seriesargs, d)
plt
# push!(plt.seriesargs, d)
# plt
end
@@ -225,9 +227,9 @@ end
:yscale => :ylog,
)
function _update_plot(plt::Plot{WinstonBackend}, d::KW)
function _update_plot_object(plt::Plot{WinstonBackend}, d::KW)
window, canvas, wplt = getWinstonItems(plt)
for k in (:xlabel, :ylabel, :title, :xlims, :ylims)
for k in (:xguide, :yguide, :title, :xlims, :ylims)
if haskey(d, k)
Winston.setattr(wplt, string(get(_winstonNames, k, k)), d[k])
end
@@ -259,14 +261,14 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
# function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
# # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
# end
# ----------------------------------------------------------------
function addWinstonLegend(plt::Plot, wplt)
if plt.plotargs[:legend] != :none
if plt.attr[:legend] != :none
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
end
end
@@ -287,9 +289,9 @@ function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
error("Gtk is the only supported display for Winston in Plots. Set `output_surface = gtk` in src/Winston.ini")
end
# initialize window
w,h = plt.plotargs[:size]
w,h = plt.attr[:size]
canvas = Gtk.GtkCanvasLeaf()
window = Gtk.GtkWindowLeaf(canvas, plt.plotargs[:windowtitle], w, h)
window = Gtk.GtkWindowLeaf(canvas, plt.attr[:window_title], w, h)
plt.o = (window, canvas, wplt)
end
@@ -300,6 +302,6 @@ function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
end
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
# TODO: display/show the Subplot object
end
# function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
# # TODO: display/show the Subplot object
# end
+9 -58
View File
@@ -18,7 +18,8 @@ colorscheme(c::Colorant; kw...) = ColorWrapper(c; kw...)
# --------------------------------------------------------------
convertColor(c::@compat(Union{AbstractString, Symbol})) = parse(Colorant, string(c))
convertColor(c::AbstractString) = parse(Colorant, c)
convertColor(c::Symbol) = parse(Colorant, string(c))
convertColor(c::Colorant) = c
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
convertColor(c::ColorScheme) = c
@@ -294,7 +295,7 @@ function generate_colorgradient(bgcolor = colorant"white";
gradient_from_list(colors)
end
function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
function get_color_palette(palette, bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
grad = if palette == :auto
generate_colorgradient(bgcolor)
else
@@ -304,7 +305,8 @@ function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper
RGBA[getColorZ(grad, z) for z in zrng]
end
function get_color_palette(palette::Vector{RGBA}, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
function get_color_palette{C<:Colorant}(palette::Vector{C},
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
palette
end
@@ -347,10 +349,10 @@ end
make255(x) = round(Int, 255 * x)
function webcolor(c::Color)
@sprintf("rgb(%d, %d, %d)", [make255(f(c)) for f in [red,green,blue]]...)
@sprintf("rgb(%d, %d, %d)", [make255(f(c)) for f in [red,green,blue]]...)
end
function webcolor(c::TransparentColor)
@sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
@sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
end
webcolor(cs::ColorScheme) = webcolor(getColor(cs))
webcolor(c) = webcolor(convertColor(c))
@@ -358,63 +360,12 @@ webcolor(c, α) = webcolor(convertColor(getColor(c), α))
# ----------------------------------------------------------------------------------
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
function handlePlotColors(::AbstractBackend, d::KW)
if :background_color in supportedArgs()
bgcolor = convertColor(d[:background_color])
else
bgcolor = _plotDefaults[:background_color]
if d[:background_color] != _plotDefaults[:background_color]
warn("Cannot set background_color with backend $(backend())")
end
end
d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100)
# set the foreground color (text, ticks, gridlines) to be white or black depending
# on how dark the background is.
fgcolor = get(d, :foreground_color, :auto)
fgcolor = if fgcolor == :auto
isdark(bgcolor) ? colorant"white" : colorant"black"
else
convertColor(fgcolor)
end
# bg/fg color
d[:background_color] = colorscheme(bgcolor)
d[:foreground_color] = colorscheme(fgcolor)
# update sub-background colors
for bgtype in ("legend", "inside", "outside")
bgsym = symbol("background_color_" * bgtype)
if d[bgsym] == :match
d[bgsym] = d[:background_color]
elseif d[bgsym] == nothing
d[bgsym] = colorscheme(RGBA(0,0,0,0))
end
end
# update sub-foreground colors
for fgtype in ("legend", "grid", "axis", "text", "border", "guide")
fgsym = symbol("foreground_color_" * fgtype)
if d[fgsym] == :match
d[fgsym] = d[:foreground_color]
elseif d[fgsym] == nothing
d[fgsym] = colorscheme(RGBA(0,0,0,0))
end
end
end
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
function getSeriesRGBColor(c, plotargs::KW, n::Int)
function getSeriesRGBColor(c, sp::Subplot, n::Int)
if c == :auto
c = autopick(plotargs[:color_palette], n)
c = autopick(sp[:color_palette], n)
end
# c should now be a subtype of ColorScheme
+138 -90
View File
@@ -12,23 +12,17 @@ compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : ang
# -------------------------------------------------------------
immutable Shape
# vertices::AVec
x::AVec
y::AVec
end
# Shape(x, y) = Shape(collect(zip(x, y)))
Shape(verts::AVec) = Shape(unzip(verts)...)
# get_xs(shape::Shape) = Float64[v[1] for v in shape.vertices]
# get_ys(shape::Shape) = Float64[v[2] for v in shape.vertices]
get_xs(shape::Shape) = shape.x
get_ys(shape::Shape) = shape.y
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
function shape_coords(shape::Shape)
# unzip(shape.vertices)
shape.x, shape.y
end
@@ -36,14 +30,10 @@ function shape_coords(shapes::AVec{Shape})
length(shapes) == 0 && return zeros(0), zeros(0)
xs = map(get_xs, shapes)
ys = map(get_ys, shapes)
# x, y = shapes[1].x, shapes[1].y #unzip(shapes[1].vertices)
x, y = map(copy, shape_coords(shapes[1]))
for shape in shapes[2:end]
# tmpx, tmpy = unzip(shape.vertices)
nanappend!(x, shape.x)
nanappend!(y, shape.y)
# x = vcat(x, NaN, tmpx)
# y = vcat(y, NaN, tmpy)
end
x, y
end
@@ -62,9 +52,6 @@ function weave(x,y; ordering = Vector[x,y])
try
push!(ret, shift!(o))
end
# try
# push!(ret, shift!(y))
# end
end
done = isempty(x) && isempty(y)
end
@@ -123,12 +110,30 @@ const _shapes = KW(
)
for n in [4,5,6,7,8]
_shapes[symbol("star$n")] = makestar(n)
_shapes[Symbol("star$n")] = makestar(n)
end
# -----------------------------------------------------------------------
center(shape::Shape) = (mean(shape.x), mean(shape.y))
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
function center(shape::Shape)
x, y = shape_coords(shape)
n = length(x)
A, Cx, Cy = 0.0, 0.0, 0.0
for i=1:n
ip1 = i==n ? 1 : i+1
A += x[i] * y[ip1] - x[ip1] * y[i]
end
A *= 0.5
for i=1:n
ip1 = i==n ? 1 : i+1
m = (x[i] * y[ip1] - x[ip1] * y[i])
Cx += (x[i] + x[ip1]) * m
Cy += (y[i] + y[ip1]) * m
end
Cx / 6A, Cy / 6A
end
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
sx, sy = shape_coords(shape)
@@ -222,7 +227,7 @@ function font(args...)
valign = arg
elseif T <: Colorant
color = arg
elseif T <: @compat Union{Symbol,AbstractString}
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
catch
@@ -242,15 +247,24 @@ end
"Wrap a string with font info"
immutable PlotText
str::@compat(AbstractString)
str::AbstractString
font::Font
end
PlotText(str) = PlotText(string(str), font())
text(t::PlotText) = t
function text(str, args...)
PlotText(string(str), font(args...))
end
annotations(::Void) = []
annotations(anns::AVec) = anns
annotations(anns) = Any[anns]
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------
immutable Stroke
@@ -261,10 +275,6 @@ immutable Stroke
end
function stroke(args...; alpha = nothing)
# defaults
# width = 1
# color = colorant"black"
# style = :solid
width = nothing
color = nothing
style = nothing
@@ -277,14 +287,12 @@ function stroke(args...; alpha = nothing)
style = arg
elseif T <: Colorant
color = arg
elseif T <: @compat Union{Symbol,AbstractString}
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
end
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
alpha = arg
# elseif typeof(arg) <: Real
elseif allReals(arg)
width = arg
else
@@ -303,9 +311,6 @@ immutable Brush
end
function brush(args...; alpha = nothing)
# defaults
# sz = 1
# color = colorant"black"
size = nothing
color = nothing
@@ -314,14 +319,12 @@ function brush(args...; alpha = nothing)
if T <: Colorant
color = arg
elseif T <: @compat Union{Symbol,AbstractString}
elseif T <: Symbol || T <: AbstractString
try
color = parse(Colorant, string(arg))
end
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
alpha = arg
# elseif typeof(arg) <: Real
elseif allReals(arg)
size = arg
else
@@ -350,12 +353,10 @@ abstract AbstractSurface
"represents a contour or surface mesh"
immutable Surface{M<:AMat} <: AbstractSurface
# x::AVec
# y::AVec
surf::M
end
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for xi in x, yi in y])
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for yi in y, xi in x])
Base.Array(surf::Surface) = surf.surf
@@ -363,7 +364,15 @@ for f in (:length, :size)
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
end
Base.copy(surf::Surface) = Surface(copy(surf.surf))
Base.eltype(surf::Surface) = eltype(surf.surf)
function expand_extrema!(a::Axis, surf::Surface)
ex = a[:extrema]
for vi in surf.surf
expand_extrema!(ex, vi)
end
ex
end
"For the case of representing a surface as a function of x/y... can possibly avoid allocations."
immutable SurfaceFunction <: AbstractSurface
@@ -372,67 +381,106 @@ end
# -----------------------------------------------------------------------
type OHLC{T<:Real}
open::T
high::T
low::T
close::T
# style is :open or :closed (for now)
immutable Arrow
style::Symbol
headlength::Float64
headwidth::Float64
end
function arrow(args...)
style = :simple
headlength = 0.3
headwidth = 0.3
setlength = false
for arg in args
T = typeof(arg)
if T == Symbol
style = arg
elseif T <: Number
# first we apply to both, but if there's more, then only change width after the first number
headwidth = Float64(arg)
if !setlength
headlength = headwidth
end
setlength = true
elseif T <: Tuple && length(arg) == 2
headlength, headwidth = Float64(arg[1]), Float64(arg[2])
else
warn("Skipped arrow arg $arg")
end
end
Arrow(style, headlength, headwidth)
end
# @require FixedSizeArrays begin
type BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
end
function Base.call(bc::BezierCurve, t::Real)
p = zero(P2)
n = length(bc.control_points)-1
for i in 0:n
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
end
p
end
Base.mean(x::Real, y::Real) = 0.5*(x+y)
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
# may create a loop if necessary. Assumes the view is [0,1]
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
mn = mean(p, q)
diff = q - p
minx, maxx = minimum(xview), maximum(xview)
miny, maxy = minimum(yview), maximum(yview)
diffpct = P2(diff[1] / (maxx - minx),
diff[2] / (maxy - miny))
# these points give the initial/final "rise"
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
upper_control = p + vertical_offset
lower_control = q - vertical_offset
# try to figure out when to loop around vs just connecting straight
# TODO: choose loop direction based on sign of p[1]??
# x_close_together = abs(diffpct[1]) <= 0.05
p_is_higher = diff[2] <= 0
inside_control_points = if p_is_higher
# add curve points which will create a loop
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
inside_offset = P2(0.3 * (maxx - minx), 0)
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
else
[]
# allow for do-block notation which gets called on every valid start/end pair which
# we need to draw an arrow
function add_arrows(func::Function, x::AVec, y::AVec)
for i=2:length(x)
xyprev = (x[i-1], y[i-1])
xy = (x[i], y[i])
if ok(xyprev) && ok(xy)
if i==length(x) || !ok(x[i+1], y[i+1])
# add the arrow from xyprev to xy
func(xyprev, xy)
end
end
end
end
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
# -----------------------------------------------------------------------
type BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
end
@compat function (bc::BezierCurve)(t::Real)
p = zero(P2)
n = length(bc.control_points)-1
for i in 0:n
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
end
p
end
# end
Base.mean(x::Real, y::Real) = 0.5*(x+y)
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
# may create a loop if necessary. Assumes the view is [0,1]
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
mn = mean(p, q)
diff = q - p
minx, maxx = minimum(xview), maximum(xview)
miny, maxy = minimum(yview), maximum(yview)
diffpct = P2(diff[1] / (maxx - minx),
diff[2] / (maxy - miny))
# these points give the initial/final "rise"
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
upper_control = p + vertical_offset
lower_control = q - vertical_offset
# try to figure out when to loop around vs just connecting straight
# TODO: choose loop direction based on sign of p[1]??
# x_close_together = abs(diffpct[1]) <= 0.05
p_is_higher = diff[2] <= 0
inside_control_points = if p_is_higher
# add curve points which will create a loop
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
inside_offset = P2(0.3 * (maxx - minx), 0)
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
else
[]
end
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
end
+342
View File
@@ -0,0 +1,342 @@
"""
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
"""
type PlotExample
header::AbstractString
desc::AbstractString
exprs::Vector{Expr}
end
# the _examples we'll run for each
const _examples = PlotExample[
PlotExample("Lines",
"A simple line plot of the columns.",
[:(begin
plot(Plots.fakedata(50,5), w=3)
end)]
),
PlotExample("Functions, adding data, and animations",
"Plot multiple functions. You can also put the function first, or use the form `plot(f, xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data: `x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with `push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the animation.",
[:(begin
p = plot([sin,cos], zeros(0), leg=false)
anim = Animation()
for x in linspace(0, 10π, 100)
push!(p, x, Float64[sin(x), cos(x)])
frame(anim)
end
end)]
),
PlotExample("Parametric plots",
"Plot function pair (x(u), y(u)).",
[:(begin
plot(sin, x->sin(2x), 0, 2π, line=4, leg=false, fill=(0,:orange))
end)]
),
PlotExample("Colors",
"Access predefined palettes (or build your own with the `colorscheme` method). Line/marker colors are auto-generated from the plot's palette, unless overridden. Set the `z` argument to turn on series gradients.",
[:(begin
y = rand(100)
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=(0,:auto), α=0.6)
scatter!(y, zcolor=abs(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs(y-0.5)+4, lab="grad")
end)]
),
PlotExample("Global",
"Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow you to pass a tuple or value which will be mapped to the relevant args automatically. The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`, `yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`",
[:(begin
y = rand(20,3)
plot(y, xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2), leg=false)
hline!(mean(y,1)+rand(1,3), line=(4,:dash,0.6,[:lightgreen :green :darkgreen]))
vline!([5,10])
title!("TITLE")
yaxis!("YLABEL", :log10)
end)]
),
# PlotExample("Two-axis",
# "Use the `axis` arguments.",
# [
# :(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT", xlabel="X", title="TITLE"))
# ]),
PlotExample("Images",
"Plot an image. y-axis is set to flipped",
[:(begin
import Images
img = Images.load(Pkg.dir("PlotReferenceImages","Plots","pyplot","0.7.0","ref1.png"))
plot(img)
end)]
),
PlotExample("Arguments",
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (marker/markersize) with arguments unique to each series (colors). Special arguments `line`, `marker`, and `fill` will automatically figure out what arguments to set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with `line`.) Note that we pass a matrix of colors, and this applies the colors to each series.",
[:(begin
ys = Vector[rand(10), rand(20)]
plot(ys, color=[:black :orange], line=(:dot,4), marker=([:hex :d],12,0.8,stroke(3,:gray)))
end)]
),
PlotExample("Build plot in pieces",
"Start with a base plot...",
[:(begin
plot(rand(100)/3, reg=true, fill=(0,:green))
end)]
),
PlotExample("",
"and add to it later.",
[:(begin
scatter!(rand(100), markersize=6, c=:orange)
end)]
),
PlotExample("Histogram2D",
"",
[:(begin
histogram2d(randn(10000), randn(10000), nbins=20)
end)]
),
PlotExample("Line types",
"",
[:(begin
linetypes = [:path :steppre :steppost :sticks :scatter]
n = length(linetypes)
x = Vector[sort(rand(20)) for i in 1:n]
y = rand(20,n)
plot(x, y, line=(linetypes,3), lab=map(string,linetypes), ms=15)
end)]
),
PlotExample("Line styles",
"",
[:(begin
styles = setdiff(supportedStyles(), [:auto])'
plot(cumsum(randn(20,length(styles)),1), style=:auto, label=map(string,styles), w=5)
end)]
),
PlotExample("Marker types",
"",
[:(begin
markers = setdiff(supportedMarkers(), [:none,:auto,Shape])'
n = length(markers)
x = linspace(0,10,n+2)[2:end-1]
y = repmat(reverse(x)', n, 1)
scatter(x, y, m=(8,:auto), lab=map(string,markers), bg=:linen, xlim=(0,10), ylim=(0,10))
end)]
),
PlotExample("Bar",
"x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
[:(begin
bar(randn(99))
end)]
),
PlotExample("Histogram",
"",
[:(begin
histogram(randn(1000), nbins=20)
end)]
),
PlotExample("Subplots",
"""
Use the `layout` keyword, and optionally the convenient `@layout` macro to generate arbitrarily complex subplot layouts.
""",
[:(begin
l = @layout([a{0.1h}; b [c;d e]])
plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false, ticks=nothing, border=false)
end)]
),
PlotExample("Adding to subplots",
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
[:(begin
plot(Plots.fakedata(100,10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
end)]
),
PlotExample("",
"",
[:(begin
srand(111)
plot!(Plots.fakedata(100,10))
end)]
),
PlotExample("Open/High/Low/Close",
"Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y` argument. This uses recipes to first convert the tuples to OHLC objects, and subsequently create a :path series with the appropriate line segments.",
[:(begin
n=20
hgt=rand(n)+1
bot=randn(n)
openpct=rand(n)
closepct=rand(n)
y = OHLC[(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i], closepct[i]*hgt[i]+bot[i]) for i in 1:n]
ohlc(y)
end)]
),
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. `annotate!(ann)` is shorthand for `plot!(; annotation=ann)`. Series annotations are used for annotating individual data points. They require only the annotation... x/y values are computed. A `PlotText` object can be build with the method `text(string, attr...)`, which wraps font and color attributes.",
[:(begin
y = rand(10)
plot(y, annotations = (3,y[3],text("this is #3",:left)), leg=false)
annotate!([(5, y[5], text("this is #5",16,:red,:center)), (10, y[10], text("this is #10",:right,20,"courier"))])
scatter!(linspace(2,8,6), rand(6), marker=(50,0.2,:orange), series_annotations = ["series","annotations","map","to","series",text("data",:green)])
end)]
),
PlotExample("Custom Markers",
"A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and the size is expected to be rougly the area of the unit circle.",
[:(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.7rand(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)]
),
PlotExample("Contours",
"Any value for fill works here. We first build a filled contour from a function, then an unfilled contour from a matrix.",
[:(begin
x = 1:0.5:20
y = 1:0.5:10
f(x,y) = (3x+y^2)*abs(sin(x)+cos(y))
X = repmat(x', length(y), 1)
Y = repmat(y, 1, length(x))
Z = map(f, X, Y)
p1 = contour(x, y, f, fill=true)
p2 = contour(x, y, Z)
plot(p1, p2)
end)]
# [:(begin
# x = 1:0.3:20
# y = x
# f(x,y) = sin(x)+cos(y)
# contour(x, y, f, fill=true)
# end)]
),
PlotExample("Pie",
"",
[:(begin
x = ["Nerds", "Hackers", "Scientists"]
y = [0.4, 0.35, 0.25]
pie(x, y, title="The Julia Community", l=0.5)
end)]
),
PlotExample("3D",
"",
[:(begin
n = 100
ts = linspace(0,8π,n)
x = ts .* map(cos,ts)
y = 0.1ts .* map(sin,ts)
z = 1:n
plot(x, y, z, zcolor=reverse(z), m=(10,0.8,:blues,stroke(0)), leg=false, cbar=true, w=5)
plot!(zeros(n),zeros(n),1:n, w=10)
end)]
),
PlotExample("DataFrames",
"Plot using DataFrame column symbols.",
[:(begin
import RDatasets
iris = RDatasets.dataset("datasets", "iris")
scatter(iris, :SepalLength, :SepalWidth, group=:Species,
title = "My awesome plot", xlabel = "Length", ylabel = "Width",
marker = (0.5, [:+ :h :star7], 12), bg=RGB(.2,.2,.2))
end)]
),
PlotExample("Groups and Subplots",
"",
[:(begin
group = rand(map(i->"group $i",1:4),100)
plot(rand(100), layout=@layout([a b;c]), group=group, n=3, linetype=[:bar :scatter :steppre])
end)]
),
PlotExample("Polar Plots",
"",
[:(begin
Θ = linspace(0,1.5π,100)
r = abs(0.1randn(100)+sin(3Θ))
plot(Θ, r, proj=:polar, m=2)
end)]
),
PlotExample("Heatmap, categorical axes, and aspect_ratio",
"",
[:(begin
xs = [string("x",i) for i=1:10]
ys = [string("y",i) for i=1:4]
z = float((1:4)*(1:10)')
heatmap(xs, ys, z, aspect_ratio=1)
end)]
),
PlotExample("Layouts, margins, label rotation, title location",
"",
[:(begin
plot(rand(100,6),layout=@layout([a b; c]),title=["A" "B" "C"],
title_location=:left, left_margin=[20mm 0mm],
bottom_margin=50px, xrotation=60)
end)]
),
PlotExample("Boxplot and Violin series recipes",
"",
[:(begin
import RDatasets
singers = RDatasets.dataset("lattice", "singer")
violin(singers, :VoicePart, :Height, marker = (0.2, :blue, stroke(0)))
boxplot!(singers, :VoicePart, :Height, marker = (0.3, :orange, stroke(2)))
end)]
)
]
# ---------------------------------------------------------------------------------
# make and display one plot
function test_examples(pkgname::Symbol, idx::Int; debug = false, disp = true)
Plots._debugMode.on = debug
info("Testing plot: $pkgname:$idx:$(_examples[idx].header)")
backend(pkgname)
backend()
map(eval, _examples[idx].exprs)
plt = current()
if disp
gui(plt)
end
plt
end
# generate all plots and create a dict mapping idx --> plt
function test_examples(pkgname::Symbol; debug = false, disp = true)
Plots._debugMode.on = debug
plts = Dict()
for i in 1:length(_examples)
try
plt = test_examples(pkgname, i, debug=debug, disp=disp)
plts[i] = plt
catch ex
# TODO: put error info into markdown?
warn("Example $pkgname:$i:$(_examples[i].header) failed with: $ex")
end
end
plts
end
+646 -150
View File
@@ -1,178 +1,674 @@
# NOTE: (0,0) is the top-left !!!
# allow pixels and percentages
const px = AbsoluteLength(0.254)
const pct = Length{:pct, Float64}(1.0)
const _cbar_width = 5mm
@compat Base.:.*(m::Measure, n::Number) = m * n
@compat Base.:.*(n::Number, m::Measure) = m * n
@compat Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
@compat Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
Base.zero(::Type{typeof(mm)}) = 0mm
Base.one(::Type{typeof(mm)}) = 1mm
Base.typemin(::typeof(mm)) = -Inf*mm
Base.typemax(::typeof(mm)) = Inf*mm
Base.convert{F<:AbstractFloat}(::Type{F}, l::AbsoluteLength) = convert(F, l.value)
# TODO: these are unintuitive and may cause tricky bugs
# @compat Base.:+(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
# @compat Base.:+(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (1 + m1.value))
# @compat Base.:-(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 - m2.value))
# @compat Base.:-(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (m1.value - 1))
@compat Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
@compat Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
@compat Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
@compat Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
Base.zero(::Type{typeof(pct)}) = 0pct
Base.one(::Type{typeof(pct)}) = 1pct
Base.typemin(::typeof(pct)) = 0pct
Base.typemax(::typeof(pct)) = 1pct
const defaultbox = BoundingBox(0mm, 0mm, 0mm, 0mm)
left(bbox::BoundingBox) = bbox.x0[1]
top(bbox::BoundingBox) = bbox.x0[2]
right(bbox::BoundingBox) = left(bbox) + width(bbox)
bottom(bbox::BoundingBox) = top(bbox) + height(bbox)
Base.size(bbox::BoundingBox) = (width(bbox), height(bbox))
# @compat Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value)
ispositive(m::Measure) = m.value > 0
# union together bounding boxes
@compat function Base.:+(bb1::BoundingBox, bb2::BoundingBox)
# empty boxes don't change the union
ispositive(width(bb1)) || return bb2
ispositive(height(bb1)) || return bb2
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)
end
# this creates a bounding box in the parent's scope, where the child bounding box
# is relative to the parent
function crop(parent::BoundingBox, child::BoundingBox)
l = left(parent) + left(child)
t = top(parent) + top(child)
w = width(child)
h = height(child)
BoundingBox(l, t, w, h)
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)]
if flipy
mms[2] = figh.value - mms[2] # flip y when origin in bottom-left
end
mms ./ Float64[figw.value, figh.value, figw.value, figh.value]
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))}")
end
# -----------------------------------------------------------
# AbstractLayout
Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))")
# this is the available area for drawing everything in this layout... as percentages of total canvas
bbox(layout::AbstractLayout) = layout.bbox
bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb)
# layouts are recursive, tree-like structures, and most will have a parent field
Base.parent(layout::AbstractLayout) = layout.parent
parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
# NOTE: these should be implemented for subplots in each backend!
# they represent the minimum size of the axes and guides
min_padding_left(layout::AbstractLayout) = 0mm
min_padding_top(layout::AbstractLayout) = 0mm
min_padding_right(layout::AbstractLayout) = 0mm
min_padding_bottom(layout::AbstractLayout) = 0mm
padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout)
padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout)
padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
_update_position!(layout::AbstractLayout) = nothing
update_child_bboxes!(layout::AbstractLayout) = nothing
width(layout::AbstractLayout) = width(layout.bbox)
height(layout::AbstractLayout) = height(layout.bbox)
plotarea(layout::AbstractLayout) = defaultbox
plotarea!(layout::AbstractLayout, bbox::BoundingBox) = nothing
attr(layout::AbstractLayout, k::Symbol) = layout.attr[k]
attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v)
attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v)
hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k)
leftpad(layout::AbstractLayout) = 0mm
toppad(layout::AbstractLayout) = 0mm
rightpad(layout::AbstractLayout) = 0mm
bottompad(layout::AbstractLayout) = 0mm
# -----------------------------------------------------------
# RootLayout
# this is the parent of the top-level layout
immutable RootLayout <: AbstractLayout end
Base.parent(::RootLayout) = nothing
parent_bbox(::RootLayout) = defaultbox
bbox(::RootLayout) = defaultbox
# -----------------------------------------------------------
# EmptyLayout
# contains blank space
type EmptyLayout <: AbstractLayout
parent::AbstractLayout
bbox::BoundingBox
attr::KW # store label, width, and height for initialization
# label # this is the label that the subplot will take (since we create a layout before initialization)
end
EmptyLayout(parent = RootLayout(); kw...) = EmptyLayout(parent, defaultbox, KW(kw))
Base.size(layout::EmptyLayout) = (0,0)
Base.length(layout::EmptyLayout) = 0
Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing
_update_min_padding!(layout::EmptyLayout) = nothing
# -----------------------------------------------------------
# GridLayout
# -----------------------------------------------------------
"Simple grid, indices are row-major."
immutable GridLayout <: SubplotLayout
nr::Int
nc::Int
# nested, gridded layout with optional size percentages
type GridLayout <: AbstractLayout
parent::AbstractLayout
minpad::Tuple # leftpad, toppad, rightpad, bottompad
bbox::BoundingBox
grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion
widths::Vector{Measure}
heights::Vector{Measure}
attr::KW
end
Base.length(layout::GridLayout) = layout.nr * layout.nc
Base.start(layout::GridLayout) = 1
Base.done(layout::GridLayout, state) = state > length(layout)
function Base.next(layout::GridLayout, state)
r = div(state-1, layout.nc) + 1
c = mod1(state, layout.nc)
(r,c), state + 1
grid(args...; kw...) = GridLayout(args...; kw...)
function GridLayout(dims...;
parent = RootLayout(),
widths = zeros(dims[2]),
heights = zeros(dims[1]),
kw...)
grid = Matrix{AbstractLayout}(dims...)
layout = GridLayout(
parent,
(20mm, 5mm, 2mm, 10mm),
defaultbox,
grid,
Measure[w*pct for w in widths],
Measure[h*pct for h in heights],
# convert(Vector{Float64}, widths),
# convert(Vector{Float64}, heights),
KW(kw))
fill!(grid, EmptyLayout(layout))
layout
end
nrows(layout::GridLayout) = layout.nr
ncols(layout::GridLayout) = layout.nc
ncols(layout::GridLayout, row::Int) = layout.nc
# get the plot index given row and column
Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c
# -----------------------------------------------------------
# RowsLayout
# -----------------------------------------------------------
"Number of plots per row"
immutable RowsLayout <: SubplotLayout
numplts::Int
rowcounts::AbstractVector{Int}
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]
function Base.setindex!(layout::GridLayout, v, r::Int, c::Int)
layout.grid[r,c] = v
end
Base.length(layout::RowsLayout) = layout.numplts
Base.start(layout::RowsLayout) = 1
Base.done(layout::RowsLayout, state) = state > length(layout)
function Base.next(layout::RowsLayout, state)
r = 1
c = 0
for i = 1:state
c += 1
if c > layout.rowcounts[r]
r += 1
c = 1
end
end
(r,c), state + 1
leftpad(layout::GridLayout) = layout.minpad[1]
toppad(layout::GridLayout) = layout.minpad[2]
rightpad(layout::GridLayout) = layout.minpad[3]
bottompad(layout::GridLayout) = layout.minpad[4]
# leftpad, toppad, rightpad, bottompad
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,:]))
)
end
nrows(layout::RowsLayout) = length(layout.rowcounts)
ncols(layout::RowsLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row])
# get the plot index given row and column
Base.getindex(layout::RowsLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c
# -----------------------------------------------------------
# FlexLayout
# -----------------------------------------------------------
"Flexible, nested layout with optional size percentages."
immutable FlexLayout <: SubplotLayout
n::Int
grid::Matrix # Nested layouts. Each position
# can be a plot index or another FlexLayout
widths::Vector{Float64}
heights::Vector{Float64}
function _update_position!(layout::GridLayout)
map(_update_position!, layout.grid)
end
typealias IntOrFlex Union{Int,FlexLayout}
Base.length(layout::FlexLayout) = layout.n
Base.start(layout::FlexLayout) = 1
Base.done(layout::FlexLayout, state) = state > length(layout)
function Base.next(layout::FlexLayout, state)
# TODO: change this method to return more info
# TODO: might consider multiple iterator types.. some backends might have an easier time row-by-row for example
error()
r = 1
c = 0
for i = 1:state
c += 1
if c > layout.rowcounts[r]
r += 1
c = 1
end
end
(r,c), state + 1
end
nrows(layout::FlexLayout) = size(layout.grid, 1)
ncols(layout::FlexLayout, row::Int) = size(layout.grid, 2)
# get the plot index given row and column
Base.getindex(layout::FlexLayout, r::Int, c::Int) = layout.grid[r,c]
# -----------------------------------------------------------
# we're taking in a nested structure of some kind... parse it out and build a FlexLayout
function subplotlayout(mat::AbstractVecOrMat; widths = nothing, heights = nothing)
n = 0
nr, nc = size(mat)
grid = Array(IntOrFlex, nr, nc)
for i=1:nr, j=1:nc
v = mat[i,j]
if isa(v, Integer)
grid[i,j] = Int(v)
n += 1
elseif isa(v, Tuple)
warn("need to handle tuples somehow... (idx, sizepct)")
grid[i,j] = nothing
elseif v == nothing
grid[i,j] = nothing
elseif isa(v, AbstractVecOrMat)
grid[i,j] = layout(v)
n += grid[i,j].n
function recompute_lengths(v)
# dump(v)
tot = 0pct
cnt = 0
for vi in v
if vi == 0pct
cnt += 1
else
error("How do we process? $v")
tot += vi
end
end
if widths == nothing
widths = ones(nc) ./ nc
end
if heights == nothing
heights = ones(nr) ./ nr
leftover = 1.0pct - tot
if cnt > 1 && leftover.value <= 0
error("Not enough length left over in layout! v = $v, cnt = $cnt, leftover = $leftover")
end
FlexLayout(n, grid, widths, heights)
# now fill in the blanks
Measure[(vi == 0pct ? leftover / cnt : vi) for vi in v]
end
# recursively compute the bounding boxes for the layout and plotarea (relative to canvas!)
function update_child_bboxes!(layout::GridLayout)
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_bottom = map(bottompad, layout.grid)
# @show minpad_left minpad_top minpad_right minpad_bottom
# get the max horizontal (left and right) padding over columns,
# and max vertical (bottom and top) padding over rows
# TODO: add extra padding here
pad_left = maximum(minpad_left, 1)
pad_top = maximum(minpad_top, 2)
pad_right = maximum(minpad_right, 1)
pad_bottom = maximum(minpad_bottom, 2)
# @show pad_left pad_top pad_right pad_bottom
# scale this up to the total padding in each direction
total_pad_horizontal = sum(pad_left + pad_right)
total_pad_vertical = sum(pad_top + pad_bottom)
# @show total_pad_horizontal total_pad_vertical
# now we can compute the total plot area in each direction
total_plotarea_horizontal = width(layout) - total_pad_horizontal
total_plotarea_vertical = height(layout) - total_pad_vertical
# @show total_plotarea_horizontal total_plotarea_vertical
# recompute widths/heights
layout.widths = recompute_lengths(layout.widths)
layout.heights = recompute_lengths(layout.heights)
# @show layout.widths layout.heights
# normalize widths/heights so they sum to 1
# denom_w = sum(layout.widths)
# denom_h = sum(layout.heights)
# @show layout.widths layout.heights denom_w, denom_h
# we have all the data we need... lets compute the plot areas and set the bounding boxes
for r=1:nr, c=1:nc
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))
# 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))
# compute child bbox
child_width = pad_left[c] + plotarea_width + pad_right[c]
child_height = pad_top[r] + plotarea_height + pad_bottom[r]
bbox!(child, BoundingBox(child_left, child_top, child_width, child_height))
# recursively update the child's children
update_child_bboxes!(child)
end
end
function subplotlayout(sz::Tuple{Int,Int})
GridLayout(sz...)
# ----------------------------------------------------------------------
calc_num_subplots(layout::AbstractLayout) = 1
function calc_num_subplots(layout::GridLayout)
tot = 0
for l in layout.grid
tot += calc_num_subplots(l)
end
tot
end
function subplotlayout(rowcounts::AVec{Int})
RowsLayout(sum(rowcounts), rowcounts)
end
function subplotlayout(numplts::Int, nr::Int, nc::Int)
# figure out how many rows/columns we need
if nr == -1
if nc == -1
nr = round(Int, sqrt(numplts))
nc = ceil(Int, numplts / nr)
function compute_gridsize(numplts::Int, nr::Int, nc::Int)
# figure out how many rows/columns we need
if nr < 1
if nc < 1
nr = round(Int, sqrt(numplts))
nc = ceil(Int, numplts / nr)
else
nr = ceil(Int, numplts / nc)
end
else
nr = ceil(Int, numplts / nc)
nc = ceil(Int, numplts / nr)
end
nr, nc
end
# ----------------------------------------------------------------------
# constructors
# pass the layout arg through
function layout_args(d::KW)
layout_args(get(d, :layout, default(:layout)))
end
function layout_args(d::KW, n_override::Integer)
layout, n = layout_args(get(d, :layout, n_override))
if n != n_override
error("When doing layout, n != n_override. You're probably trying to force existing plots into a layout that doesn't fit them.")
end
layout, n
end
function layout_args(n::Integer)
nr, nc = compute_gridsize(n, -1, -1)
GridLayout(nr, nc), n
end
function layout_args{I<:Integer}(sztup::NTuple{2,I})
nr, nc = sztup
GridLayout(nr, nc), nr*nc
end
function layout_args{I<:Integer}(sztup::NTuple{3,I})
n, nr, nc = sztup
nr, nc = compute_gridsize(n, nr, nc)
GridLayout(nr, nc), n
end
# compute number of subplots
function layout_args(layout::GridLayout)
# recursively get the size of the grid
n = calc_num_subplots(layout)
layout, n
end
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
# # pass the layout arg through
# function build_layout(d::KW)
# build_layout(get(d, :layout, default(:layout)))
# end
#
# function build_layout(n::Integer)
# nr, nc = compute_gridsize(n, -1, -1)
# build_layout(GridLayout(nr, nc), n)
# end
#
# function build_layout{I<:Integer}(sztup::NTuple{2,I})
# nr, nc = sztup
# build_layout(GridLayout(nr, nc))
# end
#
# function build_layout{I<:Integer}(sztup::NTuple{3,I})
# n, nr, nc = sztup
# nr, nc = compute_gridsize(n, nr, nc)
# build_layout(GridLayout(nr, nc), n)
# end
#
# # compute number of subplots
# function build_layout(layout::GridLayout)
# # recursively get the size of the grid
# n = calc_num_subplots(layout)
# build_layout(layout, n)
# end
function build_layout(args...)
layout, n = layout_args(args...)
build_layout(layout, n)
end
# n is the number of subplots
function build_layout(layout::GridLayout, n::Integer)
nr, nc = size(layout)
subplots = Subplot[]
spmap = SubplotMap()
i = 0
for r=1:nr, c=1:nc
l = layout[r,c]
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
sp = Subplot(backend(), parent=layout)
layout[r,c] = sp
push!(subplots, sp)
spmap[attr(l,:label,gensym())] = sp
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
end
i += 1
elseif isa(l, GridLayout)
# sub-grid
l, sps, m = build_layout(l, n-i)
append!(subplots, sps)
merge!(spmap, m)
i += length(sps)
end
i >= n && break # only add n subplots
end
layout, subplots, spmap
end
# build a layout from a list of existing Plot objects
# TODO... much of the logic overlaps with the method above... can we merge?
function build_layout(layout::GridLayout, numsp::Integer, plts::AVec{Plot})
nr, nc = size(layout)
subplots = Subplot[]
spmap = SubplotMap()
i = 0
for r=1:nr, c=1:nc
l = layout[r,c]
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
plt = shift!(plts) # grab the first plot out of the list
layout[r,c] = plt.layout
append!(subplots, plt.subplots)
merge!(spmap, plt.spmap)
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
end
i += length(plt.subplots)
elseif isa(l, GridLayout)
# sub-grid
l, sps, m = build_layout(l, numsp-i, plts)
append!(subplots, sps)
merge!(spmap, m)
i += length(sps)
end
i >= numsp && break # only add n subplots
end
layout, subplots, spmap
end
# ----------------------------------------------------------------------
# @layout macro
function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
# dump(v)
# something like {0.2w}?
if v.head == :call && v.args[1] == :*
num = v.args[2]
if length(v.args) == 3 && isa(num, Number)
units = v.args[3]
if units == :h
return kw[:h] = num*pct
elseif units == :w
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)
# return kw[idx == 1 ? :w : :h] = v
end
end
end
error("Couldn't match layout curly (idx=$idx): $v")
end
function add_layout_pct!(kw::KW, 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)
end
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)
# cellsym = gensym(:cell)
# @show expr
if iscol(expr)
create_grid_vcat(expr)
# rowsizes = map(rowsize, expr.args)
# rmin, rmax = extrema(rowsizes)
# if rmin > 0 && rmin == rmax
# # we have a grid... build the whole thing
# # note: rmin is the number of columns
# nr = length(expr.args)
# nc = rmin
#
# :(let cell = GridLayout($nr, $nc)
# $([:(cell[$r,$c] = $(create_grid(expr.args[r], c))) for r=1:nr, c=1:nc]...)
# for r=1:nr
# layout = $(create_grid(expr.args[r])
# cell[r,]
# $([:($cellsym[$r,1] = $(create_grid(expr.args[r]))) for r=1:length(expr.args)]...)
# $cellsym
# end)
# else
# # otherwise just build one row at a time
# :(let
# $cellsym = GridLayout($(length(expr.args)), 1)
# $([:($cellsym[$i,1] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
# $cellsym
# end)
# end
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
# $cellsym = GridLayout(1, $(length(expr.args)))
# $([:($cellsym[1,$i] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
# $cellsym
# end)
elseif expr.head == :curly
create_grid_curly(expr)
else
# if it's something else, just return that (might be an existing layout?)
expr
end
end
function create_grid_vcat(expr::Expr)
rowsizes = map(rowsize, expr.args)
rmin, rmax = extrema(rowsizes)
# @show rmin, rmax
if rmin > 0 && rmin == rmax
# we have a grid... build the whole thing
# note: rmin is the number of columns
nr = length(expr.args)
nc = rmin
# @show nr, nc
body = Expr(:block)
for r=1:nr
arg = expr.args[r]
# @show r, arg
if isrow(arg)
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))))
end
end
# @show body
:(let cell = GridLayout($nr, $nc)
$body
cell
end)
# :(let cell = GridLayout($nr, $nc)
# $([:(cell[$r,$c] = $(create_grid(expr.args[r], c))) for r=1:nr, c=1:nc]...)
# for r=1:nr
# layout = $(create_grid(expr.args[r])
# cell[r,]
# $([:($cellsym[$r,1] = $(create_grid(expr.args[r]))) for r=1:length(expr.args)]...)
# $cellsym
# 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
# $cellsym = GridLayout($(length(expr.args)), 1)
# $([:($cellsym[$i,1] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
# $cellsym
# end)
end
end
function create_grid_curly(expr::Expr)
s = expr.args[1]
kw = KW()
for (i,arg) in enumerate(expr.args[2:end])
add_layout_pct!(kw, arg, i, length(expr.args)-1)
end
# @show kw
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
end
function create_grid(s::Symbol)
:(EmptyLayout(label = $(QuoteNode(s)), blank = $(s == :_)))
end
macro layout(mat::Expr)
create_grid(mat)
end
# -------------------------------------------------------------------------
# make all reference the same axis extrema/values
function link_axes!(axes::Axis...)
a1 = axes[1]
for i=2:length(axes)
a2 = axes[i]
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
a2[k] = a1[k]
end
end
end
# for some vector or matrix of layouts, filter only the Subplots and link those axes
function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
subplots = filter(l -> isa(l, Subplot), a)
axes = [sp.attr[axissym] for sp in subplots]
link_axes!(axes...)
end
# don't do anything for most layout types
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)
end
end
if link in (:y, :both)
for r=1:nr
link_axes!(layout.grid[r,:], :yaxis)
end
end
if link == :all
link_axes!(layout.grid, :xaxis)
link_axes!(layout.grid, :yaxis)
end
for l in layout.grid
link_axes!(l, link)
end
else
nc = ceil(Int, numplts / nr)
end
# if it's a perfect rectangle, just create a grid
if numplts == nr * nc
return GridLayout(nr, nc)
end
# create the rowcounts vector
i = 0
rowcounts = Int[]
for r in 1:nr
cnt = min(nc, numplts - i)
push!(rowcounts, cnt)
i += cnt
end
RowsLayout(numplts, rowcounts)
end
+108 -34
View File
@@ -1,49 +1,49 @@
defaultOutputFormat(plt::AbstractPlot) = "png"
defaultOutputFormat(plt::Plot) = "png"
function png(plt::AbstractPlot, fn::@compat(AbstractString))
function png(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "png")
io = open(fn, "w")
writemime(io, MIME("image/png"), plt)
close(io)
end
png(fn::@compat(AbstractString)) = png(current(), fn)
png(fn::AbstractString) = png(current(), fn)
function svg(plt::AbstractPlot, fn::@compat(AbstractString))
function svg(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "svg")
io = open(fn, "w")
writemime(io, MIME("image/svg+xml"), plt)
close(io)
end
svg(fn::@compat(AbstractString)) = svg(current(), fn)
svg(fn::AbstractString) = svg(current(), fn)
function pdf(plt::AbstractPlot, fn::@compat(AbstractString))
function pdf(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "pdf")
io = open(fn, "w")
writemime(io, MIME("application/pdf"), plt)
close(io)
end
pdf(fn::@compat(AbstractString)) = pdf(current(), fn)
pdf(fn::AbstractString) = pdf(current(), fn)
function ps(plt::AbstractPlot, fn::@compat(AbstractString))
function ps(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "ps")
io = open(fn, "w")
writemime(io, MIME("application/postscript"), plt)
close(io)
end
ps(fn::@compat(AbstractString)) = ps(current(), fn)
ps(fn::AbstractString) = ps(current(), fn)
function tex(plt::AbstractPlot, fn::@compat(AbstractString))
function tex(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "tex")
io = open(fn, "w")
writemime(io, MIME("application/x-tex"), plt)
close(io)
end
tex(fn::@compat(AbstractString)) = tex(current(), fn)
tex(fn::AbstractString) = tex(current(), fn)
# ----------------------------------------------------------------
@@ -57,7 +57,7 @@ tex(fn::@compat(AbstractString)) = tex(current(), fn)
"tex" => tex,
)
function getExtension(fn::@compat(AbstractString))
function getExtension(fn::AbstractString)
pieces = split(fn, ".")
length(pieces) > 1 || error("Can't extract file extension: ", fn)
ext = pieces[end]
@@ -65,7 +65,7 @@ function getExtension(fn::@compat(AbstractString))
ext
end
function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
function addExtension(fn::AbstractString, ext::AbstractString)
try
oldext = getExtension(fn)
if oldext == ext
@@ -78,7 +78,7 @@ function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
end
end
function savefig(plt::AbstractPlot, fn::@compat(AbstractString))
function savefig(plt::Plot, fn::AbstractString)
# get the extension
local ext
@@ -96,28 +96,102 @@ function savefig(plt::AbstractPlot, fn::@compat(AbstractString))
end
func(plt, fn)
end
savefig(fn::@compat(AbstractString)) = savefig(current(), fn)
# savepng(args...; kw...) = savepng(current(), args...; kw...)
# savepng(plt::AbstractPlot, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
savefig(fn::AbstractString) = savefig(current(), fn)
# ---------------------------------------------------------
gui(plt::AbstractPlot = current()) = display(PlotsDisplay(), plt)
gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
# override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::AbstractPlot) = gui(plt)
# a backup for html... passes to svg
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot)
writemime(io, MIME("image/svg+xml"), plt)
function Base.display(::PlotsDisplay, plt::Plot)
prepare_output(plt)
_display(plt)
end
# override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
# ---------------------------------------------------------
const _mimeformats = Dict(
"application/eps" => "eps",
"image/eps" => "eps",
"application/pdf" => "pdf",
"image/png" => "png",
"application/postscript" => "ps",
"image/svg+xml" => "svg"
)
const _best_html_output_type = KW(
:pyplot => :png,
)
# a backup for html... passes to svg or png depending on the html_output_format arg
function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
output_type = Symbol(plt.attr[:html_output_format])
if output_type == :auto
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
end
if output_type == :png
# info("writing png to html output")
print(io, "<img src=\"data:image/png;base64,", base64encode(writemime, MIME("image/png"), plt), "\" />")
elseif output_type == :svg
# info("writing svg to html output")
writemime(io, MIME("image/svg+xml"), plt)
else
error("only png or svg allowed. got: $output_type")
end
end
# for writing to io streams... first prepare, then callback
for mime in keys(_mimeformats)
@eval function Base.writemime(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
prepare_output(plt)
_writemime(io, m, plt)
end
end
# function html_output_format(fmt)
# if fmt == "png"
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
# print(io, "<img src=\"data:image/png;base64,", base64(writemime, MIME("image/png"), plt), "\" />")
# end
# elseif fmt == "svg"
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
# writemime(io, MIME("image/svg+xml"), plt)
# end
# else
# error("only png or svg allowed. got: $fmt")
# end
# end
#
# html_output_format("svg")
# ---------------------------------------------------------
# IJulia
# ---------------------------------------------------------
const _ijulia_output = Compat.ASCIIString["text/html"]
function setup_ijulia()
# override IJulia inline display
if isijulia()
@eval begin
import IJulia
export set_ijulia_output
function set_ijulia_output(mimestr::AbstractString)
# info("Setting IJulia output format to $mimestr")
global _ijulia_output
_ijulia_output[1] = mimestr
end
function IJulia.display_dict(plt::Plot)
global _ijulia_output
Dict{Compat.ASCIIString, ByteString}(_ijulia_output[1] => sprint(writemime, _ijulia_output[1], plt))
end
end
set_ijulia_output("text/html")
end
end
# ---------------------------------------------------------
# Atom PlotPane
@@ -125,21 +199,21 @@ end
function setup_atom()
# @require Atom begin
if isatom()
if isatom() && get(ENV, "PLOTS_USE_ATOM_PLOTPANE", false) in (true, 1, "1", "true", "yes")
# @eval import Atom, Media
@eval import Atom
# connects the render function
for T in (GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend)
Atom.Media.media(AbstractPlot{T}, Atom.Media.Plot)
Atom.Media.media(Plot{T}, Atom.Media.Plot)
end
# Atom.Media.media{T <: Union{GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend}}(Plot{T}, Atom.Media.Plot)
# Atom.displaysize(::AbstractPlot) = (535, 379)
# Atom.displaytitle(plt::AbstractPlot) = "Plots.jl (backend: $(backend(plt)))"
# Atom.displaysize(::Plot) = (535, 379)
# Atom.displaytitle(plt::Plot) = "Plots.jl (backend: $(backend(plt)))"
# this is like "display"... sends an html div with the plot to the PlotPane
function Atom.Media.render(pane::Atom.PlotPane, plt::AbstractPlot)
function Atom.Media.render(pane::Atom.PlotPane, plt::Plot)
Atom.Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
end
+404 -168
View File
@@ -22,7 +22,7 @@ Base.print(io::IO, plt::Plot) = print(io, string(plt))
Base.show(io::IO, plt::Plot) = print(io, string(plt))
getplot(plt::Plot) = plt
getplotargs(plt::Plot, idx::Int = 1) = plt.plotargs
getattr(plt::Plot, idx::Int = 1) = plt.attr
convertSeriesIndex(plt::Plot, n::Int) = n
# ---------------------------------------------------------
@@ -43,17 +43,71 @@ When you pass in matrices, it splits by columns. See the documentation for more
# this creates a new plot with args/kw and sets it to be the current plot
function plot(args...; kw...)
pkg = backend()
d = KW(kw)
preprocessArgs!(d)
dumpdict(d, "After plot preprocessing")
plotargs = merge(d, getPlotArgs(pkg, d, 1))
dumpdict(plotargs, "Plot args")
plt = _create_plot(pkg, plotargs) # create a new, blank plot
# create an empty Plot then process
plt = Plot()
# plt.user_attr = d
_plot!(plt, d, args...)
end
delete!(d, :background_color)
plot!(plt, args...; d...) # add to it
# build a new plot from existing plots
# note: we split into plt1 and plts_tail so we can dispatch correctly
function plot(plt1::Plot, plts_tail::Plot...; kw...)
d = KW(kw)
preprocessArgs!(d)
# build our plot vector from the args
n = length(plts_tail) + 1
plts = Array(Plot, n)
plts[1] = plt1
for (i,plt) in enumerate(plts_tail)
plts[i+1] = plt
end
# compute the layout
layout = layout_args(d, n)[1]
num_sp = sum([length(p.subplots) for p in plts])
# create a new plot object, with subplot list/map made of existing subplots.
# note: we create a new backend figure for this new plot object
# note: all subplots and series "belong" to this new plot...
plt = Plot()
# TODO: build the user_attr dict by creating "Any matrices" for the args of each subplot
# TODO: replace this with proper processing from a merged user_attr KW
# update plot args, first with existing plots, then override with d
for p in plts
_update_plot_args(plt, p.attr)
plt.n += p.n
end
_update_plot_args(plt, d)
# pass new plot to the backend
plt.o = _create_backend_figure(plt)
plt.init = true
# create the layout and initialize the subplots
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
for (idx, sp) in enumerate(plt.subplots)
_initialize_subplot(plt, sp)
serieslist = series_list(sp)
sp.plt = plt
sp.attr[:subplot_index] = idx
for series in serieslist
push!(plt.series_list, series)
_series_added(plt, series)
end
end
# finish up
current(plt)
if get(d, :show, default(:show))
gui()
end
plt
end
@@ -73,206 +127,388 @@ end
function plot!(plt::Plot, args...; kw...)
d = KW(kw)
preprocessArgs!(d)
# merge!(plt.user_attr, d)
_plot!(plt, d, args...)
end
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; kw...)
function strip_first_letter(s::Symbol)
str = string(s)
str[1:1], Symbol(str[2:end])
end
dumpdict(d, "After plot! preprocessing")
warnOnUnsupportedArgs(plt.backend, d)
# just in case the backend needs to set up the plot (make it current or something)
_before_add_series(plt)
# this method recursively applies series recipes when the seriestype is not supported
# natively by the backend
function _apply_series_recipe(plt::Plot, d::KW)
st = d[:seriestype]
# @show st
if st in supportedTypes()
# getting ready to add the series... last update to subplot from anything
# that might have been added during series recipes
sp = d[:subplot]
sp_idx = get_subplot_index(plt, sp)
_update_subplot_args(plt, sp, d, sp_idx)
# change to a 3d projection for this subplot?
if is3d(st)
sp.attr[:projection] = "3d"
end
# initialize now that we know the first series type
if !haskey(sp.attr, :init)
_initialize_subplot(plt, sp)
sp.attr[:init] = true
end
# adjust extrema and discrete info
if st == :image
w, h = size(d[:z])
expand_extrema!(sp[:xaxis], (0,w))
expand_extrema!(sp[:yaxis], (0,h))
sp[:yaxis].d[:flip] = true
elseif !(st in (:pie, :histogram, :histogram2d))
expand_extrema!(sp, d)
end
# add the series!
warnOnUnsupportedArgs(plt.backend, d)
warnOnUnsupported(plt.backend, d)
series = Series(d)
push!(plt.series_list, series)
# @show series
_series_added(plt, series)
# # grouping
groupby = if haskey(d, :group)
extractGroupArgs(d[:group], args...)
else
nothing
end
# get a sub list of series for this seriestype
datalist = try
RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
catch
warn("Exception during apply_recipe(Val{$st}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))")
rethrow()
end
# merge plot args
if !haskey(d, :subplot)
for k in keys(_plotDefaults)
if haskey(d, k)
plt.plotargs[k] = d[k]
# assuming there was no error, recursively apply the series recipes
for data in datalist
if isa(data, RecipeData)
_apply_series_recipe(plt, data.d)
else
warn("Unhandled recipe: $(data)")
break
end
end
# merge!(plt.plotargs, d)
handlePlotColors(plt.backend, plt.plotargs)
end
end
# this is the core plotting function. recursively apply recipes to build
# a list of series KW dicts.
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
function _plot!(plt::Plot, d::KW, args...)
# d = plt.user_attr
d[:plot_object] = plt
# the grouping mechanism is a recipe on a GroupBy object
# we simply add the GroupBy object to the front of the args list to allow
# the recipe to be applied
if haskey(d, :group)
args = (extractGroupArgs(d[:group], args...), args...)
end
_add_series(plt, d, groupby, args...)
_add_annotations(plt, d)
warnOnUnsupportedScales(plt.backend, d)
# for plotting recipes, swap out the args and update the parameter dictionary
# we are keeping a queue of series that still need to be processed.
# each pass through the loop, we pop one off and apply the recipe.
# the recipe will return a list a Series objects... the ones that are
# finished (no more args) get added to the kw_list, and the rest go into the queue
# for processing.
kw_list = KW[]
still_to_process = isempty(args) ? [] : [RecipeData(copy(d), args)]
while !isempty(still_to_process)
# grab the first in line to be processed and pass it through apply_recipe
# to generate a list of RecipeData objects (data + attributes)
next_series = shift!(still_to_process)
for recipedata in RecipesBase.apply_recipe(next_series.d, next_series.args...)
# add title, axis labels, ticks, etc
if !haskey(d, :subplot)
# merge!(plt.plotargs, d)
# handlePlotColors(plt.backend, plt.plotargs)
dumpdict(plt.plotargs, "Updating plot items")
_update_plot(plt, plt.plotargs)
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
if !(typeof(recipedata) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $recipedata")
end
if isempty(recipedata.args)
# when the arg tuple is empty, that means there's nothing left to recursively
# process... finish up and add to the kw_list
kw = recipedata.d
_add_markershape(kw)
# if there was a grouping, filter the data here
_filter_input_data!(kw)
# map marker_z if it's a Function
if isa(get(kw, :marker_z, nothing), Function)
# TODO: should this take y and/or z as arguments?
kw[:marker_z] = map(kw[:marker_z], kw[:x])
end
# convert a ribbon into a fillrange
if get(kw, :ribbon, nothing) != nothing
make_fillrange_from_ribbon(kw)
end
# add the plot index
plt.n += 1
kw[:series_plotindex] = plt.n
# check that the backend will support the command and add it to the list
warnOnUnsupportedScales(plt.backend, kw)
push!(kw_list, kw)
# handle error bars by creating new recipedata data... these will have
# the same recipedata index as the recipedata they are copied from
for esym in (:xerror, :yerror)
if get(d, esym, nothing) != nothing
# we make a copy of the KW and apply an errorbar recipe
errkw = copy(kw)
errkw[:seriestype] = esym
errkw[:label] = ""
errkw[:primary] = false
push!(kw_list, errkw)
end
end
# handle smoothing by adding a new series
if get(d, :smooth, false)
x, y = kw[:x], kw[:y]
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
sx = [minimum(x), maximum(x)]
sy = β * sx + α
push!(kw_list, merge(copy(kw), KW(
:seriestype => :path,
:x => sx,
:y => sy,
:fillrange => nothing,
:label => "",
:primary => false,
)))
# don't allow something else to handle it
d[:smooth] = false
end
else
# args are non-empty, so there's still processing to do... add it back to the queue
push!(still_to_process, recipedata)
end
end
end
_update_plot_pos_size(plt, d)
# merge in anything meant for plot/subplot
for kw in kw_list
for (k,v) in kw
for defdict in (_plot_defaults, _subplot_defaults)
if haskey(defdict, k)
d[k] = pop!(kw, k)
end
end
# if haskey(_plot_defaults, k) || haskey(_subplot_defaults, k)
# d[k] = v
# end
end
end
# TODO: init subplots here
_update_plot_args(plt, d)
if !plt.init
plt.o = _create_backend_figure(plt)
# create the layout and subplots from the inputs
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
for (idx,sp) in enumerate(plt.subplots)
sp.plt = plt
sp.attr[:subplot_index] = idx
end
plt.init = true
end
# just in case the backend needs to set up the plot (make it current or something)
_prepare_plot_object(plt)
# first apply any args for the subplots
for (idx,sp) in enumerate(plt.subplots)
_update_subplot_args(plt, sp, d, idx, remove_pair = false)
end
# do we need to link any axes together?
link_axes!(plt.layout, plt[:link])
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
# this is it folks!
# TODO: we probably shouldn't use i for tracking series index, but rather explicitly track it in recipes
for (i,kw) in enumerate(kw_list)
# if !(get(kw, :seriestype, :none) in (:xerror, :yerror))
# plt.n += 1
# end
command_idx = kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
# get the Subplot object to which the series belongs
sp = get(kw, :subplot, :auto)
sp = if sp == :auto
mod1(i,length(plt.subplots))
else
slice_arg(sp, i)
end
sp = kw[:subplot] = get_subplot(plt, sp)
idx = get_subplot_index(plt, sp)
# strip out series annotations (those which are based on series x/y coords)
# and add them to the subplot attr
sp_anns = annotations(sp[:annotations])
anns = annotations(pop!(kw, :series_annotations, []))
if length(anns) > 0
x, y = kw[:x], kw[:y]
nx, ny, na = map(length, (x,y,anns))
n = max(nx, ny, na)
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
end
sp.attr[:annotations] = vcat(sp_anns, anns)
# we update subplot args in case something like the color palatte is part of the recipe
_update_subplot_args(plt, sp, kw, idx)
# set default values, select from attribute cycles, and generally set the final attributes
_add_defaults!(kw, plt, sp, command_idx)
# now we have a fully specified series, with colors chosen. we must recursively handle
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
# we check for a recipe that will convert that series type into one made up of lower-level components.
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
# be able to support step, bar, and histogram plots (and any recipes that use those components).
_apply_series_recipe(plt, kw)
end
# # everything is processed, time to compute the layout bounding boxes
# _before_layout_calcs(plt)
# w, h = plt.attr[:size]
# plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
# update_child_bboxes!(plt.layout)
#
# # TODO just need to pass plt... and we should do all non-series updates here
# _update_plot_object(plt)
current(plt)
# note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
# # do we want to show it?
if haskey(d, :show) && d[:show]
# if haskey(d, :show) && d[:show]
if get(d, :show, default(:show))
gui()
end
plt
end
# handle the grouping
function _add_series(plt::Plot, d::KW, groupby::GroupBy, args...)
starting_n = plt.n
for (i, glab) in enumerate(groupby.groupLabels)
tmpd = copy(d)
tmpd[:numUncounted] = plt.n - starting_n
_add_series(plt, tmpd, nothing, args...;
idxfilter = groupby.groupIds[i],
grouplabel = string(glab))
function _replace_linewidth(d::KW)
# get a good default linewidth... 0 for surface and heatmaps
if get(d, :linewidth, :auto) == :auto
d[:linewidth] = (get(d, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
end
end
filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
filter_data(v, idxfilter) = v
# 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)
function filter_data!(d::KW, idxfilter)
for s in (:x, :y, :z)
d[s] = filter_data(get(d, s, nothing), idxfilter)
end
w, h = plt.attr[:size]
plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
update_child_bboxes!(plt.layout)
_update_plot_object(plt)
end
# no grouping
function _add_series(plt::Plot, d::KW, ::Void, args...;
idxfilter = nothing,
grouplabel = "")
# get the list of dictionaries, one per series
dumpdict(d, "before process_inputs")
process_inputs(plt, d, args...)
dumpdict(d, "after process_inputs")
if idxfilter != nothing
# add the group name as the label if there isn't one passed in
get!(d, :label, grouplabel)
# filter the data
filter_data!(d, idxfilter)
end
seriesArgList, xmeta, ymeta = build_series_args(plt, d) #, idxfilter)
# seriesArgList, xmeta, ymeta = build_series_args(plt, groupargs..., args...; d...)
# if we were able to extract guide information from the series inputs, then update the plot
# @show xmeta, ymeta
updateDictWithMeta(d, plt.plotargs, xmeta, true)
updateDictWithMeta(d, plt.plotargs, ymeta, false)
# now we can plot the series
for (i,di) in enumerate(seriesArgList)
plt.n += 1
if !stringsSupported() && di[:linetype] != :pie
setTicksFromStringVector(d, di, :x, :xticks)
setTicksFromStringVector(d, di, :y, :yticks)
setTicksFromStringVector(d, di, :z, :zticks)
end
# remove plot args
for k in keys(_plotDefaults)
delete!(di, k)
end
# merge in plotarg_overrides
plotarg_overrides = pop!(di, :plotarg_overrides, nothing)
if plotarg_overrides != nothing
merge!(plt.plotargs, plotarg_overrides)
end
# dumpdict(plt.plotargs, "pargs", true)
dumpdict(di, "Series $i")
_add_series(plt.backend, plt, di)
end
function prepared_object(plt::Plot)
prepare_output(plt)
plt.o
end
# --------------------------------------------------------------------
# if x or y are a vector of strings, we should create a list of unique strings,
# and map x/y to be the index of the string... then set the x/y tick labels
function setTicksFromStringVector(d::KW, di::KW, sym::Symbol, ticksym::Symbol)
# if the x or y values are strings, set ticks to the unique values, and x/y to the indices of the ticks
# function get_indices(orig, labels)
# Int[findnext(labels, l, 1) for l in orig]
# end
v = di[sym]
isa(v, AbstractArray) || return
T = eltype(v)
if T <: @compat(AbstractString) || (!isempty(T.types) && all(x -> x <: @compat(AbstractString), T.types))
ticks = unique(di[sym])
di[sym] = Int[findnext(ticks, v, 1) for v in di[sym]]
if !haskey(d, ticksym) || d[ticksym] == :auto
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
end
end
end
# --------------------------------------------------------------------
_before_add_series(plt::Plot) = nothing
# --------------------------------------------------------------------
# should we update the x/y label given the meta info during input slicing?
function updateDictWithMeta(d::KW, plotargs::KW, meta::Symbol, isx::Bool)
lsym = isx ? :xlabel : :ylabel
if plotargs[lsym] == default(lsym)
d[lsym] = string(meta)
end
end
updateDictWithMeta(d::KW, plotargs::KW, meta, isx::Bool) = nothing
# --------------------------------------------------------------------
annotations(::@compat(Void)) = []
annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v
annotations{X,Y,V}(t::@compat(Tuple{X,Y,V})) = [t]
annotations(v::AVec{PlotText}) = v
annotations(v::AVec) = map(PlotText, v)
annotations(anns) = error("Expecting a tuple (or vector of tuples) for annotations: ",
"(x, y, annotation)\n got: $(typeof(anns))")
function _add_annotations(plt::Plot, d::KW)
anns = annotations(get(d, :annotation, nothing))
if !isempty(anns)
# if we just have a list of PlotText objects, then create (x,y,text) tuples
if typeof(anns) <: AVec{PlotText}
x, y = plt[plt.n]
anns = Tuple{Float64,Float64,PlotText}[(x[i], y[i], t) for (i,t) in enumerate(anns)]
end
_add_annotations(plt, anns)
end
end
# # TODO: remove?? this is the old way of handling discrete data... should be
# # replaced by the Axis type and logic
# function setTicksFromStringVector(plt::Plot, d::KW, di::KW, letter)
# sym = Symbol(letter)
# ticksym = Symbol(letter * "ticks")
# pargs = plt.attr
# v = di[sym]
#
# # do we really want to do this?
# typeof(v) <: AbstractArray || return
# isempty(v) && return
# trueOrAllTrue(_ -> typeof(_) <: AbstractString, v) || return
#
# # compute the ticks and labels
# ticks, labels = if ticksType(pargs[ticksym]) == :ticks_and_labels
# # extend the existing ticks and labels. only add to labels if they're new!
# ticks, labels = pargs[ticksym]
# newlabels = filter(_ -> !(_ in labels), unique(v))
# newticks = if isempty(ticks)
# collect(1:length(newlabels))
# else
# maximum(ticks) + collect(1:length(newlabels))
# end
# ticks = vcat(ticks, newticks)
# labels = vcat(labels, newlabels)
# ticks, labels
# else
# # create new ticks and labels
# newlabels = unique(v)
# collect(1:length(newlabels)), newlabels
# end
#
# d[ticksym] = ticks, labels
# plt.attr[ticksym] = ticks, labels
#
# # add an origsym field so that later on we can re-compute the x vector if ticks change
# origsym = Symbol(letter * "orig")
# di[origsym] = v
# di[sym] = get_indices(v, labels)
#
# # loop through existing plt.seriesargs and adjust indices if there is an origsym key
# for sargs in plt.seriesargs
# if haskey(sargs, origsym)
# # TODO: might need to call the setindex function instead to trigger a plot update for some backends??
# sargs[sym] = get_indices(sargs[origsym], labels)
# end
# end
# end
# --------------------------------------------------------------------
function Base.copy(plt::Plot)
backend(plt.backend)
plt2 = plot(; plt.plotargs...)
for sargs in plt.seriesargs
sargs = filter((k,v) -> haskey(_seriesDefaults,k), sargs)
plot!(plt2; sargs...)
end
plt2
end
# --------------------------------------------------------------------
# function Base.copy(plt::Plot)
# backend(plt.backend)
# plt2 = plot(; plt.attr...)
# for sargs in plt.seriesargs
# sargs = filter((k,v) -> haskey(_series_defaults,k), sargs)
# plot!(plt2; sargs...)
# end
# plt2
# end
# --------------------------------------------------------------------
+687 -153
View File
@@ -5,6 +5,59 @@
# a recipe should fully describe the plotting command(s) and call them, likewise for updating.
# actually... maybe those should explicitly derive from AbstractPlot???
"""
You can easily define your own plotting recipes with convenience methods:
```
@userplot type GroupHist
args
end
@recipe function f(gh::GroupHist)
# set some attributes, add some series, using gh.args as input
end
# now you can plot like:
grouphist(rand(1000,4))
```
"""
macro userplot(expr)
_userplot(expr)
end
function _userplot(expr::Expr)
if expr.head != :type
errror("Must call userplot on a type/immutable expression. Got: $expr")
end
typename = expr.args[2]
funcname = Symbol(lowercase(string(typename)))
funcname2 = Symbol(funcname, "!")
# return a code block with the type definition and convenience plotting methods
esc(quote
$expr
export $funcname, $funcname2
$funcname(args...; kw...) = plot($typename(args); kw...)
$funcname2(args...; kw...) = plot!($typename(args); kw...)
end)
end
function _userplot(sym::Symbol)
_userplot(:(type $sym
args
end))
end
# ----------------------------------------------------------------------------------
# ----------------------------------------------------------------------------------
abstract PlotRecipe
getRecipeXY(recipe::PlotRecipe) = Float64[], Float64[]
@@ -17,57 +70,452 @@ plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)
num_series(x::AMat) = size(x,2)
num_series(x) = 1
_apply_recipe(d::KW; kw...) = ()
# if it's not a recipe, just do nothing and return the args
function _apply_recipe(d::KW, args...; issubplot=false, kw...)
if issubplot && !haskey(d, :n) && !haskey(d, :layout)
# put in a sensible default
d[:n] = maximum(map(num_series, args))
# # if it's not a recipe, just do nothing and return the args
# function RecipesBase.apply_recipe(d::KW, args...; issubplot=false)
# if issubplot && !isempty(args) && !haskey(d, :n) && !haskey(d, :layout)
# # put in a sensible default
# d[:n] = maximum(map(num_series, args))
# end
# args
# end
if is_installed("DataFrames")
@eval begin
import DataFrames
DFS = Union{Symbol, AbstractArray{Symbol}}
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS)
if isa(dfs, Symbol)
get!(d, Symbol(letter * "guide"), string(dfs))
collect(df[dfs])
else
get!(d, :label, reshape(dfs, 1, length(dfs)))
Any[collect(df[s]) for s in dfs]
end
end
function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...)
extractGroupArgs(collect(df[group]))
end
function handle_group(df::DataFrames.AbstractDataFrame, d::KW)
if haskey(d, :group)
g = d[:group]
if isa(g, Symbol)
d[:group] = collect(df[g])
end
end
end
@recipe function f(df::DataFrames.AbstractDataFrame, sy::DFS)
handle_group(df, d)
handle_dfs(df, d, "y", sy)
end
@recipe function f(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS)
handle_group(df, d)
x = handle_dfs(df, d, "x", sx)
y = handle_dfs(df, d, "y", sy)
x, y
end
@recipe function f(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS)
handle_group(df, d)
x = handle_dfs(df, d, "x", sx)
y = handle_dfs(df, d, "y", sy)
z = handle_dfs(df, d, "z", sz)
x, y, z
end
end
args
end
# ---------------------------------------------------------------------------
"""
`apply_series_recipe` should take a processed series KW dict and break it up
into component parts. For example, a box plot is made up of `shape` for the
boxes, `path` for the lines, and `scatter` for the outliers.
# """
# `apply_series_recipe` should take a processed series KW dict and break it up
# into component parts. For example, a box plot is made up of `shape` for the
# boxes, `path` for the lines, and `scatter` for the outliers.
#
# Returns a Vector{KW}.
# """
# apply_series_recipe(d::KW, st) = KW[d]
Returns a Vector{KW}.
"""
apply_series_recipe(d::KW, lt) = KW[d]
# for seriestype `line`, need to sort by x values
@recipe function f(::Type{Val{:line}}, x, y, z)
indices = sortperm(x)
x := x[indices]
y := y[indices]
if typeof(z) <: AVec
z := z[indices]
end
seriestype := :path
()
end
@recipe function f(::Type{Val{:sticks}}, x, y, z)
nx = length(x)
n = 3nx
newx, newy = zeros(n), zeros(n)
for i=1:nx
rng = 3i-2:3i
newx[rng] = x[i]
newy[rng] = [0., y[i], 0.]
end
x := newx
y := newy
seriestype := :path
()
end
@recipe function f(::Type{Val{:hline}}, x, y, z)
xmin, xmax = axis_limits(d[:subplot][:xaxis])
n = length(y)
newx = repmat(Float64[xmin, xmax, NaN], n)
newy = vec(Float64[yi for i=1:3,yi=y])
x := newx
y := newy
seriestype := :path
()
end
@recipe function f(::Type{Val{:vline}}, x, y, z)
ymin, ymax = axis_limits(d[:subplot][:yaxis])
n = length(y)
newx = vec(Float64[yi for i=1:3,yi=y])
newy = repmat(Float64[ymin, ymax, NaN], n)
x := newx
y := newy
seriestype := :path
()
end
# ---------------------------------------------------------------------------
# steps
function make_steps(x, y, st)
n = length(x)
newx, newy = zeros(2n-1), zeros(2n-1)
for i=1:n
idx = 2i-1
newx[idx] = x[i]
newy[idx] = y[i]
if i > 1
newx[idx-1] = x[st == :steppre ? i-1 : i]
newy[idx-1] = y[st == :steppre ? i : i-1]
end
end
newx, newy
end
# create a path from steps
@recipe function f(::Type{Val{:steppre}}, x, y, z)
d[:x], d[:y] = make_steps(x, y, :steppre)
seriestype := :path
# create a secondary series for the markers
if d[:markershape] != :none
@series begin
seriestype := :scatter
x := x
y := y
label := ""
primary := false
()
end
markershape := :none
end
()
end
# create a path from steps
@recipe function f(::Type{Val{:steppost}}, x, y, z)
d[:x], d[:y] = make_steps(x, y, :steppost)
seriestype := :path
# create a secondary series for the markers
if d[:markershape] != :none
@series begin
seriestype := :scatter
x := x
y := y
label := ""
primary := false
()
end
markershape := :none
end
()
end
# ---------------------------------------------------------------------------
# sticks
sticks_fillfrom(fr::Void, i::Integer) = 0.0
sticks_fillfrom(fr::Number, i::Integer) = fr
sticks_fillfrom(fr::AVec, i::Integer) = fr[mod1(i, length(fr))]
# create vertical line segments from fill
@recipe function f(::Type{Val{:sticks}}, x, y, z)
n = length(x)
fr = d[:fillrange]
newx, newy = zeros(3n), zeros(3n)
for i=1:n
rng = 3i-2:3i
newx[rng] = [x[i], x[i], NaN]
newy[rng] = [sticks_fillfrom(fr,i), y[i], NaN]
end
x := newx
y := newy
fillrange := nothing
seriestype := :path
# create a secondary series for the markers
if d[:markershape] != :none
@series begin
seriestype := :scatter
x := x
y := y
label := ""
primary := false
()
end
markershape := :none
end
()
end
# ---------------------------------------------------------------------------
# create a bar plot as a filled step function
@recipe function f(::Type{Val{:bar}}, x, y, z)
nx, ny = length(x), length(y)
edges = if nx == ny
# x is centers, calc the edges
# TODO: use bar_width, etc
midpoints = x
halfwidths = diff(midpoints) * 0.5
Float64[if i == 1
midpoints[1] - halfwidths[1]
elseif i == ny+1
midpoints[i-1] + halfwidths[i-2]
else
midpoints[i-1] + halfwidths[i-1]
end for i=1:ny+1]
elseif nx == ny + 1
# x is edges
x
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))")
end
# make fillto a vector... default fills to 0
fillto = d[:fillrange]
if fillto == nothing
fillto = zeros(1)
elseif isa(fillto, Number)
fillto = Float64[fillto]
end
nf = length(fillto)
npts = 3ny + 1
heights = y
x = zeros(npts)
y = zeros(npts)
fillrng = zeros(npts)
# create the path in triplets. after the first bottom-left coord of the first bar:
# add the top-left, top-right, and bottom-right coords for each height
x[1] = edges[1]
y[1] = fillto[1]
fillrng[1] = fillto[1]
for i=1:ny
idx = 3i
rng = idx-1:idx+1
fi = fillto[mod1(i,nf)]
x[rng] = [edges[i], edges[i+1], edges[i+1]]
y[rng] = [heights[i], heights[i], fi]
fillrng[rng] = [fi, fi, fi]
end
x := x
y := y
fillrange := fillrng
seriestype := :path
()
end
# ---------------------------------------------------------------------------
# Histograms
# edges from number of bins
function calc_edges(v, bins::Integer)
vmin, vmax = extrema(v)
linspace(vmin, vmax, bins+1)
end
# just pass through arrays
calc_edges(v, bins::AVec) = v
# find the bucket index of this value
function bucket_index(vi, edges)
for (i,e) in enumerate(edges)
if vi <= e
return max(1,i-1)
end
end
return length(edges)-1
end
function my_hist(v, bins; normed = false, weights = nothing)
edges = calc_edges(v, bins)
counts = zeros(length(edges)-1)
# add a weighted count
for (i,vi) in enumerate(v)
idx = bucket_index(vi, edges)
counts[idx] += (weights == nothing ? 1.0 : weights[i])
end
# normalize by bar area?
norm_denom = normed ? sum(diff(edges) .* counts) : 1.0
if norm_denom == 0
norm_denom = 1.0
end
edges, counts ./ norm_denom
end
@recipe function f(::Type{Val{:histogram}}, x, y, z)
edges, counts = my_hist(y, d[:bins],
normed = d[:normalize],
weights = d[:weights])
x := edges
y := counts
seriestype := :bar
()
end
# ---------------------------------------------------------------------------
# Histogram 2D
# if tuple, map out bins, otherwise use the same for both
calc_edges_2d(x, y, bins) = calc_edges(x, bins), calc_edges(y, bins)
calc_edges_2d{X,Y}(x, y, bins::Tuple{X,Y}) = calc_edges(x, bins[1]), calc_edges(y, bins[2])
# the 2D version
function my_hist_2d(x, y, bins; normed = false, weights = nothing)
xedges, yedges = calc_edges_2d(x, y, bins)
counts = zeros(length(yedges)-1, length(xedges)-1)
# add a weighted count
for i=1:length(x)
r = bucket_index(y[i], yedges)
c = bucket_index(x[i], xedges)
counts[r,c] += (weights == nothing ? 1.0 : weights[i])
end
# normalize to cubic area of the imaginary surface towers
norm_denom = normed ? sum((diff(yedges) * diff(xedges)') .* counts) : 1.0
if norm_denom == 0
norm_denom = 1.0
end
xedges, yedges, counts ./ norm_denom
end
centers(v::AVec) = v[1] + cumsum(diff(v))
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
normed = d[:normalize],
weights = d[:weights])
x := centers(xedges)
y := centers(yedges)
z := Surface(counts)
seriestype := :heatmap
()
end
# ---------------------------------------------------------------------------
# Box Plot
const _box_halfwidth = 0.4
function apply_series_recipe(d::KW, ::Type{Val{:box}})
# dumpdict(d, "box before", true)
# TODO: add scatter series with outliers
notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
# function apply_series_recipe(d::KW, ::Type{Val{:box}})
@recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
# Plots.dumpdict(d, "box before", true)
# create a list of shapes, where each shape is a single boxplot
shapes = Shape[]
d[:linetype] = :shape
groupby = extractGroupArgs(d[:x])
groupby = extractGroupArgs(x)
outliers_y = Float64[]
outliers_x = Float64[]
warning = false
for (i, glabel) in enumerate(groupby.groupLabels)
# filter y values, then compute quantiles
q1,q2,q3,q4,q5 = quantile(d[:y][groupby.groupIds[i]], linspace(0,1,5))
# filter y values
values = d[:y][groupby.groupIds[i]]
# then compute quantiles
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
# notch
n = notch_width(q2, q4, length(values))
if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
warn("Boxplot's notch went outside hinges. Set notch to false.")
warning = true # Show the warning only one time
end
# make the shape
l, m, r = i - _box_halfwidth, i, i + _box_halfwidth
xcoords = [
center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
l, m, r = center - _box_halfwidth, center, center + _box_halfwidth
# internal nodes for notches
L, R = center - 0.5 * _box_halfwidth, center + 0.5 * _box_halfwidth
# outliers
if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
limit = range*(q4-q2)
inside = Float64[]
for value in values
if (value < (q2 - limit)) || (value > (q4 + limit))
push!(outliers_y, value)
push!(outliers_x, center)
else
push!(inside, value)
end
end
# change q1 and q5 to show outliers
# using maximum and minimum values inside the limits
q1, q5 = extrema(inside)
end
# Box
xcoords = notch::Bool ? [
m, l, r, m, m, NaN, # lower T
l, l, L, R, r, r, l, NaN, # lower box
l, l, L, R, r, r, l, NaN, # upper box
m, l, r, m, m, NaN, # upper T
] : [
m, l, r, m, m, NaN, # lower T
l, l, r, r, l, NaN, # lower box
l, l, r, r, l, NaN, # upper box
m, l, r, m, m # upper T
m, l, r, m, m, NaN, # upper T
]
ycoords = [
ycoords = notch::Bool ? [
q1, q1, q1, q1, q2, NaN, # lower T
q2, q3-n, q3, q3, q3-n, q2, q2, NaN, # lower box
q4, q3+n, q3, q3, q3+n, q4, q4, NaN, # upper box
q5, q5, q5, q5, q4, NaN, # upper T
] : [
q1, q1, q1, q1, q2, NaN, # lower T
q2, q3, q3, q2, q2, NaN, # lower box
q4, q3, q3, q4, q4, NaN, # upper box
@@ -76,10 +524,33 @@ function apply_series_recipe(d::KW, ::Type{Val{:box}})
push!(shapes, Shape(xcoords, ycoords))
end
d[:x], d[:y] = shape_coords(shapes)
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
KW[d]
seriestype := :shape
# n = length(groupby.groupLabels)
# xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
# clean d
pop!(d, :notch)
pop!(d, :range)
# we want to set the fields directly inside series recipes... args are ignored
d[:x], d[:y] = Plots.shape_coords(shapes)
# Outliers
@series begin
seriestype := :scatter
markershape := :ellipse
x := outliers_x
y := outliers_y
label := ""
primary := false
()
end
() # expects a tuple returned
# KW[d]
end
# ---------------------------------------------------------------------------
@@ -87,19 +558,15 @@ end
# if the user has KernelDensity installed, use this for violin plots.
# otherwise, just use a histogram
try
Pkg.installed("KernelDensity")
import KernelDensity
# warn("using KD for violin")
@eval function violin_coords(y)
kd = KernelDensity.kde(y, npoints = 30)
if is_installed("KernelDensity")
@eval import KernelDensity
@eval function violin_coords(y, bins = 30)
kd = KernelDensity.kde(y, npoints = isa(bins, Integer) ? bins : 30)
kd.density, kd.x
end
catch
# warn("using hist for violin")
@eval function violin_coords(y)
edges, widths = hist(y, 20)
else
@eval function violin_coords(y, bins = 30)
edges, widths = hist(y, isa(bins, Integer) ? bins : 30)
centers = 0.5 * (edges[1:end-1] + edges[2:end])
ymin, ymax = extrema(y)
vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
@@ -107,13 +574,13 @@ catch
end
function apply_series_recipe(d::KW, ::Type{Val{:violin}})
# function apply_series_recipe(d::KW, ::Type{Val{:violin}})
@recipe function f(::Type{Val{:violin}}, x, y, z)
# dumpdict(d, "box before", true)
# TODO: add scatter series with outliers
# create a list of shapes, where each shape is a single boxplot
shapes = Shape[]
d[:linetype] = :shape
groupby = extractGroupArgs(d[:x])
for (i, glabel) in enumerate(groupby.groupLabels)
@@ -126,22 +593,44 @@ function apply_series_recipe(d::KW, ::Type{Val{:violin}})
widths = _box_halfwidth * widths / maximum(widths)
# make the violin
xcoords = vcat(widths, -reverse(widths)) + i
xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
xcoords = vcat(widths, -reverse(widths)) + xcenter
ycoords = vcat(centers, reverse(centers))
push!(shapes, Shape(xcoords, ycoords))
end
d[:x], d[:y] = shape_coords(shapes)
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
seriestype := :shape
# n = length(groupby.groupLabels)
# xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
KW[d]
d[:x], d[:y] = shape_coords(shapes)
()
# KW[d]
end
# ---------------------------------------------------------------------------
# density
@recipe function f(::Type{Val{:density}}, x, y, z)
newx, newy = violin_coords(y, d[:bins])
if isvertical(d)
newx, newy = newy, newx
end
x := newx
y := newy
seriestype := :path
()
end
# ---------------------------------------------------------------------------
# Error Bars
function error_style!(d::KW)
d[:linetype] = :path
d[:seriestype] = :path
d[:linecolor] = d[:markerstrokecolor]
d[:linewidth] = d[:markerstrokewidth]
d[:label] = ""
@@ -180,18 +669,18 @@ end
# we will create a series of path segments, where each point represents one
# side of an errorbar
function apply_series_recipe(d::KW, ::Type{Val{:yerror}})
@recipe function f(::Type{Val{:yerror}}, x, y, z)
error_style!(d)
d[:markershape] = :hline
markershape := :hline
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
KW[d]
()
end
function apply_series_recipe(d::KW, ::Type{Val{:xerror}})
@recipe function f(::Type{Val{:xerror}}, x, y, z)
error_style!(d)
d[:markershape] = :vline
markershape := :vline
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
KW[d]
()
end
@@ -199,71 +688,48 @@ end
# quiver
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
# d[:label] = ""
# d[:linetype] = :scatter
#
# # create a second series to draw the arrow shaft
# dpath = copy(d)
# error_style!(dpath)
# dpath[:markershape] = :none
#
# velocity = error_zipit(d[:quiver])
# xorig, yorig = d[:x], d[:y]
#
# # for each point, we create an arrow of velocity vi, translated to the x/y coordinates
# # x, y = zeros(0), zeros(0)
# paths = P2[]
# arrows = P2[]
# arrowshapes = Shape[]
# for i = 1:max(length(xorig), length(yorig))
#
# # get the starting position
# xi = get_mod(xorig, i)
# yi = get_mod(yorig, i)
# p = P2(xi, yi)
#
# # get the velocity
# vi = get_mod(velocity, i)
# vx, vy = if istuple(vi)
# first(vi), last(vi)
# elseif isscalar(vi)
# vi, vi
# else
# error("unexpected vi type $(typeof(vi)) for quiver: $vi")
# end
# v = P2(vx, vy)
#
# nanappend!(paths, [p, p+v])
# push!(arrows, p+v)
# push!(arrowshapes, makearrowhead(compute_angle(v)))
#
# # # dist = sqrt(vx^2 + vy^2)
# # dist = norm(v)
# # arrow_h = 0.1dist # height of arrowhead
# # arrow_w = 0.5arrow_h # halfwidth of arrowhead
# # U1 = v ./ dist # vector of arrowhead height
# # U2 = P2(-U1[2], U1[1]) # vector of arrowhead halfwidth
# # U1 *= arrow_h
# # U2 *= arrow_w
# #
# # append!(pts, P2(xi, yi) .+ P2[(0,0), v-U1, v-U1+U2, v, v-U1-U2, v-U1, (NaN,NaN)])
# # # a1 = v - arrow_h * U1 + arrow_w * U2
# # # a2 = v - arrow_h * U1 - arrow_w * U2
# # # nanappend!(x, xi + [0.0, vx, a1[1], a2[1], vx])
# # # nanappend!(y, yi + [0.0, vy, a1[2], a2[2], vy])
# end
#
# # d[:x], d[:y] = Plots.unzip(pts)
# dpath[:x], dpath[:y] = Plots.unzip(paths)
# d[:x], d[:y] = Plots.unzip(arrows)
# d[:markershape] = arrowshapes
#
# KW[dpath, d]
# end
function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
function quiver_using_arrows(d::KW)
d[:label] = ""
d[:linetype] = :shape
d[:seriestype] = :path
if !isa(d[:arrow], Arrow)
d[:arrow] = arrow()
end
velocity = error_zipit(d[:quiver])
xorig, yorig = d[:x], d[:y]
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
x, y = zeros(0), zeros(0)
for i = 1:max(length(xorig), length(yorig))
# get the starting position
xi = get_mod(xorig, i)
yi = get_mod(yorig, i)
# get the velocity
vi = get_mod(velocity, i)
vx, vy = if istuple(vi)
first(vi), last(vi)
elseif isscalar(vi)
vi, vi
elseif isa(vi,Function)
vi(xi, yi)
else
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
end
# add the points
nanappend!(x, [xi, xi+vx, NaN])
nanappend!(y, [yi, yi+vy, NaN])
end
d[:x], d[:y] = x, y
# KW[d]
end
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
function quiver_using_hack(d::KW)
label := ""
seriestype := :shape
velocity = error_zipit(d[:quiver])
xorig, yorig = d[:x], d[:y]
@@ -303,59 +769,127 @@ function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
end
d[:x], d[:y] = Plots.unzip(pts[2:end])
KW[d]
# KW[d]
end
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
@recipe function f(::Type{Val{:quiver}}, x, y, z)
if :arrow in supportedArgs()
quiver_using_arrows(d)
else
quiver_using_hack(d)
end
()
end
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
function rotate(x::Real, y::Real, θ::Real; center = (0,0))
cx = x - center[1]
cy = y - center[2]
xrot = cx * cos(θ) - cy * sin(θ)
yrot = cy * cos(θ) + cx * sin(θ)
xrot + center[1], yrot + center[2]
# function rotate(x::Real, y::Real, θ::Real; center = (0,0))
# cx = x - center[1]
# cy = y - center[2]
# xrot = cx * cos(θ) - cy * sin(θ)
# yrot = cy * cos(θ) + cx * sin(θ)
# xrot + center[1], yrot + center[2]
# end
#
# # ---------------------------------------------------------------------------
#
# type EllipseRecipe <: PlotRecipe
# w::Float64
# h::Float64
# x::Float64
# y::Float64
# θ::Float64
# end
# EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
#
# # return x,y coords of a rotated ellipse, centered at the origin
# function rotatedEllipse(w, h, x, y, θ, rotθ)
# # # coord before rotation
# xpre = w * cos(θ)
# ypre = h * sin(θ)
#
# # rotate and translate
# r = rotate(xpre, ypre, rotθ)
# x + r[1], y + r[2]
# end
#
# function getRecipeXY(ep::EllipseRecipe)
# x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
# top = rotate(0, ep.h, ep.θ)
# right = rotate(ep.w, 0, ep.θ)
# linex = Float64[top[1], 0, right[1]] + ep.x
# liney = Float64[top[2], 0, right[2]] + ep.y
# Any[x, linex], Any[y, liney]
# end
#
# function getRecipeArgs(ep::EllipseRecipe)
# [(:line, (3, [:dot :solid], [:red :blue], :path))]
# end
# -------------------------------------------------
# TODO: this should really be in another package...
type OHLC{T<:Real}
open::T
high::T
low::T
close::T
end
Base.convert(::Type{OHLC}, tup::Tuple) = OHLC(tup...)
# Base.tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close)
# get one OHLC path
function get_xy(o::OHLC, x, xdiff)
xl, xm, xr = x-xdiff, x, x+xdiff
ox = [xl, xm, NaN,
xm, xm, NaN,
xm, xr]
oy = [o.open, o.open, NaN,
o.low, o.high, NaN,
o.close, o.close]
ox, oy
end
# ---------------------------------------------------------------------------
type EllipseRecipe <: PlotRecipe
w::Float64
h::Float64
x::Float64
y::Float64
θ::Float64
end
EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
# return x,y coords of a rotated ellipse, centered at the origin
function rotatedEllipse(w, h, x, y, θ, rotθ)
# # coord before rotation
xpre = w * cos(θ)
ypre = h * sin(θ)
# rotate and translate
r = rotate(xpre, ypre, rotθ)
x + r[1], y + r[2]
# get the joined vector
function get_xy(v::AVec{OHLC}, x = 1:length(v))
xdiff = 0.3mean(abs(diff(x)))
x_out, y_out = zeros(0), zeros(0)
for (i,ohlc) in enumerate(v)
ox,oy = get_xy(ohlc, x[i], xdiff)
nanappend!(x_out, ox)
nanappend!(y_out, oy)
end
x_out, y_out
end
function getRecipeXY(ep::EllipseRecipe)
x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
top = rotate(0, ep.h, ep.θ)
right = rotate(ep.w, 0, ep.θ)
linex = Float64[top[1], 0, right[1]] + ep.x
liney = Float64[top[2], 0, right[2]] + ep.y
Any[x, linex], Any[y, liney]
# these are for passing in a vector of OHLC objects
# TODO: when I allow `@recipe f(::Type{T}, v::T) = ...` definitions to replace convertToAnyVector,
# then I should replace these with one definition to convert to a vector of 4-tuples
# to squash ambiguity warnings...
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) = error()
# this must be OHLC?
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) = x, OHLC[OHLC(t...) for t in ohlc]
@recipe function f(x::AVec, v::AVec{OHLC})
seriestype := :path
get_xy(v, x)
end
function getRecipeArgs(ep::EllipseRecipe)
[(:line, (3, [:dot :solid], [:red :blue], :path))]
@recipe function f(v::AVec{OHLC})
seriestype := :path
get_xy(v)
end
# # -------------------------------------------------
# the series recipe, when passed vectors of 4-tuples
# -------------------------------------------------
"Sparsity plot... heatmap of non-zero values of a matrix"
+448 -365
View File
@@ -7,7 +7,7 @@
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
all3D(d::KW) = trueOrAllTrue(lt -> lt in (:contour, :heatmap, :surface, :wireframe, :contour3d), get(d, :linetype, :none))
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :heatmap, :surface, :wireframe, :contour3d), get(d, :seriestype, :none))
# missing
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
@@ -44,8 +44,8 @@ convertToAnyVector(f::Function, d::KW) = Any[f], nothing
# surface
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
# vector of OHLC
convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# # vector of OHLC
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# dates
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
@@ -62,6 +62,12 @@ function convertToAnyVector(v::AVec, d::KW)
end
end
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
function convertToAnyVector(args...)
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
end
# --------------------------------------------------------------------
@@ -83,11 +89,15 @@ compute_z(x, y, z::AbstractMatrix) = Surface(z)
compute_z(x, y, z::Void) = nothing
compute_z(x, y, z) = copy(z)
nobigs(v::AVec{BigFloat}) = map(Float64, v)
nobigs(v::AVec{BigInt}) = map(Int64, v)
nobigs(v) = v
@noinline function compute_xyz(x, y, z)
x = compute_x(x,y,z)
y = compute_y(x,y,z)
z = compute_z(x,y,z)
x, y, z
nobigs(x), nobigs(y), nobigs(z)
end
# not allowed
@@ -97,371 +107,444 @@ compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
# --------------------------------------------------------------------
# create n=max(mx,my) series arguments. the shorter list is cycled through
# note: everything should flow through this
function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
x, y, z = map(sym -> pop!(kw, sym, nothing), (:x, :y, :z))
if nothing == x == y == z
return [], nothing, nothing
end
xs, xmeta = convertToAnyVector(x, kw)
ys, ymeta = convertToAnyVector(y, kw)
zs, zmeta = convertToAnyVector(z, kw)
fr = pop!(kw, :fillrange, nothing)
fillranges, _ = if typeof(fr) <: Number
([fr],nothing)
else
convertToAnyVector(fr, kw)
end
mx = length(xs)
my = length(ys)
mz = length(zs)
ret = Any[]
for i in 1:max(mx, my, mz)
# try to set labels using ymeta
d = copy(kw)
if !haskey(d, :label) && ymeta != nothing
if isa(ymeta, Symbol)
d[:label] = string(ymeta)
elseif isa(ymeta, AVec{Symbol})
d[:label] = string(ymeta[mod1(i,length(ymeta))])
end
end
# build the series arg dict
numUncounted = pop!(d, :numUncounted, 0)
commandIndex = i + numUncounted
n = plt.n + i
dumpdict(d, "before getSeriesArgs")
d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n)
dumpdict(d, "after getSeriesArgs")
d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])
lt = d[:linetype]
# for linetype `line`, need to sort by x values
if lt == :line
# order by x
indices = sortperm(d[:x])
d[:x] = d[:x][indices]
d[:y] = d[:y][indices]
d[:linetype] = :path
end
# special handling for missing x in box plot... all the same category
if lt == :box && xs[mod1(i,mx)] == nothing
d[:x] = ones(Int, length(d[:y]))
end
# map functions to vectors
if isa(d[:marker_z], Function)
d[:marker_z] = map(d[:marker_z], d[:x])
end
# @show fillranges
d[:fillrange] = fillranges[mod1(i,length(fillranges))]
if isa(d[:fillrange], Function)
d[:fillrange] = map(d[:fillrange], d[:x])
end
# handle error bars
for esym in (:xerror, :yerror)
if get(d, esym, nothing) != nothing
# we make a copy of the KW and apply an errorbar recipe
append!(ret, apply_series_recipe(copy(d), Val{esym}))
end
end
# handle ribbons
if get(d, :ribbon, nothing) != nothing
rib = d[:ribbon]
d[:fillrange] = (d[:y] - rib, d[:y] + rib)
end
# handle quiver plots
if lt == :quiver
d[:linetype] = lt = :path
d[:linewidth] = 0
end
if get(d, :quiver, nothing) != nothing
append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
end
# now that we've processed a given series... optionally split into
# multiple dicts through a recipe (for example, a box plot is split into component
# parts... polygons, lines, and scatters)
# note: we pass in a Val type (i.e. Val{:box}) so that we can dispatch on the linetype
kwlist = apply_series_recipe(d, Val{lt})
append!(ret, kwlist)
# # add it to our series list
# push!(ret, d)
end
ret, xmeta, ymeta
end
# --------------------------------------------------------------------
# process_inputs
# --------------------------------------------------------------------
# These methods take a plot and the keyword arguments, and processes the input
# arguments (x/y/z, group, etc), populating the KW dict with appropriate values.
# --------------------------------------------------------------------
# 0 arguments
# --------------------------------------------------------------------
# don't do anything
function process_inputs(plt::AbstractPlot, d::KW)
end
# --------------------------------------------------------------------
# 1 argument
# --------------------------------------------------------------------
function process_inputs(plt::AbstractPlot, d::KW, n::Integer)
# d[:x], d[:y], d[:z] = zeros(0), zeros(0), zeros(0)
d[:x] = d[:y] = d[:z] = n
end
# no special handling... assume x and z are nothing
function process_inputs(plt::AbstractPlot, d::KW, y)
d[:y] = y
end
# matrix... is it z or y?
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T})
if all3D(d)
n,m = size(mat)
d[:x], d[:y], d[:z] = 1:n, 1:m, mat
else
d[:y] = mat
end
end
# plotting arbitrary shapes/polygons
function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
d[:x], d[:y] = shape_coords(shape)
d[:linetype] = :shape
end
function process_inputs(plt::AbstractPlot, d::KW, shapes::AVec{Shape})
d[:x], d[:y] = shape_coords(shapes)
d[:linetype] = :shape
end
function process_inputs(plt::AbstractPlot, d::KW, shapes::AMat{Shape})
x, y = [], []
for j in 1:size(shapes, 2)
tmpx, tmpy = shape_coords(vec(shapes[:,j]))
push!(x, tmpx)
push!(y, tmpy)
end
d[:x], d[:y] = x, y
d[:linetype] = :shape
end
# function without range... use the current range of the x-axis
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs)
process_inputs(plt, d, f, xmin(plt), xmax(plt))
end
# --------------------------------------------------------------------
# 2 arguments
# --------------------------------------------------------------------
function process_inputs(plt::AbstractPlot, d::KW, x, y)
d[:x], d[:y] = x, y
end
# if functions come first, just swap the order (not to be confused with parametric functions...
# as there would be more than one function passed in)
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, x)
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
process_inputs(plt, d, x, f)
end
# --------------------------------------------------------------------
# 3 arguments
# --------------------------------------------------------------------
# no special handling... just pass them through
function process_inputs(plt::AbstractPlot, d::KW, x, y, z)
d[:x], d[:y], d[:z] = x, y, z
end
# 3d line or scatter
function process_inputs(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zvec::AVec)
# default to path3d if we haven't set a 3d linetype
lt = get(d, :linetype, :none)
if lt == :scatter
d[:linetype] = :scatter3d
elseif !(lt in _3dTypes)
d[:linetype] = :path3d
end
d[:x], d[:y], d[:z] = x, y, zvec
end
# surface-like... function
function process_inputs{TX,TY}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zf::Function)
x = TX <: Number ? sort(x) : x
y = TY <: Number ? sort(y) : y
# x, y = sort(x), sort(y)
d[:z] = Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
d[:x], d[:y] = x, y
end
# surface-like... matrix grid
function process_inputs{TX,TY,TZ}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zmat::AMat{TZ})
@assert size(zmat) == (length(x), length(y))
if TX <: Number && !issorted(x)
idx = sortperm(x)
x, zmat = x[idx], zmat[idx, :]
end
if TY <: Number && !issorted(y)
idx = sortperm(y)
y, zmat = y[idx], zmat[:, idx]
end
d[:x], d[:y], d[:z] = x, y, Surface{Matrix{TZ}}(zmat)
if !like_surface(get(d, :linetype, :none))
d[:linetype] = :contour
end
end
# surfaces-like... general x, y grid
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, x::AMat{T}, y::AMat{T}, zmat::AMat{T})
@assert size(zmat) == size(x) == size(y)
# d[:x], d[:y], d[:z] = Any[x], Any[y], Surface{Matrix{Float64}}(zmat)
d[:x], d[:y], d[:z] = map(Surface{Matrix{Float64}}, (x, y, zmat))
if !like_surface(get(d, :linetype, :none))
d[:linetype] = :contour
end
end
# --------------------------------------------------------------------
# Parametric functions
# --------------------------------------------------------------------
# special handling... xmin/xmax with function(s)
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, xmin::Number, xmax::Number)
width = get(plt.plotargs, :size, (100,))[1]
x = linspace(xmin, xmax, width)
process_inputs(plt, d, x, f)
end
# special handling... xmin/xmax with parametric function(s)
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, linspace(umin, umax, numPoints))
# special handling... 3D parametric function(s)
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, fz, linspace(umin, umax, numPoints))
# --------------------------------------------------------------------
# Lists of tuples and FixedSizeArrays
# --------------------------------------------------------------------
# if we get an unhandled tuple, just splat it in
function process_inputs(plt::AbstractPlot, d::KW, tup::Tuple)
process_inputs(plt, d, tup...)
end
# (x,y) tuples
function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::AVec{Tuple{R1,R2}})
process_inputs(plt, d, unzip(xy)...)
end
function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::Tuple{R1,R2})
process_inputs(plt, d, [xy[1]], [xy[2]])
end
# (x,y,z) tuples
function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{Tuple{R1,R2,R3}})
process_inputs(plt, d, unzip(xyz)...)
end
function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::Tuple{R1,R2,R3})
process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
end
# 2D FixedSizeArrays
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}})
process_inputs(plt, d, unzip(xy)...)
end
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::FixedSizeArrays.Vec{2,T})
process_inputs(plt, d, [xy[1]], [xy[2]])
end
# 3D FixedSizeArrays
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{FixedSizeArrays.Vec{3,T}})
process_inputs(plt, d, unzip(xyz)...)
end
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::FixedSizeArrays.Vec{3,T})
process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
end
# --------------------------------------------------------------------
# handle grouping
# --------------------------------------------------------------------
# function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...)
# ret = Any[]
# error("unfinished after series reorg")
# for (i,glab) in enumerate(groupby.groupLabels)
# # TODO: don't automatically overwrite labels
# kwlist, xmeta, ymeta = process_inputs(plt, d, args...,
# idxfilter = groupby.groupIds[i],
# label = string(glab),
# numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
# append!(ret, kwlist)
# # create n=max(mx,my) series arguments. the shorter list is cycled through
# # note: everything should flow through this
# function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
# x, y, z = map(sym -> pop!(kw, sym, nothing), (:x, :y, :z))
# if nothing == x == y == z
# return [], nothing, nothing
# end
# ret, nothing, nothing # TODO: handle passing meta through
#
# xs, xmeta = convertToAnyVector(x, kw)
# ys, ymeta = convertToAnyVector(y, kw)
# zs, zmeta = convertToAnyVector(z, kw)
#
# fr = pop!(kw, :fillrange, nothing)
# fillranges, _ = if typeof(fr) <: Number
# ([fr],nothing)
# else
# convertToAnyVector(fr, kw)
# end
#
# mx = length(xs)
# my = length(ys)
# mz = length(zs)
# ret = Any[]
# for i in 1:max(mx, my, mz)
#
# # try to set labels using ymeta
# d = copy(kw)
# if !haskey(d, :label) && ymeta != nothing
# if isa(ymeta, Symbol)
# d[:label] = string(ymeta)
# elseif isa(ymeta, AVec{Symbol})
# d[:label] = string(ymeta[mod1(i,length(ymeta))])
# end
# end
#
# # build the series arg dict
# numUncounted = pop!(d, :numUncounted, 0)
# commandIndex = i + numUncounted
# n = plt.n + i
#
# dumpdict(d, "before getSeriesArgs")
# d = getSeriesArgs(plt.backend, getattr(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n)
# dumpdict(d, "after getSeriesArgs")
#
# d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])
# st = d[:seriestype]
#
# # for seriestype `line`, need to sort by x values
# if st == :line
# # order by x
# indices = sortperm(d[:x])
# d[:x] = d[:x][indices]
# d[:y] = d[:y][indices]
# d[:seriestype] = :path
# end
#
# # special handling for missing x in box plot... all the same category
# if st == :box && xs[mod1(i,mx)] == nothing
# d[:x] = ones(Int, length(d[:y]))
# end
#
# # map functions to vectors
# if isa(d[:marker_z], Function)
# d[:marker_z] = map(d[:marker_z], d[:x])
# end
#
# # @show fillranges
# d[:fillrange] = fillranges[mod1(i,length(fillranges))]
# if isa(d[:fillrange], Function)
# d[:fillrange] = map(d[:fillrange], d[:x])
# end
#
# # handle error bars
# for esym in (:xerror, :yerror)
# if get(d, esym, nothing) != nothing
# # we make a copy of the KW and apply an errorbar recipe
# append!(ret, apply_series_recipe(copy(d), Val{esym}))
# end
# end
#
# # handle ribbons
# if get(d, :ribbon, nothing) != nothing
# rib = d[:ribbon]
# d[:fillrange] = (d[:y] - rib, d[:y] + rib)
# end
#
# # handle quiver plots
# # either a series of velocity vectors are passed in (`:quiver` keyword),
# # or we just add arrows to the path
#
# # if st == :quiver
# # d[:seriestype] = st = :path
# # d[:linewidth] = 0
# # end
# if get(d, :quiver, nothing) != nothing
# append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
# elseif st == :quiver
# d[:seriestype] = st = :path
# d[:arrow] = arrow()
# end
#
# # now that we've processed a given series... optionally split into
# # multiple dicts through a recipe (for example, a box plot is split into component
# # parts... polygons, lines, and scatters)
# # note: we pass in a Val type (i.e. Val{:box}) so that we can dispatch on the seriestype
# kwlist = apply_series_recipe(d, Val{st})
# append!(ret, kwlist)
#
# # # add it to our series list
# # push!(ret, d)
# end
#
# ret, xmeta, ymeta
# end
#
#
# # --------------------------------------------------------------------
# # process_inputs
# # --------------------------------------------------------------------
#
# # These methods take a plot and the keyword arguments, and processes the input
# # arguments (x/y/z, group, etc), populating the KW dict with appropriate values.
#
# # --------------------------------------------------------------------
# # 0 arguments
# # --------------------------------------------------------------------
#
# # don't do anything
# function process_inputs(plt::AbstractPlot, d::KW)
# end
#
# # --------------------------------------------------------------------
# # 1 argument
# # --------------------------------------------------------------------
#
# function process_inputs(plt::AbstractPlot, d::KW, n::Integer)
# # d[:x], d[:y], d[:z] = zeros(0), zeros(0), zeros(0)
# d[:x] = d[:y] = d[:z] = n
# end
#
# # no special handling... assume x and z are nothing
# function process_inputs(plt::AbstractPlot, d::KW, y)
# d[:y] = y
# end
#
# # matrix... is it z or y?
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T})
# if all3D(d)
# n,m = size(mat)
# d[:x], d[:y], d[:z] = 1:n, 1:m, mat
# else
# d[:y] = mat
# end
# end
#
# # images - grays
# function process_inputs{T<:Gray}(plt::AbstractPlot, d::KW, mat::AMat{T})
# d[:seriestype] = :image
# n,m = size(mat)
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
# # handle images... when not supported natively, do a hack to use heatmap machinery
# if !nativeImagesSupported()
# d[:seriestype] = :heatmap
# d[:yflip] = true
# d[:z] = Surface(convert(Matrix{Float64}, mat.surf))
# d[:fillcolor] = ColorGradient([:black, :white])
# end
# end
#
# # images - colors
# function process_inputs{T<:Colorant}(plt::AbstractPlot, d::KW, mat::AMat{T})
# d[:seriestype] = :image
# n,m = size(mat)
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
# # handle images... when not supported natively, do a hack to use heatmap machinery
# if !nativeImagesSupported()
# d[:yflip] = true
# imageHack(d)
# end
# end
#
#
# # plotting arbitrary shapes/polygons
# function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
# d[:x], d[:y] = shape_coords(shape)
# d[:seriestype] = :shape
# end
# function process_inputs(plt::AbstractPlot, d::KW, shapes::AVec{Shape})
# d[:x], d[:y] = shape_coords(shapes)
# d[:seriestype] = :shape
# end
# function process_inputs(plt::AbstractPlot, d::KW, shapes::AMat{Shape})
# x, y = [], []
# for j in 1:size(shapes, 2)
# tmpx, tmpy = shape_coords(vec(shapes[:,j]))
# push!(x, tmpx)
# push!(y, tmpy)
# end
# d[:x], d[:y] = x, y
# d[:seriestype] = :shape
# end
#
#
# # function without range... use the current range of the x-axis
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs)
# process_inputs(plt, d, f, xmin(plt), xmax(plt))
# end
#
# # --------------------------------------------------------------------
# # 2 arguments
# # --------------------------------------------------------------------
#
# function process_inputs(plt::AbstractPlot, d::KW, x, y)
# d[:x], d[:y] = x, y
# end
#
# # if functions come first, just swap the order (not to be confused with parametric functions...
# # as there would be more than one function passed in)
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, x)
# @assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
# process_inputs(plt, d, x, f)
# end
#
# # --------------------------------------------------------------------
# # 3 arguments
# # --------------------------------------------------------------------
#
# # no special handling... just pass them through
# function process_inputs(plt::AbstractPlot, d::KW, x, y, z)
# d[:x], d[:y], d[:z] = x, y, z
# end
#
# # 3d line or scatter
# function process_inputs(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zvec::AVec)
# # default to path3d if we haven't set a 3d seriestype
# st = get(d, :seriestype, :none)
# if st == :scatter
# d[:seriestype] = :scatter3d
# elseif !(st in _3dTypes)
# d[:seriestype] = :path3d
# end
# d[:x], d[:y], d[:z] = x, y, zvec
# end
#
# # surface-like... function
# function process_inputs{TX,TY}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zf::Function)
# x = TX <: Number ? sort(x) : x
# y = TY <: Number ? sort(y) : y
# # x, y = sort(x), sort(y)
# d[:z] = Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
# d[:x], d[:y] = x, y
# end
#
# # surface-like... matrix grid
# function process_inputs{TX,TY,TZ}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zmat::AMat{TZ})
# # @assert size(zmat) == (length(x), length(y))
# # if TX <: Number && !issorted(x)
# # idx = sortperm(x)
# # x, zmat = x[idx], zmat[idx, :]
# # end
# # if TY <: Number && !issorted(y)
# # idx = sortperm(y)
# # y, zmat = y[idx], zmat[:, idx]
# # end
# d[:x], d[:y], d[:z] = x, y, Surface{Matrix{TZ}}(zmat)
# if !like_surface(get(d, :seriestype, :none))
# d[:seriestype] = :contour
# end
# end
#
# # surfaces-like... general x, y grid
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, x::AMat{T}, y::AMat{T}, zmat::AMat{T})
# @assert size(zmat) == size(x) == size(y)
# # d[:x], d[:y], d[:z] = Any[x], Any[y], Surface{Matrix{Float64}}(zmat)
# d[:x], d[:y], d[:z] = map(Surface{Matrix{Float64}}, (x, y, zmat))
# if !like_surface(get(d, :seriestype, :none))
# d[:seriestype] = :contour
# end
# end
#
#
# # --------------------------------------------------------------------
# # Parametric functions
# # --------------------------------------------------------------------
#
# # special handling... xmin/xmax with function(s)
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, xmin::Number, xmax::Number)
# width = get(plt.attr, :size, (100,))[1]
# x = linspace(xmin, xmax, width)
# process_inputs(plt, d, x, f)
# end
#
# # special handling... xmin/xmax with parametric function(s)
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
# process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, linspace(umin, umax, numPoints))
#
# # special handling... 3D parametric function(s)
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
# process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, fz, linspace(umin, umax, numPoints))
#
#
# # --------------------------------------------------------------------
# # Lists of tuples and FixedSizeArrays
# # --------------------------------------------------------------------
#
# # if we get an unhandled tuple, just splat it in
# function process_inputs(plt::AbstractPlot, d::KW, tup::Tuple)
# process_inputs(plt, d, tup...)
# end
#
# # (x,y) tuples
# function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::AVec{Tuple{R1,R2}})
# process_inputs(plt, d, unzip(xy)...)
# end
# function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::Tuple{R1,R2})
# process_inputs(plt, d, [xy[1]], [xy[2]])
# end
#
# # (x,y,z) tuples
# function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{Tuple{R1,R2,R3}})
# process_inputs(plt, d, unzip(xyz)...)
# end
# function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::Tuple{R1,R2,R3})
# process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
# end
#
# # 2D FixedSizeArrays
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}})
# process_inputs(plt, d, unzip(xy)...)
# end
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::FixedSizeArrays.Vec{2,T})
# process_inputs(plt, d, [xy[1]], [xy[2]])
# end
#
# # 3D FixedSizeArrays
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{FixedSizeArrays.Vec{3,T}})
# process_inputs(plt, d, unzip(xyz)...)
# end
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::FixedSizeArrays.Vec{3,T})
# process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
# end
#
# # --------------------------------------------------------------------
# # handle grouping
# # --------------------------------------------------------------------
#
# # function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...)
# # ret = Any[]
# # error("unfinished after series reorg")
# # for (i,glab) in enumerate(groupby.groupLabels)
# # # TODO: don't automatically overwrite labels
# # kwlist, xmeta, ymeta = process_inputs(plt, d, args...,
# # idxfilter = groupby.groupIds[i],
# # label = string(glab),
# # numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
# # append!(ret, kwlist)
# # end
# # ret, nothing, nothing # TODO: handle passing meta through
# # end
# --------------------------------------------------------------------
# For DataFrame support. Imports DataFrames and defines the necessary methods which support them.
# --------------------------------------------------------------------
function setup_dataframes()
@require DataFrames begin
get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
get_data(df::DataFrames.AbstractDataFrame, arg) = arg
function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
# d[:dataframe] = df
process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
end
# expecting the column name of a dataframe that was passed in... anything else should error
function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
if haskey(df, s)
return extractGroupArgs(df[s])
else
error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
end
end
# function getDataFrameFromKW(d::KW)
# get(d, :dataframe) do
# error("Missing dataframe argument!")
# end
# end
# # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
# convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s
# convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v
end
end
# function setup_dataframes()
# @require DataFrames begin
# # @eval begin
# # import DataFrames
#
# DFS = Union{Symbol, AbstractArray{Symbol}}
#
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS)
# if isa(dfs, Symbol)
# get!(d, Symbol(letter * "label"), string(dfs))
# collect(df[dfs])
# else
# get!(d, :label, reshape(dfs, 1, length(dfs)))
# Any[collect(df[s]) for s in dfs]
# end
# end
#
# function handle_group(df::DataFrames.AbstractDataFrame, d::KW)
# if haskey(d, :group)
# g = d[:group]
# if isa(g, Symbol)
# d[:group] = collect(df[g])
# end
# end
# end
#
# @recipe function plot(df::DataFrames.AbstractDataFrame, sy::DFS)
# handle_group(df, d)
# handle_dfs(df, d, "y", sy)
# end
#
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS)
# handle_group(df, d)
# x = handle_dfs(df, d, "x", sx)
# y = handle_dfs(df, d, "y", sy)
# x, y
# end
#
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS)
# handle_group(df, d)
# x = handle_dfs(df, d, "x", sx)
# y = handle_dfs(df, d, "y", sy)
# z = handle_dfs(df, d, "z", sz)
# x, y, z
# end
#
# # get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
# # get_data(df::DataFrames.AbstractDataFrame, arg) = arg
# #
# # function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
# # # d[:dataframe] = df
# # process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
# # end
# #
# # # expecting the column name of a dataframe that was passed in... anything else should error
# # function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
# # if haskey(df, s)
# # return extractGroupArgs(df[s])
# # else
# # error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
# # end
# # end
#
# # function getDataFrameFromKW(d::KW)
# # get(d, :dataframe) do
# # error("Missing dataframe argument!")
# # end
# # end
#
# # # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
# # convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s
# # convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v
#
# end
# end
+392
View File
@@ -0,0 +1,392 @@
# we are going to build recipes to do the processing and splitting of the args
function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
pkg = plt.backend
globalIndex = d[:series_plotindex]
# add default values to our dictionary, being careful not to delete what we just added!
for (k,v) in _series_defaults
slice_arg!(d, d, k, v, commandIndex, remove_pair = false)
end
# this is how many series belong to this subplot
plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
if get(d, :primary, true)
plotIndex += 1
end
aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex)
aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex)
# update color
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], sp, plotIndex)
# update colors
for csym in (:linecolor, :markercolor, :fillcolor)
d[csym] = if d[csym] == :match
if has_black_border_for_default(d[:seriestype]) && csym == :linecolor
:black
else
d[:seriescolor]
end
else
getSeriesRGBColor(d[csym], sp, plotIndex)
end
end
# update markerstrokecolor
c = d[:markerstrokecolor]
c = if c == :match
sp[:foreground_color_subplot]
else
getSeriesRGBColor(c, sp, plotIndex)
end
d[:markerstrokecolor] = c
# update alphas
for asym in (:linealpha, :markeralpha, :markerstrokealpha, :fillalpha)
if d[asym] == nothing
d[asym] = d[:seriesalpha]
end
end
# scatter plots don't have a line, but must have a shape
if d[:seriestype] in (:scatter, :scatter3d)
d[:linewidth] = 0
if d[:markershape] == :none
d[:markershape] = :ellipse
end
end
# set label
label = d[:label]
label = (label == "AUTO" ? "y$globalIndex" : label)
d[:label] = label
_replace_linewidth(d)
d
end
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# instead of process_inputs:
# ensure we dispatch to the slicer
immutable SliceIt end
# the catch-all recipes
@recipe function f(::Type{SliceIt}, x, y, z)
# @show "HERE", typeof((x,y,z))
xs, _ = convertToAnyVector(x, d)
ys, _ = convertToAnyVector(y, d)
zs, _ = convertToAnyVector(z, d)
fr = pop!(d, :fillrange, nothing)
fillranges, _ = if typeof(fr) <: Number
([fr],nothing)
else
convertToAnyVector(fr, d)
end
mf = length(fillranges)
# @show zs
mx = length(xs)
my = length(ys)
mz = length(zs)
# ret = Any[]
for i in 1:max(mx, my, mz)
# add a new series
di = copy(d)
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
# @show i, typeof((xi, yi, zi))
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
# @show i, typeof((di[:x], di[:y], di[:z]))
# handle fillrange
fr = fillranges[mod1(i,mf)]
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
# @show i, di[:x], di[:y], di[:z]
push!(series_list, RecipeData(di, ()))
end
nothing # don't add a series for the main block
end
# this is the default "type recipe"... just pass the object through
@recipe f{T}(::Type{T}, v::T) = v
_apply_type_recipe(d, v) = RecipesBase.apply_recipe(d, typeof(v), v)[1].args[1]
# handle "type recipes" by converting inputs, and then either re-calling or slicing
@recipe function f(x, y, z)
did_replace = false
newx = _apply_type_recipe(d, x)
x === newx || (did_replace = true)
newy = _apply_type_recipe(d, y)
y === newy || (did_replace = true)
newz = _apply_type_recipe(d, z)
z === newz || (did_replace = true)
if did_replace
newx, newy, newz
else
SliceIt, x, y, z
end
end
@recipe function f(x, y)
did_replace = false
newx = _apply_type_recipe(d, x)
x === newx || (did_replace = true)
newy = _apply_type_recipe(d, y)
y === newy || (did_replace = true)
if did_replace
newx, newy
else
SliceIt, x, y, nothing
end
end
@recipe function f(y)
newy = _apply_type_recipe(d, y)
if y !== newy
newy
else
SliceIt, nothing, y, nothing
end
end
# @recipe f(x, y, z) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), apply_recipe(typeof(z), z)
# @recipe f(x, y) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), nothing
# @recipe f(y) = SliceIt, nothing, apply_recipe(typeof(y), y), nothing
# # pass these through to the slicer
# @recipe f(x, y, z) = SliceIt, x, y, z
# @recipe f(x, y) = SliceIt, x, y, nothing
# @recipe f(y) = SliceIt, nothing, y, nothing
# # --------------------------------------------------------------------
# # 1 argument
# # --------------------------------------------------------------------
@recipe f(n::Integer) = n, n, n
# return a surface if this is a 3d plot, otherwise let it be sliced up
@recipe function f{T<:Number}(mat::AMat{T})
if all3D(d)
n,m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
else
SliceIt, nothing, mat, nothing
end
end
# # images - grays
@recipe function f{T<:Gray}(mat::AMat{T})
if nativeImagesSupported()
seriestype := :image
n, m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
else
seriestype := :heatmap
yflip --> true
fillcolor --> ColorGradient([:black, :white])
SliceIt, 1:m, 1:n, Surface(convert(Matrix{Float64}, mat))
end
end
# # images - colors
@recipe function f{T<:Colorant}(mat::AMat{T})
if nativeImagesSupported()
seriestype := :image
n, m = size(mat)
SliceIt, 1:m, 1:n, Surface(mat)
else
seriestype := :heatmap
yflip --> true
z, d[:fillcolor] = replace_image_with_heatmap(mat)
SliceIt, 1:m, 1:n, Surface(z)
end
end
#
# # plotting arbitrary shapes/polygons
@recipe function f(shape::Shape)
seriestype := :shape
shape_coords(shape)
end
@recipe function f(shapes::AVec{Shape})
seriestype := :shape
shape_coords(shapes)
end
@recipe function f(shapes::AMat{Shape})
for j in 1:size(shapes,2)
# create one series for each column
# @series shape_coords(vec(shapes[:,j]))
di = copy(d)
push!(series_list, RecipeData(di, shape_coords(vec(shapes[:,j]))))
end
nothing # don't create a series for the main block
end
#
#
# # function without range... use the current range of the x-axis
@recipe function f(f::FuncOrFuncs)
plt = d[:plot_object]
f, xmin(plt), xmax(plt)
end
#
# # --------------------------------------------------------------------
# # 2 arguments
# # --------------------------------------------------------------------
#
#
# # if functions come first, just swap the order (not to be confused with parametric functions...
# # as there would be more than one function passed in)
@recipe function f(f::FuncOrFuncs, x)
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
x, f
end
#
# # --------------------------------------------------------------------
# # 3 arguments
# # --------------------------------------------------------------------
#
#
# # 3d line or scatter
@recipe function f(x::AVec, y::AVec, z::AVec)
st = get(d, :seriestype, :none)
if st == :scatter
d[:seriestype] = :scatter3d
elseif !is3d(st)
d[:seriestype] = :path3d
end
SliceIt, x, y, z
end
@recipe function f(x::AMat, y::AMat, z::AMat)
st = get(d, :seriestype, :none)
if size(x) == size(y) == size(z)
if !is3d(st)
seriestype := :path3d
end
end
SliceIt, x, y, z
end
#
# # surface-like... function
@recipe function f(x::AVec, y::AVec, zf::Function)
# x = X <: Number ? sort(x) : x
# y = Y <: Number ? sort(y) : y
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
end
#
# # surface-like... matrix grid
@recipe function f(x::AVec, y::AVec, z::AMat)
if !like_surface(get(d, :seriestype, :none))
d[:seriestype] = :contour
end
SliceIt, x, y, Surface(z)
end
#
#
# # --------------------------------------------------------------------
# # Parametric functions
# # --------------------------------------------------------------------
#
# # special handling... xmin/xmax with parametric function(s)
@recipe f(f::FuncOrFuncs, xmin::Number, xmax::Number) = linspace(xmin, xmax, 100), f
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n)
#
# # special handling... 3D parametric function(s)
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec)
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
end
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPointsn = 200)
fx, fy, fz, linspace(umin, umax, numPoints)
end
#
#
# # --------------------------------------------------------------------
# # Lists of tuples and FixedSizeArrays
# # --------------------------------------------------------------------
#
# # if we get an unhandled tuple, just splat it in
@recipe f(tup::Tuple) = tup
#
# # (x,y) tuples
@recipe f{R1<:Number,R2<:Number}(xy::AVec{Tuple{R1,R2}}) = unzip(xy)
@recipe f{R1<:Number,R2<:Number}(xy::Tuple{R1,R2}) = [xy[1]], [xy[2]]
#
# # (x,y,z) tuples
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::AVec{Tuple{R1,R2,R3}}) = unzip(xyz)
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::Tuple{R1,R2,R3}) = [xyz[1]], [xyz[2]], [xyz[3]]
# these might be points+velocity, or OHLC or something else
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::AVec{Tuple{R1,R2,R3,R4}}) = get(d,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::Tuple{R1,R2,R3,R4}) = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
#
# # 2D FixedSizeArrays
@recipe f{T<:Number}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = unzip(xy)
@recipe f{T<:Number}(xy::FixedSizeArrays.Vec{2,T}) = [xy[1]], [xy[2]]
#
# # 3D FixedSizeArrays
@recipe f{T<:Number}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = unzip(xyz)
@recipe f{T<:Number}(xyz::FixedSizeArrays.Vec{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]]
#
# # --------------------------------------------------------------------
# # handle grouping
# # --------------------------------------------------------------------
# @recipe function f(groupby::GroupBy, args...)
# for (i,glab) in enumerate(groupby.groupLabels)
# # create a new series, with the label of the group, and an idxfilter (to be applied in slice_and_dice)
# # TODO: use @series instead
# @show i, glab, groupby.groupIds[i]
# di = copy(d)
# get!(di, :label, string(glab))
# get!(di, :idxfilter, groupby.groupIds[i])
# push!(series_list, RecipeData(di, args))
# end
# nothing
# end
# split the group into 1 series per group, and set the label and idxfilter for each
@recipe function f(groupby::GroupBy, args...)
for (i,glab) in enumerate(groupby.groupLabels)
@series begin
label --> string(glab)
idxfilter --> groupby.groupIds[i]
args
end
end
end
-331
View File
@@ -1,331 +0,0 @@
# ------------------------------------------------------------
Base.string(subplt::Subplot) = "Subplot{$(subplt.backend) p=$(subplt.p) n=$(subplt.n)}"
Base.print(io::IO, subplt::Subplot) = print(io, string(subplt))
Base.show(io::IO, subplt::Subplot) = print(io, string(subplt))
function Base.copy(subplt::Subplot)
subplot(subplt.plts, subplt.layout, subplt.plotargs)
end
Base.getindex(subplt::Subplot, args...) = subplt.plts[subplt.layout[args...]]
# --------------------------------------------------------------------
getplot(subplt::Subplot, idx::Int = subplt.n) = subplt.plts[mod1(idx, subplt.p)]
getplotargs(subplt::Subplot, idx::Int) = getplot(subplt, idx).plotargs
convertSeriesIndex(subplt::Subplot, n::Int) = ceil(Int, n / subplt.p)
# ------------------------------------------------------------
function validateSubplotSupported()
if !subplotSupported()
error(CURRENT_BACKEND.sym, " does not support the subplot/subplot! commands at this time. Try one of: ", join(filter(pkg->subplotSupported(_backend_instance(pkg)), backends()),", "))
end
end
"""
Create a series of plots:
```
y = rand(100,3)
subplot(y; n = 3) # create an automatic grid, and let it figure out the nr/nc... will put plots 1 and 2 on the first row, and plot 3 by itself on the 2nd row
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows to 1 (so there are n columns)
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns to 1 (so there are n rows)
subplot(y; layout = [1, 2]) # explicit layout by row... plot #1 goes by itself in the first row, plots 2 and 3 split the 2nd row (note the n kw is unnecessary)
subplot(plts, n; nr = -1, nc = -1) # build a layout from existing plots
subplot(plts, layout) # build a layout from existing plots
```
"""
function subplot(args...; kw...)
validateSubplotSupported()
d = KW(kw)
preprocessArgs!(d)
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; kw..., issubplot=true)
# figure out the layout
layoutarg = get(d, :layout, nothing)
if layoutarg != nothing
layout = subplotlayout(layoutarg)
else
n = get(d, :n, -1)
if n < 0
error("You must specify either layout or n when creating a subplot: ", d)
end
layout = subplotlayout(n, get(d, :nr, -1), get(d, :nc, -1))
end
# initialize the individual plots
pkg = backend()
plts = Plot{typeof(pkg)}[]
for i in 1:length(layout)
di = getPlotArgs(pkg, d, i)
di[:subplot] = true
dumpdict(di, "Plot args (subplot $i)")
push!(plts, _create_plot(pkg, di))
end
# create the object and do the plotting
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout, d, false, false, false, (r,c) -> (nothing,nothing))
subplot!(subplt, args...; d...)
subplt
end
# ------------------------------------------------------------------------------------------------
# NOTE: for the subplot calls building from existing plots, we need the first plot to be separate to ensure dispatch calls this instead of the more general subplot(args...; kw...)
# grid layout
function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...)
d = KW(kw)
layout = if haskey(d, :layout)
subplotlayout(d[:layout])
else
subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
end
# layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
subplot(vcat(plt1, plts...), layout, d)
end
# explicit layout
function subplot{P,I<:Integer}(pltsPerRow::AVec{I}, plt1::Plot{P}, plts::Plot{P}...; kw...)
layout = subplotlayout(pltsPerRow)
subplot(vcat(plt1, plts...), layout, KW(kw))
end
# this will be called internally
function subplot{P<:AbstractBackend}(plts::AVec{Plot{P}}, layout::SubplotLayout, d::KW)
validateSubplotSupported()
p = length(layout)
n = sum([plt.n for plt in plts])
subplt = Subplot(nothing, collect(plts), P(), p, n, layout, KW(), false, false, false, (r,c) -> (nothing,nothing))
_preprocess_subplot(subplt, d)
_postprocess_subplot(subplt, d)
subplt
end
# TODO: hcat/vcat subplots and plots together arbitrarily
# ------------------------------------------------------------------------------------------------
function _preprocess_subplot(subplt::Subplot, d::KW, args = ())
validateSubplotSupported()
preprocessArgs!(d)
# for plotting recipes, swap out the args and update the parameter dictionary
args = _apply_recipe(d, args...; d..., issubplot=true)
dumpdict(d, "After subplot! preprocessing")
# get the full plotargs, overriding any new settings
# TODO: subplt.plotargs should probably be merged sooner and actually used
# for color selection, etc. (i.e. if we overwrite the subplot palettes to [:heat :rainbow])
# then we need to overwrite plt[1].plotargs[:color_palette] to :heat before it's actually used
# for color selection!
# first merge the new args into the subplot's plotargs. then process the plot args and merge
# those into the plot's plotargs. (example... `palette = [:blues :reds]` goes into subplt.plotargs,
# then the ColorGradient for :blues/:reds is merged into plot 1/2 plotargs, which is then used for color selection)
for i in 1:length(subplt.layout)
subplt.plts[i].plotargs = getPlotArgs(backend(), merge(subplt.plts[i].plotargs, d), i)
end
merge!(subplt.plotargs, d)
# process links. TODO: extract to separate function
for s in (:linkx, :linky, :linkfunc)
if haskey(d, s)
setfield!(subplt, s, d[s])
delete!(d, s)
end
end
args
end
function _postprocess_subplot(subplt::Subplot, d::KW)
# init (after plot creation)
if !subplt.initialized
subplt.initialized = _create_subplot(subplt, false)
end
# add title, axis labels, ticks, etc
for (i,plt) in enumerate(subplt.plts)
di = plt.plotargs
dumpdict(di, "Updating sp $i")
_update_plot(plt, di)
end
_update_plot_pos_size(subplt, d)
# handle links
subplt.linkx && link_axis(subplt, true)
subplt.linky && link_axis(subplt, false)
# set this to be current
current(subplt)
end
# ------------------------------------------------------------------------------------------------
"""
Adds to a subplot.
"""
# current subplot
function subplot!(args...; kw...)
validateSubplotSupported()
subplot!(current(), args...; kw...)
end
# not allowed:
function subplot!(plt::Plot, args...; kw...)
error("Can't call subplot! on a Plot!")
end
# # this adds to a specific subplot... most plot commands will flow through here
function subplot!(subplt::Subplot, args...; kw...)
# validateSubplotSupported()
d = KW(kw)
args = _preprocess_subplot(subplt, d, args)
# create the underlying object (each backend will do this differently)
# note: we call it once before doing the individual plots, and once after
# this is because some backends need to set up the subplots and then plot,
# and others need to do it the other way around
if !subplt.initialized
subplt.initialized = _create_subplot(subplt, true)
end
groupby = if haskey(d, :group)
extractGroupArgs(d[:group], args...)
else
nothing
end
_add_series_subplot(subplt, d, groupby, args...)
_postprocess_subplot(subplt, d)
# show it automatically?
if haskey(d, :show) && d[:show]
gui()
end
subplt
end
# not allowed:
function plot!(subplt::Subplot, args...; kw...)
error("Can't call plot! on a Subplot!")
end
# given a fully processed KW, add the series to the Plot
function _add_series_subplot(plt::Plot, d::KW)
setTicksFromStringVector(d, d, :x, :xticks)
setTicksFromStringVector(d, d, :y, :yticks)
# this is the actual call to the backend
_add_series(plt.backend, plt, d)
_add_annotations(plt, d)
warnOnUnsupportedScales(plt.backend, d)
end
# handle the grouping... add a series for each group
function _add_series_subplot(subplt::Subplot, d::KW, groupby::GroupBy, args...)
starting_n = subplt.n
for (i, glab) in enumerate(groupby.groupLabels)
tmpd = copy(d)
tmpd[:numUncounted] = subplt.n - starting_n
_add_series_subplot(subplt, tmpd, nothing, args...;
idxfilter = groupby.groupIds[i],
grouplabel = string(glab))
end
end
# process, filter, and add to the correct plot
function _add_series_subplot(subplt::Subplot, d::KW, ::Void, args...;
idxfilter = nothing,
grouplabel = "")
process_inputs(subplt, d, args...)
if idxfilter != nothing
# add the group name as the label if there isn't one passed in
get!(d, :label, grouplabel)
# filter the data
filter_data!(d, idxfilter)
end
kwList, xmeta, ymeta = build_series_args(subplt, d)
# TODO: something useful with meta info?
for (i,di) in enumerate(kwList)
subplt.n += 1
plt = getplot(subplt)
plt.n += 1
# cleanup the dictionary that we pass into the plot! command
di[:show] = false
di[:subplot] = true
for k in (:title, :xlabel, :xticks, :xlims, :xscale, :xflip,
:ylabel, :yticks, :ylims, :yscale, :yflip)
delete!(di, k)
end
dumpdict(di, "subplot! kwList $i")
dumpdict(plt.plotargs, "plt.plotargs before plotting")
_add_series_subplot(plt, di)
end
end
# --------------------------------------------------------------------
# handle "linking" the subplot axes together
# each backend should implement the _remove_axis and _expand_limits methods
function link_axis(subplt::Subplot, isx::Bool)
# collect the list of plots and the expanded limits for those plots that should be linked on this axis
includedPlots = Any[]
# lims = [Inf, -Inf]
lims = Dict{Int,Any}() # maps column to xlim
for (i,(r,c)) in enumerate(subplt.layout)
# shouldlink will be a bool or nothing. if nothing, then use linkx/y (which is true if we get to this code)
shouldlink = subplt.linkfunc(r,c)[isx ? 1 : 2]
if shouldlink == nothing || shouldlink
plt = subplt.plts[i]
# if we don't have this
k = isx ? c : r
if (firstone = !haskey(lims, k))
lims[k] = [Inf, -Inf]
end
isinner = (isx && r < nrows(subplt.layout)) || (!isx && !firstone)
push!(includedPlots, (plt, isinner, k))
_expand_limits(lims[k], plt, isx)
end
end
# do the axis adjustments
for (plt, isinner, k) in includedPlots
if isinner
_remove_axis(plt, isx)
end
(isx ? xlims! : ylims!)(plt, lims[k]...)
end
end
+46
View File
@@ -0,0 +1,46 @@
function Subplot{T<:AbstractBackend}(::T; parent = RootLayout())
Subplot{T}(
parent,
(20mm, 5mm, 2mm, 10mm),
defaultbox,
defaultbox,
KW(),
nothing,
nothing
)
end
plotarea(sp::Subplot) = sp.plotarea
plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox)
Base.size(sp::Subplot) = (1,1)
Base.length(sp::Subplot) = 1
Base.getindex(sp::Subplot, r::Int, c::Int) = sp
leftpad(sp::Subplot) = sp.minpad[1]
toppad(sp::Subplot) = sp.minpad[2]
rightpad(sp::Subplot) = sp.minpad[3]
bottompad(sp::Subplot) = sp.minpad[4]
get_subplot(plt::Plot, sp::Subplot) = sp
get_subplot(plt::Plot, i::Integer) = plt.subplots[i]
get_subplot(plt::Plot, k) = plt.spmap[k]
get_subplot(series::Series) = series.d[:subplot]
get_subplot_index(plt::Plot, idx::Integer) = idx
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(_ -> _ === sp, plt.subplots)
series_list(sp::Subplot) = filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
function should_add_to_legend(series::Series)
!(series.d[:label] == "" || series.d[:seriestype] in (
:hexbin,:histogram2d,:hline,:vline,
:contour,:contour3d,:surface,:wireframe,
:heatmap,:path3d,:scatter3d, :pie, :image
))
end
# ----------------------------------------------------------------------
+65
View File
@@ -0,0 +1,65 @@
const _invisible = RGBA(0,0,0,0)
const _themes = KW(
:default => KW(
:bg => :white,
:bglegend => :match,
:bginside => :match,
:bgoutside => :match,
:fg => :auto,
:fglegend => :match,
:fggrid => :match,
:fgaxis => :match,
:fgtext => :match,
:fgborder => :match,
:fgguide => :match,
)
)
function add_theme(sym::Symbol, theme::KW)
_themes[sym] = theme
end
# add a new theme, using an existing theme as the base
function add_theme(sym::Symbol;
base = :default, # start with this theme
bg = _themes[base][:bg],
bglegend = _themes[base][:bglegend],
bginside = _themes[base][:bginside],
bgoutside = _themes[base][:bgoutside],
fg = _themes[base][:fg],
fglegend = _themes[base][:fglegend],
fggrid = _themes[base][:fggrid],
fgaxis = _themes[base][:fgaxis],
fgtext = _themes[base][:fgtext],
fgborder = _themes[base][:fgborder],
fgguide = _themes[base][:fgguide],
kw...)
_themes[sym] = merge(KW(
:bg => bg,
:bglegend => bglegend,
:bginside => bginside,
:bgoutside => bgoutside,
:fg => fg,
:fglegend => fglegend,
:fggrid => fggrid,
:fgaxis => fgaxis,
:fgtext => fgtext,
:fgborder => fgborder,
:fgguide => fgguide,
), KW(kw))
end
add_theme(:ggplot2,
bglegend = _invisible,
bginside = :lightgray,
fg = :white,
fglegend = _invisible,
fgtext = :gray,
fgguide = :black
)
function set_theme(sym::Symbol)
default(; _themes[sym]...)
end
+67 -30
View File
@@ -1,55 +1,92 @@
# TODO: I declare lots of types here because of the lacking ability to do forward declarations in current Julia
# I should move these to the relevant files when something like "extern" is implemented
typealias AVec AbstractVector
typealias AMat AbstractMatrix
typealias KW Dict{Symbol,Any}
immutable PlotsDisplay <: Display end
abstract AbstractBackend
abstract AbstractPlot{T<:AbstractBackend}
abstract AbstractLayout
typealias KW Dict{Symbol,Any}
# -----------------------------------------------------------
immutable InputWrapper{T}
obj::T
end
wrap{T}(obj::T) = InputWrapper{T}(obj)
Base.isempty(wrapper::InputWrapper) = false
# -----------------------------------------------------------
# Plot
# a single subplot
type Subplot{T<:AbstractBackend} <: AbstractLayout
parent::AbstractLayout
minpad::Tuple # leftpad, toppad, rightpad, bottompad
bbox::BoundingBox # the canvas area which is available to this subplot
plotarea::BoundingBox # the part where the data goes
attr::KW # args specific to this subplot
o # can store backend-specific data... like a pyplot ax
plt # the enclosing Plot object (can't give it a type because of no forward declarations)
end
# -----------------------------------------------------------
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
type Axis
sp::Subplot
d::KW
end
type Extrema
emin::Float64
emax::Float64
end
Extrema() = Extrema(Inf, -Inf)
# -----------------------------------------------------------
typealias SubplotMap Dict{Any, Subplot}
# -----------------------------------------------------------
type Series
d::KW
end
attr(series::Series, k::Symbol) = series.d[k]
attr!(series::Series, v, k::Symbol) = (series.d[k] = v)
# -----------------------------------------------------------
type Plot{T<:AbstractBackend} <: AbstractPlot{T}
o # the backend's plot object
backend::T # the backend type
n::Int # number of series
plotargs::KW # arguments for the whole plot
seriesargs::Vector{KW} # arguments for each series
backend::T # the backend type
n::Int # number of series
attr::KW # arguments for the whole plot
user_attr::KW # raw arg inputs (after aliases). these are used as the input dict in `_plot!`
series_list::Vector{Series} # arguments for each series
o # the backend's plot object
subplots::Vector{Subplot}
spmap::SubplotMap # provide any label as a map to a subplot
layout::AbstractLayout
init::Bool
end
# -----------------------------------------------------------
# Layout
# -----------------------------------------------------------
abstract SubplotLayout
# -----------------------------------------------------------
# Subplot
# -----------------------------------------------------------
type Subplot{T<:AbstractBackend, L<:SubplotLayout} <: AbstractPlot{T}
o # the underlying object
plts::Vector{Plot{T}} # the individual plots
backend::T
p::Int # number of plots
n::Int # number of series
layout::L
plotargs::KW
initialized::Bool
linkx::Bool
linky::Bool
linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y
function Plot()
Plot(backend(), 0, KW(), KW(), Series[], nothing,
Subplot[], SubplotMap(), EmptyLayout(), false)
end
# TODO: make a decision... should plt[1] return the first subplot or the first series??
# Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
Base.getindex(plt::Plot, s::Symbol) = plt.spmap[s]
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
attr(plt::Plot, k::Symbol) = plt.attr[k]
attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
# -----------------------------------------------------------------------
+111 -64
View File
@@ -25,7 +25,7 @@ function histogramHack(; kw...)
edges, midpoints, buckets, counts = binData(d[:y], d[:bins])
d[:x] = midpoints
d[:y] = float(counts)
d[:linetype] = :bar
d[:seriestype] = :bar
d[:fillrange] = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
d
end
@@ -64,7 +64,7 @@ function barHack(; kw...)
d[:x] = x
d[:y] = y
d[:linetype] = :path
d[:seriestype] = :path
d[:fillrange] = fillrange
d
end
@@ -94,12 +94,12 @@ function sticksHack(; kw...)
# change the line args
dLine[:x] = x
dLine[:y] = y
dLine[:linetype] = :path
dLine[:seriestype] = :path
dLine[:markershape] = :none
dLine[:fillrange] = nothing
# change the scatter args
dScatter[:linetype] = :none
dScatter[:seriestype] = :none
dLine, dScatter
end
@@ -114,6 +114,27 @@ function regressionXY(x, y)
regx, regy
end
function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
@show T, size(z)
n, m = size(z)
# idx = 0
colors = ColorGradient(vec(z))
newz = reshape(linspace(0, 1, n*m), n, m)
newz, colors
# newz = zeros(n, m)
# for i=1:n, j=1:m
# push!(colors, T(z[i,j]...))
# newz[i,j] = idx / (n*m-1)
# idx += 1
# end
# newz, ColorGradient(colors)
end
function imageHack(d::KW)
:heatmap in supportedTypes() || error("Neither :image or :heatmap are supported!")
d[:seriestype] = :heatmap
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
end
# ---------------------------------------------------------------
# ------------------------------------------------------------------------------------
@@ -135,13 +156,19 @@ maketuple{T,S}(x::@compat(Tuple{T,S})) = x
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
unzip{T,S}(xy::AVec{Tuple{T,S}}) = [x[1] for x in xy], [y[2] for y in xy]
unzip{T,S,R}(xyz::AVec{Tuple{T,S,R}}) = [x[1] for x in xyz], [y[2] for y in xyz], [z[3] for z in xyz]
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[x[1] for x in xy], T[y[2] for y in xy]
unzip{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t in xy]
unzip{X,Y,Z}(xyz::AVec{Tuple{X,Y,Z}}) = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
unzip{X,Y,U,V}(xyuv::AVec{Tuple{X,Y,U,V}}) = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]]
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[x[1] for x in xyz], T[y[2] for y in xyz], T[z[3] for z in xyz]
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
unzip{T}(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
function _expand_limits(lims, x)
try
@@ -172,13 +199,20 @@ function replaceType(vec, val)
push!(vec, val)
end
function replaceAlias!(d::KW, k::Symbol, aliases::KW)
if haskey(aliases, k)
d[aliases[k]] = pop!(d, k)
end
end
function replaceAliases!(d::KW, aliases::KW)
ks = collect(keys(d))
for k in ks
if haskey(aliases, k)
d[aliases[k]] = d[k]
delete!(d, k)
end
replaceAlias!(d, k, aliases)
# if haskey(aliases, k)
# d[aliases[k]] = d[k]
# delete!(d, k)
# end
end
end
@@ -201,7 +235,7 @@ end
function fakedata(sz...)
y = zeros(sz...)
for r in 2:size(y,1)
y[r,:] = 0.95 * y[r-1,:] + randn(size(y,2))'
y[r,:] = 0.95 * vec(y[r-1,:]) + randn(size(y,2))
end
y
end
@@ -209,6 +243,14 @@ end
isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited
isatom() = isdefined(Main, :Atom) && Main.Atom.isconnected()
function is_installed(pkgstr::AbstractString)
try
Pkg.installed(pkgstr) === nothing ? false: true
catch
false
end
end
istuple(::Tuple) = true
istuple(::Any) = false
isvector(::AVec) = true
@@ -232,8 +274,8 @@ limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :invalid
axis_symbol(letter, postfix) = symbol(letter * postfix)
axis_symbols(letter, postfix...) = map(s -> axis_symbol(letter, s), postfix)
# axis_Symbol(letter, postfix) = Symbol(letter * postfix)
# axis_symbols(letter, postfix...) = map(s -> axis_Symbol(letter, s), postfix)
Base.convert{T<:Real}(::Type{Vector{T}}, rng::Range{T}) = T[x for x in rng]
Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]
@@ -252,6 +294,40 @@ function indices_and_unique_values(z::AbstractArray)
newz, vals
end
# this is a helper function to determine whether we need to transpose a surface matrix.
# it depends on whether the backend matches rows to x (transpose_on_match == true) or vice versa
# for example: PyPlot sends rows to y, so transpose_on_match should be true
function transpose_z(d::KW, z, transpose_on_match::Bool = true)
if d[:match_dimensions] == transpose_on_match
z'
else
z
end
end
function ok(x::Number, y::Number, z::Number = 0)
isfinite(x) && isfinite(y) && isfinite(z)
end
ok(tup::Tuple) = ok(tup...)
# compute one side of a fill range from a ribbon
function make_fillrange_side(y, rib)
frs = zeros(length(y))
for (i, (yi, ri)) in enumerate(zip(y, cycle(rib)))
frs[i] = yi + ri
end
frs
end
# turn a ribbon into a fillrange
function make_fillrange_from_ribbon(kw::KW)
y, rib = kw[:y], kw[:ribbon]
rib = wraptuple(rib)
rib1, rib2 = -first(rib), last(rib)
kw[:ribbon] = nothing
kw[:fillrange] = make_fillrange_side(y, rib1), make_fillrange_side(y, rib2)
end
# ---------------------------------------------------------------
wraptuple(x::@compat(Tuple)) = x
@@ -276,7 +352,7 @@ allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
"""
Allows temporary setting of backend and defaults for Plots. Settings apply only for the `do` block. Example:
```
with(:gadfly, size=(400,400), type=:hist) do
with(:gadfly, size=(400,400), type=:histogram) do
plot(rand(10))
plot(rand(10))
end
@@ -315,7 +391,7 @@ function with(f::Function, args...; kw...)
# # as in: with(:gadfly, :scatter, :legend, :grid) do; ...; end
# # TODO: can we generalize this enough to also do something similar in the plot commands??
# k = :linetype
# k = :seriestype
# if arg in _allTypes
# olddefs[k] = default(k)
# newdefs[k] = arg
@@ -386,6 +462,7 @@ function dumpdict(d::KW, prefix = "", alwaysshow = false)
end
println()
end
DD(d::KW, prefix = "") = dumpdict(d, prefix, true)
function dumpcallstack()
@@ -411,24 +488,32 @@ tovec(v::AbstractVector) = v
tovec(v::Void) = zeros(0)
function getxy(plt::Plot, i::Integer)
d = plt.seriesargs[i]
d = plt.series_list[i].d
tovec(d[:x]), tovec(d[:y])
end
function getxyz(plt::Plot, i::Integer)
d = plt.seriesargs[i]
d = plt.series_list[i].d
tovec(d[:x]), tovec(d[:y]), tovec(d[:z])
end
function setxy!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y] = xy
series = plt.series_list[i]
series.d[:x], series.d[:y] = xy
sp = series.d[:subplot]
expand_extrema!(sp.attr[:xaxis], xy[1])
expand_extrema!(sp.attr[:yaxis], xy[2])
_series_updated(plt, series)
end
function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y], d[:z] = xyz
series = plt.series_list[i]
series.d[:x], series.d[:y], series.d[:z] = xyz
sp = series.d[:subplot]
expand_extrema!(sp.attr[:xaxis], xy[1])
expand_extrema!(sp.attr[:yaxis], xy[2])
expand_extrema!(sp.attr[:zaxis], xy[3])
_series_updated(plt, series)
end
# -------------------------------------------------------
# indexing notation
@@ -535,44 +620,6 @@ end
# ---------------------------------------------------------------
# ---------------------------------------------------------------
# graphs detailing the features that each backend supports
function supportGraph(allvals, func)
vals = reverse(sort(allvals))
bs = sort(backends())
x = ASCIIString[]
y = ASCIIString[]
for val in vals
for b in bs
supported = func(Plots._backend_instance(b))
if val in supported
push!(x, string(b))
push!(y, string(val))
end
end
end
n = length(vals)
scatter(x, y, m=:rect, ms=10, size=(300,100+18*n), leg=false)
end
supportGraphArgs() = supportGraph(_allArgs, supportedArgs)
supportGraphTypes() = supportGraph(_allTypes, supportedTypes)
supportGraphStyles() = supportGraph(_allStyles, supportedStyles)
supportGraphMarkers() = supportGraph(_allMarkers, supportedMarkers)
supportGraphScales() = supportGraph(_allScales, supportedScales)
supportGraphAxes() = supportGraph(_allAxes, supportedAxes)
function dumpSupportGraphs()
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
supportGraphMarkers, supportGraphScales, supportGraphAxes)
plt = func()
png(joinpath(Pkg.dir("ExamplePlots"), "docs", "examples", "img", "supported", "$(string(func))"))
end
end
# ---------------------------------------------------------------
# ---------------------------------------------------------------
@@ -593,9 +640,9 @@ mm2px(mm::Real) = float(px / MM_PER_PX)
"Smallest x in plot"
xmin(plt::Plot) = minimum([minimum(d[:x]) for d in plt.seriesargs])
xmin(plt::Plot) = minimum([minimum(series.d[:x]) for series in plt.series_list])
"Largest x in plot"
xmax(plt::Plot) = maximum([maximum(d[:x]) for d in plt.seriesargs])
xmax(plt::Plot) = maximum([maximum(series.d[:x]) for series in plt.series_list])
"Extrema of x-values in plot"
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))
+3 -3
View File
@@ -1,11 +1,10 @@
julia 0.4
RecipesBase
Colors
Reexport
Requires
Measures
FactCheck
Cairo
Gadfly
Images
PyPlot
@osx QuartzImageIO
@@ -14,3 +13,4 @@ DataFrames
RDatasets
VisualRegressionTests
UnicodePlots
Glob
+57 -26
View File
@@ -1,6 +1,6 @@
using VisualRegressionTests
using ExamplePlots
# using ExamplePlots
import DataFrames, RDatasets
@@ -14,6 +14,7 @@ end
using Plots, FactCheck
using Glob
default(size=(500,300))
@@ -21,37 +22,67 @@ default(size=(500,300))
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
const _current_plots_version = v"0.7.0"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
Plots._debugMode.on = debug
example = Plots._examples[idx]
info("Testing plot: $pkg:$idx:$(example.header)")
backend(pkg)
backend()
# first
Plots._debugMode.on = debug
example = ExamplePlots._examples[idx]
info("Testing plot: $pkg:$idx:$(example.header)")
backend(pkg)
backend()
# ensure consistent results
srand(1234)
# ensure consistent results
srand(1234)
# reference image directory setup
# refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
refdir = Pkg.dir("PlotReferenceImages", "Plots", string(pkg))
fn = "ref$idx.png"
# reference image directory setup
refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
# firgure out version info
G = glob(relpath(refdir) * "/*")
# @show refdir fn G
versions = map(fn -> VersionNumber(split(fn,"/")[end]), G)
versions = reverse(sort(versions))
# @show refdir fn versions
# test function
func = (fn, idx) -> begin
map(eval, example.exprs)
png(fn)
end
reffn = nothing
newdir = joinpath(refdir, string(_current_plots_version))
newfn = joinpath(newdir, fn)
for v in versions
try
tmpfn = joinpath(refdir, string(v), fn)
# @show "trying", tmpfn
f = open(tmpfn)
reffn = tmpfn
end
end
try
run(`mkdir -p $refdir`)
catch err
display(err)
end
reffn = joinpath(refdir, "ref$idx.png")
# now we have the fn (if any)... do the comparison
# @show reffn
if reffn == nothing
reffn = newfn
end
# @show reffn
# return
# the test
vtest = VisualTest(func, reffn, idx)
test_images(vtest, popup=popup, sigma=sigma, eps=eps)
# test function
func = (fn, idx) -> begin
map(eval, example.exprs)
png(fn)
end
try
run(`mkdir -p $newdir`)
catch err
display(err)
end
# reffn = joinpath(refdir, "ref$idx.png")
# the test
vtest = VisualTest(func, reffn, idx)
test_images(vtest, popup=popup, sigma=sigma, eps=eps, newfn = newfn)
end
function image_comparison_facts(pkg::Symbol;
@@ -60,7 +91,7 @@ function image_comparison_facts(pkg::Symbol;
debug = false, # print debug information?
sigma = [1,1], # number of pixels to "blur"
eps = 1e-2) # acceptable error (percent)
for i in 1:length(ExamplePlots._examples)
for i in 1:length(Plots._examples)
i in skip && continue
if only == nothing || i in only
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
+28 -13
View File
@@ -7,30 +7,30 @@ srand(1234)
default(show=false, reuse=true)
img_eps = 5e-2
facts("Gadfly") do
@fact gadfly() --> Plots.GadflyBackend()
@fact backend() --> Plots.GadflyBackend()
@fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
@fact plot(Int[1,2,3], rand(3)) --> not(nothing)
@fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
@fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
image_comparison_facts(:gadfly, skip=[4,6,19,23,24,27], eps=img_eps)
end
# facts("Gadfly") do
# @fact gadfly() --> Plots.GadflyBackend()
# @fact backend() --> Plots.GadflyBackend()
#
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
# @fact plot(Int[1,2,3], rand(3)) --> not(nothing)
# @fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
# @fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
#
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], eps=img_eps)
# end
facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend()
image_comparison_facts(:pyplot, skip=[19], eps=img_eps)
image_comparison_facts(:pyplot, skip=[30], eps=img_eps)
end
facts("GR") do
@fact gr() --> Plots.GRBackend()
@fact backend() --> Plots.GRBackend()
@linux_only image_comparison_facts(:gr, skip=[24], eps=img_eps)
@linux_only image_comparison_facts(:gr, skip=[30], eps=img_eps)
end
facts("Plotly") do
@@ -70,6 +70,21 @@ end
facts("Axes") do
p = plot()
axis = p.subplots[1][:xaxis]
@fact typeof(axis) --> Axis
@fact Plots.discrete_value!(axis, "HI") --> (0.5, 1)
@fact Plots.discrete_value!(axis, :yo) --> (1.5, 2)
@fact extrema(axis) --> (0.5,1.5)
@fact 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])
@fact extrema(axis) --> (0.5, 7.5)
end
FactCheck.exitstatus()
+6 -1
View File
@@ -7,7 +7,9 @@ Pkg.build("ImageMagick")
Pkg.clone("GR")
Pkg.build("GR")
Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
# Pkg.clone("https://github.com/JuliaPlots/RecipesBase.jl.git")
# Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
# Pkg.clone("https://github.com/JunoLab/Blink.jl.git")
# Pkg.build("Blink")
@@ -15,6 +17,9 @@ Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
# Blink.AtomShell.install()
# Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")
Pkg.checkout("RecipesBase")
Pkg.clone("VisualRegressionTests")
ENV["PYTHON"] = ""
Pkg.add("PyPlot")
Pkg.build("PyPlot")