Compare commits

..

162 Commits

Author SHA1 Message Date
Tom Breloff 25bf71aff2 0.6 fixes 2017-02-21 12:10:38 -05:00
Tom Breloff 33d6c3426a inline docs for plot 2017-01-18 13:45:02 -05:00
Tom Breloff 9d038ea05e starting no-conditional reorg; replaced FixedSizeArrays with StaticArrays 2017-01-18 12:08:20 -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
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
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
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
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
Thomas Breloff c798fe6822 NEWS; bump version 2016-09-22 13:49:51 -04:00
Thomas Breloff 5f4dfdd5d2 add_backend/add_backend_string redesign 2016-09-22 13:47:33 -04:00
Thomas Breloff 1f7043e02a allow curly after grid in layout macro 2016-09-21 15:51:10 -04:00
Thomas Breloff c651be7a12 support pdf and eps in plotlyjs 2016-09-21 14:17:38 -04:00
31 changed files with 2256 additions and 939 deletions
+1 -2
View File
@@ -2,10 +2,9 @@
language: julia
os:
- linux
- osx
# - osx
julia:
- 0.5
- nightly
matrix:
allow_failures:
- julia: nightly
+43
View File
@@ -12,6 +12,49 @@
## 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
- allow curly after grid: `@layout [a b; grid(4,4){0.8h}]`
- add_backend redesign
#### 0.9.2
- glvisualize backend (@SimonDanisch)
+3 -3
View File
@@ -1,9 +1,9 @@
julia 0.5-
julia 0.5
RecipesBase
PlotUtils
PlotThemes
Reexport
Compat
FixedSizeArrays
Measures
Showoff
StaticArrays
+34 -53
View File
@@ -1,40 +1,28 @@
__precompile__()
__precompile__(true)
module Plots
# using Compat
using Reexport
# @reexport using Colors
# using Requires
using FixedSizeArrays
using StaticArrays
@reexport using RecipesBase
using Base.Meta
@reexport using PlotUtils
@reexport using PlotThemes
import Showoff
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 +58,8 @@ export
savefig,
png,
gui,
inline,
closeall,
backend,
backends,
@@ -77,12 +67,10 @@ export
backend_object,
add_backend,
aliases,
# dataframes,
Shape,
text,
font,
Axis,
stroke,
brush,
Surface,
@@ -94,11 +82,12 @@ export
Animation,
frame,
gif,
mov,
mp4,
animate,
@animate,
@gif,
spy,
test_examples,
iter_segments,
coords,
@@ -110,9 +99,7 @@ export
center,
P2,
P3,
BezierCurve,
curve_points,
directed_curve
BezierCurve
# ---------------------------------------------------------
@@ -206,28 +193,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 +235,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.",
+82 -16
View File
@@ -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
+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
+98 -26
View File
@@ -19,10 +19,11 @@ macro init_backend(s)
export $sym
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
backend_name(::$T) = Symbol($str)
backend_package_name(::$T) = Symbol("Plots", $(string(s)))
push!(_backends, Symbol($str))
_backendType[Symbol($str)] = $T
_backendSymbol[$T] = Symbol($str)
include("backends/" * $str * ".jl")
# include("backends/" * $str * ".jl")
end)
end
@@ -31,8 +32,12 @@ include("backends/web.jl")
# ---------------------------------------------------------
add_backend(pkg::Symbol) = add_backend(_backend_instance(pkg))
add_backend(b::AbstractBackend) = warn("No custom install defined for $(backend_name(b))")
function add_backend(pkg::Symbol)
info("To do a standard install of $pkg, copy and run this:\n\n")
println(add_backend_string(_backend_instance(pkg)))
println()
end
add_backend_string(b::AbstractBackend) = warn("No custom install defined for $(backend_name(b))")
# don't do anything as a default
_create_backend_figure(plt::Plot) = nothing
@@ -47,15 +52,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
@@ -113,7 +149,7 @@ function pickDefaultBackend()
# the ordering/inclusion of this package list is my semi-arbitrary guess at
# which one someone will want to use if they have the package installed...accounting for
# features, speed, and robustness
for pkgstr in ("PyPlot", "GR", "PlotlyJS", "Immerse", "Gadfly", "UnicodePlots")
for pkgstr in ("GR", "PlotlyJS", "PyPlot", "UnicodePlots")
if Pkg.installed(pkgstr) != nothing
return backend(Symbol(lowercase(pkgstr)))
end
@@ -147,6 +183,7 @@ function backend()
_initialize_backend(inst)
catch err
warn("Couldn't initialize $sym. (might need to install it?)")
add_backend(sym)
rethrow(err)
end
@@ -227,22 +264,32 @@ function merge_with_base_supported(v::AVec)
end
# @init_backend Immerse
# @init_backend Gadfly
@init_backend PyPlot
# @init_backend Qwt
@init_backend UnicodePlots
# @init_backend Winston
# @init_backend Bokeh
# @init_backend PyPlot
# @init_backend UnicodePlots
@init_backend Plotly
@init_backend PlotlyJS
# @init_backend PlotlyJS
@init_backend GR
@init_backend GLVisualize
@init_backend PGFPlots
# @init_backend GLVisualize
# @init_backend PGFPlots
# @init_backend InspectDR
_initialize_backend(::PlotlyBackend) = include("backends/plotly.jl")
function _initialize_backend(b::AbstractBackend)
bname = backend_name(b)
@eval import $(backend_package_name(b))
end
# ---------------------------------------------------------
const _attr = KW()
const _seriestype = KW()
const _marker = KW()
const _style = KW()
const _scale = KW()
using Base.Meta
# create the various `is_xxx_supported` and `supported_xxxs` methods
# by default they pass through to checking membership in `_gr_xxx`
for s in (:attr, :seriestype, :marker, :style, :scale)
@@ -257,13 +304,38 @@ for s in (:attr, :seriestype, :marker, :style, :scale)
for bend in backends()
bend_type = typeof(_backend_instance(bend))
v = Symbol("_", bend, "_", s)
# v = Symbol("_", bend, "_", s)
v = Symbol("_", s)
@eval begin
$f(::$bend_type, $s::Symbol) = $s in $v
$f2(::$bend_type) = $v
$f(::$bend_type, $s::Symbol) = $s in $v[$(quot(bend))]
$f2(::$bend_type) = $v[$(quot(bend))]
end
end
end
# is_subplot_supported(::AbstractBackend) = false
# is_subplot_supported() = is_subplot_supported(backend())
# ---------------------------------------------------------
# # create the various `is_xxx_supported` and `supported_xxxs` methods
# # by default they pass through to checking membership in `_gr_xxx`
# for s in (:attr, :seriestype, :marker, :style, :scale)
# f = Symbol("is_", s, "_supported")
# f2 = Symbol("supported_", s, "s")
# @eval begin
# $f(::AbstractBackend, $s) = false
# $f(bend::AbstractBackend, $s::AbstractVector) = all(v -> $f(bend, v), $s)
# $f($s) = $f(backend(), $s)
# $f2() = $f2(backend())
# end
#
# for bend in backends()
# bend_type = typeof(_backend_instance(bend))
# v = Symbol("_", bend, "_", s)
# @eval begin
# $f(::$bend_type, $s::Symbol) = $s in $v
# $f2(::$bend_type) = $v
# end
# end
# end
#
# # is_subplot_supported(::AbstractBackend) = false
# # is_subplot_supported() = is_subplot_supported(backend())
File diff suppressed because it is too large Load Diff
+112 -40
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,
@@ -42,6 +43,12 @@ const _gr_marker = _allMarkers
const _gr_scale = [:identity, :log10]
is_marker_supported(::GRBackend, shape::Shape) = true
function add_backend_string(::GRBackend)
"""
Pkg.add("GR")
Pkg.build("GR")
"""
end
function _initialize_backend(::GRBackend; kw...)
@eval begin
@@ -125,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
@@ -153,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
@@ -257,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
@@ -275,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
@@ -331,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
@@ -342,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
@@ -349,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)
@@ -403,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
# --------------------------------------------------------------------------------------
@@ -430,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
@@ -527,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
@@ -544,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
@@ -555,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
@@ -578,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)
@@ -616,12 +652,15 @@ 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],
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
@@ -629,12 +668,15 @@ 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),
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
@@ -678,9 +720,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]
@@ -708,12 +751,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
@@ -742,7 +789,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
@@ -794,16 +842,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)
@@ -890,8 +941,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)
@@ -904,6 +955,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
@@ -1010,18 +1068,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
@@ -1030,7 +1097,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()
@@ -1038,7 +1105,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()
+488
View File
@@ -0,0 +1,488 @@
# 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
#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)
#TODO: Support :asinh, :sqrt
if :log2 == s
return InspectDR.AxisScale(:log2)
elseif :log10 == s
return InspectDR.AxisScale(:log10)
elseif :ln == s
return InspectDR.AxisScale(:ln)
else #identity
return InspectDR.AxisScale(:lin)
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])
# 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.grid(vmajor=true, hmajor=true)
const gridoff = InspectDR.grid()
const plot = sp.o
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
xscale = _inspectdr_getscale(xaxis[:scale])
yscale = _inspectdr_getscale(yaxis[:scale])
plot.axes = InspectDR.AxesRect(xscale, yscale)
xmin, xmax = axis_limits(xaxis)
ymin, ymax = axis_limits(yaxis)
plot.ext = InspectDR.PExtents2D() #reset
plot.ext_full = InspectDR.PExtents2D(xmin, xmax, ymin, ymax)
a = plot.annotation
a.title = sp[:title]
a.xlabel = xaxis[:guide]; a.ylabel = yaxis[:guide]
l = plot.layout
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])
)
#No independent control of grid???
l.grid = sp[:grid]? gridon: gridoff
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
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]
_initialize_subplot(plt, sp)
_inspectdr_setupsubplot(sp)
sp.o.layout.frame.fillcolor =
_inspectdr_mapcolor(plt[:background_color_outside])
# add the annotations
for ann in sp[:annotations]
_inspectdr_add_annotations(mplot.subplots[i], 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})
sp.minpad = (20mm, 5mm, 2mm, 10mm)
#TODO: Add support for padding.
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
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) =
throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated"))
_inspectdr_show(io::IO, mime::MIME, mplot) = show(io, mime, mplot)
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))
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))
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
+13 -1
View File
@@ -40,6 +40,12 @@ const _pgfplots_scale = [:identity, :ln, :log2, :log10]
# --------------------------------------------------------------------------------------
function add_backend_string(::PGFPlotsBackend)
"""
Pkg.add("PGFPlots")
Pkg.build("PGFPlots")
"""
end
function _initialize_backend(::PGFPlotsBackend; kw...)
@eval begin
@@ -308,12 +314,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)
+117 -21
View File
@@ -20,20 +20,22 @@ 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,
])
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 = [
@@ -47,12 +49,15 @@ is_subplot_supported(::PlotlyBackend) = true
# --------------------------------------------------------------------------------------
function add_backend(::PlotlyBackend)
function add_backend_string(::PlotlyBackend)
"""
Pkg.build("Plots")
"""
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
@@ -89,6 +94,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,
@@ -97,6 +116,7 @@ function plotly_font(font::Font, color = font.color)
)
end
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
KW(
:text => val,
@@ -113,7 +133,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
@@ -148,11 +168,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
@@ -164,6 +214,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
:title => axis[:guide],
:showgrid => sp[:grid],
:zeroline => false,
:ticks => "inside",
)
if letter in (:x,:y)
@@ -171,10 +222,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])
@@ -186,7 +234,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
@@ -227,6 +275,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] != ""
@@ -246,8 +295,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(
@@ -262,17 +309,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]
@@ -309,6 +371,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",
@@ -332,7 +395,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
@@ -341,6 +404,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]
@@ -364,6 +431,13 @@ 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
# set the "type"
if st in (:path, :scatter, :scattergl)
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
@@ -382,17 +456,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")
@@ -400,8 +478,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"
@@ -543,8 +634,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)
@@ -575,7 +670,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})
+34 -9
View File
@@ -10,15 +10,17 @@ const _plotlyjs_scale = _plotly_scale
# --------------------------------------------------------------------------------------
function add_backend(::PlotlyJSBackend)
if !is_installed("PlotlyJS")
function add_backend_string(::PlotlyJSBackend)
"""
if !Plots.is_installed("PlotlyJS")
Pkg.add("PlotlyJS")
end
if !is_installed("Rsvg")
if !Plots.is_installed("Rsvg")
Pkg.add("Rsvg")
end
@eval import Blink
import Blink
Blink.AtomShell.install()
"""
end
@@ -38,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
@@ -54,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
@@ -75,15 +86,29 @@ 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 _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
tmpfn = tempname() * ".png"
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
tmpfn = tempname() * "." * ext
PlotlyJS.savefig(plt.o, tmpfn)
write(io, read(open(tmpfn)))
end
_show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "png")
_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)
end
function closeall(::PlotlyJSBackend)
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
close(current().o)
end
end
+82 -48
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,
@@ -48,13 +47,15 @@ is_marker_supported(::PyPlotBackend, shape::Shape) = true
# --------------------------------------------------------------------------------------
function add_backend(::PyPlotBackend)
if !is_installed("PyPlot")
withenv("PYTHON" => "") do
Pkg.add("PyPlot")
Pkg.build("PyPlot")
end
function add_backend_string(::PyPlotBackend)
"""
if !Plots.is_installed("PyPlot")
Pkg.add("PyPlot")
end
withenv("PYTHON" => "") do
Pkg.build("PyPlot")
end
"""
end
function _initialize_backend(::PyPlotBackend)
@@ -62,7 +63,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)
@@ -115,13 +116,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
@@ -138,7 +141,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
@@ -244,6 +247,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)
@@ -354,7 +363,7 @@ function py_bbox_title(ax)
end
function py_dpi_scale(plt::Plot{PyPlotBackend}, ptsz)
ptsz * DPI / plt[:dpi]
ptsz * plt[:dpi] / DPI
end
# ---------------------------------------------------------------------------
@@ -435,6 +444,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
@@ -550,16 +562,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
@@ -625,21 +667,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],
@@ -728,12 +765,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),
@@ -761,16 +797,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
@@ -840,6 +866,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
# --------------------------------------------------------------------------
@@ -1215,3 +1247,5 @@ for (mime, fmt) in _pyplot_mimeformats
)
end
end
closeall(::PyPlotBackend) = PyPlot.plt[:close]("all")
+42 -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]
@@ -28,6 +29,13 @@ warnOnUnsupported_args(::UnicodePlotsBackend, d::KW) = nothing
# --------------------------------------------------------------------------------------
function add_backend_string(::UnicodePlotsBackend)
"""
Pkg.add("UnicodePlots")
Pkg.build("UnicodePlots")
"""
end
function _initialize_backend(::UnicodePlotsBackend; kw...)
@eval begin
import UnicodePlots
@@ -37,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)
@@ -58,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))
+175 -70
View File
@@ -1,7 +1,7 @@
typealias P2 FixedSizeArrays.Vec{2,Float64}
typealias P3 FixedSizeArrays.Vec{3,Float64}
typealias P2 StaticArrays.SVector{2,Float64}
typealias P3 StaticArrays.SVector{3,Float64}
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
@@ -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
@@ -175,13 +184,13 @@ function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
shape
end
function Base.scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
function 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
@@ -507,55 +632,35 @@ end
# -----------------------------------------------------------------------
type BezierCurve{T <: FixedSizeArrays.Vec}
control_points::Vector{T}
type BezierCurve{T <: StaticArrays.SVector}
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)
Base.mean{N,T<:Real}(ps::StaticArrays.SVector{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)]
),
+61 -4
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
@@ -503,6 +529,12 @@ function build_layout(layout::GridLayout, n::Integer)
i += 1
elseif isa(l, GridLayout)
# sub-grid
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
end
l, sps, m = build_layout(l, n-i)
append!(subplots, sps)
merge!(spmap, m)
@@ -539,6 +571,12 @@ function build_layout(layout::GridLayout, numsp::Integer, plts::AVec{Plot})
i += length(plt.subplots)
elseif isa(l, GridLayout)
# sub-grid
if get(l.attr, :width, :auto) != :auto
layout.widths[c] = attr(l,:width)
end
if get(l.attr, :height, :auto) != :auto
layout.heights[r] = attr(l,:height)
end
l, sps, m = build_layout(l, numsp-i, plts)
append!(subplots, sps)
merge!(spmap, m)
@@ -635,12 +673,18 @@ function create_grid_vcat(expr::Expr)
end
function create_grid_curly(expr::Expr)
s = expr.args[1]
kw = KW()
for (i,arg) in enumerate(expr.args[2:end])
add_layout_pct!(kw, arg, i, length(expr.args)-1)
end
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
s = expr.args[1]
if isa(s, Expr) && s.head == :call && s.args[1] == :grid
create_grid(:(grid($(s.args[2:end]...), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto))))))
elseif isa(s, Symbol)
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
else
error("Unknown use of curly brackets: $expr")
end
end
function create_grid(s::Symbol)
@@ -674,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...)
+41 -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,11 @@ 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
else
#
@@ -279,14 +311,17 @@ 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)))
end
# special handling for PlotlyJS to pass through to that render method
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
Plots.prepare_output(plt)
Media.render(pane, plt.o)
end
end
end
+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
+25 -10
View File
@@ -29,12 +29,12 @@ convertSeriesIndex(plt::Plot, n::Int) = n
"""
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
The main plot command. Use `plot` to create a new plot object, and `plot!` to mutate an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the current
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
plot(args...; kw...) # creates a new Plot, and sets it to be the current
plot!(args...; kw...) # adds to the `current` Plot
plot!(plotobj, args...; kw...) # adds to the Plot `plotobj`
```
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
@@ -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
+65 -237
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)
@@ -507,6 +465,11 @@ centers(v::AVec) = 0.5 * (v[1:end-1] + v[2:end])
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
normed = d[:normalize],
weights = d[:weights])
for (i,c) in enumerate(counts)
if c == 0
counts[i] = NaN
end
end
x := centers(xedges)
y := centers(yedges)
z := Surface(counts)
@@ -532,179 +495,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 +526,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 +732,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 +789,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
+48 -21
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
@@ -455,13 +482,13 @@ end
#
# # 2D FixedSizeArrays
@recipe f{T<:Number}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = unzip(xy)
@recipe f{T<:Number}(xy::FixedSizeArrays.Vec{2,T}) = [xy[1]], [xy[2]]
@recipe f{T<:Number}(xy::AVec{StaticArrays.SVector{2,T}}) = unzip(xy)
@recipe f{T<:Number}(xy::StaticArrays.SVector{2,T}) = [xy[1]], [xy[2]]
#
# # 3D FixedSizeArrays
@recipe f{T<:Number}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = unzip(xyz)
@recipe f{T<:Number}(xyz::FixedSizeArrays.Vec{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]]
@recipe f{T<:Number}(xyz::AVec{StaticArrays.SVector{3,T}}) = unzip(xyz)
@recipe f{T<:Number}(xyz::StaticArrays.SVector{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]]
#
# # --------------------------------------------------------------------
+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))
# -----------------------------------------------------------------------
+42 -29
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,20 +265,20 @@ 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]
unzip{X,Y,U,V}(xyuv::AVec{Tuple{X,Y,U,V}}) = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]]
unzip{T}(xy::AVec{StaticArrays.SVector{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
unzip{T}(xy::StaticArrays.SVector{2,T}) = T[xy[1]], T[xy[2]]
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
unzip{T}(xyz::AVec{StaticArrays.SVector{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
unzip{T}(xyz::StaticArrays.SVector{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
unzip{T}(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
unzip{T}(xyuv::AVec{StaticArrays.SVector{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
unzip{T}(xyuv::StaticArrays.SVector{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
function _expand_limits(lims, x)
@@ -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.3"
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)
+2 -2
View File
@@ -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)