Compare commits

..

210 Commits

Author SHA1 Message Date
Michael Krabbe Borregaard 45a04d5309 Merge pull request #713 from oschulz/new-hist-dev
Change histogram implementation, use StatsPlots, add new histogram st…
2017-03-01 22:32:06 +01:00
Oliver Schulz 6420f6fdc9 Conform to Plots.jl coding style 2017-03-01 17:33:22 +01:00
Oliver Schulz 19a9726e61 Change histogram implementation, use StatsPlots, add new histogram styles
New series recipes for binned data:

* barbins
* scatterbins
* stepbins

New series recipes for histogram:

* barhist (histogram is now an alias for this)
* scatterhist
* stephist

Supports plotting 1D and 2D StatsBase histograms, seriestype can be set to
bar(bins), scatter(bins) or step(bins).

Also adds support for some common auto-binning modes:

* :sturges, :auto - Sturges' formula
* :sqrt - Square-root choice
* :rice - Rice Rule
* :scott - Scott's normal reference rule
* :fd - Freedman–Diaconis rule

Maybe these could be contributed to StatsBase at some point.

Error bars currently don't work correctly for scatterbins and scatterhist,
due to problem with manipulating error bars in a series recipe, but do work
for "plot(h::StatsBase.Histogram, seriestype = :scatter)" (works around
the problem by calling scatter directly, it seems that error bars can be
manipulated correctly in a type recipe).
2017-03-01 14:24:00 +01:00
Michael Krabbe Borregaard 34c0361d1a Merge pull request #707 from mkborregaard/new-histogram
fix uneven bins
2017-02-26 08:33:30 +01:00
Michael K. Borregaard 24c27737c1 fix uneven bins 2017-02-26 08:32:52 +01:00
Tom Breloff 9472c0c5e5 skip pyplot image test 2017-02-24 15:19:41 -05:00
Tom Breloff 89fe3c9150 Merge remote-tracking branch 'origin/dev' 2017-02-24 11:02:50 -05:00
Tom Breloff aec08c74e0 Merge pull request #697 from lwabeke/add_clims_for_plotly
Attempt to add support for clims to plotly backend
2017-02-22 11:02:45 -05:00
Tom Breloff 48e8cb5eb1 Merge pull request #698 from joshday/jd_gr_label
Change GR label for scatterplot
2017-02-22 10:58:49 -05:00
Josh Day fd947ab887 only one, smaller gr marker 2017-02-22 10:40:16 -05:00
Leon Wabeke 5395b41852 Attempt to add support for clims to plotly backend
Added support for clims, seems to work on heatmap, surface and contour.
On surface zlims be still be set to be different from clims.
2017-02-22 15:57:41 +02:00
Tom Breloff bdc815066e Merge remote-tracking branch 'origin/dev' 2017-02-21 12:12:35 -05:00
Tom Breloff 44f4f22fdd Merge pull request #683 from sebastianpech/master
Fix halign for annotations in pgfplots backend
2017-02-14 15:11:28 -05:00
Sebastian Pech 5ae2044d02 Fix halign for annotations in pgfplots backend
PGFPlot uses inverted right and left parameters.
2017-02-14 21:04:13 +01:00
Tom Breloff 5e80a05f32 Merge pull request #679 from jheinen/dev
gr: added support for hexbin plots
2017-02-14 14:20:27 -05:00
Josef Heinen eb7534d319 gr: corrected alignment of rotated axes labels 2017-02-14 09:55:46 +01:00
Josef Heinen 471b09bd6c gr: added support for hexbin plots 2017-02-10 11:24:48 +01:00
Tom Breloff a4549cbfa2 Merge pull request #674 from mkborregaard/better-docs
WIP: lookup attributes at the console
2017-02-09 09:09:18 -05:00
Michael K. Borregaard d663267cde improved println call 2017-02-09 11:12:46 +01:00
Michael K. Borregaard 35a56962fc more concise output 2017-02-09 10:40:08 +01:00
Michael K. Borregaard 82161e60bc removed unnecessary Plots qualifier 2017-02-09 00:12:16 +01:00
Michael K. Borregaard 51a47cb2b1 Added basic support for searching attributes at the console 2017-02-08 23:51:15 +01:00
Michael K. Borregaard 9804debe86 Merge remote-tracking branch 'JuliaPlots/master' 2017-02-08 17:22:33 +01:00
Tom Breloff 1555876280 Merge pull request #669 from YongHee-Kim/master
enable standalone window for plotlyjs() backend
2017-02-06 09:40:43 -05:00
Tom Breloff 1d08e6619b Merge pull request #668 from ma-laforge/inspectdr
Make compatible with InspectDR v0.1.4.
2017-02-06 09:10:27 -05:00
YongHee Kim 0880487be2 enable standalone window for plotlyjs() backen 2017-02-06 21:29:41 +09:00
Tom Breloff 2ac4ffc576 Merge pull request #663 from fuzzybear3965/master
path spaces fix stolen from http://stackoverflow.com/a/13692919
2017-02-03 10:24:36 -05:00
Tom Breloff 7b2a0a92c4 Merge pull request #667 from sebastianpech/master
Fix/Extend pgfplots functions
2017-02-03 10:23:02 -05:00
Sebastian Pech d883274586 Fix comments 2017-02-03 08:25:55 +01:00
Sebastian Pech 803de95367 Merge branch 'master' of https://github.com/JuliaPlots/Plots.jl 2017-02-03 08:22:23 +01:00
MA Laforge 3ca39728d8 Make compatible with InspectDR v0.1.4. 2017-02-02 20:38:34 -05:00
Sebastian Pech 1111000217 Add support for ticks rotation 2017-02-02 17:39:34 +01:00
Sebastian Pech dd505fedcc Add support for polar axis 2017-02-02 16:43:30 +01:00
Sebastian Pech 48ff2fb8b8 Merge branch 'master' of https://github.com/JuliaPlots/Plots.jl 2017-02-02 16:06:59 +01:00
Sebastian Pech d5eafad48e Add annotations and series_annotations to pgfplots 2017-02-02 16:05:17 +01:00
John Rinehart 2e1c86c0c7 path spaces fix stolen from http://stackoverflow.com/a/13692919
Spaces in Windows paths resulted in improper DOS command. Fix designed
according to the above-referenced StackOverflow post.
2017-01-31 23:57:29 -05:00
Tom Breloff c728bf52b8 Merge pull request #662 from ma-laforge/inspectdr
Fix inversion in padding.
2017-01-28 11:55:11 -05:00
MA Laforge 95a26f5080 Fix inversion in padding. 2017-01-28 11:47:10 -05:00
Sebastian Pech 85eaef6aa9 Merge branch 'master' of https://github.com/JuliaPlots/Plots.jl 2017-01-26 15:19:57 +01:00
Tom Breloff dee160f2b8 Merge pull request #656 from ma-laforge/inspectdr
Add support for layout & plot size.
2017-01-25 11:47:58 -05:00
Tom Breloff 6d38e52321 Merge pull request #659 from ma-laforge/pyplot_qtinit
Fix exception calling backend() in _initialize_backend().
2017-01-25 11:47:31 -05:00
MA Laforge caed94328f Fix exception calling backend() in _initialize_backend(). 2017-01-25 11:40:47 -05:00
MA Laforge ebf92d3033 Add support for layout & plot size.
Also:
Add basic support for polar plots.
Add support for plot title & background color of entire figure.
2017-01-23 21:25:55 -05:00
Sebastian Pech f8b84185ac Add rounding and obtain plot height from layout bbox 2017-01-23 14:28:26 +01:00
Sebastian Pech caf86686dd Fix yshift of subplots.
Calculation of coordinate transformation from bb to pgf did not consider the
height of the plot.
2017-01-23 13:56:40 +01:00
Sebastian Pech bcd5d9ef90 Fix issue where every series is plotted with a fillcolor 2017-01-22 16:57:28 +01:00
Sebastian Pech 1a91eacdcb Enable histogram plotting in pgfplot 2017-01-20 15:26:54 +01:00
Tom Breloff 0421fe9a82 Merge pull request #651 from huckl3b3rry87/pull-request/43a21364
Pull request/43a21364
2017-01-19 22:02:55 -05:00
Huckleberry Febbo 43a213643b changed code so it does not automatically change qt version 2017-01-19 18:04:09 -05:00
Huckleberry Febbo cd8939fe0b finished fixing segfault issue when qt>v4.8.5 on Ubuntu using pyplot 2017-01-19 17:32:37 -05:00
Huckleberry Febbo c250d7c073 working on fixing pyplot issue for ubuntu 2017-01-19 14:45:16 -05:00
Tom Breloff 3b570edea1 added back precompilation 2017-01-13 09:33:02 -05:00
Tom Breloff 199a3306db fix support for fill_z in pyplot 2017-01-11 10:37:54 -05:00
Tom Breloff 3236e06fb9 wrap_surfaces for 3 matrices 2017-01-10 11:35:57 -05:00
Tom Breloff 492618ae67 Merge pull request #641 from tbreloff/surfacecolor
Add fill_z attribute and plotly impl
2017-01-09 16:17:49 -05:00
Naoki Saito c5737db4f5 Add fill_z attribute and plotly impl 2017-01-09 16:03:14 -05:00
Tom Breloff 40b49b9873 Merge pull request #631 from ma-laforge/inspectdr
Inspectdr
2017-01-09 15:18:04 -05:00
MA Laforge 1fa34574e8 More fixes requested by Tom:
Add add_backend_string().
Remove un-necessary test: true == plt[:show].
2017-01-09 15:14:39 -05:00
Michael K. Borregaard 4c2db68037 Merge remote-tracking branch 'tbreloff/master' 2017-01-09 11:42:33 -07:00
Tom Breloff 1c522159df Merge remote-tracking branch 'origin/master' into dev 2017-01-09 13:22:07 -05:00
Tom Breloff 967dca7912 Merge remote-tracking branch 'origin/dev' into dev 2017-01-09 13:20:18 -05:00
Tom Breloff 7d66c502f4 support tex in pgfplots; closes #637 2017-01-09 13:19:37 -05:00
Tom Breloff 9b9e2fe6cd Merge pull request #636 from jheinen/dev
gr: use GKSwstype environment (required for QML)
2017-01-09 12:07:09 -05:00
Tom Breloff 47ee0a4c97 Merge pull request #633 from tbreloff/dev
fix arrays of funcs; closes #632
2017-01-09 12:06:45 -05:00
Tom Breloff f10bda463c Merge pull request #635 from anowacki/nowacki/default-font-family
Use `sans-serif` as default font family (fix #593)
2017-01-09 12:05:53 -05:00
Josef Heinen 72d336265b gr: use GKSwstype environment (required for QML) 2017-01-06 19:48:41 +01:00
Andy Nowacki a087a6694b Use sans-serif as default font family (fix #593)
Rather than specifying a specific typeface, set the font
*family* in the `family` field of `Font` as `sans-serif`
by default.

Fixes #593.
2017-01-06 16:45:40 +00:00
Michael K. Borregaard 5eaf6b2adc Merge remote-tracking branch 'tbreloff/master' 2017-01-06 14:50:11 +01:00
Thomas Breloff 57840a37d0 fix for func assert 2017-01-05 15:44:56 -07:00
Thomas Breloff aedcb372f6 forgot utils.jl 2017-01-05 15:30:34 -07:00
Thomas Breloff 7f554d3c84 fix arrays of funcs; closes #632 2017-01-05 15:25:08 -07:00
MA Laforge edf1daa7d7 Improve gui handling & remove global state.
Also:
Add AVec support for colors.
Move init code back to _initialize_backend.
2017-01-05 14:39:50 -05:00
Thomas Breloff f554bf925e try catch for func recipe, move adapted_grid to f,xmin,xmax recipe; closes #610; closes #621 2017-01-05 10:18:47 -07:00
Tom Breloff 9676309ba2 Merge pull request #623 from KristofferC/kc/plot_adapt
use adapted_grid from PlotUtilities for single functions
2017-01-05 09:56:37 -07:00
MA Laforge c78444fc24 Improve support for Shapes, markers & colors.
Details:
Control font & background color.
Support arbitrary marker shapes.
Support more pre-defined markers.
Support Shapes in plots.
Support ln-plots.

Also:
Fix ncolumns.
Update supported section.
2017-01-04 17:41:05 -05:00
Michael K. Borregaard 4ab51d4bfa Merge remote-tracking branch 'tbreloff/master' 2017-01-02 10:16:07 +01:00
MA Laforge e381c13dc6 Initial support for InspectDR. 2016-12-31 15:41:11 -05:00
Kristoffer Carlsson ec31898f25 use adapted_grid from PlotUtilities for single functions 2016-12-25 00:38:58 +01:00
Tom Breloff a88d78a2e9 Merge pull request #604 from pabloferz/pz/plotlyjs
Proper "id" synchronization of plotlyjs plots
2016-12-24 19:05:44 +01:00
Tom Breloff 92430afba9 Merge pull request #618 from ChrisRackauckas/patch-2
Fix precompile disable
2016-12-24 19:04:38 +01:00
Christopher Rackauckas 8f60a4df8d Fix precompile disable 2016-12-19 14:49:44 -08:00
Michael K. Borregaard 08c9e627ab Merge remote-tracking branch 'tbreloff/master' 2016-12-16 10:10:39 +01:00
Tom Breloff c092d3cab4 Merge branch 'master' into dev 2016-12-13 14:54:46 -05:00
Pablo Zubieta 46ad076751 Proper id synchronization of plotlyjs plots 2016-12-10 23:49:17 -06:00
Thomas Breloff 07731614a9 fix ComplexPlot; closes #598 2016-12-02 20:37:35 -05:00
Tom Breloff d8ffd729f4 Merge pull request #597 from tbreloff/dev
Output/Animation improvements
2016-12-01 15:26:21 -05:00
Tom Breloff b23d94eeb8 inline display 2016-12-01 15:22:51 -05:00
Tom Breloff 4ed7144ca4 animation fixes: show for videos, imagemagick init 2016-12-01 10:23:41 -05:00
Tom Breloff ad924e8cc9 Merge pull request #595 from tbreloff/dev
buildanimation: mov and mp4
2016-11-30 09:54:26 -05:00
Tom Breloff aa929ec24f buildanimation: mov and mp4 2016-11-30 09:50:28 -05:00
Tom Breloff 07f7fd3c7a Merge remote-tracking branch 'origin/master' into dev 2016-11-28 12:58:08 -05:00
Tom Breloff 83beca098b Merge pull request #591 from pabloferz/pz/plotlyjs
Fix embeding of PlotlyJS plots in Jupyter
2016-11-28 12:57:35 -05:00
Pablo Zubieta 2c3d35d249 Fix embeding of PlotlyJS plots in Jupyter 2016-11-28 11:53:14 -06:00
Tom Breloff 90e655d979 use_remote for plotly html output 2016-11-28 11:02:32 -05:00
Tom Breloff 8204b51213 Merge remote-tracking branch 'origin/master' into dev 2016-11-28 10:13:40 -05:00
Tom Breloff c3f40d0555 allow end in Plot/Subplot getindex; closes #587 2016-11-28 10:13:00 -05:00
Tom Breloff cbd134987b Merge pull request #590 from piever/pull-request/167fd687
Added savefig html
2016-11-27 23:26:37 -05:00
Pietro Vertechi 167fd68774 Added savefig html 2016-11-28 04:15:09 +00:00
Tom Breloff 2b8cc7cfde Merge pull request #589 from piever/pull-request/62280fb2
Allow errorbars in plots with categorical x axis (see #549 )
2016-11-26 21:56:44 -05:00
Pietro Vertechi 62280fb24b Modified error_coords to allow errorbars in plots with categorical x-axis 2016-11-26 01:49:48 +00:00
Tom Breloff 2a7604ef5f gr: fix 3d grid 2016-11-22 13:45:11 -05:00
Tom Breloff 0b47aafd14 Merge pull request #585 from tbreloff/sd/dev
use more unique name
2016-11-22 12:36:09 -05:00
Simon Danisch 24eefbed2b use more unique name 2016-11-22 18:35:12 +01:00
Tom Breloff ab7a73c879 Merge pull request #584 from tbreloff/sd/dev
fix closing window
2016-11-22 11:25:13 -05:00
Simon Danisch 81e0868725 fix closing window 2016-11-22 17:16:01 +01:00
Tom Breloff eba1e366bb _canvas_type in unicodeplots 2016-11-21 14:49:39 -05:00
Tom Breloff e9ccdef447 Merge pull request #579 from piever/pull-request/bdce27dd
Implemented legend position in plotly
2016-11-21 11:21:39 -05:00
Pietro Vertechi bdce27dd7b Implemented legend position in plotly 2016-11-20 15:35:58 +00:00
Tom Breloff 436ab89d2d Merge pull request #570 from tbreloff/sd/dev
refactors + rename for new GLWindow version
2016-11-18 09:17:11 -05:00
SimonDanisch 74c2db4909 rebase 2016-11-18 00:49:33 +01:00
SimonDanisch e12c3f332e add missing to_vec 2016-11-18 00:44:54 +01:00
SimonDanisch 34a64db91d changes for markers etc 2016-11-18 00:44:53 +01:00
SimonDanisch e2a4a1e2ea clean up signals propperly 2016-11-18 00:44:53 +01:00
SimonDanisch 22f12316ef refactors + rename from GLWindow 2016-11-18 00:44:53 +01:00
Tom Breloff e24acece49 shape_coords fixes; makekw 2016-11-17 13:45:49 -05:00
Tom Breloff dd7462bd3f travis 2016-11-17 13:07:58 -05:00
Tom Breloff da7e0cba3e deprecate curve_points and shape_coords and extend coords; move directed_curve/shorten_segment/random_control_point to PlotRecipes 2016-11-17 13:00:56 -05:00
Tom Breloff 50219aa872 Merge pull request #573 from swt30/subplot-methods
Enable shorthand methods on subplots too
2016-11-17 12:47:16 -05:00
Tom Breloff 000660b43a return font from EachAnn; several annotation fixes; tests fixes 2016-11-17 11:28:33 -05:00
Tom Breloff fbeaa59f18 curve offset 2016-11-16 13:55:07 -05:00
Scott Thomas 578e601368 Enable shorthand methods on subplots too
Shorthand methods like `title!` and `annotate!` currently only
work on fully-fledged plots; this change broadens the method
definitions so that they work on subplots too.
2016-11-16 17:28:39 +00:00
Tom Breloff 247fe27f03 limit subplot attr when setting subplot kw 2016-11-16 09:23:39 -05:00
Tom Breloff ee2e807cc2 isnothing 2016-11-15 17:05:26 -05:00
Tom Breloff c4a880c870 plotlyjs surf fix 2016-11-14 16:45:01 -05:00
Tom Breloff 4eb6fac881 series_annotations scalefactor 2016-11-14 13:29:32 -05:00
Tom Breloff 0ab37f49fa plotlyjs reuse window; plotlyjs closeall; closes #567 2016-11-14 09:43:04 -05:00
Tom Breloff 376ee02041 fix series_annotations size 2016-11-11 17:43:46 -05:00
Tom Breloff c779dc6577 fix maker-->marker 2016-11-11 13:17:50 -05:00
Tom Breloff ad71e81338 rtri/ltri shapes; go between in directed_curve 2016-11-11 11:10:06 -05:00
Thomas Breloff 9f2ba09fc4 fix colors example 2016-11-09 17:08:28 -05:00
Simon 1e6cee2c5d Merge pull request #564 from tbreloff/sd/dev
Sd/dev
2016-11-09 17:35:38 +01:00
Thomas Breloff 4dc742489d fix cycle in fillrange 2016-11-08 11:32:17 -05:00
SimonDanisch d751b985ac incorperate new text scaling 2016-11-08 16:55:25 +01:00
SimonDanisch 8a58190cce fix mesh particle size issue 2016-11-07 23:50:09 +01:00
SimonDanisch 612ca15fc0 variable change from GLVisualize 2016-11-07 23:50:09 +01:00
Thomas Breloff 2200b76054 directed_curve 2016-11-07 00:20:14 -05:00
Thomas Breloff 08771395b8 rewrite directed_curve 2016-11-06 11:46:22 -05:00
Thomas Breloff e3c06cad57 cleanup and comments 2016-11-04 15:54:26 -04:00
Thomas Breloff 4dfadeaf15 series_annotations refactor and gr/pyplot fixes 2016-11-04 15:39:53 -04:00
Thomas Breloff af1896dc36 don't export Axis/Plot/etc; closes #552; fix series_annotations; support SeriesAnnotations in gr; fix scale/translate 2016-11-03 22:02:23 -04:00
Thomas Breloff 350ffdee25 Base.cycle to cycle; text_size; animate; MixedMeasures; SeriesAnnotations 2016-11-03 13:29:20 -04:00
Thomas Breloff 281b92c262 gr arrow tail 2016-11-02 14:09:31 -04:00
Thomas Breloff 75f1cf8cbb complexplot; Arrow side; gr arrows 2016-11-02 14:00:03 -04:00
Thomas Breloff bf0f44773f basic support for arrow in GR 2016-11-02 13:22:50 -04:00
Thomas Breloff dfb89ef7f5 scalefontsize/scalefontsizes; closes #560 2016-11-02 10:20:33 -04:00
Thomas Breloff 5ef7126a3a meta_checkout; closes #544 2016-11-02 10:00:38 -04:00
Tom Breloff c9835a53e3 Merge pull request #559 from tbreloff/sd/dev
fix mesh particle size bug and better window creation
2016-11-01 12:08:21 -04:00
Tom Breloff 5ea48e6fcd Merge pull request #557 from mkborregaard/fix-markerstrokecolor-doc
fix docs for msc
2016-11-01 12:07:54 -04:00
Tom Breloff 1d1a703e3c Merge pull request #554 from ChrisRackauckas/patch-2
Remove precompilation
2016-11-01 12:07:33 -04:00
SimonDanisch 8110fec4ef fix mesh particle + better window creation 2016-11-01 12:28:37 +01:00
Michael K. Borregaard 67ecf4e350 fix docs for msc
specifies msc defaults to foreground_color_subplots
2016-10-31 14:21:49 +01:00
Christopher Rackauckas e8ed0a3034 Remove precompilation
Precompilation has never played nice with Plots, but now it has gotten even worse. Getting rid of precompilation fixes #543 and in normal use cases, the vast majority of time to plot comes from importing the other plotting libraries, so I did not notice a significant performance loss.
2016-10-28 22:21:40 -07:00
SimonDanisch add962c8ea Merge branch 'dev' into sd/dev 2016-10-28 11:39:52 +02:00
SimonDanisch 9bed393210 changes for new GLVisualize 2016-10-28 11:39:10 +02:00
Thomas Breloff 3aac2b2895 check for empty labs 2016-10-25 22:33:53 -04:00
Thomas Breloff aa6814a6d3 rename update! to attr! 2016-10-18 20:45:56 -04:00
Thomas Breloff d291582ad9 fix gr flipped heatmap 2016-10-14 13:49:47 -04:00
Thomas Breloff ebc591747a fix empty axes; closes #536 2016-10-14 10:42:09 -04:00
SimonDanisch 456602b79f various fixes 2016-10-14 00:39:54 +02:00
Thomas Breloff b1835a91af gr extrema fix; themes gradient_name fix 2016-10-13 13:24:25 -04:00
Thomas Breloff 0a8d88a4fe NEWS; bump version 2016-10-13 09:56:27 -04:00
Thomas Breloff e2adcbb486 fix gradient in theme 2016-10-13 09:53:25 -04:00
Thomas Breloff aee13d295c fix plotlyjs lims: apply scalefunc 2016-10-12 15:11:51 -04:00
Thomas Breloff 6677e0e0a0 fix link_axes for nested singleton subplot 2016-10-12 14:52:30 -04:00
Thomas Breloff 9d64914e62 reset theme with :none/:default 2016-10-12 13:34:07 -04:00
Thomas Breloff d136977fc4 fix date/datetime 2016-10-12 13:25:19 -04:00
Thomas Breloff 7fb995094e added closeall for gr/pyplot 2016-10-12 11:28:43 -04:00
Thomas Breloff f002f66890 add PlotThemes to REQUIRE; add warning for DataFrames 2016-10-12 09:30:49 -04:00
Thomas Breloff 9de3ed8cf0 remove Compat from REQUIRE 2016-10-11 19:20:07 -04:00
Thomas Breloff 7ff843ec1a msw alias 2016-10-11 16:50:58 -04:00
Thomas Breloff b181788dc2 add PlotThemes dep, change set_theme to theme 2016-10-11 15:36:56 -04:00
Thomas Breloff 81302c1e9d NEWS; bump version 2016-10-11 12:43:38 -04:00
Thomas Breloff 77fbf3eb51 use drawimage for heatmap 2016-10-11 10:32:51 -04:00
Tom Breloff 837c0c06ed Merge pull request #530 from jheinen/dev
gr: changed heatmap code
2016-10-11 09:32:53 -04:00
Josef Heinen 93e7bc2a2e gr: changed heatmap code 2016-10-11 15:03:31 +02:00
Thomas Breloff 702ab3c504 remove plotly png output... point to plotlyjs 2016-10-10 07:34:51 -04:00
Thomas Breloff b9dea42be6 fix Date recipe; closes #529 2016-10-10 07:02:10 -04:00
Thomas Breloff ae608d4d78 staged date fix, still not working 2016-10-10 01:08:29 -04:00
Thomas Breloff 5e8c88d657 spy recipes 2016-10-09 00:52:53 -04:00
Thomas Breloff 9bbe0f9414 gr: image transpose fix 2016-10-08 23:19:26 -04:00
Thomas Breloff f290748c72 gr: don't set GKS_WSTYPE for windows/use_default 2016-10-08 23:03:07 -04:00
Thomas Breloff 96821f6303 remove spy; support spy seriestype in unicodeplots; recipes cleanup 2016-10-08 22:39:49 -04:00
Thomas Breloff d290c6702a handle series_attr merge and add_defaults when combining subplots; closes #526 2016-10-08 22:00:22 -04:00
Thomas Breloff 2877cb7160 gr: replaced withenv calls with _gr_wstype and gr_set_output 2016-10-07 14:54:26 -04:00
Thomas Breloff 14fd3c94b5 fix plotly horizontal bars 2016-10-07 13:49:00 -04:00
Thomas Breloff f615a5d05e experimenting with gr heatmap 2016-10-07 12:53:18 -04:00
Thomas Breloff 04a4357684 fix plotly aspect_ratio and reword arg desc; closes #523 2016-10-07 12:14:06 -04:00
Thomas Breloff a4c25321d8 histogram2d puts NaN for 0 count; fix pyplot NaNs in heatmap; change default markersize to 4 2016-10-07 11:33:02 -04:00
Thomas Breloff 068282af55 aliases for subplot colors; default(d,k); fg_color/fg_color_sp; fix tick_padding for nothing/false 2016-10-06 12:45:21 -04:00
Tom Breloff 24078661c9 Merge pull request #522 from dennybiasiolli/dennybiasiolli-patch-1
Fix typo in args.jl
2016-10-06 09:01:28 -04:00
Tom Breloff 54670d4259 Merge pull request #517 from fedxa/dpi-fix
Fix for dpi in pyplot backend
2016-10-06 09:01:05 -04:00
Denny Biasiolli bc276acb96 Fix typo in args.jl
Fix #521
2016-10-06 14:29:24 +02:00
Fedor Bezrukov 9e8869acf1 Fix for dpi in pyplot backend 2016-10-03 00:37:50 +03:00
Thomas Breloff c39f5e1483 plotly: always set tickangle 2016-09-30 13:48:27 -04:00
Thomas Breloff f41a09b68e plotly: support aspect_ratio 2016-09-30 13:44:37 -04:00
Thomas Breloff 4416fc919e improve tick_padding and fix rotation for plotly/gr 2016-09-30 12:46:38 -04:00
Thomas Breloff 5ed913ba5e plotly: plotly_surface_data and wireframes; closes #479 2016-09-30 11:25:29 -04:00
Thomas Breloff f4329a9483 support color for plotly bar; closes #513 2016-09-30 10:40:26 -04:00
Thomas Breloff 24a4c31f0d widen bar plots; improve ggplot2 theme 2016-09-29 16:14:46 -04:00
Thomas Breloff 2b1c4685d2 glvisualize MetaPkg 2016-09-27 15:55:28 -04:00
Thomas Breloff 980a92c625 remove show; fix plotlyjs _series_updated, closes #505 2016-09-27 13:05:28 -04:00
Thomas Breloff 54923c082c reset_extrema; setxyz for z matrix 2016-09-27 12:51:20 -04:00
Thomas Breloff 7ce783705a Atom match PlotPane size and support plotlyjs 2016-09-26 11:52:24 -04:00
Thomas Breloff e23cf3377e gr wstype default; gr tighten tick pos 2016-09-24 23:16:35 -04:00
Thomas Breloff bed9beafdc fix axis widen when lims is set 2016-09-24 21:59:58 -04:00
Thomas Breloff fe1b759787 faster plotIndex count; withenv logic and cairox11/cairopng in gr 2016-09-23 12:24:56 -04:00
SimonDanisch 8c6c81e8d0 Merge branch 'sd/labels' into sd/dev 2016-09-23 12:47:26 +02:00
SimonDanisch 874e94218c add labels 2016-09-23 12:42:38 +02:00
Thomas Breloff 755a70bf77 added series_list to Subplot object to improve performance 2016-09-22 19:36:25 -04:00
Thomas Breloff c58de34e63 layout error msg 2016-09-22 17:36:08 -04:00
33 changed files with 2514 additions and 962 deletions
+1 -2
View File
@@ -2,10 +2,9 @@
language: julia
os:
- linux
- osx
# - osx
julia:
- 0.5
- nightly
matrix:
allow_failures:
- julia: nightly
+37
View File
@@ -12,6 +12,43 @@
## 0.9 (current master/dev)
#### 0.9.5
- added dependency on PlotThemes
- set_theme --> theme
- remove Compat from REQUIRE
- warning for DataFrames without StatPlots
- closeall exported and implemented for gr/pyplot
- fix DateTime recipe
- reset theme with theme(:none)
- fix link_axes! for nested subplots
- fix plotly lims for log scale
#### 0.9.4
- optimizations surrounding Subplot.series_list
- better Atom support, support plotlyjs
- gr:
- gks_wstype defaults and gr_set_output
- heatmap uses GR.drawimage
- histogram2d puts NaN for zeros
- improved support of NaN in heatmaps
- rebuilt spy recipes to output scatters with marker_z set
- deprecate png support in plotly... point to plotlyjs
- fixes:
- axis widen with lims set
- reset_extrema, setxyz
- bar plot widen
- better tick padding
- consistent tick rotation
- consistent aspect_ratio
- pyplot dpi
- plotly horizontal bars
- handle series attributes when combining subplots
- gr images transposed
- converted Date/DateTime to new type recipe approach for arrays
- issues closed include: #505 #513 #479 #523 #526 #529
#### 0.9.3
- support pdf and eps in plotlyjs backend
+3 -2
View File
@@ -1,9 +1,10 @@
julia 0.5-
julia 0.5
RecipesBase
PlotUtils
PlotThemes
Reexport
Compat
FixedSizeArrays
Measures
Showoff
StatsBase
+39 -51
View File
@@ -1,40 +1,29 @@
__precompile__()
__precompile__(true)
module Plots
# using Compat
using Reexport
# @reexport using Colors
# using Requires
using FixedSizeArrays
@reexport using RecipesBase
using Base.Meta
@reexport using PlotUtils
@reexport using PlotThemes
import Showoff
import StatsBase
export
AbstractPlot,
Plot,
Subplot,
AbstractLayout,
GridLayout,
grid,
EmptyLayout,
bbox,
plotarea,
@layout,
AVec,
AMat,
KW,
wrap,
set_theme,
add_theme,
theme,
plot,
plot!,
update!,
attr!,
current,
default,
@@ -70,6 +59,8 @@ export
savefig,
png,
gui,
inline,
closeall,
backend,
backends,
@@ -77,12 +68,10 @@ export
backend_object,
add_backend,
aliases,
# dataframes,
Shape,
text,
font,
Axis,
stroke,
brush,
Surface,
@@ -94,11 +83,12 @@ export
Animation,
frame,
gif,
mov,
mp4,
animate,
@animate,
@gif,
spy,
test_examples,
iter_segments,
coords,
@@ -111,8 +101,8 @@ export
P2,
P3,
BezierCurve,
curve_points,
directed_curve
plotattr
# ---------------------------------------------------------
@@ -140,6 +130,7 @@ include("animation.jl")
include("output.jl")
include("examples.jl")
include("arg_desc.jl")
include("plotattr.jl")
# ---------------------------------------------------------
@@ -158,6 +149,9 @@ end
@shorthands bar
@shorthands barh
@shorthands histogram
@shorthands barhist
@shorthands stephist
@shorthands scatterhist
@shorthands histogram2d
@shorthands density
@shorthands heatmap
@@ -206,28 +200,29 @@ yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
title!(plt::Plot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
zlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
xlims!(plt::Plot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
ylims!(plt::Plot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
zlims!(plt::Plot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
xticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
yticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
xticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:AbstractString}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
annotate!(plt::Plot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
annotate!{T<:Tuple}(plt::Plot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
xflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
yflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
xaxis!(plt::Plot, args...; kw...) = plot!(plt; xaxis = args, kw...)
yaxis!(plt::Plot, args...; kw...) = plot!(plt; yaxis = args, kw...)
let PlotOrSubplot = Union{Plot, Subplot}
title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
zlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
xticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
yticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
xticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
annotate!{T<:Tuple}(plt::PlotOrSubplot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
end
# ---------------------------------------------------------
@@ -247,11 +242,4 @@ end
# ---------------------------------------------------------
# if VERSION >= v"0.4.0-dev+5512"
# include("precompile.jl")
# _precompile_()
# end
# ---------------------------------------------------------
end # module
+85 -38
View File
@@ -1,62 +1,109 @@
immutable Animation
dir::String
frames::Vector{String}
dir::String
frames::Vector{String}
end
function Animation()
tmpdir = convert(String, mktempdir())
Animation(tmpdir, String[])
tmpdir = convert(String, mktempdir())
Animation(tmpdir, String[])
end
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
end
giffn() = (isijulia() ? "tmp.gif" : tempname()*".gif")
movfn() = (isijulia() ? "tmp.mov" : tempname()*".mov")
mp4fn() = (isijulia() ? "tmp.mp4" : tempname()*".mp4")
type FrameIterator
itr
every::Int
kw
end
FrameIterator(itr; every=1, kw...) = FrameIterator(itr, every, kw)
"""
Animate from an iterator which returns the plot args each iteration.
"""
function animate(fitr::FrameIterator, fn = giffn(); kw...)
anim = Animation()
for (i, plotargs) in enumerate(fitr.itr)
if mod1(i, fitr.every) == 1
plot(wraptuple(plotargs)...; fitr.kw...)
frame(anim)
end
end
gif(anim, fn; kw...)
end
# most things will implement this
function animate(obj, fn = giffn(); every=1, fps=20, loop=0, kw...)
animate(FrameIterator(obj, every, kw), fn; fps=fps, loop=loop)
end
# -----------------------------------------------
"Wraps the location of an animated gif so that it can be displayed"
immutable AnimatedGif
filename::String
filename::String
end
function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif"); fps::Integer = 20, loop::Integer = 0)
fn = abspath(fn)
file_extension(fn) = Base.Filesystem.splitext(fn)[2][2:end]
try
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim.dir, fn; kw...)
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim.dir, fn; kw...)
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim.dir, fn; kw...)
# high quality
speed = round(Int, 100 / fps)
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
include(file)
const _imagemagick_initialized = Ref(false)
function buildanimation(animdir::AbstractString, fn::AbstractString;
fps::Integer = 20, loop::Integer = 0)
fn = abspath(fn)
try
if !_imagemagick_initialized[]
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
include(file)
end
_imagemagick_initialized[] = true
end
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
# high quality
speed = round(Int, 100 / fps)
run(`convert -delay $speed -loop $loop $(joinpath(animdir, "*.png")) -alpha off $fn`)
catch err
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 -loop $loop -i $(animdir)/%06d.png -y $fn`)
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
end
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
run(`convert -delay $speed -loop $loop $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
catch err
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
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 -loop $loop -i $(anim.dir)/%06d.png -y $fn`)
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
end
info("Saved animation to ", fn)
AnimatedGif(fn)
info("Saved animation to ", fn)
AnimatedGif(fn)
end
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
ext = file_extension(agif.filename)
write(io, if ext == "gif"
"<img src=\"$(relpath(agif.filename))?$(rand())>\" />"
elseif ext in ("mov", "mp4")
"<video controls><source src=\"$(relpath(agif.filename))?$(rand())>\" type=\"video/$ext\"></video>"
else
error("Cannot show animation with extension $ext: $agif")
end)
end
@@ -122,7 +169,7 @@ Example:
```
"""
macro gif(forloop::Expr, args...)
_animate(forloop, args...; callgif = true)
_animate(forloop, args...; callgif = true)
end
"""
@@ -131,13 +178,13 @@ Collect one frame per for-block iteration and return an `Animation` object.
Example:
```
p = plot(1)
anim = @animate for x=0:0.1:5
p = plot(1)
anim = @animate for x=0:0.1:5
push!(p, 1, sin(x))
end
gif(anim)
end
gif(anim)
```
"""
macro animate(forloop::Expr, args...)
_animate(forloop, args...)
_animate(forloop, args...)
end
+3 -2
View File
@@ -19,7 +19,7 @@ const _arg_desc = KW(
: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`.",
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
:bins => "Integer, NTuple{2,Integer}, AbstractVector. For histogram-types, defines the number of bins, or the edges, of the histogram.",
:smooth => "Bool. Add a regression line?",
@@ -29,6 +29,7 @@ const _arg_desc = KW(
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
:levels => "Integer, NTuple{2,Integer}. 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)",
@@ -85,7 +86,7 @@ const _arg_desc = KW(
: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).",
:aspect_ratio => "Symbol (:equal) or Number. Plot area is resized so that 1 y-unit is the same size as `apect_ratio` x-units.",
: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.",
+84 -18
View File
@@ -77,7 +77,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
add_non_underscore_aliases!(_typeAliases)
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :barhist, :barbins, :density)
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image)
@@ -123,6 +123,16 @@ const _markerAliases = Dict{Symbol,Symbol}(
:dtri => :dtriangle,
:downtri => :dtriangle,
:downtriangle => :dtriangle,
:> => :rtriangle,
:rt => :rtriangle,
:rtri => :rtriangle,
:righttri => :rtriangle,
:righttriangle => :rtriangle,
:< => :ltriangle,
:lt => :ltriangle,
:ltri => :ltriangle,
:lighttri => :ltriangle,
:lighttriangle => :ltriangle,
# :+ => :cross,
:plus => :cross,
# :x => :xcross,
@@ -165,7 +175,7 @@ const _series_defaults = KW(
:markershape => :none,
:markercolor => :match,
:markeralpha => nothing,
:markersize => 6,
:markersize => 4,
:markerstrokestyle => :solid,
:markerstrokewidth => 1,
:markerstrokecolor => :match,
@@ -178,6 +188,7 @@ const _series_defaults = KW(
:z => nothing, # depth for contour, surface, etc
:marker_z => nothing, # value for color scale
:line_z => nothing,
:fill_z => nothing,
:levels => 15,
:orientation => :vertical,
:bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side)
@@ -194,7 +205,7 @@ const _series_defaults = KW(
:match_dimensions => false, # do rows match x (true) or y (false) for heatmap/image/spy? see issue 196
# this ONLY effects whether or not the z-matrix is transposed for a heatmap display!
:subplot => :auto, # which subplot(s) does this series belong to?
:series_annotations => [], # a list of annotations which apply to the coordinates of this series
:series_annotations => nothing, # a list of annotations which apply to the coordinates of this series
:primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow
# one logical series to be broken up (path and markers, for example)
:hover => nothing, # text to display when hovering over the data points
@@ -352,13 +363,16 @@ end
add_aliases(:seriescolor, :c, :color, :colour)
add_aliases(:linecolor, :lc, :lcolor, :lcolour, :linecolour)
add_aliases(:markercolor, :mc, :mcolor, :mcolour, :markercolour)
add_aliases(:markerstokecolor, :msc, :mscolor, :mscolour, :markerstokecolour)
add_aliases(:markerstrokecolor, :msc, :mscolor, :mscolour, :markerstrokecolour)
add_aliases(:markerstrokewidth, :msw, :mswidth)
add_aliases(:fillcolor, :fc, :fcolor, :fcolour, :fillcolour)
add_aliases(:background_color, :bg, :bgcolor, :bg_color, :background,
:background_colour, :bgcolour, :bg_colour)
add_aliases(:background_color_legend, :bg_legend, :bglegend, :bgcolor_legend, :bg_color_legend, :background_legend,
:background_colour_legend, :bgcolour_legend, :bg_colour_legend)
add_aliases(:background_color_subplot, :bg_subplot, :bgsubplot, :bgcolor_subplot, :bg_color_subplot, :background_subplot,
:background_colour_subplot, :bgcolour_subplot, :bg_colour_subplot)
add_aliases(:background_color_inside, :bg_inside, :bginside, :bgcolor_inside, :bg_color_inside, :background_inside,
:background_colour_inside, :bgcolour_inside, :bg_colour_inside)
add_aliases(:background_color_outside, :bg_outside, :bgoutside, :bgcolor_outside, :bg_color_outside, :background_outside,
@@ -367,6 +381,8 @@ add_aliases(:foreground_color, :fg, :fgcolor, :fg_color, :foreground,
:foreground_colour, :fgcolour, :fg_colour)
add_aliases(:foreground_color_legend, :fg_legend, :fglegend, :fgcolor_legend, :fg_color_legend, :foreground_legend,
:foreground_colour_legend, :fgcolour_legend, :fg_colour_legend)
add_aliases(:foreground_color_subplot, :fg_subplot, :fgsubplot, :fgcolor_subplot, :fg_color_subplot, :foreground_subplot,
:foreground_colour_subplot, :fgcolour_subplot, :fg_colour_subplot)
add_aliases(:foreground_color_grid, :fg_grid, :fggrid, :fgcolor_grid, :fg_color_grid, :foreground_grid,
:foreground_colour_grid, :fgcolour_grid, :fg_colour_grid, :gridcolor)
add_aliases(:foreground_color_title, :fg_title, :fgtitle, :fgcolor_title, :fg_color_title, :foreground_title,
@@ -383,7 +399,7 @@ add_aliases(:foreground_color_guide, :fg_guide, :fgguide, :fgcolor_guide, :fg_co
# alphas
add_aliases(:seriesalpha, :alpha, :α, :opacity)
add_aliases(:linealpha, :la, :lalpha, :lα, :lineopacity, :lopacity)
add_aliases(:makeralpha, :ma, :malpha, :mα, :makeropacity, :mopacity)
add_aliases(:markeralpha, :ma, :malpha, :mα, :markeropacity, :mopacity)
add_aliases(:markerstrokealpha, :msa, :msalpha, :msα, :markerstrokeopacity, :msopacity)
add_aliases(:fillalpha, :fa, :falpha, :fα, :fillopacity, :fopacity)
@@ -416,6 +432,7 @@ add_aliases(:zguide, :zlabel, :zlab, :zl)
add_aliases(:zlims, :zlim, :zlimit, :zlimits)
add_aliases(:zticks, :ztick)
add_aliases(:zrotation, :zrot, :zr)
add_aliases(:fill_z, :fillz, :fz, :surfacecolor, :surfacecolour, :sc, :surfcolor, :surfcolour)
add_aliases(:legend, :leg, :key)
add_aliases(:colorbar, :cb, :cbar, :colorkey)
add_aliases(:clims, :clim, :cbarlims, :cbar_lims, :climits, :color_limits)
@@ -435,7 +452,7 @@ add_aliases(:match_dimensions, :transpose, :transpose_z)
add_aliases(:subplot, :sp, :subplt, :splt)
add_aliases(:projection, :proj)
add_aliases(:title_location, :title_loc, :titleloc, :title_position, :title_pos, :titlepos, :titleposition, :title_align, :title_alignment)
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation)
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation, :text, :txt, :texts, :txts)
add_aliases(:html_output_format, :format, :fmt, :html_format)
add_aliases(:orientation, :direction, :dir)
add_aliases(:inset_subplots, :inset, :floating)
@@ -456,6 +473,7 @@ end
`default(key)` returns the current default value for that key
`default(key, value)` sets the current default value for that key
`default(; kw...)` will set the current default value for each key/value pair
`default(d, key)` returns the key from d if it exists, otherwise `default(key)`
"""
function default(k::Symbol)
@@ -492,6 +510,11 @@ function default(; kw...)
end
end
function default(d::KW, k::Symbol)
get(d, k, default(k))
end
# -----------------------------------------------------------------------------
@@ -681,6 +704,11 @@ function preprocessArgs!(d::KW)
end
delete!(d, :fill)
# handle series annotations
if haskey(d, :series_annotations)
d[:series_annotations] = series_annotations(wraptuple(d[:series_annotations])...)
end
# convert into strokes and brushes
if haskey(d, :arrow)
@@ -831,6 +859,7 @@ function convertLegendValue(val::Symbol)
end
convertLegendValue(val::Bool) = val ? :best : :none
convertLegendValue(val::Void) = :none
convertLegendValue{S<:Real, T<:Real}(v::Tuple{S,T}) = v
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
# -----------------------------------------------------------------------------
@@ -974,6 +1003,27 @@ Base.get(series::Series, k::Symbol, v) = get(series.d, k, v)
# -----------------------------------------------------------------------------
function fg_color(d::KW)
fg = default(d, :foreground_color)
if fg == :auto
bg = plot_color(default(d, :background_color))
fg = isdark(bg) ? colorant"white" : colorant"black"
else
plot_color(fg)
end
end
function fg_color_sp(d::KW)
fgsp = default(d, :foreground_color_subplot)
if fg == :match
fg_color(d)
else
plot_color(fgsp)
end
end
# update attr from an input dictionary
function _update_plot_args(plt::Plot, d_in::KW)
for (k,v) in _plot_defaults
@@ -981,21 +1031,30 @@ function _update_plot_args(plt::Plot, d_in::KW)
end
# handle colors
bg = plot_color(plt.attr[:background_color])
fg = plt.attr[:foreground_color]
if fg == :auto
fg = isdark(bg) ? colorant"white" : colorant"black"
end
plt.attr[:background_color] = bg
plt.attr[:foreground_color] = plot_color(fg)
d = plt.attr
plt[:background_color] = plot_color(d[:background_color])
plt[:foreground_color] = fg_color(d)
# bg = plot_color(plt.attr[:background_color])
# fg = plt.attr[:foreground_color]
# if fg == :auto
# fg = isdark(bg) ? colorant"white" : colorant"black"
# end
# plt.attr[:background_color] = bg
# plt.attr[:foreground_color] = plot_color(fg)
color_or_nothing!(plt.attr, :background_color_outside)
end
# -----------------------------------------------------------------------------
function _update_subplot_periphery(sp::Subplot, anns::AVec)
# extend annotations
sp.attr[:annotations] = vcat(anns, sp[:annotations])
# extend annotations, and ensure we always have a (x,y,PlotText) tuple
newanns = vcat(anns, sp[:annotations])
for (i,ann) in enumerate(newanns)
x,y,tmp = ann
ptxt = isa(tmp, PlotText) ? tmp : text(tmp)
newanns[i] = (x,y,ptxt)
end
sp.attr[:annotations] = newanns
# handle legend/colorbar
sp.attr[:legend] = convertLegendValue(sp.attr[:legend])
@@ -1059,7 +1118,7 @@ function _update_axis(axis::Axis, d_in::KW, letter::Symbol, subplot_index::Int)
end
# update the axis
update!(axis, args...; kw...)
attr!(axis, args...; kw...)
return
end
@@ -1143,7 +1202,14 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
end
# this is how many series belong to this subplot
plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
# plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
plotIndex = 0
for series in sp.series_list
if series[:primary]
plotIndex += 1
end
end
# plotIndex = count(series -> series[:primary], sp.series_list)
if get(d, :primary, true)
plotIndex += 1
end
@@ -1194,7 +1260,7 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
end
# scatter plots don't have a line, but must have a shape
if d[:seriestype] in (:scatter, :scatter3d)
if d[:seriestype] in (:scatter, :scatterbins, :scatterhist, :scatter3d)
d[:linewidth] = 0
if d[:markershape] == :none
d[:markershape] = :circle
+21 -7
View File
@@ -29,7 +29,7 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
d[:discrete_values] = []
# update the defaults
update!(Axis([sp], d), args...; kw...)
attr!(Axis([sp], d), args...; kw...)
end
function get_axis(sp::Subplot, letter::Symbol)
@@ -83,7 +83,7 @@ function process_axis_arg!(d::KW, arg, letter = "")
end
# update an Axis object with magic args and keywords
function update!(axis::Axis, args...; kw...)
function attr!(axis::Axis, args...; kw...)
# first process args
d = axis.d
for arg in args
@@ -224,6 +224,17 @@ end
# -------------------------------------------------------------------------
function reset_extrema!(sp::Subplot)
for asym in (:x,:y,:z)
sp[Symbol(asym,:axis)][:extrema] = Extrema()
end
for series in sp.series_list
expand_extrema!(sp, series.d)
end
end
function expand_extrema!(ex::Extrema, v::Number)
ex.emin = min(v, ex.emin)
ex.emax = max(v, ex.emax)
@@ -342,7 +353,7 @@ end
# so lazy out and don't widen
function default_should_widen(axis::Axis)
should_widen = false
if axis[:scale] == :identity
if axis[:scale] == :identity && !is_2tuple(axis[:lims])
for sp in axis.sps
for series in series_list(sp)
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
@@ -370,6 +381,9 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
if amax <= amin && isfinite(amin)
amax = amin + 1.0
end
if !isfinite(amin) && !isfinite(amax)
amin, amax = 0.0, 1.0
end
if should_widen
widen(amin, amax)
else
@@ -462,11 +476,11 @@ function axis_drawing_info(sp::Subplot)
t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin)))
push!(spine_segs, (xmin,ymin), (xmax,ymin)) # bottom spine
push!(spine_segs, (xmin,ymax), (xmax,ymax)) # top spine
# push!(spine_segs, (xmin,ymax), (xmax,ymax)) # top spine
for xtick in xticks[1]
push!(spine_segs, (xtick, ymin), (xtick, t1)) # bottom tick
push!(grid_segs, (xtick, t1), (xtick, t2)) # vertical grid
push!(spine_segs, (xtick, ymax), (xtick, t2)) # top tick
# push!(spine_segs, (xtick, ymax), (xtick, t2)) # top tick
end
end
@@ -477,11 +491,11 @@ function axis_drawing_info(sp::Subplot)
t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin)))
push!(spine_segs, (xmin,ymin), (xmin,ymax)) # left spine
push!(spine_segs, (xmax,ymin), (xmax,ymax)) # right spine
# push!(spine_segs, (xmax,ymin), (xmax,ymax)) # right spine
for ytick in yticks[1]
push!(spine_segs, (xmin, ytick), (t1, ytick)) # left tick
push!(grid_segs, (t1, ytick), (t2, ytick)) # horizontal grid
push!(spine_segs, (xmax, ytick), (t2, ytick)) # right tick
# push!(spine_segs, (xmax, ytick), (t2, ytick)) # right tick
end
end
+38 -6
View File
@@ -51,15 +51,46 @@ _before_layout_calcs(plt::Plot) = nothing
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
# TODO: this should account for both tick font and the size/length/rotation of tick labels
"Returns the (width,height) of a text label."
function text_size(lablen::Int, sz::Number, rot::Number = 0)
# we need to compute the size of the ticks generically
# this means computing the bounding box and then getting the width/height
# note:
ptsz = sz * pt
width = 0.8lablen * ptsz
# now compute the generalized "height" after rotation as the "opposite+adjacent" of 2 triangles
height = abs(sind(rot)) * width + abs(cosd(rot)) * ptsz
width = abs(sind(rot+90)) * width + abs(cosd(rot+90)) * ptsz
width, height
end
text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(lab), sz, rot)
# account for the size/length/rotation of tick labels
function tick_padding(axis::Axis)
ptsz = axis[:tickfont].pointsize * pt
if axis[:ticks] in (nothing,false)
ticks = get_ticks(axis)
if ticks == nothing
0mm
elseif axis[:letter] == :x
2mm + ptsz
else
8mm
vals, labs = ticks
isempty(labs) && return 0mm
# ptsz = axis[:tickfont].pointsize * pt
longest_label = maximum(length(lab) for lab in labs)
# generalize by "rotating" y labels
rot = axis[:rotation] + (axis[:letter] == :y ? 90 : 0)
# # we need to compute the size of the ticks generically
# # this means computing the bounding box and then getting the width/height
# labelwidth = 0.8longest_label * ptsz
#
#
# # now compute the generalized "height" after rotation as the "opposite+adjacent" of 2 triangles
# hgt = abs(sind(rot)) * labelwidth + abs(cosd(rot)) * ptsz + 1mm
# hgt
# get the height of the rotated label
text_size(longest_label, axis[:tickfont].pointsize, rot)[2]
end
end
@@ -245,6 +276,7 @@ end
@init_backend GR
@init_backend GLVisualize
@init_backend PGFPlots
@init_backend InspectDR
# ---------------------------------------------------------
File diff suppressed because it is too large Load Diff
+109 -41
View File
@@ -30,6 +30,7 @@ const _gr_attr = merge_with_base_supported([
:normalize, :weights,
:inset_subplots,
:bar_width,
:arrow,
])
const _gr_seriestype = [
:path, :scatter,
@@ -131,7 +132,7 @@ gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(cycle(c,1)))
# draw line segments, splitting x/y into contiguous/finite segments
# note: this can be used for shapes by passing func `GR.fillarea`
function gr_polyline(x, y, func = GR.polyline)
function gr_polyline(x, y, func = GR.polyline; arrowside=:none)
iend = 0
n = length(x)
while iend < n-1
@@ -159,6 +160,12 @@ function gr_polyline(x, y, func = GR.polyline)
# if we found a start and end, draw the line segment, otherwise we're done
if istart > 0 && iend > 0
func(x[istart:iend], y[istart:iend])
if arrowside in (:head,:both)
GR.drawarrow(x[iend-1], y[iend-1], x[iend], y[iend])
end
if arrowside in (:tail,:both)
GR.drawarrow(x[istart+1], y[istart+1], x[istart], y[istart])
end
else
break
end
@@ -263,11 +270,13 @@ end
# draw ONE Shape
function gr_draw_marker(xi, yi, msize, shape::Shape)
sx, sy = shape_coords(shape)
sx, sy = coords(shape)
# convert to ndc coords (percentages of window)
GR.selntran(0)
xi, yi = GR.wctondc(xi, yi)
GR.fillarea(xi + sx * 0.0015msize,
yi + sy * 0.0015msize)
ms_ndc_x, ms_ndc_y = gr_pixels_to_ndc(msize, msize)
GR.fillarea(xi .+ sx .* ms_ndc_x,
yi .+ sy .* ms_ndc_y)
GR.selntran(1)
end
@@ -281,10 +290,11 @@ end
# draw the markers, one at a time
function gr_draw_markers(series::Series, x, y, msize, mz)
shape = series[:markershape]
if shape != :none
shapes = series[:markershape]
if shapes != :none
for i=1:length(x)
msi = cycle(msize, i)
shape = cycle(shapes, i)
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
@@ -337,10 +347,11 @@ end
const _gr_point_mult = zeros(1)
# set the font attributes... assumes _gr_point_mult has been populated already
function gr_set_font(f::Font; halign = f.halign, valign = f.valign, color = f.color)
function gr_set_font(f::Font; halign = f.halign, valign = f.valign,
color = f.color, rotation = f.rotation)
family = lowercase(f.family)
GR.setcharheight(_gr_point_mult[1] * f.pointsize)
GR.setcharup(sin(f.rotation), cos(f.rotation))
GR.setcharup(sind(-rotation), cosd(-rotation))
if haskey(gr_font_family, family)
GR.settextfontprec(100 + gr_font_family[family], GR.TEXT_PRECISION_STRING)
end
@@ -348,6 +359,14 @@ function gr_set_font(f::Font; halign = f.halign, valign = f.valign, color = f.co
GR.settextalign(gr_halign[halign], gr_valign[valign])
end
function gr_nans_to_infs!(z)
for (i,zi) in enumerate(z)
if zi == NaN
z[i] = Inf
end
end
end
# --------------------------------------------------------------------------------------
# viewport plot area
@@ -355,6 +374,9 @@ end
# values are [xmin, xmax, ymin, ymax]. they range [0,1].
const viewport_plotarea = zeros(4)
# the size of the current plot in pixels
const gr_plot_size = zeros(2)
function gr_viewport_from_bbox(bb::BoundingBox, w, h, viewport_canvas)
viewport = zeros(4)
viewport[1] = viewport_canvas[2] * (left(bb) / w)
@@ -409,6 +431,13 @@ gr_view_ycenter() = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
gr_view_xdiff() = viewport_plotarea[2] - viewport_plotarea[1]
gr_view_ydiff() = viewport_plotarea[4] - viewport_plotarea[3]
function gr_pixels_to_ndc(x_pixels, y_pixels)
w,h = gr_plot_size
totx = w * gr_view_xdiff()
toty = h * gr_view_ydiff()
x_pixels / totx, y_pixels / toty
end
# --------------------------------------------------------------------------------------
@@ -436,6 +465,7 @@ function gr_display(plt::Plot)
# compute the viewport_canvas, normalized to the larger dimension
viewport_canvas = Float64[0,1,0,1]
w, h = plt[:size]
gr_plot_size[:] = [w, h]
if w > h
ratio = float(h) / w
msize = display_width_ratio * w
@@ -533,16 +563,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# set the scale flags and window
xmin, xmax, ymin, ymax = data_lims
scale = 0
scaleop = 0
xtick, ytick = 1, 1
if xmax > xmin && ymax > ymin
# NOTE: for log axes, the major_x and major_y - if non-zero (omit labels) - control the minor grid lines (1 = draw 9 minor grid lines, 2 = no minor grid lines)
# NOTE: for log axes, the x_tick and y_tick - if non-zero (omit axes) - only affect the output appearance (1 = nomal, 2 = scientiic notation)
xaxis[:scale] == :log10 && (scale |= GR.OPTION_X_LOG)
yaxis[:scale] == :log10 && (scale |= GR.OPTION_Y_LOG)
xaxis[:flip] && (scale |= GR.OPTION_FLIP_X)
yaxis[:flip] && (scale |= GR.OPTION_FLIP_Y)
if scale & GR.OPTION_X_LOG == 0
xaxis[:scale] == :log10 && (scaleop |= GR.OPTION_X_LOG)
yaxis[:scale] == :log10 && (scaleop |= GR.OPTION_Y_LOG)
xaxis[:flip] && (scaleop |= GR.OPTION_FLIP_X)
yaxis[:flip] && (scaleop |= GR.OPTION_FLIP_Y)
if scaleop & GR.OPTION_X_LOG == 0
majorx = 1 #5
xtick = GR.tick(xmin, xmax) / majorx
else
@@ -550,7 +580,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
xtick = 2 # scientific notation
majorx = 2 # no minor grid lines
end
if scale & GR.OPTION_Y_LOG == 0
if scaleop & GR.OPTION_Y_LOG == 0
majory = 1 #5
ytick = GR.tick(ymin, ymax) / majory
else
@@ -561,7 +591,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# NOTE: setwindow sets the "data coordinate" limits of the current "viewport"
GR.setwindow(xmin, xmax, ymin, ymax)
GR.setscale(scale)
GR.setscale(scaleop)
end
# draw the axes
@@ -584,8 +614,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# GR.setlinetype(GR.LINETYPE_DOTTED)
if sp[:grid]
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
GR.grid3d(xtick, 0, ztick, xmin, ymax, zmin, 2, 0, 2)
GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0)
end
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
@@ -622,12 +652,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# x labels
flip = sp[:yaxis][:flip]
mirror = sp[:xaxis][:mirror]
gr_set_font(sp[:xaxis][:tickfont], valign = (mirror ? :bottom : :top), color = sp[:xaxis][:foreground_color_axis])
gr_set_font(sp[:xaxis][:tickfont],
halign = (:left, :hcenter, :right)[sign(sp[:xaxis][:rotation]) + 2],
valign = (mirror ? :bottom : :top),
color = sp[:xaxis][:foreground_color_axis],
rotation = sp[:xaxis][:rotation])
for (cv, dv) in zip(xticks...)
# use xor ($) to get the right y coords
xi, yi = GR.wctondc(cv, (flip $ mirror) ? ymax : ymin)
# @show cv dv ymin xi yi flip mirror (flip $ mirror)
gr_text(xi, yi + (mirror ? 1 : -1) * 0.01, string(dv))
gr_text(xi, yi + (mirror ? 1 : -1) * 2e-3, string(dv))
end
end
@@ -635,12 +669,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# y labels
flip = sp[:xaxis][:flip]
mirror = sp[:yaxis][:mirror]
gr_set_font(sp[:yaxis][:tickfont], halign = (mirror ? :left : :right), color = sp[:yaxis][:foreground_color_axis])
gr_set_font(sp[:yaxis][:tickfont],
halign = (mirror ? :left : :right),
valign = (:top, :vcenter, :bottom)[sign(sp[:yaxis][:rotation]) + 2],
color = sp[:yaxis][:foreground_color_axis],
rotation = sp[:yaxis][:rotation])
for (cv, dv) in zip(yticks...)
# use xor ($) to get the right y coords
xi, yi = GR.wctondc((flip $ mirror) ? xmax : xmin, cv)
# @show cv dv xmin xi yi
gr_text(xi + (mirror ? 1 : -1) * 0.01, yi, string(dv))
gr_text(xi + (mirror ? 1 : -1) * 2e-3, yi, string(dv))
end
end
@@ -684,9 +722,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
GR.restorestate()
# TODO: can we remove?
gr_set_font(xaxis[:tickfont])
# GR.setcolormap(1000 + GR.COLORMAP_COOLWARM)
# this needs to be here to point the colormap to the right indices
GR.setcolormap(1000 + GR.COLORMAP_COOLWARM)
for (idx, series) in enumerate(series_list(sp))
st = series[:seriestype]
@@ -714,12 +753,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
x, y, z = series[:x], series[:y], series[:z]
frng = series[:fillrange]
# add custom frame shapes to markershape?
series_annotations_shapes!(series)
# -------------------------------------------------------
# recompute data
if typeof(z) <: Surface
if st == :heatmap
expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
end
# if st == :heatmap
# expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
# expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
# end
z = vec(transpose_z(series, z.surf, false))
elseif ispolar(sp)
if frng != nothing
@@ -748,7 +791,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# draw the line(s)
if st == :path
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
gr_polyline(x, y)
arrowside = isa(series[:arrow], Arrow) ? series[:arrow].side : :none
gr_polyline(x, y; arrowside = arrowside)
end
end
@@ -800,16 +844,19 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
cmap && gr_colorbar(sp)
elseif st == :heatmap
# z = vec(transpose_z(series, z.surf, false))
zmin, zmax = gr_lims(zaxis, true)
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (zmin = clims[1])
isfinite(clims[2]) && (zmax = clims[2])
end
GR.setspace(zmin, zmax, 0, 90)
# GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
GR.surface(x, y, z, GR.OPTION_HEATMAP)
grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z]
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
round(Int, blue(c) * 255) << 16 +
round(Int, green(c) * 255) << 8 +
round(Int, red(c) * 255) ), colors)
GR.drawimage(xmin, xmax, ymax, ymin, length(x), length(y), rgba)
cmap && gr_colorbar(sp)
elseif st in (:path3d, :scatter3d)
@@ -896,8 +943,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
elseif st == :image
img = series[:z].surf
h, w = size(img)
z = transpose_z(series, series[:z].surf, true)
h, w = size(z)
if eltype(z) <: Colors.AbstractGray
grey = round(UInt8, float(z) * 255)
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
@@ -910,6 +957,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
end
# this is all we need to add the series_annotations text
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, x, y)
gr_set_font(fnt)
gr_text(GR.wctondc(xi, yi)..., str)
end
GR.restorestate()
end
@@ -963,7 +1017,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
if series[:markershape] != :none
gr_draw_markers(series, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
gr_draw_markers(series, xpos - .035, ypos, 6, nothing)
end
if typeof(series[:label]) <: Array
@@ -1016,18 +1070,27 @@ const _gr_mimeformats = Dict(
"image/svg+xml" => "svg",
)
const _gr_wstype_default = @static if is_linux()
"x11"
# "cairox11"
elseif is_apple()
"quartz"
else
"use_default"
end
const _gr_wstype = Ref(get(ENV, "GKSwstype", _gr_wstype_default))
gr_set_output(wstype::String) = (_gr_wstype[] = wstype)
for (mime, fmt) in _gr_mimeformats
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
GR.emergencyclosegks()
wstype = haskey(ENV, "GKS_WSTYPE") ? ENV["GKS_WSTYPE"] : "0"
filepath = tempname() * "." * $fmt
ENV["GKS_WSTYPE"] = $fmt
ENV["GKSwstype"] = $fmt
ENV["GKS_FILEPATH"] = filepath
gr_display(plt)
GR.emergencyclosegks()
write(io, readstring(filepath))
ENV["GKS_WSTYPE"] = wstype
rm(filepath)
end
end
@@ -1036,7 +1099,7 @@ function _display(plt::Plot{GRBackend})
if plt[:display_type] == :inline
GR.emergencyclosegks()
filepath = tempname() * ".pdf"
ENV["GKS_WSTYPE"] = "pdf"
ENV["GKSwstype"] = "pdf"
ENV["GKS_FILEPATH"] = filepath
gr_display(plt)
GR.emergencyclosegks()
@@ -1044,7 +1107,12 @@ function _display(plt::Plot{GRBackend})
println(content)
rm(filepath)
else
ENV["GKS_DOUBLE_BUF"] = "true"
ENV["GKS_DOUBLE_BUF"] = true
if _gr_wstype[] != "use_default"
ENV["GKSwstype"] = _gr_wstype[]
end
gr_display(plt)
end
end
closeall(::GRBackend) = GR.emergencyclosegks()
+531
View File
@@ -0,0 +1,531 @@
# https://github.com/ma-laforge/InspectDR.jl
#=TODO:
Tweak scale factor for width & other sizes
Not supported by InspectDR:
:foreground_color_grid
:foreground_color_border
:polar,
Add in functionality to Plots.jl:
:aspect_ratio,
=#
# ---------------------------------------------------------------------------
#TODO: remove features
const _inspectdr_attr = merge_with_base_supported([
:annotations,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
:label,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:markerstrokestyle, #Causes warning not to have it... what is this?
:fillcolor, :fillalpha, #:fillrange,
# :bins, :bar_width, :bar_edges, :bar_position,
:title, :title_location, :titlefont,
:window_title,
:guide, :lims, :scale, #:ticks, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend, #:colorbar,
# :marker_z,
# :line_z,
# :levels,
# :ribbon, :quiver, :arrow,
# :orientation,
:overwrite_figure,
:polar,
# :normalize, :weights,
# :contours, :aspect_ratio,
:match_dimensions,
# :clims,
# :inset_subplots,
:dpi,
# :colorbar_title,
])
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
const _inspectdr_seriestype = [
:path, :scatter, :shape #, :steppre, :steppost
]
#see: _allMarkers, _shape_keys
const _inspectdr_marker = Symbol[
:none, :auto,
:circle, :rect, :diamond,
:cross, :xcross,
:utriangle, :dtriangle, :rtriangle, :ltriangle,
:pentagon, :hexagon, :heptagon, :octagon,
:star4, :star5, :star6, :star7, :star8,
:vline, :hline, :+, :x,
]
const _inspectdr_scale = [:identity, :ln, :log2, :log10]
is_marker_supported(::InspectDRBackend, shape::Shape) = true
_inspectdr_to_pixels(bb::BoundingBox) =
InspectDR.BoundingBox(to_pixels(left(bb)), to_pixels(right(bb)), to_pixels(top(bb)), to_pixels(bottom(bb)))
#Do we avoid Map to avoid possible pre-comile issues?
function _inspectdr_mapglyph(s::Symbol)
s == :rect && return :square
return s
end
function _inspectdr_mapglyph(s::Shape)
x, y = coords(s)
return InspectDR.GlyphPolyline(x, y)
end
# py_marker(markers::AVec) = map(py_marker, markers)
function _inspectdr_mapglyph(markers::AVec)
warn("Vectors of markers are currently unsupported in InspectDR.")
_inspectdr_mapglyph(markers[1])
end
_inspectdr_mapglyphsize(v::Real) = v
function _inspectdr_mapglyphsize(v::Vector)
warn("Vectors of marker sizes are currently unsupported in InspectDR.")
_inspectdr_mapglyphsize(v[1])
end
_inspectdr_mapcolor(v::Colorant) = v
function _inspectdr_mapcolor(g::PlotUtils.ColorGradient)
warn("Color gradients are currently unsupported in InspectDR.")
#Pick middle color:
_inspectdr_mapcolor(g.colors[div(1+end,2)])
end
function _inspectdr_mapcolor(v::AVec)
warn("Vectors of colors are currently unsupported in InspectDR.")
#Pick middle color:
_inspectdr_mapcolor(v[div(1+end,2)])
end
#Hack: suggested point size does not seem adequate relative to plot size, for some reason.
_inspectdr_mapptsize(v) = 1.5*v
function _inspectdr_add_annotations(plot, x, y, val)
#What kind of annotation is this?
end
#plot::InspectDR.Plot2D
function _inspectdr_add_annotations(plot, x, y, val::PlotText)
vmap = Dict{Symbol, Symbol}(:top=>:t, :bottom=>:b) #:vcenter
hmap = Dict{Symbol, Symbol}(:left=>:l, :right=>:r) #:hcenter
align = Symbol(get(vmap, val.font.valign, :c), get(hmap, val.font.halign, :c))
fnt = InspectDR.Font(val.font.family, val.font.pointsize,
color =_inspectdr_mapcolor(val.font.color)
)
ann = InspectDR.atext(val.str, x=x, y=y,
font=fnt, angle=val.font.rotation, align=align
)
InspectDR.add(plot, ann)
return
end
# ---------------------------------------------------------------------------
function _inspectdr_getscale(s::Symbol, yaxis::Bool)
#TODO: Support :asinh, :sqrt
kwargs = yaxis? (:tgtmajor=>8, :tgtminor=>2): () #More grid lines on y-axis
if :log2 == s
return InspectDR.AxisScale(:log2; kwargs...)
elseif :log10 == s
return InspectDR.AxisScale(:log10; kwargs...)
elseif :ln == s
return InspectDR.AxisScale(:ln; kwargs...)
else #identity
return InspectDR.AxisScale(:lin; kwargs...)
end
end
# ---------------------------------------------------------------------------
function add_backend_string(::InspectDRBackend)
"""
if !Plots.is_installed("InspectDR")
Pkg.add("InspectDR")
end
"""
end
function _initialize_backend(::InspectDRBackend; kw...)
@eval begin
import InspectDR
export InspectDR
#Glyph used when plotting "Shape"s:
const INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
)
type InspecDRPlotRef
mplot::Union{Void, InspectDR.Multiplot}
gui::Union{Void, InspectDR.GtkPlot}
end
_inspectdr_getmplot(::Any) = nothing
_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot
_inspectdr_getgui(::Any) = nothing
_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed? nothing: gplot)
_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui)
end
end
# ---------------------------------------------------------------------------
# Create the window/figure for this backend.
function _create_backend_figure(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
gplot = _inspectdr_getgui(plt.o)
#:overwrite_figure: want to reuse current figure
if plt[:overwrite_figure] && mplot != nothing
mplot.subplots = [] #Reset
if gplot != nothing #Ensure still references current plot
gplot.src = mplot
end
else #want new one:
mplot = InspectDR.Multiplot()
gplot = nothing #Will be created later
end
#break link with old subplots
for sp in plt.subplots
sp.o = nothing
end
return InspecDRPlotRef(mplot, gplot)
end
# ---------------------------------------------------------------------------
# # this is called early in the pipeline, use it to make the plot current or something
# function _prepare_plot_object(plt::Plot{InspectDRBackend})
# end
# ---------------------------------------------------------------------------
# Set up the subplot within the backend object.
function _initialize_subplot(plt::Plot{InspectDRBackend}, sp::Subplot{InspectDRBackend})
plot = sp.o
#Don't do anything without a "subplot" object: Will process later.
if nothing == plot; return; end
plot.data = []
plot.markers = [] #Clear old markers
plot.atext = [] #Clear old annotation
plot.apline = [] #Clear old poly lines
return plot
end
# ---------------------------------------------------------------------------
# Add one series to the underlying backend object.
# Called once per series
# NOTE: Seems to be called when user calls plot()... even if backend
# plot, sp.o has not yet been constructed...
function _series_added(plt::Plot{InspectDRBackend}, series::Series)
st = series[:seriestype]
sp = series[:subplot]
plot = sp.o
#Don't do anything without a "subplot" object: Will process later.
if nothing == plot; return; end
_vectorize(v) = isa(v, Vector)? v: collect(v) #InspectDR only supports vectors
x = _vectorize(series[:x]); y = _vectorize(series[:y])
#No support for polar grid... but can still perform polar transformation:
if ispolar(sp)
Θ = x; r = y
x = r.*cos(Θ); y = r.*sin(Θ)
end
# doesn't handle mismatched x/y - wrap data (pyplot behaviour):
nx = length(x); ny = length(y)
if nx < ny
series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
elseif ny > nx
series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
end
#= TODO: Eventually support
series[:fillcolor] #I think this is fill under line
zorder = series[:series_plotindex]
For st in :shape:
zorder = series[:series_plotindex],
=#
if st in (:shape,)
nmax = 0
for (i,rng) in enumerate(iter_segments(x, y))
nmax = i
if length(rng) > 1
linewidth = series[:linewidth]
linecolor = _inspectdr_mapcolor(cycle(series[:linecolor], i))
fillcolor = _inspectdr_mapcolor(cycle(series[:fillcolor], i))
line = InspectDR.line(
style=:solid, width=linewidth, color=linecolor
)
apline = InspectDR.PolylineAnnotation(
x[rng], y[rng], line=line, fillcolor=fillcolor
)
push!(plot.apline, apline)
end
end
i = (nmax >= 2? div(nmax, 2): nmax) #Must pick one set of colors for legend
if i > 1 #Add dummy waveform for legend entry:
linewidth = series[:linewidth]
linecolor = _inspectdr_mapcolor(cycle(series[:linecolor], i))
fillcolor = _inspectdr_mapcolor(cycle(series[:fillcolor], i))
wfrm = InspectDR.add(plot, Float64[], Float64[], id=series[:label])
wfrm.line = InspectDR.line(
style=:none, width=linewidth, #linewidth affects glyph
)
wfrm.glyph = InspectDR.glyph(
shape = INSPECTDR_GLYPH_SHAPE, size = 8,
color = linecolor, fillcolor = fillcolor
)
end
elseif st in (:path, :scatter) #, :steppre, :steppost)
#NOTE: In Plots.jl, :scatter plots have 0-linewidths (I think).
linewidth = series[:linewidth]
#More efficient & allows some support for markerstrokewidth:
_style = (0==linewidth? :none: series[:linestyle])
wfrm = InspectDR.add(plot, x, y, id=series[:label])
wfrm.line = InspectDR.line(
style = _style,
width = series[:linewidth],
color = series[:linecolor],
)
#InspectDR does not control markerstrokewidth independently.
if :none == _style
#Use this property only if no line is displayed:
wfrm.line.width = series[:markerstrokewidth]
end
wfrm.glyph = InspectDR.glyph(
shape = _inspectdr_mapglyph(series[:markershape]),
size = _inspectdr_mapglyphsize(series[:markersize]),
color = _inspectdr_mapcolor(series[:markerstrokecolor]),
fillcolor = _inspectdr_mapcolor(series[:markercolor]),
)
end
# this is all we need to add the series_annotations text
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, x, y)
_inspectdr_add_annotations(plot, xi, yi, PlotText(str, fnt))
end
return
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{InspectDRBackend}, series::Series)
#Nothing to do
end
# ---------------------------------------------------------------------------
function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
const gridon = InspectDR.GridRect(vmajor=true, hmajor=true)
const gridoff = InspectDR.GridRect()
const plot = sp.o
const strip = plot.strips[1] #Only 1 strip supported with Plots.jl
#No independent control of grid???
strip.grid = sp[:grid]? gridon: gridoff
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
xmin, xmax = axis_limits(xaxis)
ymin, ymax = axis_limits(yaxis)
if ispolar(sp)
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
rmax = max(abs(ymin), abs(ymax))
xmin, xmax = -rmax, rmax
ymin, ymax = -rmax, rmax
end
plot.xext = InspectDR.PExtents1D() #reset
strip.yext = InspectDR.PExtents1D() #reset
plot.xext_full = InspectDR.PExtents1D(xmin, xmax)
strip.yext_full = InspectDR.PExtents1D(ymin, ymax)
a = plot.annotation
a.title = sp[:title]
a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]]
l = plot.layout
l.frame.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
l.framedata.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
l.framedata.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
l.fnttitle = InspectDR.Font(sp[:titlefont].family,
_inspectdr_mapptsize(sp[:titlefont].pointsize),
color = _inspectdr_mapcolor(sp[:foreground_color_title])
)
#Cannot independently control fonts of axes with InspectDR:
l.fntaxlabel = InspectDR.Font(xaxis[:guidefont].family,
_inspectdr_mapptsize(xaxis[:guidefont].pointsize),
color = _inspectdr_mapcolor(xaxis[:foreground_color_guide])
)
l.fntticklabel = InspectDR.Font(xaxis[:tickfont].family,
_inspectdr_mapptsize(xaxis[:tickfont].pointsize),
color = _inspectdr_mapcolor(xaxis[:foreground_color_text])
)
leg = l.legend
leg.enabled = (sp[:legend] != :none)
#leg.width = 150 #TODO: compute???
leg.font = InspectDR.Font(sp[:legendfont].family,
_inspectdr_mapptsize(sp[:legendfont].pointsize),
color = _inspectdr_mapcolor(sp[:foreground_color_legend])
)
leg.frame.fillcolor = _inspectdr_mapcolor(sp[:background_color_legend])
end
# called just before updating layout bounding boxes... in case you need to prep
# for the calcs
function _before_layout_calcs(plt::Plot{InspectDRBackend})
const mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end
mplot.title = plt[:plot_title]
if "" == mplot.title
#Don't use window_title... probably not what you want.
#mplot.title = plt[:window_title]
end
mplot.frame.fillcolor = _inspectdr_mapcolor(plt[:background_color_outside])
resize!(mplot.subplots, length(plt.subplots))
nsubplots = length(plt.subplots)
for (i, sp) in enumerate(plt.subplots)
if !isassigned(mplot.subplots, i)
mplot.subplots[i] = InspectDR.Plot2D()
end
sp.o = mplot.subplots[i]
plot = sp.o
_initialize_subplot(plt, sp)
_inspectdr_setupsubplot(sp)
graphbb = _inspectdr_to_pixels(plotarea(sp))
plot.plotbb = InspectDR.plotbounds(plot.layout, graphbb)
# add the annotations
for ann in sp[:annotations]
_inspectdr_add_annotations(plot, ann...)
end
end
#Do not yet support absolute plot positionning.
#Just try to make things look more-or less ok:
if nsubplots <= 1
mplot.ncolumns = 1
elseif nsubplots <= 4
mplot.ncolumns = 2
elseif nsubplots <= 6
mplot.ncolumns = 3
elseif nsubplots <= 12
mplot.ncolumns = 4
else
mplot.ncolumns = 5
end
for series in plt.series_list
_series_added(plt, series)
end
return
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{InspectDRBackend})
plot = sp.o
if !isa(plot, InspectDR.Plot2D); return sp.minpad; end
#Computing plotbounds with 0-BoundingBox returns required padding:
bb = InspectDR.plotbounds(plot.layout, InspectDR.BoundingBox(0,0,0,0))
#NOTE: plotbounds always pads for titles, legends, etc. even if not in use.
#TODO: possibly zero-out items not in use??
# add in the user-specified margin to InspectDR padding:
leftpad = abs(bb.xmin)*px + sp[:left_margin]
toppad = abs(bb.ymin)*px + sp[:top_margin]
rightpad = abs(bb.xmax)*px + sp[:right_margin]
bottompad = abs(bb.ymax)*px + sp[:bottom_margin]
sp.minpad = (leftpad, toppad, rightpad, bottompad)
end
# ----------------------------------------------------------------
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
function _update_plot_object(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end
#TODO: should plotbb be computed here??
gplot = _inspectdr_getgui(plt.o)
if nothing == gplot; return; end
gplot.src = mplot #Ensure still references current plot
InspectDR.refresh(gplot)
return
end
# ----------------------------------------------------------------
const _inspectdr_mimeformats_dpi = Dict(
"image/png" => "png"
)
const _inspectdr_mimeformats_nodpi = Dict(
"image/svg+xml" => "svg",
"application/eps" => "eps",
"image/eps" => "eps",
# "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface
"application/pdf" => "pdf"
)
_inspectdr_show(io::IO, mime::MIME, ::Void, w, h) =
throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated"))
function _inspectdr_show(io::IO, mime::MIME, mplot, w, h)
InspectDR._show(io, mime, mplot, Float64(w), Float64(h))
end
for (mime, fmt) in _inspectdr_mimeformats_dpi
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend})
dpi = plt[:dpi]#TODO: support
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
end
end
for (mime, fmt) in _inspectdr_mimeformats_nodpi
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend})
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
end
end
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show
# ----------------------------------------------------------------
# Display/show the plot (open a GUI window, or browser page, for example).
function _display(plt::Plot{InspectDRBackend})
mplot = _inspectdr_getmplot(plt.o)
if nothing == mplot; return; end
gplot = _inspectdr_getgui(plt.o)
if nothing == gplot
gplot = display(InspectDR.GtkDisplay(), mplot)
else
#redundant... Plots.jl will call _update_plot_object:
#InspectDR.refresh(gplot)
end
plt.o = InspecDRPlotRef(mplot, gplot)
return gplot
end
+57 -9
View File
@@ -3,7 +3,7 @@
# significant contributions by: @pkofod
const _pgfplots_attr = merge_with_base_supported([
# :annotations,
:annotations,
# :background_color_legend,
:background_color_inside,
# :background_color_outside,
@@ -27,12 +27,12 @@ const _pgfplots_attr = merge_with_base_supported([
# :ribbon, :quiver, :arrow,
# :orientation,
# :overwrite_figure,
# :polar,
:polar,
# :normalize, :weights, :contours,
:aspect_ratio,
# :match_dimensions,
])
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape]
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
@@ -98,6 +98,12 @@ const _pgf_series_extrastyle = KW(
:xsticks => "xcomb",
)
const _pgf_annotation_halign = KW(
:center => "",
:left => "right",
:right => "left"
)
# --------------------------------------------------------------------------------------
# takes in color,alpha, and returns color and alpha appropriate for pgf style
@@ -136,6 +142,19 @@ function pgf_marker(d::KW)
}"""
end
function pgf_add_annotation!(o,x,y,val)
# Construct the style string.
# Currently supports color and orientation
cstr,a = pgf_color(val.font.color)
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
x, y,
style="""
$(get(_pgf_annotation_halign,val.font.halign,"")),
color=$cstr, draw opacity=$(convert(Float16,a)),
rotate=$(val.font.rotation)
"""))
end
# --------------------------------------------------------------------------------------
function pgf_series(sp::Subplot, series::Series)
@@ -147,7 +166,7 @@ function pgf_series(sp::Subplot, series::Series)
push!(style, pgf_linestyle(d))
push!(style, pgf_marker(d))
if d[:fillrange] != nothing
if d[:fillrange] != nothing || st in (:shape,)
push!(style, pgf_fillstyle(d))
end
@@ -211,6 +230,9 @@ function pgf_axis(sp::Subplot, letter)
# axis guide
kw[Symbol(letter,:label)] = axis[:guide]
# Add ticklabel rotations
push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}")
# flip/reverse?
axis[:flip] && push!(style, "$letter dir=reverse")
@@ -249,8 +271,11 @@ end
function _update_plot_object(plt::Plot{PGFPlotsBackend})
plt.o = PGFPlots.Axis[]
# Obtain the total height of the plot by extracting the maximal bottom
# coordinate from the bounding box.
total_height = bottom(bbox(plt.layout))
for sp in plt.subplots
# first build the PGFPlots.Axis object
# first build the PGFPlots.Axis object
style = ["unbounded coords=jump"]
kw = KW()
@@ -265,10 +290,12 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
# bounding box values are in mm
# note: bb origin is top-left, pgf is bottom-left
# A round on 2 decimal places should be enough precision for 300 dpi
# plots.
bb = bbox(sp)
push!(style, """
xshift = $(left(bb).value)mm,
yshift = $((height(bb) - (bottom(bb))).value)mm,
yshift = $(round((total_height - (bottom(bb))).value,2))mm,
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
""")
kw[:width] = "$(width(bb).value)mm"
@@ -288,19 +315,34 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
kw[:legendPos] = _pgfplots_legend_pos[legpos]
end
o = PGFPlots.Axis(; style = style, kw...)
axisf = PGFPlots.Axis
if sp[:projection] == :polar
axisf = PGFPlots.PolarAxis
end
o = axisf(; style = style, kw...)
# add the series object to the PGFPlots.Axis
for series in series_list(sp)
push!(o, pgf_series(sp, series))
# add series annotations
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt))
end
end
# add the annotations
for ann in sp[:annotations]
pgf_add_annotation!(o,ann...)
end
# add the PGFPlots.Axis to the list
push!(plt.o, o)
end
end
function _show(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
show(io, mime, plt.o)
end
@@ -314,12 +356,18 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
# read it into io
write(io, readall(open(fn)))
write(io, readstring(open(fn)))
# cleanup
PGFPlots.cleanup(plt.o)
end
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
fn = tempname()*".tex"
PGFPlots.save(fn, backend_object(plt), include_preamble=false)
write(io, readstring(open(fn)))
end
function _display(plt::Plot{PGFPlotsBackend})
# prepare the object
pgfplt = PGFPlots.plot(plt.o)
+120 -20
View File
@@ -20,20 +20,23 @@ const _plotly_attr = merge_with_base_supported([
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
:marker_z, :levels,
:marker_z, :fill_z, :levels,
:ribbon, :quiver,
:orientation,
# :overwrite_figure,
:polar,
:normalize, :weights,
# :contours, :aspect_ratio,
# :contours,
:aspect_ratio,
:hover,
:inset_subplots,
:bar_width,
:clims,
])
const _plotly_seriestype = [
:path, :scatter, :bar, :pie, :heatmap,
:contour, :surface, :path3d, :scatter3d, :shape, :scattergl,
:contour, :surface, :wireframe, :path3d, :scatter3d, :shape, :scattergl,
]
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
const _plotly_marker = [
@@ -55,6 +58,7 @@ end
const _plotly_js_path = joinpath(dirname(@__FILE__), "..", "..", "deps", "plotly-latest.min.js")
const _plotly_js_path_remote = "https://cdn.plot.ly/plotly-latest.min.js"
function _initialize_backend(::PlotlyBackend; kw...)
@eval begin
@@ -91,6 +95,20 @@ end
# ----------------------------------------------------------------
const _plotly_legend_pos = KW(
:right => [1., 0.5],
:left => [0., 0.5],
:top => [0.5, 1.],
:bottom => [0.5, 0.],
:bottomleft => [0., 0.],
:bottomright => [1., 0.],
:topright => [1., 1.],
:topleft => [0., 1.]
)
plotly_legend_pos(pos::Symbol) = get(_plotly_legend_pos, pos, [1.,1.])
plotly_legend_pos{S<:Real, T<:Real}(v::Tuple{S,T}) = v
function plotly_font(font::Font, color = font.color)
KW(
:family => font.family,
@@ -99,6 +117,7 @@ function plotly_font(font::Font, color = font.color)
)
end
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
KW(
:text => val,
@@ -115,7 +134,7 @@ function plotly_annotation_dict(x, y, ptxt::PlotText; xref="paper", yref="paper"
:font => plotly_font(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
:rotation => ptxt.font.rotation,
:rotation => -ptxt.font.rotation,
))
end
@@ -150,11 +169,41 @@ function plotly_scale(scale::Symbol)
end
end
function shrink_by(lo, sz, ratio)
amt = 0.5 * (1.0 - ratio) * sz
lo + amt, sz - 2amt
end
function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
aspect_ratio = sp[:aspect_ratio]
if aspect_ratio != :none
if aspect_ratio == :equal
aspect_ratio = 1.0
end
xmin,xmax = axis_limits(sp[:xaxis])
ymin,ymax = axis_limits(sp[:yaxis])
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
parea_ratio = width(plotarea) / height(plotarea)
if want_ratio > parea_ratio
# need to shrink y
ratio = parea_ratio / want_ratio
pcts[2], pcts[4] = shrink_by(pcts[2], pcts[4], ratio)
elseif want_ratio < parea_ratio
# need to shrink x
ratio = want_ratio / parea_ratio
pcts[1], pcts[3] = shrink_by(pcts[1], pcts[3], ratio)
end
pcts
end
pcts
end
# 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)
pcts = plotly_apply_aspect_ratio(sp, sp.plotarea, pcts)
i1,i2 = (letter == :x ? (1,3) : (2,4))
[pcts[i1], pcts[i1]+pcts[i2]]
end
@@ -166,6 +215,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
:title => axis[:guide],
:showgrid => sp[:grid],
:zeroline => false,
:ticks => "inside",
)
if letter in (:x,:y)
@@ -173,10 +223,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
ax[:anchor] = "$(letter==:x ? :y : :x)$(plotly_subplot_index(sp))"
end
rot = axis[:rotation]
if rot != 0
ax[:tickangle] = rot
end
ax[:tickangle] = -axis[:rotation]
if !(axis[:ticks] in (nothing, :none))
ax[:titlefont] = plotly_font(axis[:guidefont], axis[:foreground_color_guide])
@@ -188,7 +235,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
# lims
lims = axis[:lims]
if lims != :auto && limsType(lims) == :limits
ax[:range] = lims
ax[:range] = map(scalefunc(axis[:scale]), lims)
end
# flip
@@ -229,6 +276,7 @@ function plotly_layout(plt::Plot)
for sp in plt.subplots
spidx = plotly_subplot_index(sp)
# add an annotation for the title... positioned horizontally relative to plotarea,
# but vertically just below the top of the subplot bounding box
if sp[:title] != ""
@@ -248,8 +296,6 @@ function plotly_layout(plt::Plot)
d_out[:plot_bgcolor] = rgba_string(sp[:background_color_inside])
# TODO: x/y axis tick values/labels
# if any(is3d, seriesargs)
if is3d(sp)
d_out[:scene] = KW(
@@ -264,17 +310,32 @@ function plotly_layout(plt::Plot)
# legend
d_out[:showlegend] = sp[:legend] != :none
xpos,ypos = plotly_legend_pos(sp[:legend])
if sp[:legend] != :none
d_out[:legend] = KW(
:bgcolor => rgba_string(sp[:background_color_legend]),
:bordercolor => rgba_string(sp[:foreground_color_legend]),
:font => plotly_font(sp[:legendfont], sp[:foreground_color_legend]),
:x => xpos,
:y => ypos
)
end
# annotations
append!(d_out[:annotations], KW[plotly_annotation_dict(ann...; xref = "x$spidx", yref = "y$spidx") for ann in sp[:annotations]])
# series_annotations
for series in series_list(sp)
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
push!(d_out[:annotations], plotly_annotation_dict(
xi,
yi,
PlotText(str,fnt); xref = "x$spidx", yref = "y$spidx")
)
end
end
# # arrows
# for sargs in seriesargs
# a = sargs[:arrow]
@@ -311,6 +372,7 @@ end
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
# plotly_colorscale(c, alpha = nothing) = plotly_colorscale(cgrad(), alpha)
const _plotly_markers = KW(
:rect => "square",
:xcross => "x",
@@ -334,7 +396,7 @@ function plotly_close_shapes(x, y)
xs, ys = nansplit(x), nansplit(y)
for i=1:length(xs)
shape = Shape(xs[i], ys[i])
xs[i], ys[i] = shape_coords(shape)
xs[i], ys[i] = coords(shape)
end
nanvcat(xs), nanvcat(ys)
end
@@ -343,6 +405,10 @@ plotly_data(v) = collect(v)
plotly_data(surf::Surface) = surf.surf
plotly_data{R<:Rational}(v::AbstractArray{R}) = float(v)
plotly_surface_data(series::Series, a::AbstractVector) = a
plotly_surface_data(series::Series, a::AbstractMatrix) = transpose_z(series, a, false)
plotly_surface_data(series::Series, a::Surface) = plotly_surface_data(series, a.surf)
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
function plotly_series(plt::Plot, series::Series)
st = series[:seriestype]
@@ -366,6 +432,18 @@ function plotly_series(plt::Plot, series::Series)
hasmarker = isscatter || series[:markershape] != :none
hasline = st in (:path, :path3d)
# for surface types, set the data
if st in (:heatmap, :contour, :surface, :wireframe)
for letter in [:x,:y,:z]
d_out[letter] = plotly_surface_data(series, series[letter])
end
end
clims = sp[:clims]
if is_2tuple(clims)
d_out[:zmin], d_out[:zmax] = clims
end
# set the "type"
if st in (:path, :scatter, :scattergl)
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
@@ -384,17 +462,21 @@ function plotly_series(plt::Plot, series::Series)
elseif st == :bar
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
d_out[:orientation] = isvertical(series) ? "v" : "h"
d_out[:x], d_out[:y], d_out[:orientation] = if isvertical(series)
x, y, "v"
else
y, x, "h"
end
d_out[:marker] = KW(:color => rgba_string(series[:fillcolor]))
elseif st == :heatmap
d_out[:type] = "heatmap"
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
# d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
elseif st == :contour
d_out[:type] = "contour"
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
# d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
# d_out[:showscale] = series[:colorbar] != :none
d_out[:ncontours] = series[:levels]
d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines")
@@ -402,8 +484,21 @@ function plotly_series(plt::Plot, series::Series)
elseif st in (:surface, :wireframe)
d_out[:type] = "surface"
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
# d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
if st == :wireframe
d_out[:hidesurface] = true
wirelines = KW(
:show => true,
:color => rgba_string(series[:linecolor]),
:highlightwidth => series[:linewidth],
)
d_out[:contours] = KW(:x => wirelines, :y => wirelines, :z => wirelines)
else
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
if series[:fill_z] != nothing
d_out[:surfacecolor] = plotly_surface_data(series, series[:fill_z])
end
end
elseif st == :pie
d_out[:type] = "pie"
@@ -545,8 +640,12 @@ end
# ----------------------------------------------------------------
const _use_remote = Ref(false)
function html_head(plt::Plot{PlotlyBackend})
"<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
jsfilename = _use_remote[] ? _plotly_js_path_remote : _plotly_js_path
# "<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
"<script src=\"$jsfilename\"></script>"
end
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
@@ -577,7 +676,8 @@ end
function _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
show_png_from_html(io, plt)
# show_png_from_html(io, plt)
error("png output from the plotly backend is not supported. Please use plotlyjs instead.")
end
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
+35 -5
View File
@@ -40,7 +40,11 @@ end
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
PlotlyJS.plot()
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
else
PlotlyJS.plot()
end
end
@@ -56,10 +60,15 @@ end
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
z = series[:z]
if z != nothing
kw[:z] = (isa(z,Surface) ? transpose_z(series, series[:z].surf, false) : z,)
end
PlotlyJS.restyle!(
plt.o,
findfirst(plt.series_list, series),
KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
kw
)
end
@@ -77,7 +86,11 @@ end
# ----------------------------------------------------------------
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
show(io, MIME("text/html"), plt.o)
if isijulia() && !_use_remote[]
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
else
show(io, MIME("text/html"), plt.o)
end
end
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
@@ -89,6 +102,23 @@ _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hac
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "pdf")
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "eps")
function _display(plt::Plot{PlotlyJSBackend})
display(plt.o)
function write_temp_html(plt::Plot{PlotlyJSBackend})
filename = string(tempname(), ".html")
savefig(plt, filename)
filename
end
function _display(plt::Plot{PlotlyJSBackend})
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
display(plt.o)
else
standalone_html_window(plt)
end
end
function closeall(::PlotlyJSBackend)
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
close(current().o)
end
end
+89 -43
View File
@@ -18,8 +18,7 @@ const _pyplot_attr = merge_with_base_supported([
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
:marker_z,
:line_z,
:marker_z, :line_z, :fill_z,
:levels,
:ribbon, :quiver, :arrow,
:orientation,
@@ -56,6 +55,10 @@ function add_backend_string(::PyPlotBackend)
withenv("PYTHON" => "") do
Pkg.build("PyPlot")
end
import Conda
Conda.add("qt=4.8.5")
# now restart julia!
"""
end
@@ -64,7 +67,7 @@ function _initialize_backend(::PyPlotBackend)
# problem: https://github.com/tbreloff/Plots.jl/issues/308
# solution: hack from @stevengj: https://github.com/stevengj/PyPlot.jl/pull/223#issuecomment-229747768
otherdisplays = splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays))
import PyPlot
import PyPlot, PyCall
import LaTeXStrings: latexstring
append!(Base.Multimedia.displays, otherdisplays)
@@ -82,7 +85,17 @@ function _initialize_backend(::PyPlotBackend)
const pycollections = PyPlot.pywrap(PyPlot.pyimport("matplotlib.collections"))
const pyart3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d.art3d"))
end
if is_linux()
@eval begin
# avoid Conda update that causes Segfault with qt >=4.8.6 on Ubuntu https://github.com/JuliaPy/PyPlot.jl/issues/234
import Conda
kw = Conda._installed_packages_dict()
if (!haskey(kw,"qt") || (qt_version=get(kw,"qt",0)[1]!=v"4.8.5"))
print("\n If the code has a Segmentation fault error switch to qt v4.8.5 by pasting the following code into julia: \n \n")
print(add_backend_string(PyPlotBackend()))
end
end
end
# we don't want every command to update the figure
PyPlot.ioff()
end
@@ -117,13 +130,15 @@ py_color(grad::ColorGradient) = py_color(grad.colors)
function py_colormap(grad::ColorGradient)
pyvals = [(z, py_color(grad[z])) for z in grad.values]
pycolors.LinearSegmentedColormap[:from_list]("tmp", pyvals)
cm = pycolors.LinearSegmentedColormap[:from_list]("tmp", pyvals)
cm[:set_bad](color=(0,0,0,0.0), alpha=0.0)
cm
end
py_colormap(c) = py_colormap(cgrad())
function py_shading(c, z, α=nothing)
cmap = py_colormap(c, α)
function py_shading(c, z)
cmap = py_colormap(c)
ls = pycolors.pymember("LightSource")(270,45)
ls[:shade](z, cmap, vert_exag=0.1, blend_mode="soft")
end
@@ -140,7 +155,7 @@ function py_linestyle(seriestype::Symbol, linestyle::Symbol)
end
function py_marker(marker::Shape)
x, y = shape_coords(marker)
x, y = coords(marker)
n = length(x)
mat = zeros(n+1,2)
for i=1:n
@@ -246,6 +261,12 @@ function labelfunc(scale::Symbol, backend::PyPlotBackend)
end
end
function py_mask_nans(z)
# PyPlot.pywrap(pynp.ma[:masked_invalid](PyPlot.pywrap(z)))
PyCall.pycall(pynp.ma[:masked_invalid], Any, z)
# pynp.ma[:masked_where](pynp.isnan(z),z)
end
# ---------------------------------------------------------------------------
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
@@ -356,7 +377,7 @@ function py_bbox_title(ax)
end
function py_dpi_scale(plt::Plot{PyPlotBackend}, ptsz)
ptsz * DPI / plt[:dpi]
ptsz * plt[:dpi] / DPI
end
# ---------------------------------------------------------------------------
@@ -437,6 +458,9 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
error("Only numbers and vectors are supported with levels keyword")
end
# add custom frame shapes to markershape?
series_annotations_shapes!(series, :xy)
# for each plotting command, optionally build and add a series handle to the list
# line plot
@@ -552,16 +576,46 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
else
xyargs
end
handle = ax[:scatter](xyargs...;
label = series[:label],
zorder = series[:series_plotindex] + 0.5,
marker = py_marker(series[:markershape]),
s = py_dpi_scale(plt, series[:markersize] .^ 2),
edgecolors = py_markerstrokecolor(series),
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
extrakw...
)
push!(handles, handle)
if isa(series[:markershape], AbstractVector{Shape})
# this section will create one scatter per data point to accomodate the
# vector of shapes
handle = []
x,y = xyargs
shapes = series[:markershape]
msc = py_markerstrokecolor(series)
lw = py_dpi_scale(plt, series[:markerstrokewidth])
for i=1:length(y)
extrakw[:c] = if series[:marker_z] == nothing
py_color_fix(py_color(cycle(series[:markercolor],i)), x)
else
extrakw[:c]
end
push!(handle, ax[:scatter](cycle(x,i), cycle(y,i);
label = series[:label],
zorder = series[:series_plotindex] + 0.5,
marker = py_marker(cycle(shapes,i)),
s = py_dpi_scale(plt, cycle(series[:markersize],i) .^ 2),
edgecolors = msc,
linewidths = lw,
extrakw...
))
end
push!(handles, handle)
else
# do a normal scatter plot
handle = ax[:scatter](xyargs...;
label = series[:label],
zorder = series[:series_plotindex] + 0.5,
marker = py_marker(series[:markershape]),
s = py_dpi_scale(plt, series[:markersize] .^ 2),
edgecolors = py_markerstrokecolor(series),
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
extrakw...
)
push!(handles, handle)
end
end
if st == :hexbin
@@ -627,21 +681,16 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
x = repmat(x', length(y), 1)
y = repmat(y, 1, length(series[:x]))
end
# z = z'
z = transpose_z(series, z)
if st == :surface
if series[:marker_z] != nothing
extrakw[:facecolors] = py_shading(series[:fillcolor], series[:marker_z], series[:fillalpha])
if series[:fill_z] != nothing
# the surface colors are different than z-value
extrakw[:facecolors] = py_shading(series[:fillcolor], transpose_z(series, series[:fill_z].surf))
extrakw[:shade] = false
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
else
extrakw[:cmap] = py_fillcolormap(series)
needs_colorbar = true
end
needs_colorbar = true
end
handle = ax[st == :surface ? :plot_surface : :plot_wireframe](x, y, z;
label = series[:label],
@@ -730,12 +779,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end
clims = sp[:clims]
if is_2tuple(clims)
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
zmin, zmax = extrema(z)
extrakw[:vmin] = (is_2tuple(clims) && isfinite(clims[1])) ? clims[1] : zmin
extrakw[:vmax] = (is_2tuple(clims) && isfinite(clims[2])) ? clims[2] : zmax
handle = ax[:pcolormesh](x, y, z;
handle = ax[:pcolormesh](x, y, py_mask_nans(z);
label = series[:label],
zorder = series[:series_plotindex],
cmap = py_fillcolormap(series),
@@ -763,16 +811,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
push!(handle, ax[:add_patch](patches))
end
end
# path = py_path(x, y)
# patches = pypatches.pymember("PathPatch")(path;
# label = series[:label],
# zorder = series[:series_plotindex],
# edgecolor = py_linecolor(series),
# facecolor = py_fillcolor(series),
# linewidth = py_dpi_scale(plt, series[:linewidth]),
# fill = true
# )
# handle = ax[:add_patch](patches)
push!(handles, handle)
end
@@ -842,6 +880,12 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
)
push!(handles, handle)
end
# this is all we need to add the series_annotations text
anns = series[:series_annotations]
for (xi,yi,str,fnt) in EachAnn(anns, x, y)
py_add_annotations(sp, xi, yi, PlotText(str, fnt))
end
end
# --------------------------------------------------------------------------
@@ -1217,3 +1261,5 @@ for (mime, fmt) in _pyplot_mimeformats
)
end
end
closeall(::PyPlotBackend) = PyPlot.plt[:close]("all")
+35 -2
View File
@@ -16,7 +16,8 @@ const _unicodeplots_seriestype = [
:path, :scatter,
# :bar,
:shape,
:histogram2d
:histogram2d,
:spy
]
const _unicodeplots_style = [:auto, :solid]
const _unicodeplots_marker = [:none, :auto, :circle]
@@ -44,6 +45,18 @@ end
# -------------------------------
const _canvas_type = Ref(:auto)
function _canvas_map()
KW(
:braille => UnicodePlots.BrailleCanvas,
:ascii => UnicodePlots.AsciiCanvas,
:block => UnicodePlots.BlockCanvas,
:dot => UnicodePlots.DotCanvas,
:density => UnicodePlots.DensityCanvas,
)
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, width, height)
@@ -65,7 +78,27 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
canvas_type = isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
ct = _canvas_type[]
canvas_type = if ct == :auto
isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
else
_canvas_map()[ct]
end
# special handling for spy
if length(sp.series_list) == 1
series = sp.series_list[1]
if series[:seriestype] == :spy
push!(plt.o, UnicodePlots.spy(
series[:z].surf,
width = width,
height = height,
title = sp[:title],
canvas = canvas_type
))
continue
end
end
# # make it a bar canvas if plotting bar
# if any(series -> series[:seriestype] == :bar, series_list(sp))
+1 -1
View File
@@ -27,7 +27,7 @@ function open_browser_window(filename::AbstractString)
return run(`xdg-open $(filename)`)
end
@static if is_windows()
return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`)
end
warn("Unknown OS... cannot open browser window.")
end
+170 -65
View File
@@ -23,21 +23,24 @@ immutable Shape
# end
end
Shape(verts::AVec) = Shape(unzip(verts)...)
Shape(s::Shape) = deepcopy(s)
get_xs(shape::Shape) = shape.x
get_ys(shape::Shape) = shape.y
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
#deprecated
@deprecate shape_coords coords
function shape_coords(shape::Shape)
function coords(shape::Shape)
shape.x, shape.y
end
function shape_coords(shapes::AVec{Shape})
function coords(shapes::AVec{Shape})
length(shapes) == 0 && return zeros(0), zeros(0)
xs = map(get_xs, shapes)
ys = map(get_ys, shapes)
x, y = map(copy, shape_coords(shapes[1]))
x, y = map(copy, coords(shapes[1]))
for shape in shapes[2:end]
nanappend!(x, shape.x)
nanappend!(y, shape.y)
@@ -72,13 +75,13 @@ function makestar(n; offset = -0.5, radius = 1.0)
z2 = z1 + π / (n)
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
Shape(weave(outercircle, innercircle)[1:end-2])
Shape(weave(outercircle, innercircle))
end
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
function makeshape(n; offset = -0.5, radius = 1.0)
z = offset * π
Shape(partialcircle(z, z + 2π, n+1, radius)[1:end-1])
Shape(partialcircle(z, z + 2π, n+1, radius))
end
@@ -88,7 +91,7 @@ function makecross(; offset = -0.5, radius = 1.0)
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
Shape(weave(outercircle, innercircle,
ordering=Vector[outercircle,innercircle,outercircle])[1:end-2])
ordering=Vector[outercircle,innercircle,outercircle]))
end
@@ -110,6 +113,8 @@ const _shape_keys = Symbol[
:xcross,
:utriangle,
:dtriangle,
:rtriangle,
:ltriangle,
:pentagon,
:heptagon,
:octagon,
@@ -127,8 +132,10 @@ const _shapes = KW(
:circle => makeshape(20),
:rect => makeshape(4, offset=-0.25),
:diamond => makeshape(4),
:utriangle => makeshape(3),
:dtriangle => makeshape(3, offset=0.5),
:utriangle => makeshape(3, offset=0.5),
:dtriangle => makeshape(3, offset=-0.5),
:rtriangle => makeshape(3, offset=0.0),
:ltriangle => makeshape(3, offset=1.0),
:pentagon => makeshape(5),
:hexagon => makeshape(6),
:heptagon => makeshape(7),
@@ -143,12 +150,14 @@ for n in [4,5,6,7,8]
_shapes[Symbol("star$n")] = makestar(n)
end
Shape(k::Symbol) = deepcopy(_shapes[k])
# -----------------------------------------------------------------------
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
function center(shape::Shape)
x, y = shape_coords(shape)
x, y = coords(shape)
n = length(x)
A, Cx, Cy = 0.0, 0.0, 0.0
for i=1:n
@@ -166,7 +175,7 @@ function center(shape::Shape)
end
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
sx, sy = shape_coords(shape)
sx, sy = coords(shape)
cx, cy = c
for i=1:length(sx)
sx[i] = (sx[i] - cx) * x + cx
@@ -177,11 +186,11 @@ end
function Base.scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
shapecopy = deepcopy(shape)
scale!(shape, x, y, c)
scale!(shapecopy, x, y, c)
end
function translate!(shape::Shape, x::Real, y::Real = x)
sx, sy = shape_coords(shape)
sx, sy = coords(shape)
for i=1:length(sx)
sx[i] += x
sy[i] += y
@@ -191,7 +200,7 @@ end
function translate(shape::Shape, x::Real, y::Real = x)
shapecopy = deepcopy(shape)
translate!(shape, x, y)
translate!(shapecopy, x, y)
end
function rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
@@ -208,7 +217,7 @@ function rotate(x::Real, y::Real, θ::Real, c = center(shape))
end
function rotate!(shape::Shape, Θ::Real, c = center(shape))
x, y = shape_coords(shape)
x, y = coords(shape)
cx, cy = c
for i=1:length(x)
xi = rotate_x(x[i], y[i], Θ, cx, cy)
@@ -226,7 +235,7 @@ end
# -----------------------------------------------------------------------
immutable Font
type Font
family::AbstractString
pointsize::Int
halign::Symbol
@@ -239,7 +248,7 @@ end
function font(args...)
# defaults
family = "Helvetica"
family = "sans-serif"
pointsize = 14
halign = :hcenter
valign = :vcenter
@@ -283,6 +292,17 @@ function font(args...)
Font(family, pointsize, halign, valign, rotation, color)
end
function scalefontsize(k::Symbol, factor::Number)
f = default(k)
f.pointsize = round(Int, factor * f.pointsize)
default(k, f)
end
function scalefontsizes(factor::Number)
for k in (:titlefont, :guidefont, :tickfont, :legendfont)
scalefontsize(k, factor)
end
end
"Wrap a string with font info"
immutable PlotText
str::AbstractString
@@ -296,11 +316,7 @@ function text(str, args...)
PlotText(string(str), font(args...))
end
annotations(::Void) = []
annotations(anns::AVec) = anns
annotations(anns) = Any[anns]
Base.length(t::PlotText) = length(t.str)
# -----------------------------------------------------------------------
@@ -314,9 +330,9 @@ immutable Stroke
end
function stroke(args...; alpha = nothing)
width = nothing
color = nothing
style = nothing
width = 1
color = :black
style = :solid
for arg in args
T = typeof(arg)
@@ -350,8 +366,8 @@ immutable Brush
end
function brush(args...; alpha = nothing)
size = nothing
color = nothing
size = 1
color = :black
for arg in args
T = typeof(arg)
@@ -376,6 +392,109 @@ end
# -----------------------------------------------------------------------
type SeriesAnnotations
strs::AbstractVector # the labels/names
font::Font
baseshape::Nullable
scalefactor::Tuple
end
function series_annotations(strs::AbstractVector, args...)
fnt = font()
shp = Nullable{Any}()
scalefactor = (1,1)
for arg in args
if isa(arg, Shape) || (isa(arg, AbstractVector) && eltype(arg) == Shape)
shp = Nullable(arg)
elseif isa(arg, Font)
fnt = arg
elseif isa(arg, Symbol) && haskey(_shapes, arg)
shp = _shapes[arg]
elseif isa(arg, Number)
scalefactor = (arg,arg)
elseif is_2tuple(arg)
scalefactor = arg
else
warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
end
end
# if scalefactor != 1
# for s in get(shp)
# scale!(s, scalefactor, scalefactor, (0,0))
# end
# end
SeriesAnnotations(strs, fnt, shp, scalefactor)
end
series_annotations(anns::SeriesAnnotations) = anns
series_annotations(::Void) = nothing
function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
anns = series[:series_annotations]
# msw,msh = anns.scalefactor
# ms = series[:markersize]
# msw,msh = if isa(ms, AbstractVector)
# 1,1
# elseif is_2tuple(ms)
# ms
# else
# ms,ms
# end
# @show msw msh
if anns != nothing && !isnull(anns.baseshape)
# we use baseshape to overwrite the markershape attribute
# with a list of custom shapes for each
msw,msh = anns.scalefactor
msize = Float64[]
shapes = Shape[begin
str = cycle(anns.strs,i)
# get the width and height of the string (in mm)
sw, sh = text_size(str, anns.font.pointsize)
# how much to scale the base shape?
# note: it's a rough assumption that the shape fills the unit box [-1,-1,1,1],
# so we scale the length-2 shape by 1/2 the total length
scalar = (backend() == PyPlotBackend() ? 1.7 : 1.0)
xscale = 0.5to_pixels(sw) * scalar
yscale = 0.5to_pixels(sh) * scalar
# we save the size of the larger direction to the markersize list,
# and then re-scale a copy of baseshape to match the w/h ratio
maxscale = max(xscale, yscale)
push!(msize, maxscale)
baseshape = cycle(get(anns.baseshape),i)
shape = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
end for i=1:length(anns.strs)]
series[:markershape] = shapes
series[:markersize] = msize
end
return
end
type EachAnn
anns
x
y
end
Base.start(ea::EachAnn) = 1
Base.done(ea::EachAnn, i) = ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
function Base.next(ea::EachAnn, i)
tmp = cycle(ea.anns.strs,i)
str,fnt = if isa(tmp, PlotText)
tmp.str, tmp.font
else
tmp, ea.anns.font
end
((cycle(ea.x,i), cycle(ea.y,i), str, fnt), i+1)
end
annotations(::Void) = []
annotations(anns::AVec) = anns
annotations(anns) = Any[anns]
annotations(sa::SeriesAnnotations) = sa
# -----------------------------------------------------------------------
"type which represents z-values for colors and sizes (and anything else that might come up)"
immutable ZValues
values::Vector{Float64}
@@ -452,19 +571,25 @@ Base.eltype{T}(vol::Volume{T}) = T
# style is :open or :closed (for now)
immutable Arrow
style::Symbol
side::Symbol # :head (default), :tail, or :both
headlength::Float64
headwidth::Float64
end
function arrow(args...)
style = :simple
side = :head
headlength = 0.3
headwidth = 0.3
setlength = false
for arg in args
T = typeof(arg)
if T == Symbol
style = arg
if arg in (:head, :tail, :both)
side = arg
else
style = arg
end
elseif T <: Number
# first we apply to both, but if there's more, then only change width after the first number
headwidth = Float64(arg)
@@ -478,7 +603,7 @@ function arrow(args...)
warn("Skipped arrow arg $arg")
end
end
Arrow(style, headlength, headwidth)
Arrow(style, side, headlength, headwidth)
end
@@ -508,54 +633,34 @@ end
# -----------------------------------------------------------------------
type BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
control_points::Vector{T}
end
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
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))
@deprecate curve_points coords
coords(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
[]
function directed_curve(args...; kw...)
error("directed_curve has been moved to PlotRecipes")
end
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
function extrema_plus_buffer(v, buffmult = 0.2)
vmin,vmax = extrema(v)
vdiff = vmax-vmin
buffer = vdiff * buffmult
vmin - buffer, vmax + buffer
end
+1 -1
View File
@@ -40,7 +40,7 @@ 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)
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=0, α=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)]
),
+41 -2
View File
@@ -96,6 +96,32 @@ 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
# -----------------------------------------------------------
# points combined by x/y, pct, and length
type MixedMeasures
xy::Float64
pct::Float64
len::AbsoluteLength
end
function resolve_mixed(mix::MixedMeasures, sp::Subplot, letter::Symbol)
xy = mix.xy
pct = mix.pct
if mix.len != 0mm
f = (letter == :x ? width : height)
totlen = f(plotarea(sp))
@show totlen
pct += mix.len / totlen
end
if pct != 0
amin, amax = axis_limits(sp[Symbol(letter,:axis)])
xy += pct * (amax-amin)
end
xy
end
# -----------------------------------------------------------
# AbstractLayout
@@ -437,7 +463,7 @@ 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.")
error("When doing layout, n ($n) != n_override ($(n_override)). You're probably trying to force existing plots into a layout that doesn't fit them.")
end
layout, n
end
@@ -692,9 +718,22 @@ function link_axes!(axes::Axis...)
end
end
# figure out which subplots to link
function link_subplots(a::AbstractArray{AbstractLayout}, axissym::Symbol)
subplots = []
for l in a
if isa(l, Subplot)
push!(subplots, l)
elseif isa(l, GridLayout) && size(l) == (1,1)
push!(subplots, l[1,1])
end
end
subplots
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)
subplots = link_subplots(a, axissym)
axes = [sp.attr[axissym] for sp in subplots]
if length(axes) > 0
link_axes!(axes...)
+39 -6
View File
@@ -52,6 +52,16 @@ function tex(plt::Plot, fn::AbstractString)
end
tex(fn::AbstractString) = tex(current(), fn)
function html(plt::Plot, fn::AbstractString)
fn = addExtension(fn, "html")
io = open(fn, "w")
_use_remote[] = true
show(io, MIME("text/html"), plt)
_use_remote[] = false
close(io)
end
html(fn::AbstractString) = html(current(), fn)
# ----------------------------------------------------------------
@@ -63,6 +73,7 @@ const _savemap = Dict(
"ps" => ps,
"eps" => eps,
"tex" => tex,
"html" => html,
)
function getExtension(fn::AbstractString)
@@ -111,6 +122,13 @@ savefig(fn::AbstractString) = savefig(current(), fn)
gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
# IJulia only... inline display
function inline(plt::Plot = current())
isijulia() || error("inline() is IJulia-only")
Main.IJulia.clear_output(true)
display(Main.IJulia.InlineDisplay(), plt)
end
function Base.display(::PlotsDisplay, plt::Plot)
prepare_output(plt)
_display(plt)
@@ -119,6 +137,13 @@ end
# override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
_do_plot_show(plt, showval::Bool) = showval && gui(plt)
function _do_plot_show(plt, showval::Symbol)
showval == :gui && gui(plt)
showval in (:inline,:ijulia) && inline(plt)
end
# ---------------------------------------------------------
const _mimeformats = Dict(
@@ -129,6 +154,7 @@ const _mimeformats = Dict(
"application/postscript" => "ps",
"image/svg+xml" => "svg",
"text/plain" => "txt",
"application/x-tex" => "tex",
)
const _best_html_output_type = KW(
@@ -172,6 +198,8 @@ for mime in keys(_mimeformats)
end
end
closeall() = closeall(backend())
# ---------------------------------------------------------
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
@@ -268,7 +296,15 @@ function setup_atom()
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
# this is like "display"... sends an html div with the plot to the PlotPane
function Media.render(pane::Atom.PlotPane, plt::Plot)
Media.render(pane, Atom.div(Atom.HTML(stringmime(MIME("text/html"), plt))))
# temporarily overwrite size to be Atom.plotsize
sz = plt[:size]
plt[:size] = Juno.plotsize()
Media.render(pane, Atom.div(".fill", Atom.HTML(stringmime(MIME("text/html"), plt))))
plt[:size] = sz
end
# special handling for PlotlyJS
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
display(Plots.PlotsDisplay(), plt)
end
else
#
@@ -279,11 +315,8 @@ function setup_atom()
end
end
# Atom.displaysize(::Plot) = (535, 379)
# Atom.displaytitle(plt::Plot) = "Plots.jl (backend: $(backend(plt)))"
# special handling for plotly/plotlyjs
function Media.render{B<:Union{PlotlyBackend,PlotlyJSBackend}}(pane::Atom.PlotPane, plt::Plot{B})
# special handling for plotly... use PlotsDisplay
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend})
display(Plots.PlotsDisplay(), plt)
s = "PlotPane turned off. The plotly and plotlyjs backends cannot render in the PlotPane due to javascript issues."
Media.render(pane, Atom.div(Atom.HTML(s)))
+19 -11
View File
@@ -283,7 +283,11 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
# override subplot/axis args. `sp_attrs` take precendence
for (idx,sp) in enumerate(plt.subplots)
attr = merge(d, get(sp_attrs, sp, KW()))
attr = if !haskey(d, :subplot) || d[:subplot] == idx
merge(d, get(sp_attrs, sp, KW()))
else
get(sp_attrs, sp, KW())
end
_update_subplot_args(plt, sp, attr, idx, false)
end
@@ -333,14 +337,17 @@ function _prepare_annotations(sp::Subplot, d::KW)
# strip out series annotations (those which are based on series x/y coords)
# and add them to the subplot attr
sp_anns = annotations(sp[:annotations])
anns = annotations(pop!(d, :series_annotations, []))
if length(anns) > 0
x, y = d[:x], d[:y]
nx, ny, na = map(length, (x,y,anns))
n = max(nx, ny, na)
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
end
sp.attr[:annotations] = vcat(sp_anns, anns)
# series_anns = annotations(pop!(d, :series_annotations, []))
# if isa(series_anns, SeriesAnnotations)
# series_anns.x = d[:x]
# series_anns.y = d[:y]
# elseif length(series_anns) > 0
# x, y = d[:x], d[:y]
# nx, ny, na = map(length, (x,y,series_anns))
# n = max(nx, ny, na)
# series_anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(series_anns[mod1(i,na)])) for i=1:n]
# end
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
end
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
@@ -355,11 +362,12 @@ function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
end
end
function _add_the_series(plt, d)
function _add_the_series(plt, sp, d)
warnOnUnsupported_args(plt.backend, d)
warnOnUnsupported(plt.backend, d)
series = Series(d)
push!(plt.series_list, series)
push!(sp.series_list, series)
_series_added(plt, series)
end
@@ -382,7 +390,7 @@ function _process_seriesrecipe(plt::Plot, d::KW)
sp = _prepare_subplot(plt, d)
_prepare_annotations(sp, d)
_expand_subplot_extrema(sp, d, st)
_add_the_series(plt, d)
_add_the_series(plt, sp, d)
else
# get a sub list of series for this seriestype
+21 -6
View File
@@ -89,8 +89,16 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
plt.o = _create_backend_figure(plt)
plt.init = true
series_attr = KW()
for (k,v) in d
if haskey(_series_defaults, k)
series_attr[k] = pop!(d,k)
end
end
# create the layout and initialize the subplots
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
cmdidx = 1
for (idx, sp) in enumerate(plt.subplots)
_initialize_subplot(plt, sp)
serieslist = series_list(sp)
@@ -100,8 +108,11 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
sp.plt = plt
sp.attr[:subplot_index] = idx
for series in serieslist
merge!(series.d, series_attr)
_add_defaults!(series.d, plt, sp, cmdidx)
push!(plt.series_list, series)
_series_added(plt, series)
cmdidx += 1
end
end
@@ -115,9 +126,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
# finish up
current(plt)
if get(d, :show, default(:show))
gui()
end
_do_plot_show(plt, get(d, :show, default(:show)))
plt
end
@@ -150,6 +159,11 @@ end
function _plot!(plt::Plot, d::KW, args::Tuple)
d[:plot_object] = plt
if !isempty(args) && !isdefined(Main, :StatPlots) &&
first(split(string(typeof(args[1])), ".")) == "DataFrames"
warn("You're trying to plot a DataFrame, but this functionality is provided by StatPlots")
end
# --------------------------------
# "USER RECIPES"
# --------------------------------
@@ -218,9 +232,10 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
current(plt)
# do we want to force display?
if plt[:show]
gui(plt)
end
# if plt[:show]
# gui(plt)
# end
_do_plot_show(plt, plt[:show])
plt
end
+56
View File
@@ -0,0 +1,56 @@
const _attribute_defaults = Dict(:Series => _series_defaults,
:Subplot => _subplot_defaults,
:Plot => _plot_defaults,
:Axis => _axis_defaults)
attrtypes() = join(keys(_attribute_defaults), ", ")
attributes(attrtype::Symbol) = sort(collect(keys(_attribute_defaults[attrtype])))
function lookup_aliases(attrtype, attribute)
attribute = Symbol(attribute)
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
in(attribute, keys(_attribute_defaults[attrtype])) && return attribute
error("There is no attribute named $attribute in $attrtype")
end
function plotattr()
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
end
function plotattr(attrtype::Symbol)
in(attrtype, keys(_attribute_defaults)) || error("Viable options are $(attrtypes())")
println("Defined $attrtype attributes are:\n$(join(attributes(attrtype), ", "))")
end
function plotattr(attribute::AbstractString)
attribute = Symbol(attribute)
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
for (k, v) in _attribute_defaults
if in(attribute, keys(v))
return plotattr(k, "$attribute")
end
end
error("There is no attribute named $attribute")
end
function plotattr(attrtype::Symbol, attribute::AbstractString)
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
attribute = Symbol(lookup_aliases(attrtype, attribute))
desc = get(_arg_desc, attribute, "")
first_period_idx = findfirst(desc, '.')
typedesc = desc[1:first_period_idx-1]
desc = strip(desc[first_period_idx+1:end])
als = keys(filter((_,v)->v==attribute, _keyAliases)) |> collect |> sort
als = join(map(string,als), ", ")
def = _attribute_defaults[attrtype][attribute]
# Looks up the different elements and plots them
println("$attribute ", typedesc == "" ? "" : "{$typedesc}", "\n",
als == "" ? "" : "$als\n",
"\n$desc\n",
"$(attrtype) attribute, ", def == "" ? "" : " default: $def")
end
+280 -302
View File
@@ -93,57 +93,11 @@ end
# ----------------------------------------------------------------------------------
num_series(x::AMat) = size(x,2)
num_series(x) = 1
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T"))
# # TODO: remove when StatPlots is ready
# if is_installed("DataFrames")
# @eval begin
# import DataFrames
# # if it's one symbol, set the guide and return the column
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol)
# get!(d, Symbol(letter * "guide"), string(sym))
# collect(df[sym])
# end
# # if it's an array of symbols, set the labels and return a Vector{Any} of columns
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, syms::AbstractArray{Symbol})
# get!(d, :label, reshape(syms, 1, length(syms)))
# Any[collect(df[s]) for s in syms]
# end
# # for anything else, no-op
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything)
# anything
# end
# # handle grouping by DataFrame column
# function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...)
# extractGroupArgs(collect(df[group]))
# end
# # if a DataFrame is the first arg, lets swap symbols out for columns
# @recipe function f(df::DataFrames.AbstractDataFrame, args...)
# # if any of these attributes are symbols, swap out for the df column
# for k in (:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror)
# if haskey(d, k) && isa(d[k], Symbol)
# d[k] = collect(df[d[k]])
# end
# end
# # return a list of new arguments
# tuple(Any[handle_dfs(df, d, (i==1 ? "x" : i==2 ? "y" : "z"), arg) for (i,arg) in enumerate(args)]...)
# end
# end
# end
# ---------------------------------------------------------------------------
@@ -405,11 +359,15 @@ end
push!(yseg, yi, fi, fi, yi, yi)
end
# widen limits out a bit
expand_extrema!(axis, widen(extrema(xseg.pts)...))
# switch back
if !isvertical(d)
xseg, yseg = yseg, xseg
end
# reset orientation
orientation := default(:orientation)
@@ -420,101 +378,256 @@ end
end
@deps bar shape
# ---------------------------------------------------------------------------
# Histograms
# edges from number of bins
function calc_edges(v, bins::Integer)
vmin, vmax = extrema(v)
linspace(vmin, vmax, bins+1)
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
@recipe function f(::Type{Val{:barbins}}, x, y, z)
edge, weights = x, y
if (d[:bar_width] == nothing)
bar_width := diff(edge)
end
x := _bin_centers(edge)
y := weights
seriestype := :bar
()
end
@deps barbins bins
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
edge, weights = x, y
xerror := diff(edge)/2
x := _bin_centers(edge)
y := weights
seriestype := :scatter
()
end
@deps scatterbins scatter
function _stepbins_path(edge, weights)
nbins = length(linearindices(weights))
if length(linearindices(edge)) != nbins + 1
error("Edge vector must be 1 longer than weight vector")
end
it_e, it_w = start(edge), start(weights)
px, it_e = next(edge, it_e)
py = zero(eltype(weights))
npathpts = 2 * nbins + 2
x = Vector{eltype(px)}(npathpts)
y = Vector{eltype(py)}(npathpts)
x[1], y[1] = px, py
i = 2
while (i < npathpts - 1)
py, it_w = next(weights, it_w)
x[i], y[i] = px, py
i += 1
px, it_e = next(edge, it_e)
x[i], y[i] = px, py
i += 1
end
assert(i == npathpts)
x[end], y[end] = px, zero(py)
(x, y)
end
# just pass through arrays
calc_edges(v, bins::AVec) = bins
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
edge, weights = x, y
# 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)
axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis]
xpts, ypts = _stepbins_path(edge, weights)
if !Plots.isvertical(d)
xpts, ypts = ypts, xpts
end
# create a secondary series for the markers
if d[:markershape] != :none
@series begin
seriestype := :scatter
x := Plots._bin_centers(edge)
y := weights
fillrange := nothing
label := ""
primary := false
()
end
markershape := :none
xerror := :none
yerror := :none
end
x := xpts
y := ypts
seriestype := :path
ylims --> [0, 1.1 * maximum(weights)]
()
end
Plots.@deps stepbins path
function _auto_binning_nbins{N}(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto)
_cl(x) = max(ceil(Int, x), 1)
_iqr(v) = quantile(v, 0.75) - quantile(v, 0.25)
_span(v) = maximum(v) - minimum(v)
n_samples = length(linearindices(first(vs)))
# Estimator for number of samples in one row/column of bins along each axis:
n = max(1, n_samples^(1/N))
v = vs[dim]
if mode == :sqrt # Square-root choice
_cl(sqrt(n))
elseif mode == :sturges || mode ==:auto # Sturges' formula
_cl(log2(n)) + 1
elseif mode == :rice # Rice Rule
_cl(2 * n^(1/3))
elseif mode == :scott # Scott's normal reference rule
_cl(_span(v) / (3.5 * std(v) / n^(1/3)))
elseif mode == :fd # FreedmanDiaconis rule
_cl(_span(v) / (2 * _iqr(v) / n^(1/3)))
else
error("Unknown auto-binning mode $mode")
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)
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) = StatsBase.histrange(vs[dim], binning, :left)
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) = binning
# add a weighted count
for (i,vi) in enumerate(v)
idx = bucket_index(vi, edges)
counts[idx] += (weights == nothing ? 1.0 : weights[i])
end
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::NTuple{N}) =
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...))
# normalize by bar area?
norm_denom = normed ? sum(diff(edges) .* counts) : 1.0
if norm_denom == 0
norm_denom = 1.0
end
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) =
map(dim -> _hist_edge(vs, dim, binning), (1:N...))
edges, counts ./ norm_denom
_hist_norm_mode(mode::Symbol) = mode
_hist_norm_mode(mode::Bool) = mode ? :norm : :none
function _make_hist{N}(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing)
edges = _hist_edges(vs, binning)
h = float( weights == nothing ?
StatsBase.fit(StatsBase.Histogram, vs, edges) :
StatsBase.fit(StatsBase.Histogram, vs, weights, edges)
)
normalize!(h, mode = _hist_norm_mode(normed))
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
seriestype := :barhist
()
end
@deps histogram bar
@deps histogram barhist
@recipe function f(::Type{Val{:barhist}}, x, y, z)
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.weights
seriestype := :barbins
()
end
@deps barhist barbins
@recipe function f(::Type{Val{:stephist}}, x, y, z)
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.weights
seriestype := :stepbins
()
end
@deps stephist stepbins
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.weights
seriestype := :scatterbins
()
end
@deps scatterhist scatterbins
@recipe function f{T, E}(h::StatsBase.Histogram{T, 1, E})
seriestype --> :barbins
st_map = Dict(
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
)
seriestype := get(st_map, d[:seriestype], d[:seriestype])
if d[:seriestype] == :scatterbins
# Workaround, error bars currently not set correctly by scatterbins
xerror --> diff(h.edges[1])/2
seriestype := :scatter
(Plots._bin_centers(h.edges[1]), h.weights)
else
(h.edges[1], h.weights)
end
end
@recipe function f{H <: StatsBase.Histogram}(hv::AbstractVector{H})
for h in hv
@series begin
h
end
end
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])
@recipe function f(::Type{Val{:bins2d}}, x, y, z)
edge_x, edge_y, weights = x, y, z.surf
# 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])
float_weights = float(weights)
if is(float_weights, weights)
float_weights = deepcopy(float_weights)
end
for (i, c) in enumerate(float_weights)
if c == 0
float_weights[i] = NaN
end
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
x := Plots._bin_centers(edge_x)
y := Plots._bin_centers(edge_y)
z := Surface(float_weights)
xedges, yedges, counts ./ norm_denom
end
centers(v::AVec) = 0.5 * (v[1:end-1] + v[2:end])
@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)
linewidth := 0
match_dimensions := true
seriestype := :heatmap
()
end
@deps histogram2d heatmap
Plots.@deps bins2d heatmap
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
h = _make_hist((x, y), d[:bins], normed = d[:normalize], weights = d[:weights])
x := h.edges[1]
y := h.edges[2]
z := Surface(h.weights)
seriestype := :bins2d
()
end
@deps histogram2d bins2d
@recipe function f{T, E}(h::StatsBase.Histogram{T, 2, E})
seriestype --> :bins2d
(h.edges[1], h.edges[2], Surface(h.weights))
end
# ---------------------------------------------------------------------------
@@ -532,179 +645,6 @@ end
# note: don't add dependencies because this really isn't a drop-in replacement
# # TODO: move boxplots and violin plots to StatPlots when it's ready
# # ---------------------------------------------------------------------------
# # Box Plot
# const _box_halfwidth = 0.4
# notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
# @recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
# xsegs, ysegs = Segments(), Segments()
# glabels = sort(collect(unique(x)))
# warning = false
# outliers_x, outliers_y = zeros(0), zeros(0)
# for (i,glabel) in enumerate(glabels)
# # filter y
# values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
# # compute quantiles
# q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
# # notch
# n = notch_width(q2, q4, length(values))
# # warn on inverted notches?
# 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
# center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
# hw = d[:bar_width] == nothing ? _box_halfwidth : 0.5cycle(d[:bar_width], i)
# l, m, r = center - hw, center, center + hw
# # internal nodes for notches
# L, R = center - 0.5 * hw, center + 0.5 * hw
# # 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
# if notch
# push!(xsegs, m, l, r, m, m) # lower T
# push!(xsegs, l, l, L, R, r, r, l) # lower box
# push!(xsegs, l, l, L, R, r, r, l) # upper box
# push!(xsegs, m, l, r, m, m) # upper T
# push!(ysegs, q1, q1, q1, q1, q2) # lower T
# push!(ysegs, q2, q3-n, q3, q3, q3-n, q2, q2) # lower box
# push!(ysegs, q4, q3+n, q3, q3, q3+n, q4, q4) # upper box
# push!(ysegs, q5, q5, q5, q5, q4) # upper T
# else
# push!(xsegs, m, l, r, m, m) # lower T
# push!(xsegs, l, l, r, r, l) # lower box
# push!(xsegs, l, l, r, r, l) # upper box
# push!(xsegs, m, l, r, m, m) # upper T
# push!(ysegs, q1, q1, q1, q1, q2) # lower T
# push!(ysegs, q2, q3, q3, q2, q2) # lower box
# push!(ysegs, q4, q3, q3, q4, q4) # upper box
# push!(ysegs, q5, q5, q5, q5, q4) # upper T
# end
# end
# # Outliers
# @series begin
# seriestype := :scatter
# markershape := :circle
# markercolor := d[:fillcolor]
# markeralpha := d[:fillalpha]
# markerstrokecolor := d[:linecolor]
# markerstrokealpha := d[:linealpha]
# x := outliers_x
# y := outliers_y
# primary := false
# ()
# end
# seriestype := :shape
# x := xsegs.pts
# y := ysegs.pts
# ()
# end
# @deps boxplot shape scatter
# # ---------------------------------------------------------------------------
# # Violin Plot
# const _violin_warned = [false]
# # if the user has KernelDensity installed, use this for violin plots.
# # otherwise, just use a histogram
# if is_installed("KernelDensity")
# @eval import KernelDensity
# @eval function violin_coords(y; trim::Bool=false)
# kd = KernelDensity.kde(y, npoints = 200)
# if trim
# xmin, xmax = extrema(y)
# inside = Bool[ xmin <= x <= xmax for x in kd.x]
# return(kd.density[inside], kd.x[inside])
# end
# kd.density, kd.x
# end
# else
# @eval function violin_coords(y; trim::Bool=false)
# if !_violin_warned[1]
# warn("Install the KernelDensity package for best results.")
# _violin_warned[1] = true
# end
# edges, widths = my_hist(y, 10)
# centers = 0.5 * (edges[1:end-1] + edges[2:end])
# ymin, ymax = extrema(y)
# vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
# end
# end
# @recipe function f(::Type{Val{:violin}}, x, y, z; trim=true)
# xsegs, ysegs = Segments(), Segments()
# glabels = sort(collect(unique(x)))
# for glabel in glabels
# widths, centers = violin_coords(y[filter(i -> cycle(x,i) == glabel, 1:length(y))], trim=trim)
# isempty(widths) && continue
# # normalize
# widths = _box_halfwidth * widths / maximum(widths)
# # make the violin
# xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
# xcoords = vcat(widths, -reverse(widths)) + xcenter
# ycoords = vcat(centers, reverse(centers))
# push!(xsegs, xcoords)
# push!(ysegs, ycoords)
# end
# seriestype := :shape
# x := xsegs.pts
# y := ysegs.pts
# ()
# end
# @deps violin shape
# # ---------------------------------------------------------------------------
# # density
# @recipe function f(::Type{Val{:density}}, x, y, z; trim=false)
# newx, newy = violin_coords(y, trim=trim)
# if isvertical(d)
# newx, newy = newy, newx
# end
# x := newx
# y := newy
# seriestype := :path
# ()
# end
# @deps density path
# ---------------------------------------------------------------------------
# contourf - filled contours
@@ -736,8 +676,7 @@ end
function error_coords(xorig, yorig, ebar)
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
x, y = zeros(0), zeros(0)
x, y = Array(float_extended_type(xorig), 0), Array(Float64, 0)
# for each point, create a line segment from the bottom to the top of the errorbar
for i = 1:max(length(xorig), length(yorig))
xi = cycle(xorig, i)
@@ -943,15 +882,52 @@ end
# series recipe or moved to PlotRecipes
"Sparsity plot... heatmap of non-zero values of a matrix"
function spy{T<:Real}(z::AMat{T}; kw...)
mat = map(zi->float(zi!=0), z)'
xn, yn = size(mat)
heatmap(mat; leg=false, yflip=true, aspect_ratio=:equal,
xlim=(0.5, xn+0.5), ylim=(0.5, yn+0.5),
kw...)
# "Sparsity plot... heatmap of non-zero values of a matrix"
# function spy{T<:Real}(z::AMat{T}; kw...)
# mat = map(zi->float(zi!=0), z)'
# xn, yn = size(mat)
# heatmap(mat; leg=false, yflip=true, aspect_ratio=:equal,
# xlim=(0.5, xn+0.5), ylim=(0.5, yn+0.5),
# kw...)
# end
# Only allow matrices through, and make it seriestype :spy so the backend can
# optionally handle it natively.
@userplot Spy
@recipe function f(g::Spy)
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
seriestype := :spy
mat = g.args[1]
n,m = size(mat)
Plots.SliceIt, 1:m, 1:n, Surface(mat)
end
@recipe function f(::Type{Val{:spy}}, x,y,z)
yflip := true
aspect_ratio := 1
rs, cs, zs = findnz(z.surf)
xlim := extrema(cs)
ylim := extrema(rs)
if d[:markershape] == :none
markershape := :circle
end
if d[:markersize] == default(:markersize)
markersize := 1
end
markerstrokewidth := 0
marker_z := zs
label := ""
x := cs
y := rs
z := nothing
seriestype := :scatter
()
end
# -------------------------------------------------
"Adds a+bx... straight line over the current plot"
function abline!(plt::Plot, a, b; kw...)
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
@@ -963,14 +939,16 @@ abline!(args...; kw...) = abline!(current(), args...; kw...)
# -------------------------------------------------
# Dates
@recipe function f{T<:AbstractArray{Date}}(::Type{T}, dts::T)
date_formatter = dt -> string(convert(Date, dt))
xformatter := date_formatter
map(dt->convert(Int,dt), dts)
end
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int,dt), dt -> string(convert(Date,dt)))
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int,dt), dt -> string(convert(DateTime,dt)))
@recipe function f{T<:AbstractArray{DateTime}}(::Type{T}, dts::T)
date_formatter = dt -> string(convert(DateTime, dt))
xformatter := date_formatter
map(dt->convert(Int,dt), dts)
# -------------------------------------------------
# Complex Numbers
@userplot ComplexPlot
@recipe function f(cp::ComplexPlot)
xguide --> "Real Part"
yguide --> "Imaginary Part"
seriestype --> :scatter
real(cp.args[1]), imag(cp.args[1])
end
+44 -17
View File
@@ -6,7 +6,7 @@
# This should cut down on boilerplate code and allow more focused dispatch on type
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
typealias FuncOrFuncs Union{Function, AVec{Function}}
typealias FuncOrFuncs{F} Union{F, Vector{F}, Matrix{F}}
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
@@ -42,8 +42,8 @@ convertToAnyVector(v::Volume, d::KW) = Any[v], nothing
# # vector of OHLC
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# dates
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
# # dates
# convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
# list of things (maybe other vectors, functions, or something else)
function convertToAnyVector(v::AVec, d::KW)
@@ -96,8 +96,8 @@ nobigs(v) = v
end
# not allowed
compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
compute_xyz{F<:Function}(x::Void, y::FuncOrFuncs{F}, z) = error("If you want to plot the function `$y`, you need to define the x values!")
compute_xyz{F<:Function}(x::Void, y::Void, z::FuncOrFuncs{F}) = error("If you want to plot the function `$z`, you need to define x and y values!")
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
# --------------------------------------------------------------------
@@ -257,12 +257,23 @@ end
# # 1 argument
# # --------------------------------------------------------------------
# helper function to ensure relevant attributes are wrapped by Surface
function wrap_surfaces(d::KW)
if haskey(d, :fill_z)
v = d[:fill_z]
if !isa(v, Surface)
d[:fill_z] = Surface(v)
end
end
end
@recipe f(n::Integer) = is3d(get(d,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
# return a surface if this is a 3d plot, otherwise let it be sliced up
@recipe function f{T<:Union{Integer,AbstractFloat}}(mat::AMat{T})
if all3D(d)
n,m = size(mat)
wrap_surfaces(d)
SliceIt, 1:m, 1:n, Surface(mat)
else
SliceIt, nothing, mat, nothing
@@ -274,6 +285,7 @@ end
if all3D(d)
mat = fmt.data
n,m = size(mat)
wrap_surfaces(d)
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
else
SliceIt, nothing, fmt, nothing
@@ -322,18 +334,18 @@ end
@recipe function f(shape::Shape)
seriestype --> :shape
shape_coords(shape)
coords(shape)
end
@recipe function f(shapes::AVec{Shape})
seriestype --> :shape
shape_coords(shapes)
coords(shapes)
end
@recipe function f(shapes::AMat{Shape})
seriestype --> :shape
for j in 1:size(shapes,2)
@series shape_coords(vec(shapes[:,j]))
@series coords(vec(shapes[:,j]))
end
end
@@ -341,9 +353,13 @@ end
# function without range... use the current range of the x-axis
@recipe function f(f::FuncOrFuncs)
@recipe function f{F<:Function}(f::FuncOrFuncs{F})
plt = d[:plot_object]
xmin,xmax = axis_limits(plt[1][:xaxis])
xmin, xmax = try
axis_limits(plt[1][:xaxis])
catch
-5, 5
end
f, xmin, xmax
end
@@ -356,8 +372,9 @@ 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)
@recipe function f(f::FuncOrFuncs, x)
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
@recipe function f{F<:Function}(f::FuncOrFuncs{F}, x)
F2 = typeof(x)
@assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here
x, f
end
@@ -386,6 +403,7 @@ end
# seriestype := :path3d
# end
# end
wrap_surfaces(d)
SliceIt, x, y, z
end
@@ -395,6 +413,7 @@ end
@recipe function f(x::AVec, y::AVec, zf::Function)
# x = X <: Number ? sort(x) : x
# y = Y <: Number ? sort(y) : y
wrap_surfaces(d)
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
end
@@ -405,6 +424,7 @@ end
if !like_surface(get(d, :seriestype, :none))
d[:seriestype] = :contour
end
wrap_surfaces(d)
SliceIt, x, y, Surface(z)
end
@@ -416,16 +436,23 @@ end
#
# # 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)
@recipe function f(f::Function, xmin::Number, xmax::Number)
xs = adapted_grid(f, (xmin, xmax))
xs, f
end
@recipe function f{F<:Function}(fs::AbstractArray{F}, xmin::Number, xmax::Number)
xs = Any[adapted_grid(f, (xmin, xmax)) for f in fs]
xs, fs
end
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, 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)
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, 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, numPoints = 200)
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200)
fx, fy, fz, linspace(umin, umax, numPoints)
end
+2 -1
View File
@@ -3,6 +3,7 @@
function Subplot{T<:AbstractBackend}(::T; parent = RootLayout())
Subplot{T}(
parent,
Series[],
(20mm, 5mm, 2mm, 10mm),
defaultbox,
defaultbox,
@@ -33,7 +34,7 @@ get_subplot(series::Series) = series.d[:subplot]
get_subplot_index(plt::Plot, idx::Integer) = Int(idx)
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(_ -> _ === sp, plt.subplots)
series_list(sp::Subplot) = filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
function should_add_to_legend(series::Series)
series.d[:primary] && series.d[:label] != "" &&
+35 -60
View File
@@ -1,65 +1,40 @@
const _invisible = RGBA(0,0,0,0)
function theme(s::Symbol; kw...)
# reset?
if s == :none || s == :default
PlotUtils._default_gradient[] = :inferno
default(;
bg = :white,
bglegend = :match,
bginside = :match,
bgoutside = :match,
fg = :auto,
fglegend = :match,
fggrid = :match,
fgaxis = :match,
fgtext = :match,
fgborder = :match,
fgguide = :match,
palette = :auto
)
return
end
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,
# update the default gradient and other defaults
thm = PlotThemes._themes[s]
if thm.gradient != nothing
PlotUtils._default_gradient[] = PlotThemes.gradient_name(s)
end
default(;
bg = thm.bg_secondary,
bginside = thm.bg_primary,
fg = thm.lines,
fgtext = thm.text,
fgguide = thm.text,
fglegend = thm.text,
palette = thm.palette,
kw...
)
)
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
@deprecate set_theme(s) theme(s)
+18 -8
View File
@@ -21,11 +21,21 @@ wrap{T}(obj::T) = InputWrapper{T}(obj)
Base.isempty(wrapper::InputWrapper) = false
# -----------------------------------------------------------
type Series
d::KW
end
attr(series::Series, k::Symbol) = series.d[k]
attr!(series::Series, v, k::Symbol) = (series.d[k] = v)
# -----------------------------------------------------------
# a single subplot
type Subplot{T<:AbstractBackend} <: AbstractLayout
parent::AbstractLayout
series_list::Vector{Series} # arguments for each series
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
@@ -56,14 +66,6 @@ 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}
backend::T # the backend type
@@ -88,10 +90,18 @@ end
# -----------------------------------------------------------------------
Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
Base.length(plt::Plot) = length(plt.subplots)
Base.endof(plt::Plot) = length(plt)
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
Base.size(plt::Plot) = size(plt.layout)
Base.size(plt::Plot, i::Integer) = size(plt.layout)[i]
Base.ndims(plt::Plot) = 2
# attr(plt::Plot, k::Symbol) = plt.attr[k]
# attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
Base.getindex(sp::Subplot, i::Integer) = series_list(sp)[i]
Base.endof(sp::Subplot) = length(series_list(sp))
# -----------------------------------------------------------------------
+36 -23
View File
@@ -228,25 +228,34 @@ function Base.next(itr::SegmentsIterator, nextidx::Int)
istart:iend, i
end
# Find minimal type that can contain NaN and x
# To allow use of NaN separated segments with categorical x axis
float_extended_type{T}(x::AbstractArray{T}) = Union{T,Float64}
float_extended_type{T<:Real}(x::AbstractArray{T}) = Float64
# ------------------------------------------------------------------------------------
nop() = nothing
notimpl() = error("This has not been implemented yet")
Base.cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
Base.cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
isnothing(x::Void) = true
isnothing(x) = false
Base.cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
Base.cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
Base.cycle(v, idx::Int) = v
cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
Base.cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
Base.cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
Base.cycle(v, indices::AVec{Int}) = fill(v, length(indices))
cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
cycle(v, idx::Int) = v
Base.cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
Base.cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
cycle(v, indices::AVec{Int}) = fill(v, length(indices))
cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
makevec(v::AVec) = v
makevec{T}(v::T) = T[v]
@@ -256,7 +265,7 @@ maketuple(x::Real) = (x,x)
maketuple{T,S}(x::Tuple{T,S}) = x
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
mapFuncOrFuncs{F<:Function}(fs::AVec{F}, u::AVec) = [map(f, u) for f in fs]
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]
@@ -460,7 +469,7 @@ 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)))
for (i, (yi, ri)) in enumerate(zip(y, Base.cycle(rib)))
frs[i] = yi + ri
end
frs
@@ -490,6 +499,8 @@ zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
# ---------------------------------------------------------------
makekw(; kw...) = KW(kw)
wraptuple(x::Tuple) = x
wraptuple(x) = (x,)
@@ -660,26 +671,28 @@ function setxy!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer)
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])
reset_extrema!(sp)
_series_updated(plt, series)
end
function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
series = plt.series_list[i]
series.d[:x], series.d[:y], series.d[:z] = xyz
sp = series.d[:subplot]
expand_extrema!(sp.attr[:xaxis], xyz[1])
expand_extrema!(sp.attr[:yaxis], xyz[2])
expand_extrema!(sp.attr[:zaxis], xyz[3])
reset_extrema!(sp)
_series_updated(plt, series)
end
function setxyz!{X,Y,Z<:AbstractMatrix}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
setxyz!(plt, (xyz[1], xyz[2], Surface(xyz[3])), i)
end
# -------------------------------------------------------
# indexing notation
# Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
Base.setindex!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer) = setxy!(plt, xy, i)
Base.setindex!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) = setxyz!(plt, xyz, i)
Base.setindex!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer) = (setxy!(plt, xy, i); plt)
Base.setindex!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) = (setxyz!(plt, xyz, i); plt)
# -------------------------------------------------------
@@ -712,28 +725,28 @@ Base.push!(series::Series, xi, yi, zi) = (push_x!(series,xi); push_y!(series,yi)
# -------------------------------------------------------
function update!(series::Series; kw...)
function attr!(series::Series; kw...)
d = KW(kw)
preprocessArgs!(d)
for (k,v) in d
if haskey(_series_defaults, k)
series[k] = v
else
warn("unused key $k in series update")
warn("unused key $k in series attr")
end
end
_series_updated(series[:subplot].plt, series)
series
end
function update!(sp::Subplot; kw...)
function attr!(sp::Subplot; kw...)
d = KW(kw)
preprocessArgs!(d)
for (k,v) in d
if haskey(_subplot_defaults, k)
sp[k] = v
else
warn("unused key $k in subplot update")
warn("unused key $k in subplot attr")
end
end
sp
+1 -1
View File
@@ -24,7 +24,7 @@ default(size=(500,300))
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
const _current_plots_version = v"0.9.4"
const _current_plots_version = v"0.9.6"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
+3 -3
View File
@@ -23,7 +23,7 @@ facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotBackend()
@fact backend() --> Plots.PyPlotBackend()
image_comparison_facts(:pyplot, skip=[25,30], eps=img_eps)
image_comparison_facts(:pyplot, skip=[6,25,30], eps=img_eps)
end
facts("GR") do
@@ -67,7 +67,7 @@ facts("UnicodePlots") do
@fact backend() --> Plots.UnicodePlotsBackend()
# lets just make sure it runs without error
@fact isa(plot(rand(10)), Plot) --> true
@fact isa(plot(rand(10)), Plots.Plot) --> true
end
@@ -75,7 +75,7 @@ end
facts("Axes") do
p = plot()
axis = p.subplots[1][:xaxis]
@fact typeof(axis) --> Axis
@fact typeof(axis) --> Plots.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)