Compare commits
286 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c2e8b5a1fa | |||
| 72c4962e97 | |||
| 789cf1d66b | |||
| 64ec922d7f | |||
| 0eab0dc680 | |||
| 2b48f50de4 | |||
| fa60eacfc5 | |||
| d2a4136d4f | |||
| 7d88a746ea | |||
| 7f4537bdc6 | |||
| 8ef70f07f0 | |||
| 17b7e37885 | |||
| 35c17044e1 | |||
| 7085d98f41 | |||
| ee73e32b0e | |||
| 371430c172 | |||
| 495753b517 | |||
| 211be0a863 | |||
| 12c9a8e6c3 | |||
| fb5d9e0c01 | |||
| 61a2d962c0 | |||
| 440622830a | |||
| 2652990432 | |||
| 652e2f27b6 | |||
| 77a82eaa6a | |||
| 624a181b23 | |||
| 865ac52442 | |||
| 4f064d7c02 | |||
| facb67e94e | |||
| 124c838e06 | |||
| 026e894e1d | |||
| 2ed0f1661a | |||
| 00f285cba3 | |||
| ddf5bf472f | |||
| 534c7799e5 | |||
| b254aaa081 | |||
| 76f2860c74 | |||
| 5ebcb77d0d | |||
| 4847752ef4 | |||
| 84ec8d61ae | |||
| 0f13551b6d | |||
| 3e66c6cce4 | |||
| 09de3a5e7a | |||
| 00483d4c0d | |||
| 1e3d10ad31 | |||
| dc31cd7103 | |||
| cffb670f72 | |||
| 781fea7431 | |||
| 78b0b7e01d | |||
| e96367cd4b | |||
| 30af3c7f8a | |||
| 264405827e | |||
| 083721bbc1 | |||
| 91ed04ff83 | |||
| cbbd4fd029 | |||
| f6bc7721a8 | |||
| 32ec9e82bf | |||
| 69a7a8a04e | |||
| ccb5194bb5 | |||
| 2d280edf0c | |||
| afe4817150 | |||
| 6754b3746e | |||
| 287f83f4d0 | |||
| bf518a961e | |||
| 4a55467eef | |||
| 56cf5249f9 | |||
| 17e8bc6949 | |||
| 02ede8020f | |||
| 54158a0340 | |||
| 7ce96a4e3d | |||
| 2067b70445 | |||
| d02658e0ba | |||
| 27e68ed6a9 | |||
| b3e0ac017f | |||
| 8bc437a84e | |||
| 11e5a94fc4 | |||
| c11941cbc1 | |||
| f792aea98e | |||
| 12e5937a91 | |||
| 60b9cd8789 | |||
| 41e4c80494 | |||
| bd2756c0bf | |||
| 1f3c4948c6 | |||
| 062f9a195c | |||
| a3e8af40b2 | |||
| 49f4773e81 | |||
| ee78385b4c | |||
| e99ccd0051 | |||
| cbc022524b | |||
| 34d9d042aa | |||
| 22f69a99e2 | |||
| 4ee5539e3d | |||
| 048636e894 | |||
| 5797ad2d5b | |||
| ba9a8d52cc | |||
| ee18a9dd6c | |||
| 517c2f456c | |||
| e8734daa4d | |||
| 1fd7c6e2f1 | |||
| bc638ea06e | |||
| fd37a9c660 | |||
| fc460554e7 | |||
| 73490903a3 | |||
| 964c0ac420 | |||
| f59ad1620a | |||
| 4797f18819 | |||
| b0d73a3314 | |||
| 562e60f964 | |||
| fde0fa673b | |||
| c93d5e7830 | |||
| e305b973b2 | |||
| 506d3611fd | |||
| 14b644777d | |||
| b9c23c75f4 | |||
| af60d03060 | |||
| adb66cae9d | |||
| aeaa0c5f8d | |||
| b009da4b47 | |||
| 8abd70bed8 | |||
| 91cd6c1954 | |||
| 7a702489d0 | |||
| d33331f650 | |||
| 8e3bd07c91 | |||
| ad55551819 | |||
| 1d38e50281 | |||
| cef0882e80 | |||
| 5a07c4917e | |||
| e498d091cd | |||
| 8ba30c5453 | |||
| 685e2eaff7 | |||
| 6ca8471c64 | |||
| e30f4652af | |||
| 9acb89ba8a | |||
| 9b9a775a41 | |||
| a6f11cdb31 | |||
| 777642ef55 | |||
| d1d7fd258f | |||
| 7e4635de26 | |||
| d7b2173066 | |||
| 55e9c8c9eb | |||
| c34e1d3c19 | |||
| f89b1ba303 | |||
| fc7b6dd0c6 | |||
| 1b5240a513 | |||
| 4bcf43c84b | |||
| b57abfd563 | |||
| bfc26fa407 | |||
| e7b435b47f | |||
| dd6842522f | |||
| f56d944870 | |||
| 772b79306d | |||
| 4ef2c6286b | |||
| a5f7c1b867 | |||
| 4d8ef2a008 | |||
| 8bc28d472f | |||
| 3753ab1b55 | |||
| babc600915 | |||
| b2f92e246e | |||
| 92f9ccce5c | |||
| 0ace28b784 | |||
| bd06f0c713 | |||
| 4e5c88eb74 | |||
| 004ad15875 | |||
| 1c0a731ff6 | |||
| 9a5f42c251 | |||
| c8cdade884 | |||
| bbff66a041 | |||
| 6b6394ba97 | |||
| d11c6a20bf | |||
| 6c396e1d88 | |||
| bffd9bcb53 | |||
| 7cee448d68 | |||
| c273bf6a7b | |||
| 047a13a8dc | |||
| 717aed5a06 | |||
| 7d58090e59 | |||
| 501df8cfb9 | |||
| 7f6fe09a00 | |||
| d183d579cf | |||
| bfa2fd2edc | |||
| 7c257a09f6 | |||
| da4111579d | |||
| 6b117234bc | |||
| c4bac660cf | |||
| ee2e7d509c | |||
| 461e93fc56 | |||
| c74adc6521 | |||
| 7cc60d0b0c | |||
| d8f0612f5a | |||
| cd4b41a537 | |||
| fdb2ee2847 | |||
| 9c4cc5acda | |||
| f901254519 | |||
| 18032bb9af | |||
| a7a5fe36f9 | |||
| 32e1536955 | |||
| 77c2ee6782 | |||
| dbe5b78307 | |||
| 9578fe03b9 | |||
| ef48b66ac4 | |||
| b9d586c009 | |||
| 1d51982cb0 | |||
| 046985277d | |||
| d679337700 | |||
| 2d5f896069 | |||
| de51beebe8 | |||
| 80b25657bc | |||
| 0db60348aa | |||
| 5017024557 | |||
| 94863d69b1 | |||
| 06466b060f | |||
| 3ac9f3d9cc | |||
| d335a0221d | |||
| a118d73871 | |||
| 6c13d87a00 | |||
| 9e2264c841 | |||
| c0248069a1 | |||
| 5ba4c10a18 | |||
| 82ceb71bd0 | |||
| a4636fd88d | |||
| e19e25473c | |||
| bde415edd7 | |||
| 2004090ce5 | |||
| 18ea542cc4 | |||
| 11a856fbb3 | |||
| 1ed7899296 | |||
| a79a40b1e8 | |||
| d4075a1ef5 | |||
| 99f3b7ae99 | |||
| fcb5b355b1 | |||
| 43d26af34d | |||
| 5e1c67c042 | |||
| 11ef2be506 | |||
| 0f9bb7793c | |||
| decd7c2f60 | |||
| edf88ffdf6 | |||
| 435a7c809b | |||
| d79c66e5b6 | |||
| 10a8520805 | |||
| cf6c16102b | |||
| 32baaea5cb | |||
| 27e2795576 | |||
| c21f3810b9 | |||
| 877e7ab940 | |||
| 7a9a871be3 | |||
| de31ce1c73 | |||
| 8b6dba8301 | |||
| af859b4c13 | |||
| 9b5295ff00 | |||
| c1a94d704e | |||
| c2b56878fc | |||
| 4757bd89c0 | |||
| 1e56bf939d | |||
| dbd5ae46dd | |||
| 7c6095b6e8 | |||
| 04888646e7 | |||
| f6ed3fbb76 | |||
| 7bf2918438 | |||
| 92fefc7089 | |||
| e6df4d977d | |||
| 62ca787e6e | |||
| 32d59fff61 | |||
| 1949b6fe0f | |||
| 95a5b5025a | |||
| aa22fd2ff2 | |||
| 50df9a70c9 | |||
| 6d88e150d0 | |||
| b1817e513b | |||
| ce737a9491 | |||
| 6ff4dce62a | |||
| 89b5582d0d | |||
| 09c5e8a375 | |||
| a1a7dcd6b8 | |||
| d223d872fb | |||
| 91eaa32ad7 | |||
| 6cdccf4353 | |||
| ac9aee97fd | |||
| 5be95be9ae | |||
| e402f5dfce | |||
| 61be113714 | |||
| a637f43edd | |||
| 4bb43887a4 | |||
| 802b20be6f | |||
| 216a9c3666 | |||
| d1980ef978 | |||
| b3db49b4ae |
@@ -3,13 +3,85 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- All new development should target 0.14!
|
||||
- Minor version 0.17 is the last one to support Julia 0.6!!
|
||||
- Minor version 0.11 is the last one to support Julia 0.5!!
|
||||
- Critical bugfixes only
|
||||
- `backports` branch is for Julia 0.5
|
||||
|
||||
---
|
||||
## (current master)
|
||||
- All new development should target Julia 0.7!
|
||||
|
||||
## 0.17.4
|
||||
- fix thickness_scaling for pyplot
|
||||
|
||||
## 0.17.3
|
||||
- Log-scale heatmap edge computation
|
||||
- Fix size and dpi for GR and PyPlot
|
||||
- Fix fillrange with line segments on PyPlot and Plotly
|
||||
- fix flip for heatmap and image on GR
|
||||
- New attributes for PGFPlots
|
||||
- Widen axes for most series types and log scales
|
||||
- Plotly: fix log scale with no ticks
|
||||
- Fix axis flip on Plotly
|
||||
- Fix hover and zcolor interaction in Plotly
|
||||
- WebIO integration for PlotlyJS backend
|
||||
|
||||
## 0.17.2
|
||||
- fix single subplot in plotly
|
||||
- implement `(xyz)lims = :round`
|
||||
- PyPlot: fix bg_legend = invisible()
|
||||
- set fallback tick specification for axes with discrete values
|
||||
- restructure of show methods
|
||||
|
||||
## 0.17.1
|
||||
- Fix contour for PGFPlots
|
||||
- 32Bit fix: Int64 -> Int
|
||||
- Make series of shapes and segments toggle together in Plotly(JS)
|
||||
- Fix marker arguments
|
||||
- Fix processing order of series recipes
|
||||
- Fix Plotly(JS) ribbon
|
||||
- Contour plots with x,y in grid form on PyPlot
|
||||
|
||||
## 0.17.0
|
||||
- Add GR dependency to make it the default backend
|
||||
- Improve histogram2d bin estimation
|
||||
- Allow vector arguments for certain series attributes and support line_z and fill_z on GR, PyPlot, Plotly(JS) and PGFPlots
|
||||
- Automatic scientific notation for tick labels
|
||||
- Allow to set the theme in PLOTS_DEFAULTS
|
||||
- Implement plots_heatmap seriestype providing a Plots recipe for heatmaps
|
||||
|
||||
## 0.16.0
|
||||
- fix 3D plotting in PyPlot
|
||||
- Infinite objects
|
||||
|
||||
## 0.15.1
|
||||
|
||||
- fix scientific notation for labels in GR
|
||||
- fix labels with logscale
|
||||
- fix image cropping with GR
|
||||
- fix grouping of annotations
|
||||
- fix annotations in Plotly
|
||||
- allow saving notebook with plots as pdf from IJulia
|
||||
- fix fillrange and ribbon for step recipes
|
||||
- implement native ticks that respond to zoom
|
||||
- fix bar plot with one bar
|
||||
- contour labels and colorbar fixes
|
||||
- interactive linked axis for PyPlot
|
||||
- add `NamedTuple` syntax to group with named legend
|
||||
- use bar recipe in Plotly
|
||||
- implement categorical ticks
|
||||
|
||||
## 0.15.0
|
||||
|
||||
- improve resolution of png output of GR with savefig()
|
||||
- add check for ticks=nothing
|
||||
- allow transparency in heatmaps
|
||||
- fix line_z for GR
|
||||
- fix legendcolor for pyplot
|
||||
- fix pyplot ignoring alpha values of images
|
||||
- don't let `abline!` change subplot limits
|
||||
- update showtheme recipe
|
||||
|
||||
## 0.14.2
|
||||
|
||||
|
||||
+9
-2
@@ -1,4 +1,4 @@
|
||||
__precompile__(false)
|
||||
__precompile__(true)
|
||||
|
||||
module Plots
|
||||
|
||||
@@ -14,6 +14,7 @@ using Base.Meta
|
||||
@reexport using PlotThemes
|
||||
import Showoff
|
||||
import StatsBase
|
||||
import JSON
|
||||
|
||||
using Requires
|
||||
|
||||
@@ -182,10 +183,13 @@ include("output.jl")
|
||||
@shorthands histogram2d
|
||||
@shorthands density
|
||||
@shorthands heatmap
|
||||
@shorthands plots_heatmap
|
||||
@shorthands hexbin
|
||||
@shorthands sticks
|
||||
@shorthands hline
|
||||
@shorthands vline
|
||||
@shorthands hspan
|
||||
@shorthands vspan
|
||||
@shorthands ohlc
|
||||
@shorthands contour
|
||||
@shorthands contourf
|
||||
@@ -293,8 +297,11 @@ const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
# for compatibility with Requires.jl:
|
||||
@init begin
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
if haskey(Main.PLOTS_DEFAULTS, :theme)
|
||||
theme(Main.PLOTS_DEFAULTS[:theme])
|
||||
end
|
||||
for (k,v) in Main.PLOTS_DEFAULTS
|
||||
default(k, v)
|
||||
k == :theme || default(k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+6
-3
@@ -68,7 +68,8 @@ mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim.dir, fn, false;
|
||||
function buildanimation(animdir::AbstractString, fn::AbstractString,
|
||||
is_animated_gif::Bool=true;
|
||||
fps::Integer = 20, loop::Integer = 0,
|
||||
variable_palette::Bool=false)
|
||||
variable_palette::Bool=false,
|
||||
show_msg::Bool=true)
|
||||
fn = abspath(fn)
|
||||
|
||||
if is_animated_gif
|
||||
@@ -86,7 +87,7 @@ function buildanimation(animdir::AbstractString, fn::AbstractString,
|
||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
||||
end
|
||||
|
||||
info("Saved animation to ", fn)
|
||||
show_msg && info("Saved animation to ", fn)
|
||||
AnimatedGif(fn)
|
||||
end
|
||||
|
||||
@@ -115,6 +116,7 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
# add the call to frame to the end of each iteration
|
||||
animsym = gensym("anim")
|
||||
countersym = gensym("counter")
|
||||
freqassert = :()
|
||||
block = forloop.args[2]
|
||||
|
||||
# create filter
|
||||
@@ -127,7 +129,7 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
# filter every `freq` frames (starting with the first frame)
|
||||
@assert n == 2
|
||||
freq = args[2]
|
||||
@assert isa(freq, Integer) && freq > 0
|
||||
freqassert = :(@assert isa($freq, Integer) && $freq > 0)
|
||||
:(mod1($countersym, $freq) == 1)
|
||||
|
||||
elseif args[1] == :when
|
||||
@@ -147,6 +149,7 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
|
||||
# full expression:
|
||||
esc(quote
|
||||
$freqassert # if filtering, check frequency is an Integer > 0
|
||||
$animsym = Animation() # init animation object
|
||||
$countersym = 1 # init iteration counter
|
||||
$forloop # for loop, saving a frame after each iteration
|
||||
|
||||
+5
-2
@@ -43,6 +43,7 @@ const _arg_desc = KW(
|
||||
:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes).",
|
||||
:weights => "AbstractVector. Used in histogram types for weighted counts.",
|
||||
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
|
||||
:contour_labels => "Bool. Show labels at the contour lines?",
|
||||
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
|
||||
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
|
||||
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
|
||||
@@ -64,6 +65,7 @@ const _arg_desc = KW(
|
||||
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
|
||||
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
|
||||
:dpi => "Number. Dots Per Inch of output figures",
|
||||
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
|
||||
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
|
||||
:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
|
||||
:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides",
|
||||
@@ -109,7 +111,7 @@ const _arg_desc = KW(
|
||||
|
||||
# axis args
|
||||
:guide => "String. Axis guide (label).",
|
||||
:lims => "NTuple{2,Number}. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).",
|
||||
:lims => "NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example). `:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]",
|
||||
:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
|
||||
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
|
||||
:rotation => "Number. Degrees rotation of tick labels.",
|
||||
@@ -138,5 +140,6 @@ const _arg_desc = KW(
|
||||
:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)",
|
||||
:gridlinewidth => "Number. Width of the grid lines (in pixels)",
|
||||
:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`",
|
||||
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`"
|
||||
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
|
||||
:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.",
|
||||
)
|
||||
|
||||
+78
-33
@@ -159,6 +159,21 @@ const _markerAliases = Dict{Symbol,Symbol}(
|
||||
:spike => :vline,
|
||||
)
|
||||
|
||||
const _positionAliases = Dict{Symbol,Symbol}(
|
||||
:top_left => :topleft,
|
||||
:tl => :topleft,
|
||||
:top_center => :topcenter,
|
||||
:tc => :topcenter,
|
||||
:top_right => :topright,
|
||||
:tr => :topright,
|
||||
:bottom_left => :bottomleft,
|
||||
:bl => :bottomleft,
|
||||
:bottom_center => :bottomcenter,
|
||||
:bc => :bottomcenter,
|
||||
:bottom_right => :bottomright,
|
||||
:br => :bottomright,
|
||||
)
|
||||
|
||||
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
const _logScales = [:ln, :log2, :log10]
|
||||
const _logScaleBases = Dict(:ln => e, :log2 => 2.0, :log10 => 10.0)
|
||||
@@ -257,6 +272,7 @@ const _series_defaults = KW(
|
||||
:normalize => false, # do we want a normalized histogram?
|
||||
:weights => nothing, # optional weights for histograms (1D and 2D)
|
||||
:contours => false, # add contours to 3d surface and wireframe plots
|
||||
:contour_labels => false,
|
||||
: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?
|
||||
@@ -285,6 +301,7 @@ const _plot_defaults = KW(
|
||||
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
|
||||
# the parent layout and the relative bounding box of inset subplots
|
||||
:dpi => DPI, # dots per inch for images, etc
|
||||
:thickness_scaling => 1,
|
||||
:display_type => :auto,
|
||||
:extra_kwargs => KW(),
|
||||
)
|
||||
@@ -365,6 +382,7 @@ const _axis_defaults = KW(
|
||||
:gridlinewidth => 0.5,
|
||||
:tick_direction => :in,
|
||||
:showaxis => true,
|
||||
:widen => true,
|
||||
)
|
||||
|
||||
const _suppress_warnings = Set{Symbol}([
|
||||
@@ -559,6 +577,7 @@ add_aliases(:gridstyle, :grid_style, :gridlinestyle, :grid_linestyle, :grid_ls,
|
||||
add_aliases(:framestyle, :frame_style, :frame, :axesstyle, :axes_style, :boxstyle, :box_style, :box, :borderstyle, :border_style, :border)
|
||||
add_aliases(:tick_direction, :tickdirection, :tick_dir, :tickdir, :tick_orientation, :tickorientation, :tick_or, :tickor)
|
||||
add_aliases(:camera, :cam, :viewangle, :view_angle)
|
||||
add_aliases(:contour_labels, :contourlabels, :clabels, :clabs)
|
||||
|
||||
# add all pluralized forms to the _keyAliases dict
|
||||
for arg in keys(_series_defaults)
|
||||
@@ -734,6 +753,10 @@ function processFillArg(d::KW, arg)
|
||||
elseif allAlphas(arg)
|
||||
d[:fillalpha] = arg
|
||||
|
||||
# fillrange provided as vector or number
|
||||
elseif typeof(arg) <: Union{AbstractArray{<:Real}, Real}
|
||||
d[:fillrange] = arg
|
||||
|
||||
elseif !handleColors!(d, arg, :fillcolor)
|
||||
|
||||
d[:fillrange] = arg
|
||||
@@ -995,15 +1018,28 @@ function extractGroupArgs(v::AVec, args...; legendEntry = string)
|
||||
GroupBy(map(legendEntry, groupLabels), groupIds)
|
||||
end
|
||||
|
||||
legendEntryFromTuple(ns::Tuple) = string(("$n " for n in ns)...)
|
||||
legendEntryFromTuple(ns::Tuple) = join(ns, ' ')
|
||||
|
||||
# this is when given a tuple of vectors of values to group by
|
||||
function extractGroupArgs(vs::Tuple, args...)
|
||||
(vs == ()) && return GroupBy([""], [1:size(args[1],1)])
|
||||
v = collect(zip(vs...))
|
||||
isempty(vs) && return GroupBy([""], [1:size(args[1],1)])
|
||||
v = map(tuple, vs...)
|
||||
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple)
|
||||
end
|
||||
|
||||
# allow passing NamedTuples for a named legend entry
|
||||
@require NamedTuples begin
|
||||
legendEntryFromTuple(ns::NamedTuples.NamedTuple) =
|
||||
join(["$k = $v" for (k, v) in zip(keys(ns), values(ns))], ", ")
|
||||
|
||||
function extractGroupArgs(vs::NamedTuples.NamedTuple, args...)
|
||||
isempty(vs) && return GroupBy([""], [1:size(args[1],1)])
|
||||
NT = eval(:(NamedTuples.@NT($(keys(vs)...)))){map(eltype, vs)...}
|
||||
v = map(NT, vs...)
|
||||
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple)
|
||||
end
|
||||
end
|
||||
|
||||
# expecting a mapping of "group label" to "group indices"
|
||||
function extractGroupArgs(idxmap::Dict{T,V}, args...) where {T, V<:AVec{Int}}
|
||||
groupLabels = sortedkeys(idxmap)
|
||||
@@ -1294,11 +1330,9 @@ end
|
||||
|
||||
function _update_subplot_periphery(sp::Subplot, anns::AVec)
|
||||
# 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)
|
||||
newanns = []
|
||||
for ann in vcat(anns, sp[:annotations])
|
||||
append!(newanns, process_annotation(sp, ann...))
|
||||
end
|
||||
sp.attr[:annotations] = newanns
|
||||
|
||||
@@ -1418,18 +1452,18 @@ end
|
||||
|
||||
|
||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
||||
function getSeriesRGBColor(c, α, sp::Subplot, n::Int)
|
||||
function getSeriesRGBColor(c, sp::Subplot, n::Int)
|
||||
if c == :auto
|
||||
c = autopick(sp[:color_palette], n)
|
||||
elseif isa(c, Int)
|
||||
c = autopick(sp[:color_palette], c)
|
||||
end
|
||||
plot_color(c, α)
|
||||
plot_color(c)
|
||||
end
|
||||
|
||||
function ensure_gradient!(d::KW, csym::Symbol, asym::Symbol)
|
||||
if !isa(d[csym], ColorGradient)
|
||||
d[csym] = cgrad(alpha = d[asym])
|
||||
d[csym] = typeof(d[asym]) <: AbstractVector ? cgrad() : cgrad(alpha = d[asym])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1441,26 +1475,19 @@ function _replace_linewidth(d::KW)
|
||||
end
|
||||
|
||||
function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
pkg = plt.backend
|
||||
globalIndex = d[:series_plotindex]
|
||||
|
||||
# add default values to our dictionary, being careful not to delete what we just added!
|
||||
for (k,v) in _series_defaults
|
||||
slice_arg!(d, d, k, v, commandIndex, false)
|
||||
end
|
||||
|
||||
# this is how many series belong to this subplot
|
||||
# plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
|
||||
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
|
||||
return d
|
||||
end
|
||||
|
||||
|
||||
function _update_series_attributes!(d::KW, plt::Plot, sp::Subplot)
|
||||
pkg = plt.backend
|
||||
globalIndex = d[:series_plotindex]
|
||||
plotIndex = _series_index(d, sp)
|
||||
|
||||
aliasesAndAutopick(d, :linestyle, _styleAliases, supported_styles(pkg), plotIndex)
|
||||
aliasesAndAutopick(d, :markershape, _markerAliases, supported_markers(pkg), plotIndex)
|
||||
@@ -1476,29 +1503,31 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
end
|
||||
|
||||
# update series color
|
||||
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], d[:seriesalpha], sp, plotIndex)
|
||||
d[:seriescolor] = getSeriesRGBColor.(d[:seriescolor], sp, plotIndex)
|
||||
|
||||
# update other colors
|
||||
for s in (:line, :marker, :fill)
|
||||
csym, asym = Symbol(s,:color), Symbol(s,:alpha)
|
||||
d[csym] = if d[csym] == :auto
|
||||
plot_color(if has_black_border_for_default(d[:seriestype]) && s == :line
|
||||
plot_color.(if has_black_border_for_default(d[:seriestype]) && s == :line
|
||||
sp[:foreground_color_subplot]
|
||||
else
|
||||
d[:seriescolor]
|
||||
end, d[asym])
|
||||
end)
|
||||
elseif d[csym] == :match
|
||||
plot_color(d[:seriescolor], d[asym])
|
||||
plot_color.(d[:seriescolor])
|
||||
else
|
||||
getSeriesRGBColor(d[csym], d[asym], sp, plotIndex)
|
||||
getSeriesRGBColor.(d[csym], sp, plotIndex)
|
||||
end
|
||||
end
|
||||
|
||||
# update markerstrokecolor
|
||||
d[:markerstrokecolor] = if d[:markerstrokecolor] == :match
|
||||
plot_color(sp[:foreground_color_subplot], d[:markerstrokealpha])
|
||||
plot_color(sp[:foreground_color_subplot])
|
||||
elseif d[:markerstrokecolor] == :auto
|
||||
getSeriesRGBColor.(d[:markercolor], sp, plotIndex)
|
||||
else
|
||||
getSeriesRGBColor(d[:markerstrokecolor], d[:markerstrokealpha], sp, plotIndex)
|
||||
getSeriesRGBColor.(d[:markerstrokecolor], sp, plotIndex)
|
||||
end
|
||||
|
||||
# if marker_z, fill_z or line_z are set, ensure we have a gradient
|
||||
@@ -1528,3 +1557,19 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
_replace_linewidth(d)
|
||||
d
|
||||
end
|
||||
|
||||
function _series_index(d, sp)
|
||||
idx = 0
|
||||
for series in series_list(sp)
|
||||
if series[:primary]
|
||||
idx += 1
|
||||
end
|
||||
if series == d
|
||||
return idx
|
||||
end
|
||||
end
|
||||
if get(d, :primary, true)
|
||||
idx += 1
|
||||
end
|
||||
return idx
|
||||
end
|
||||
|
||||
+59
-29
@@ -212,12 +212,20 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
formatter = axis[:formatter]
|
||||
if formatter == :auto
|
||||
# the default behavior is to make strings of the scaled values and then apply the labelfunc
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :auto))
|
||||
elseif formatter == :plain
|
||||
# Leave the numbers in plain format
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :plain))
|
||||
elseif formatter == :scientific
|
||||
Showoff.showoff(unscaled_ticks, :scientific)
|
||||
else
|
||||
# there was an override for the formatter... use that on the unscaled ticks
|
||||
map(formatter, unscaled_ticks)
|
||||
# if the formatter left us with numbers, still apply the default formatter
|
||||
# However it leave us with the problem of unicode number decoding by the backend
|
||||
# if eltype(unscaled_ticks) <: Number
|
||||
# Showoff.showoff(unscaled_ticks, :auto)
|
||||
# end
|
||||
end
|
||||
else
|
||||
# no finite ticks to show...
|
||||
@@ -234,12 +242,21 @@ function get_ticks(axis::Axis)
|
||||
ticks = _transform_ticks(axis[:ticks])
|
||||
ticks in (nothing, false) && return nothing
|
||||
|
||||
# treat :native ticks as :auto
|
||||
ticks = ticks == :native ? :auto : ticks
|
||||
|
||||
dvals = axis[:discrete_values]
|
||||
cv, dv = if !isempty(dvals) && ticks == :auto
|
||||
# discrete ticks...
|
||||
axis[:continuous_values], dvals
|
||||
elseif ticks == :auto
|
||||
if ispolar(axis.sps[1]) && axis[:letter] == :x
|
||||
cv, dv = if typeof(ticks) <: Symbol
|
||||
if !isempty(dvals)
|
||||
# discrete ticks...
|
||||
n = length(dvals)
|
||||
rng = if ticks == :auto
|
||||
Int[round(Int,i) for i in linspace(1, n, 15)]
|
||||
else # if ticks == :all
|
||||
1:n
|
||||
end
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
elseif ispolar(axis.sps[1]) && axis[:letter] == :x
|
||||
#force theta axis to be full circle
|
||||
(collect(0:pi/4:7pi/4), string.(0:45:315))
|
||||
else
|
||||
@@ -247,8 +264,13 @@ function get_ticks(axis::Axis)
|
||||
optimal_ticks_and_labels(axis)
|
||||
end
|
||||
elseif typeof(ticks) <: Union{AVec, Int}
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
if !isempty(dvals) && typeof(ticks) <: Int
|
||||
rng = Int[round(Int,i) for i in linspace(1, length(dvals), ticks)]
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
else
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
end
|
||||
elseif typeof(ticks) <: NTuple{2, Any}
|
||||
# assuming we're passed (ticks, labels)
|
||||
ticks
|
||||
@@ -257,13 +279,7 @@ function get_ticks(axis::Axis)
|
||||
end
|
||||
# @show ticks dvals cv dv
|
||||
|
||||
# TODO: better/smarter cutoff values for sampling ticks
|
||||
if length(cv) > 30 && ticks == :auto
|
||||
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
|
||||
cv[rng], dv[rng]
|
||||
else
|
||||
cv, dv
|
||||
end
|
||||
return cv, dv
|
||||
end
|
||||
|
||||
_transform_ticks(ticks) = ticks
|
||||
@@ -284,8 +300,8 @@ end
|
||||
|
||||
|
||||
function expand_extrema!(ex::Extrema, v::Number)
|
||||
ex.emin = NaNMath.min(v, ex.emin)
|
||||
ex.emax = NaNMath.max(v, ex.emax)
|
||||
ex.emin = isfinite(v) ? min(v, ex.emin) : ex.emin
|
||||
ex.emax = isfinite(v) ? max(v, ex.emax) : ex.emax
|
||||
ex
|
||||
end
|
||||
|
||||
@@ -300,8 +316,8 @@ expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
|
||||
|
||||
function expand_extrema!(axis::Axis, v::Tuple{MIN,MAX}) where {MIN<:Number,MAX<:Number}
|
||||
ex = axis[:extrema]
|
||||
ex.emin = NaNMath.min(v[1], ex.emin)
|
||||
ex.emax = NaNMath.max(v[2], ex.emax)
|
||||
ex.emin = isfinite(v[1]) ? min(v[1], ex.emin) : ex.emin
|
||||
ex.emax = isfinite(v[2]) ? max(v[2], ex.emax) : ex.emax
|
||||
ex
|
||||
end
|
||||
function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
|
||||
@@ -323,6 +339,9 @@ function expand_extrema!(sp::Subplot, d::KW)
|
||||
else
|
||||
letter == :x ? :y : letter == :y ? :x : :z
|
||||
end]
|
||||
if letter != :z && d[:seriestype] == :straightline && any(series[:seriestype] != :straightline for series in series_list(sp)) && data[1] != data[2]
|
||||
data = [NaN]
|
||||
end
|
||||
axis = sp[Symbol(letter, "axis")]
|
||||
|
||||
if isa(data, Volume)
|
||||
@@ -399,21 +418,23 @@ end
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# push the limits out slightly
|
||||
function widen(lmin, lmax)
|
||||
span = lmax - lmin
|
||||
function widen(lmin, lmax, scale = :identity)
|
||||
f, invf = scalefunc(scale), invscalefunc(scale)
|
||||
span = f(lmax) - f(lmin)
|
||||
# eps = NaNMath.max(1e-16, min(1e-2span, 1e-10))
|
||||
eps = NaNMath.max(1e-16, 0.03span)
|
||||
lmin-eps, lmax+eps
|
||||
invf(f(lmin)-eps), invf(f(lmax)+eps)
|
||||
end
|
||||
|
||||
# figure out if widening is a good idea. if there's a scale set it's too tricky,
|
||||
# so lazy out and don't widen
|
||||
# figure out if widening is a good idea.
|
||||
const _widen_seriestypes = (:line, :path, :steppre, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d)
|
||||
|
||||
function default_should_widen(axis::Axis)
|
||||
should_widen = false
|
||||
if axis[:scale] == :identity && !is_2tuple(axis[:lims])
|
||||
if !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
|
||||
if series.d[:seriestype] in _widen_seriestypes
|
||||
should_widen = true
|
||||
end
|
||||
end
|
||||
@@ -422,6 +443,13 @@ function default_should_widen(axis::Axis)
|
||||
should_widen
|
||||
end
|
||||
|
||||
function round_limits(amin,amax)
|
||||
scale = 10^(1-round(log10(amax - amin)))
|
||||
amin = floor(amin*scale)/scale
|
||||
amax = ceil(amax*scale)/scale
|
||||
amin, amax
|
||||
end
|
||||
|
||||
# using the axis extrema and limit overrides, return the min/max value for this axis
|
||||
function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis))
|
||||
ex = axis[:extrema]
|
||||
@@ -450,8 +478,10 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
|
||||
else
|
||||
amin, amax
|
||||
end
|
||||
elseif should_widen
|
||||
widen(amin, amax)
|
||||
elseif should_widen && axis[:widen]
|
||||
widen(amin, amax, axis[:scale])
|
||||
elseif lims == :round
|
||||
round_limits(amin,amax)
|
||||
else
|
||||
amin, amax
|
||||
end
|
||||
@@ -552,7 +582,7 @@ function axis_drawing_info(sp::Subplot)
|
||||
end
|
||||
push!(xaxis_segs, (xmin, y1), (xmax, y1))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && length(xticks) > 1
|
||||
if sp[:framestyle] == :origin && !(xticks in (nothing,false)) && length(xticks) > 1
|
||||
showticks = xticks[1] .!= 0
|
||||
xticks = (xticks[1][showticks], xticks[2][showticks])
|
||||
end
|
||||
@@ -591,7 +621,7 @@ function axis_drawing_info(sp::Subplot)
|
||||
end
|
||||
push!(yaxis_segs, (x1, ymin), (x1, ymax))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && length(yticks) > 1
|
||||
if sp[:framestyle] == :origin && !(yticks in (nothing,false)) && length(yticks) > 1
|
||||
showticks = yticks[1] .!= 0
|
||||
yticks = (yticks[1][showticks], yticks[2][showticks])
|
||||
end
|
||||
|
||||
@@ -51,7 +51,7 @@ const _glvisualize_attr = merge_with_base_supported([
|
||||
:tick_direction,
|
||||
])
|
||||
const _glvisualize_seriestype = [
|
||||
:path, :shape,
|
||||
:path, :shape, :straightline,
|
||||
:scatter, :hexbin,
|
||||
:bar, :boxplot,
|
||||
:heatmap, :image, :volume,
|
||||
@@ -287,7 +287,13 @@ function topoints(::Type{P}, array) where P
|
||||
end
|
||||
function extract_points(d)
|
||||
dim = is3d(d) ? 3 : 2
|
||||
array = (d[:x], d[:y], d[:z])[1:dim]
|
||||
array = if d[:seriestype] == :straightline
|
||||
straightline_data(d)
|
||||
elseif d[:seriestype] == :shape
|
||||
shape_data(d)
|
||||
else
|
||||
(d[:x], d[:y], d[:z])[1:dim]
|
||||
end
|
||||
topoints(Point{dim, Float32}, array)
|
||||
end
|
||||
function make_gradient(grad::Vector{C}) where C <: Colorant
|
||||
@@ -1102,7 +1108,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
|
||||
kw_args[:stroke_width] = Float32(d[:linewidth]/100f0)
|
||||
end
|
||||
vis = GL.gl_surface(x, y, z, kw_args)
|
||||
elseif (st in (:path, :path3d)) && d[:linewidth] > 0
|
||||
elseif (st in (:path, :path3d, :straightline)) && d[:linewidth] > 0
|
||||
kw = copy(kw_args)
|
||||
points = Plots.extract_points(d)
|
||||
extract_linestyle(d, kw)
|
||||
@@ -1215,7 +1221,7 @@ end
|
||||
|
||||
function gl_image(img, kw_args)
|
||||
rect = kw_args[:primitive]
|
||||
kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(rect.x, rect.y, rect.h, rect.w) # seems to be flipped
|
||||
kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(rect.x, rect.y, rect.w, rect.h)
|
||||
visualize(img, Style(:default), kw_args)
|
||||
end
|
||||
|
||||
@@ -1460,7 +1466,7 @@ function make_label(sp, series, i)
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
kw_args = KW()
|
||||
if (st in (:path, :path3d)) && d[:linewidth] > 0
|
||||
if (st in (:path, :path3d, :straightline)) && d[:linewidth] > 0
|
||||
points = Point2f0[(0, ho), (w, ho)]
|
||||
kw = KW()
|
||||
extract_linestyle(d, kw)
|
||||
|
||||
+135
-93
@@ -45,9 +45,10 @@ const _gr_attr = merge_with_base_supported([
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _gr_seriestype = [
|
||||
:path, :scatter,
|
||||
:path, :scatter, :straightline,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :path3d, :scatter3d, :surface, :wireframe,
|
||||
:shape
|
||||
@@ -134,7 +135,7 @@ const gr_font_family = Dict(
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function gr_getcolorind(c)
|
||||
GR.settransparency(float(alpha(c)))
|
||||
gr_set_transparency(float(alpha(c)))
|
||||
convert(Int, GR.inqcolorfromrgb(red(c), green(c), blue(c)))
|
||||
end
|
||||
|
||||
@@ -142,6 +143,8 @@ gr_set_linecolor(c) = GR.setlinecolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_transparency(α::Real) = GR.settransparency(clamp(α, 0, 1))
|
||||
function gr_set_transparency(::Void) end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
@@ -193,7 +196,7 @@ gr_inqtext(x, y, s::Symbol) = gr_inqtext(x, y, string(s))
|
||||
function gr_inqtext(x, y, s)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.inqtextext(x, y, s[2:end-1])
|
||||
elseif search(s, '\\') != 0
|
||||
elseif search(s, '\\') != 0 || contains(s, "10^{")
|
||||
GR.inqtextext(x, y, s)
|
||||
else
|
||||
GR.inqtext(x, y, s)
|
||||
@@ -205,7 +208,7 @@ gr_text(x, y, s::Symbol) = gr_text(x, y, string(s))
|
||||
function gr_text(x, y, s)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.mathtex(x, y, s[2:end-1])
|
||||
elseif search(s, '\\') != 0
|
||||
elseif search(s, '\\') != 0 || contains(s, "10^{")
|
||||
GR.textext(x, y, s)
|
||||
else
|
||||
GR.text(x, y, s)
|
||||
@@ -222,11 +225,14 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
sinf = sind.(a)
|
||||
cosf = cosd.(a)
|
||||
rtick_values, rtick_labels = get_ticks(yaxis)
|
||||
if yaxis[:formatter] in (:scientific, :auto) && yaxis[:ticks] in (:auto, :native)
|
||||
rtick_labels = convert_sci_unicode.(rtick_labels)
|
||||
end
|
||||
|
||||
#draw angular grid
|
||||
if xaxis[:grid]
|
||||
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
|
||||
GR.settransparency(xaxis[:gridalpha])
|
||||
gr_set_transparency(xaxis[:gridalpha])
|
||||
for i in 1:length(α)
|
||||
GR.polyline([sinf[i], 0], [cosf[i], 0])
|
||||
end
|
||||
@@ -235,7 +241,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
#draw radial grid
|
||||
if yaxis[:grid]
|
||||
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
|
||||
GR.settransparency(yaxis[:gridalpha])
|
||||
gr_set_transparency(yaxis[:gridalpha])
|
||||
for i in 1:length(rtick_values)
|
||||
r = (rtick_values[i] - rmin) / (rmax - rmin)
|
||||
if r <= 1.0 && r >= 0.0
|
||||
@@ -246,7 +252,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
end
|
||||
|
||||
#prepare to draw ticks
|
||||
GR.settransparency(1)
|
||||
gr_set_transparency(1)
|
||||
GR.setlinecolorind(90)
|
||||
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_HALF)
|
||||
|
||||
@@ -315,9 +321,6 @@ function normalize_zvals(zv::AVec, clims::NTuple{2, <:Real})
|
||||
end
|
||||
end
|
||||
|
||||
gr_alpha(α::Void) = 1
|
||||
gr_alpha(α::Real) = α
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# draw ONE Shape
|
||||
@@ -350,25 +353,18 @@ function gr_draw_markers(series::Series, x, y, msize, mz)
|
||||
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
|
||||
|
||||
# draw a filled in shape, slightly bigger, to estimate a stroke
|
||||
if series[:markerstrokewidth] > 0
|
||||
cfunc(_cycle(series[:markerstrokecolor], i)) #, series[:markerstrokealpha])
|
||||
cfunc(get_markerstrokecolor(series, i))
|
||||
gr_set_transparency(get_markerstrokealpha(series, i))
|
||||
gr_draw_marker(x[i], y[i], msi + series[:markerstrokewidth], shape)
|
||||
end
|
||||
|
||||
# draw the shape
|
||||
if mz == nothing
|
||||
cfunc(_cycle(series[:markercolor], i)) #, series[:markeralpha])
|
||||
else
|
||||
# pick a color from the pre-loaded gradient
|
||||
ci = round(Int, 1000 + _cycle(mz, i) * 255)
|
||||
cfuncind(ci)
|
||||
GR.settransparency(_gr_gradient_alpha[ci-999])
|
||||
end
|
||||
# don't draw filled area if marker shape is 1D
|
||||
# draw the shape - don't draw filled area if marker shape is 1D
|
||||
if !(shape in (:hline, :vline, :+, :x))
|
||||
cfunc(get_markercolor(series, i))
|
||||
gr_set_transparency(get_markeralpha(series, i))
|
||||
gr_draw_marker(x[i], y[i], msi, shape)
|
||||
end
|
||||
end
|
||||
@@ -387,7 +383,7 @@ end
|
||||
function gr_set_line(lw, style, c) #, a)
|
||||
GR.setlinetype(gr_linetype[style])
|
||||
w, h = gr_plot_size
|
||||
GR.setlinewidth(max(0, lw / ((w + h) * 0.001)))
|
||||
GR.setlinewidth(_gr_thickness_scaling[1] * max(0, lw / ((w + h) * 0.001)))
|
||||
gr_set_linecolor(c) #, a)
|
||||
end
|
||||
|
||||
@@ -400,6 +396,7 @@ end
|
||||
|
||||
# this stores the conversion from a font pointsize to "percentage of window height" (which is what GR uses)
|
||||
const _gr_point_mult = 0.0018 * ones(1)
|
||||
const _gr_thickness_scaling = ones(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,
|
||||
@@ -543,9 +540,12 @@ function gr_set_gradient(c)
|
||||
end
|
||||
|
||||
# this is our new display func... set up the viewport_canvas, compute bounding boxes, and display each subplot
|
||||
function gr_display(plt::Plot)
|
||||
function gr_display(plt::Plot, fmt="")
|
||||
GR.clearws()
|
||||
|
||||
_gr_thickness_scaling[1] = plt[:thickness_scaling]
|
||||
dpi_factor = plt[:dpi] / Plots.DPI
|
||||
|
||||
# collect some monitor/display sizes in meters and pixels
|
||||
display_width_meters, display_height_meters, display_width_px, display_height_px = GR.inqdspsize()
|
||||
display_width_ratio = display_width_meters / display_width_px
|
||||
@@ -557,14 +557,14 @@ function gr_display(plt::Plot)
|
||||
gr_plot_size[:] = [w, h]
|
||||
if w > h
|
||||
ratio = float(h) / w
|
||||
msize = display_width_ratio * w
|
||||
msize = display_width_ratio * w * dpi_factor
|
||||
GR.setwsviewport(0, msize, 0, msize * ratio)
|
||||
GR.setwswindow(0, 1, 0, ratio)
|
||||
viewport_canvas[3] *= ratio
|
||||
viewport_canvas[4] *= ratio
|
||||
else
|
||||
ratio = float(w) / h
|
||||
msize = display_height_ratio * h
|
||||
msize = display_height_ratio * h * dpi_factor
|
||||
GR.setwsviewport(0, msize * ratio, 0, msize)
|
||||
GR.setwswindow(0, ratio, 0, 1)
|
||||
viewport_canvas[1] *= ratio
|
||||
@@ -576,7 +576,7 @@ function gr_display(plt::Plot)
|
||||
|
||||
# update point mult
|
||||
px_per_pt = px / pt
|
||||
_gr_point_mult[1] = 1.5 * px_per_pt / max(h,w)
|
||||
_gr_point_mult[1] = 1.5 * _gr_thickness_scaling[1] * px_per_pt / max(h,w)
|
||||
|
||||
# subplots:
|
||||
for sp in plt.subplots
|
||||
@@ -622,13 +622,14 @@ function gr_get_ticks_size(ticks, i)
|
||||
end
|
||||
|
||||
function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
dpi = sp.plt[:thickness_scaling]
|
||||
if !haskey(ENV, "GKSwstype")
|
||||
if isijulia() || (isdefined(Main, :Juno) && Juno.isactive())
|
||||
ENV["GKSwstype"] = "svg"
|
||||
end
|
||||
end
|
||||
# Add margin given by the user
|
||||
leftpad = 2mm + sp[:left_margin]
|
||||
leftpad = 4mm + sp[:left_margin]
|
||||
toppad = 2mm + sp[:top_margin]
|
||||
rightpad = 4mm + sp[:right_margin]
|
||||
bottompad = 2mm + sp[:bottom_margin]
|
||||
@@ -638,7 +639,7 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
end
|
||||
# Add margin for x and y ticks
|
||||
xticks, yticks = axis_drawing_info(sp)[1:2]
|
||||
if !(xticks in (nothing, false))
|
||||
if !(xticks in (nothing, false, :none))
|
||||
flip, mirror = gr_set_xticks_font(sp)
|
||||
l = gr_get_ticks_size(xticks, 2)
|
||||
if mirror
|
||||
@@ -647,7 +648,7 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
bottompad += 1mm + gr_plot_size[2] * l * px
|
||||
end
|
||||
end
|
||||
if !(yticks in (nothing, false))
|
||||
if !(yticks in (nothing, false, :none))
|
||||
flip, mirror = gr_set_yticks_font(sp)
|
||||
l = gr_get_ticks_size(yticks, 1)
|
||||
if mirror
|
||||
@@ -664,7 +665,7 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
if sp[:yaxis][:guide] != ""
|
||||
leftpad += 4mm
|
||||
end
|
||||
sp.minpad = (leftpad, toppad, rightpad, bottompad)
|
||||
sp.minpad = Tuple(dpi * [leftpad, toppad, rightpad, bottompad])
|
||||
end
|
||||
|
||||
function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
@@ -714,6 +715,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
if st == :heatmap
|
||||
outside_ticks = true
|
||||
for ax in (sp[:xaxis], sp[:yaxis])
|
||||
v = series[ax[:letter]]
|
||||
if diff(collect(extrema(diff(v))))[1] > 1e-6*std(v)
|
||||
warn("GR: heatmap only supported with equally spaced data.")
|
||||
end
|
||||
end
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale]), heatmap_edges(series[:y], sp[:yaxis][:scale])
|
||||
xy_lims = x[1], x[end], y[1], y[end]
|
||||
expand_extrema!(sp[:xaxis], x)
|
||||
@@ -765,7 +772,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# draw the axes
|
||||
gr_set_font(tickfont(xaxis))
|
||||
GR.setlinewidth(1)
|
||||
GR.setlinewidth(sp.plt[:thickness_scaling])
|
||||
|
||||
if is3d(sp)
|
||||
zmin, zmax = gr_lims(zaxis, true)
|
||||
@@ -780,10 +787,23 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
ztick = GR.tick(zmin, zmax) / 2
|
||||
ticksize = 0.01 * (viewport_plotarea[2] - viewport_plotarea[1])
|
||||
|
||||
# GR.setlinetype(GR.LINETYPE_DOTTED)
|
||||
xaxis[:grid] && GR.grid3d(xtick, 0, 0, xmin, ymax, zmin, 2, 0, 0)
|
||||
yaxis[:grid] && GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0)
|
||||
zaxis[:grid] && GR.grid3d(0, 0, ztick, xmin, ymax, zmin, 0, 0, 2)
|
||||
if xaxis[:grid]
|
||||
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
|
||||
gr_set_transparency(xaxis[:gridalpha])
|
||||
GR.grid3d(xtick, 0, 0, xmin, ymax, zmin, 2, 0, 0)
|
||||
end
|
||||
if yaxis[:grid]
|
||||
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
|
||||
gr_set_transparency(yaxis[:gridalpha])
|
||||
GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0)
|
||||
end
|
||||
if zaxis[:grid]
|
||||
gr_set_line(zaxis[:gridlinewidth], zaxis[:gridstyle], zaxis[:foreground_color_grid])
|
||||
gr_set_transparency(zaxis[:gridalpha])
|
||||
GR.grid3d(0, 0, ztick, xmin, ymax, zmin, 0, 0, 2)
|
||||
end
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_axis])
|
||||
gr_set_transparency(1)
|
||||
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
|
||||
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
|
||||
|
||||
@@ -806,15 +826,15 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# gr_set_linecolor(sp[:foreground_color_grid])
|
||||
# GR.grid(xtick, ytick, 0, 0, majorx, majory)
|
||||
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
|
||||
GR.settransparency(xaxis[:gridalpha])
|
||||
gr_set_transparency(xaxis[:gridalpha])
|
||||
gr_polyline(coords(xgrid_segs)...)
|
||||
end
|
||||
if yaxis[:grid]
|
||||
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
|
||||
GR.settransparency(yaxis[:gridalpha])
|
||||
gr_set_transparency(yaxis[:gridalpha])
|
||||
gr_polyline(coords(ygrid_segs)...)
|
||||
end
|
||||
GR.settransparency(1.0)
|
||||
gr_set_transparency(1.0)
|
||||
|
||||
# axis lines
|
||||
if xaxis[:showaxis]
|
||||
@@ -833,7 +853,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
if xaxis[:showaxis]
|
||||
if sp[:framestyle] in (:zerolines, :grid)
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_grid])
|
||||
GR.settransparency(xaxis[:gridalpha])
|
||||
gr_set_transparency(xaxis[:gridalpha])
|
||||
else
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_axis])
|
||||
end
|
||||
@@ -843,7 +863,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
if yaxis[:showaxis]
|
||||
if sp[:framestyle] in (:zerolines, :grid)
|
||||
gr_set_line(1, :solid, yaxis[:foreground_color_grid])
|
||||
GR.settransparency(yaxis[:gridalpha])
|
||||
gr_set_transparency(yaxis[:gridalpha])
|
||||
else
|
||||
gr_set_line(1, :solid, yaxis[:foreground_color_axis])
|
||||
end
|
||||
@@ -860,9 +880,15 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# use xor ($) to get the right y coords
|
||||
xi, yi = GR.wctondc(cv, sp[:framestyle] == :origin ? 0 : xor(flip, mirror) ? ymax : ymin)
|
||||
# @show cv dv ymin xi yi flip mirror (flip $ mirror)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
dv = xaxis[:scale] in (:ln, :log10, :log2) && xaxis[:ticks] == :auto ? string(dv, "\\ ") : string(dv)
|
||||
gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0), dv)
|
||||
if xaxis[:ticks] in (:auto, :native)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if xaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
elseif xaxis[:formatter] in (:scientific, :auto)
|
||||
dv = convert_sci_unicode(dv)
|
||||
end
|
||||
end
|
||||
gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0), string(dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -873,9 +899,15 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# use xor ($) to get the right y coords
|
||||
xi, yi = GR.wctondc(sp[:framestyle] == :origin ? 0 : xor(flip, mirror) ? xmax : xmin, cv)
|
||||
# @show cv dv xmin xi yi
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
dv = yaxis[:scale] in (:ln, :log10, :log2) && yaxis[:ticks] == :auto ? string(dv, "\\ ") : string(dv)
|
||||
gr_text(xi + (mirror ? 1 : -1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0), yi, dv)
|
||||
if yaxis[:ticks] in (:auto, :native)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if yaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
elseif yaxis[:formatter] in (:scientific, :auto)
|
||||
dv = convert_sci_unicode(dv)
|
||||
end
|
||||
end
|
||||
gr_text(xi + (mirror ? 1 : -1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0), yi, string(dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -883,10 +915,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
intensity = sp[:framestyle] == :semi ? 0.5 : 1.0
|
||||
if sp[:framestyle] in (:box, :semi)
|
||||
gr_set_line(intensity, :solid, xaxis[:foreground_color_border])
|
||||
GR.settransparency(intensity)
|
||||
gr_set_transparency(intensity)
|
||||
gr_polyline(coords(xborder_segs)...)
|
||||
gr_set_line(intensity, :solid, yaxis[:foreground_color_border])
|
||||
GR.settransparency(intensity)
|
||||
gr_set_transparency(intensity)
|
||||
gr_polyline(coords(yborder_segs)...)
|
||||
end
|
||||
end
|
||||
@@ -977,31 +1009,32 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
x, y = convert_to_polar(x, y, (rmin, rmax))
|
||||
end
|
||||
|
||||
if st in (:path, :scatter)
|
||||
if st == :straightline
|
||||
x, y = straightline_data(series)
|
||||
end
|
||||
|
||||
if st in (:path, :scatter, :straightline)
|
||||
if length(x) > 1
|
||||
lz = series[:line_z]
|
||||
segments_iterator = if lz != nothing && length(lz) > 1
|
||||
[i:(i + 1) for i in 1:(length(x) - 1)]
|
||||
else
|
||||
iter_segments(x, y)
|
||||
end
|
||||
segments = iter_segments(series)
|
||||
# do area fill
|
||||
if frng != nothing
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
fr_from, fr_to = (is_2tuple(frng) ? frng : (y, frng))
|
||||
for (i, rng) in enumerate(segments_iterator)
|
||||
gr_set_fillcolor(get_fillcolor(sp, series, i))
|
||||
for (i, rng) in enumerate(segments)
|
||||
gr_set_fillcolor(get_fillcolor(series, i))
|
||||
fx = _cycle(x, vcat(rng, reverse(rng)))
|
||||
fy = vcat(_cycle(fr_from,rng), _cycle(fr_to,reverse(rng)))
|
||||
series[:fillalpha] != nothing && GR.settransparency(series[:fillalpha])
|
||||
gr_set_transparency(get_fillalpha(series, i))
|
||||
GR.fillarea(fx, fy)
|
||||
end
|
||||
end
|
||||
|
||||
# draw the line(s)
|
||||
if st == :path
|
||||
for (i, rng) in enumerate(segments_iterator)
|
||||
gr_set_line(series[:linewidth], series[:linestyle], get_linecolor(sp, series, i)) #, series[:linealpha])
|
||||
if st in (:path, :straightline)
|
||||
for (i, rng) in enumerate(segments)
|
||||
gr_set_line(get_linewidth(series, i), get_linestyle(series, i), get_linecolor(series, i)) #, series[:linealpha])
|
||||
gr_set_transparency(get_linealpha(series, i))
|
||||
arrowside = isa(series[:arrow], Arrow) ? series[:arrow].side : :none
|
||||
gr_polyline(x[rng], y[rng]; arrowside = arrowside)
|
||||
end
|
||||
@@ -1009,34 +1042,34 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
|
||||
if series[:markershape] != :none
|
||||
if series[:marker_z] != nothing
|
||||
zmin, zmax = extrema(series[:marker_z])
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
end
|
||||
gr_draw_markers(series, x, y, clims)
|
||||
end
|
||||
|
||||
elseif st == :contour
|
||||
zmin, zmax = clims
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
if typeof(series[:levels]) <: Array
|
||||
if typeof(series[:levels]) <: AbstractArray
|
||||
h = series[:levels]
|
||||
else
|
||||
h = linspace(zmin, zmax, series[:levels])
|
||||
h = series[:levels] > 1 ? linspace(zmin, zmax, series[:levels]) : [(zmin + zmax) / 2]
|
||||
end
|
||||
if series[:fillrange] != nothing
|
||||
GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
|
||||
else
|
||||
GR.setlinetype(gr_linetype[series[:linestyle]])
|
||||
GR.setlinewidth(max(0, series[:linewidth] / (sum(gr_plot_size) * 0.001)))
|
||||
GR.contour(x, y, h, z, 1000)
|
||||
GR.setlinetype(gr_linetype[get_linestyle(series)])
|
||||
GR.setlinewidth(max(0, get_linewidth(series) / (sum(gr_plot_size) * 0.001)))
|
||||
if plot_color(series[:linecolor]) == [plot_color(:black)]
|
||||
GR.contour(x, y, h, z, 0 + (series[:contour_labels] == true ? 1 : 0))
|
||||
else
|
||||
GR.contour(x, y, h, z, 1000 + (series[:contour_labels] == true ? 1 : 0))
|
||||
end
|
||||
end
|
||||
|
||||
# create the colorbar of contour levels
|
||||
if cmap
|
||||
gr_set_line(1, :solid, yaxis[:foreground_color_axis])
|
||||
gr_set_viewport_cmap(sp)
|
||||
l = round.(Int32, 1000 + (h - ignorenan_minimum(h)) / (ignorenan_maximum(h) - ignorenan_minimum(h)) * 255)
|
||||
l = (length(h) > 1) ? round.(Int32, 1000 + (h - ignorenan_minimum(h)) / (ignorenan_maximum(h) - ignorenan_minimum(h)) * 255) : Int32[1000, 1255]
|
||||
GR.setwindow(xmin, xmax, zmin, zmax)
|
||||
GR.cellarray(xmin, xmax, zmax, zmin, 1, length(l), l)
|
||||
ztick = 0.5 * GR.tick(zmin, zmax)
|
||||
@@ -1059,9 +1092,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
elseif st == :heatmap
|
||||
xmin, xmax, ymin, ymax = xy_lims
|
||||
zmin, zmax = clims
|
||||
m, n = length(x), length(y)
|
||||
xinds = sort(1:m, rev = xaxis[:flip])
|
||||
yinds = sort(1:n, rev = yaxis[:flip])
|
||||
z = reshape(reshape(z, m, n)[xinds, yinds], m*n)
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
|
||||
colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z]
|
||||
colors = [plot_color(grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)], series[:fillalpha]) 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 +
|
||||
@@ -1074,13 +1111,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
if st == :path3d
|
||||
if length(x) > 1
|
||||
lz = series[:line_z]
|
||||
segments_iterator = if lz != nothing && length(lz) > 1
|
||||
[i:(i + 1) for i in 1:(length(x) - 1)]
|
||||
else
|
||||
iter_segments(x, y, z)
|
||||
end
|
||||
for (i, rng) in enumerate(segments_iterator)
|
||||
gr_set_line(series[:linewidth], series[:linestyle], get_linecolor(sp, series, i)) #, series[:linealpha])
|
||||
segments = iter_segments(series)
|
||||
for (i, rng) in enumerate(segments)
|
||||
gr_set_line(get_linewidth(series, i), get_linestyle(series, i), get_linecolor(series, i)) #, series[:linealpha])
|
||||
gr_set_transparency(get_linealpha(series, i))
|
||||
GR.polyline3d(x[rng], y[rng], z[rng])
|
||||
end
|
||||
end
|
||||
@@ -1141,6 +1175,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.selntran(1)
|
||||
|
||||
elseif st == :shape
|
||||
x, y = shape_data(series)
|
||||
for (i,rng) in enumerate(iter_segments(x, y))
|
||||
if length(rng) > 1
|
||||
# connect to the beginning
|
||||
@@ -1150,11 +1185,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
xseg, yseg = x[rng], y[rng]
|
||||
|
||||
# draw the interior
|
||||
gr_set_fill(get_fillcolor(sp, series, i))
|
||||
gr_set_fill(get_fillcolor(series, i))
|
||||
gr_set_transparency(get_fillalpha(series, i))
|
||||
GR.fillarea(xseg, yseg)
|
||||
|
||||
# draw the shapes
|
||||
gr_set_line(series[:linewidth], :solid, get_linecolor(sp, series, i))
|
||||
gr_set_line(get_linewidth(series, i), get_linestyle(series, i), get_linecolor(series, i))
|
||||
gr_set_transparency(get_linealpha(series, i))
|
||||
GR.polyline(xseg, yseg)
|
||||
end
|
||||
end
|
||||
@@ -1162,9 +1199,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st == :image
|
||||
z = transpose_z(series, series[:z].surf, true)'
|
||||
w, h = size(z)
|
||||
w, h = length(x), length(y)
|
||||
xinds = sort(1:w, rev = xaxis[:flip])
|
||||
yinds = sort(1:h, rev = yaxis[:flip])
|
||||
z = z[xinds, yinds]
|
||||
xmin, xmax = ignorenan_extrema(series[:x]); ymin, ymax = ignorenan_extrema(series[:y])
|
||||
if eltype(z) <: Colors.AbstractGray
|
||||
grey = round(UInt8, float(z) * 255)
|
||||
grey = round.(UInt8, float(z) * 255)
|
||||
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
|
||||
else
|
||||
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
|
||||
@@ -1185,7 +1226,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# draw the colorbar
|
||||
if cmap && st != :contour # special colorbar with steps is drawn for contours
|
||||
gr_set_line(1, :solid, yaxis[:foreground_color_axis])
|
||||
GR.settransparency(1)
|
||||
gr_set_transparency(1)
|
||||
gr_colorbar(sp, clims)
|
||||
end
|
||||
|
||||
@@ -1232,29 +1273,30 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
if sp[:legendtitle] != nothing
|
||||
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_HALF)
|
||||
gr_set_textcolor(sp[:legendfontcolor])
|
||||
GR.settransparency(1)
|
||||
gr_set_transparency(1)
|
||||
gr_text(xpos - 0.03 + 0.5*w, ypos, string(sp[:legendtitle]))
|
||||
ypos -= dy
|
||||
end
|
||||
for series in series_list(sp)
|
||||
should_add_to_legend(series) || continue
|
||||
st = series[:seriestype]
|
||||
gr_set_line(series[:linewidth], series[:linestyle], get_linecolor(sp, series)) #, series[:linealpha])
|
||||
gr_set_line(get_linewidth(series), get_linestyle(series), get_linecolor(series)) #, series[:linealpha])
|
||||
|
||||
if (st == :shape || series[:fillrange] != nothing) && series[:ribbon] == nothing
|
||||
gr_set_fill(get_fillcolor(sp, series)) #, series[:fillalpha])
|
||||
gr_set_fill(get_fillcolor(series)) #, series[:fillalpha])
|
||||
l, r = xpos-0.07, xpos-0.01
|
||||
b, t = ypos-0.4dy, ypos+0.4dy
|
||||
x = [l, r, r, l, l]
|
||||
y = [b, b, t, t, b]
|
||||
GR.settransparency(gr_alpha(series[:fillalpha]))
|
||||
gr_set_transparency(get_fillalpha(series))
|
||||
gr_polyline(x, y, GR.fillarea)
|
||||
GR.settransparency(gr_alpha(series[:linealpha]))
|
||||
gr_set_transparency(get_linealpha(series))
|
||||
gr_set_line(get_linewidth(series), get_linestyle(series), get_linecolor(series))
|
||||
st == :shape && gr_polyline(x, y)
|
||||
end
|
||||
|
||||
if st == :path
|
||||
GR.settransparency(gr_alpha(series[:linealpha]))
|
||||
if st in (:path, :straightline)
|
||||
gr_set_transparency(get_linealpha(series))
|
||||
if series[:fillrange] == nothing || series[:ribbon] != nothing
|
||||
GR.polyline([xpos - 0.07, xpos - 0.01], [ypos, ypos])
|
||||
else
|
||||
@@ -1294,7 +1336,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
end
|
||||
for ann in sp[:annotations]
|
||||
x, y, val = ann
|
||||
x, y, val = locate_annotation(sp, ann...)
|
||||
x, y = if is3d(sp)
|
||||
# GR.wc3towc(x, y, z)
|
||||
else
|
||||
@@ -1335,7 +1377,7 @@ for (mime, fmt) in _gr_mimeformats
|
||||
env = get(ENV, "GKSwstype", "0")
|
||||
ENV["GKSwstype"] = $fmt
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
gr_display(plt, $fmt)
|
||||
GR.emergencyclosegks()
|
||||
write(io, readstring(filepath))
|
||||
rm(filepath)
|
||||
|
||||
+13
-6
@@ -29,7 +29,7 @@ Read from .hdf5 file using:
|
||||
==#
|
||||
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "hdf5.jl"))
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "hdf5.jl"))
|
||||
end
|
||||
|
||||
import FixedPointNumbers: N0f8 #In core Julia
|
||||
@@ -97,7 +97,7 @@ const _hdf5_attr = merge_with_base_supported([
|
||||
:colorbar_title,
|
||||
])
|
||||
const _hdf5_seriestype = [
|
||||
:path, :steppre, :steppost, :shape,
|
||||
:path, :steppre, :steppost, :shape, :straightline,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
@@ -167,6 +167,7 @@ function _initialize_backend(::HDF5Backend)
|
||||
# "PLOTTEXT" => PlotText,
|
||||
"COLORGRADIENT" => ColorGradient,
|
||||
"AXIS" => Axis,
|
||||
"SURFACE" => Surface,
|
||||
"SUBPLOT" => Subplot,
|
||||
"NULLABLE" => Nullable,
|
||||
)
|
||||
@@ -239,10 +240,6 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{HDF5Backend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Display/show the plot (open a GUI window, or browser page, for example).
|
||||
function _display(plt::Plot{HDF5Backend})
|
||||
msg = "HDF5 interface does not support `display()` function."
|
||||
@@ -407,6 +404,11 @@ function _hdf5plot_gwrite(grp, k::String, v::Axis)
|
||||
_hdf5plot_writetype(grp, Axis)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Surface)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_gwrite(grp, "data2d", v.surf)
|
||||
_hdf5plot_writetype(grp, Surface)
|
||||
end
|
||||
#TODO: "Properly" support Nullable using _hdf5plot_writetype?
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Nullable)
|
||||
if isnull(v)
|
||||
@@ -577,6 +579,11 @@ function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid)
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
return Axis([], kwlist)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Surface}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
data2d = _hdf5plot_read(grp, "data2d")
|
||||
return Surface(data2d)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Subplot}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
idx = _hdf5plot_read(grp, "index")
|
||||
|
||||
@@ -57,7 +57,7 @@ const _inspectdr_attr = merge_with_base_supported([
|
||||
])
|
||||
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _inspectdr_seriestype = [
|
||||
:path, :scatter, :shape #, :steppre, :steppost
|
||||
:path, :scatter, :shape, :straightline, #, :steppre, :steppost
|
||||
]
|
||||
#see: _allMarkers, _shape_keys
|
||||
const _inspectdr_marker = Symbol[
|
||||
@@ -243,7 +243,11 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series)
|
||||
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])
|
||||
x, y = if st == :straightline
|
||||
straightline_data(series)
|
||||
else
|
||||
_vectorize(series[:x]), _vectorize(series[:y])
|
||||
end
|
||||
|
||||
#No support for polar grid... but can still perform polar transformation:
|
||||
if ispolar(sp)
|
||||
@@ -268,6 +272,7 @@ For st in :shape:
|
||||
=#
|
||||
|
||||
if st in (:shape,)
|
||||
x, y = shape_data(series)
|
||||
nmax = 0
|
||||
for (i,rng) in enumerate(iter_segments(x, y))
|
||||
nmax = i
|
||||
@@ -299,7 +304,7 @@ For st in :shape:
|
||||
color = linecolor, fillcolor = fillcolor
|
||||
)
|
||||
end
|
||||
elseif st in (:path, :scatter) #, :steppre, :steppost)
|
||||
elseif st in (:path, :scatter, :straightline) #, :steppre, :steppost)
|
||||
#NOTE: In Plots.jl, :scatter plots have 0-linewidths (I think).
|
||||
linewidth = series[:linewidth]
|
||||
#More efficient & allows some support for markerstrokewidth:
|
||||
@@ -518,7 +523,6 @@ for (mime, fmt) in _inspectdr_mimeformats_nodpi
|
||||
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
|
||||
end
|
||||
end
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
+197
-73
@@ -8,11 +8,12 @@ end
|
||||
|
||||
const _pgfplots_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
# :background_color_legend,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
# :foreground_color_legend,
|
||||
:foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
@@ -27,7 +28,7 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
:colorbar,
|
||||
:marker_z, #:levels,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
@@ -38,8 +39,9 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
:tick_direction,
|
||||
:framestyle,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape]
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape, :straightline,]
|
||||
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||
@@ -148,47 +150,69 @@ function pgf_colormap(grad::ColorGradient)
|
||||
end,", ")
|
||||
end
|
||||
|
||||
function pgf_fillstyle(d::KW)
|
||||
cstr,a = pgf_color(d[:fillcolor])
|
||||
pgf_thickness_scaling(plt::Plot) = plt[:thickness_scaling]
|
||||
pgf_thickness_scaling(sp::Subplot) = pgf_thickness_scaling(sp.plt)
|
||||
pgf_thickness_scaling(series) = pgf_thickness_scaling(series[:subplot])
|
||||
|
||||
function pgf_fillstyle(d, i = 1)
|
||||
cstr,a = pgf_color(get_fillcolor(d, i))
|
||||
fa = get_fillalpha(d, i)
|
||||
if fa != nothing
|
||||
a = fa
|
||||
end
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
end
|
||||
|
||||
function pgf_linestyle(d::KW)
|
||||
cstr,a = pgf_color(d[:linecolor])
|
||||
function pgf_linestyle(linewidth::Real, color, α = 1, linestyle = "solid")
|
||||
cstr, a = pgf_color(plot_color(color, α))
|
||||
"""
|
||||
color = $cstr,
|
||||
draw opacity=$a,
|
||||
line width=$(d[:linewidth]),
|
||||
$(get(_pgfplots_linestyles, d[:linestyle], "solid"))"""
|
||||
draw opacity = $a,
|
||||
line width = $linewidth,
|
||||
$(get(_pgfplots_linestyles, linestyle, "solid"))"""
|
||||
end
|
||||
|
||||
function pgf_marker(d::KW)
|
||||
shape = d[:markershape]
|
||||
cstr, a = pgf_color(d[:markercolor])
|
||||
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor])
|
||||
function pgf_linestyle(d, i = 1)
|
||||
lw = pgf_thickness_scaling(d) * get_linewidth(d, i)
|
||||
lc = get_linecolor(d, i)
|
||||
la = get_linealpha(d, i)
|
||||
ls = get_linestyle(d, i)
|
||||
return pgf_linestyle(lw, lc, la, ls)
|
||||
end
|
||||
|
||||
function pgf_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
|
||||
fs = fontsize * thickness_scaling
|
||||
return string("{\\fontsize{", fs, " pt}{", 1.3fs, " pt}", font, "}")
|
||||
end
|
||||
|
||||
function pgf_marker(d, i = 1)
|
||||
shape = _cycle(d[:markershape], i)
|
||||
cstr, a = pgf_color(plot_color(get_markercolor(d, i), get_markeralpha(d, i)))
|
||||
cstr_stroke, a_stroke = pgf_color(plot_color(get_markerstrokecolor(d, i), get_markerstrokealpha(d, i)))
|
||||
"""
|
||||
mark = $(get(_pgfplots_markers, shape, "*")),
|
||||
mark size = $(0.5 * d[:markersize]),
|
||||
mark size = $(pgf_thickness_scaling(d) * 0.5 * _cycle(d[:markersize], i)),
|
||||
mark options = {
|
||||
color = $cstr_stroke, draw opacity = $a_stroke,
|
||||
fill = $cstr, fill opacity = $a,
|
||||
line width = $(d[:markerstrokewidth]),
|
||||
line width = $(pgf_thickness_scaling(d) * _cycle(d[:markerstrokewidth], i)),
|
||||
rotate = $(shape == :dtriangle ? 180 : 0),
|
||||
$(get(_pgfplots_linestyles, d[:markerstrokestyle], "solid"))
|
||||
$(get(_pgfplots_linestyles, _cycle(d[:markerstrokestyle], i), "solid"))
|
||||
}"""
|
||||
end
|
||||
|
||||
function pgf_add_annotation!(o,x,y,val)
|
||||
function pgf_add_annotation!(o, x, y, val, thickness_scaling = 1)
|
||||
# Construct the style string.
|
||||
# Currently supports color and orientation
|
||||
cstr,a = pgf_color(val.font.color)
|
||||
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation)
|
||||
"""))
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation),
|
||||
font=$(pgf_font(val.font.pointsize, thickness_scaling))
|
||||
"""))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
@@ -196,34 +220,17 @@ end
|
||||
function pgf_series(sp::Subplot, series::Series)
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(d))
|
||||
push!(style, pgf_marker(d))
|
||||
|
||||
if d[:fillrange] != nothing || st in (:shape,)
|
||||
push!(style, pgf_fillstyle(d))
|
||||
end
|
||||
|
||||
# add to legend?
|
||||
if sp[:legend] != :none && should_add_to_legend(series)
|
||||
kw[:legendentry] = d[:label]
|
||||
if st == :shape || d[:fillrange] != nothing
|
||||
push!(style, "area legend")
|
||||
end
|
||||
else
|
||||
push!(style, "forget plot")
|
||||
end
|
||||
series_collection = PGFPlots.Plot[]
|
||||
|
||||
# function args
|
||||
args = if st == :contour
|
||||
args = if st == :contour
|
||||
d[:z].surf, d[:x], d[:y]
|
||||
elseif is3d(st)
|
||||
d[:x], d[:y], d[:z]
|
||||
elseif d[:marker_z] != nothing
|
||||
# If a marker_z is used pass it as third coordinate to a 2D plot.
|
||||
# See "Scatter Plots" in PGFPlots documentation
|
||||
d[:x], d[:y], d[:marker_z]
|
||||
elseif st == :straightline
|
||||
straightline_data(series)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
elseif ispolar(sp)
|
||||
theta, r = filter_radial_data(d[:x], d[:y], axis_limits(sp[:yaxis]))
|
||||
rad2deg.(theta), r
|
||||
@@ -237,34 +244,131 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
else
|
||||
a
|
||||
end, args)
|
||||
# for (i,a) in enumerate(args)
|
||||
# if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
# args[i] = collect(a)
|
||||
# end
|
||||
# end
|
||||
|
||||
# include additional style, then add to the kw
|
||||
if st in (:contour, :histogram2d)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(d))
|
||||
push!(style, pgf_marker(d))
|
||||
push!(style, "forget plot")
|
||||
|
||||
kw[:style] = join(style, ',')
|
||||
func = if st == :histogram2d
|
||||
PGFPlots.Histogram2
|
||||
else
|
||||
kw[:labels] = series[:contour_labels]
|
||||
kw[:levels] = series[:levels]
|
||||
PGFPlots.Contour
|
||||
end
|
||||
push!(series_collection, func(args...; kw...))
|
||||
|
||||
else
|
||||
# series segments
|
||||
segments = iter_segments(series)
|
||||
for (i, rng) in enumerate(segments)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(d, i))
|
||||
push!(style, pgf_marker(d, i))
|
||||
|
||||
if st == :shape
|
||||
push!(style, pgf_fillstyle(d, i))
|
||||
end
|
||||
|
||||
# add to legend?
|
||||
if i == 1 && sp[:legend] != :none && should_add_to_legend(series)
|
||||
if d[:fillrange] != nothing
|
||||
push!(style, "forget plot")
|
||||
push!(series_collection, pgf_fill_legend_hack(d, args))
|
||||
else
|
||||
kw[:legendentry] = d[:label]
|
||||
if st == :shape # || d[:fillrange] != nothing
|
||||
push!(style, "area legend")
|
||||
end
|
||||
end
|
||||
else
|
||||
push!(style, "forget plot")
|
||||
end
|
||||
|
||||
seg_args = (arg[rng] for arg in args)
|
||||
|
||||
# include additional style, then add to the kw
|
||||
if haskey(_pgf_series_extrastyle, st)
|
||||
push!(style, _pgf_series_extrastyle[st])
|
||||
end
|
||||
kw[:style] = join(style, ',')
|
||||
|
||||
# add fillrange
|
||||
if series[:fillrange] != nothing && st != :shape
|
||||
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
|
||||
end
|
||||
|
||||
# build/return the series object
|
||||
func = if st == :path3d
|
||||
PGFPlots.Linear3
|
||||
elseif st == :scatter
|
||||
PGFPlots.Scatter
|
||||
else
|
||||
PGFPlots.Linear
|
||||
end
|
||||
push!(series_collection, func(seg_args...; kw...))
|
||||
end
|
||||
end
|
||||
series_collection
|
||||
end
|
||||
|
||||
function pgf_fillrange_series(series, i, fillrange, args...)
|
||||
st = series[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, "line width = 0")
|
||||
push!(style, "draw opacity = 0")
|
||||
push!(style, pgf_fillstyle(series, i))
|
||||
push!(style, pgf_marker(series, i))
|
||||
push!(style, "forget plot")
|
||||
if haskey(_pgf_series_extrastyle, st)
|
||||
push!(style, _pgf_series_extrastyle[st])
|
||||
end
|
||||
kw[:style] = join(style, ',')
|
||||
func = is3d(series) ? PGFPlots.Linear3 : PGFPlots.Linear
|
||||
return func(pgf_fillrange_args(fillrange, args...)...; kw...)
|
||||
end
|
||||
|
||||
# build/return the series object
|
||||
function pgf_fillrange_args(fillrange, x, y)
|
||||
n = length(x)
|
||||
x_fill = [x; x[n:-1:1]; x[1]]
|
||||
y_fill = [y; _cycle(fillrange, n:-1:1); y[1]]
|
||||
return x_fill, y_fill
|
||||
end
|
||||
|
||||
function pgf_fillrange_args(fillrange, x, y, z)
|
||||
n = length(x)
|
||||
x_fill = [x; x[n:-1:1]; x[1]]
|
||||
y_fill = [y; y[n:-1:1]; x[1]]
|
||||
z_fill = [z; _cycle(fillrange, n:-1:1); z[1]]
|
||||
return x_fill, y_fill, z_fill
|
||||
end
|
||||
|
||||
function pgf_fill_legend_hack(d, args)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(d, 1))
|
||||
push!(style, pgf_marker(d, 1))
|
||||
push!(style, pgf_fillstyle(d, 1))
|
||||
push!(style, "area legend")
|
||||
kw[:legendentry] = d[:label]
|
||||
kw[:style] = join(style, ',')
|
||||
st = d[:seriestype]
|
||||
func = if st == :path3d
|
||||
PGFPlots.Linear3
|
||||
elseif st == :scatter
|
||||
PGFPlots.Scatter
|
||||
elseif st == :histogram2d
|
||||
PGFPlots.Histogram2
|
||||
elseif st == :contour
|
||||
PGFPlots.Contour
|
||||
else
|
||||
PGFPlots.Linear
|
||||
end
|
||||
func(args...; kw...)
|
||||
return func(([arg[1]] for arg in args)...; kw...)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function pgf_axis(sp::Subplot, letter)
|
||||
@@ -272,14 +376,18 @@ function pgf_axis(sp::Subplot, letter)
|
||||
style = []
|
||||
kw = KW()
|
||||
|
||||
# turn off scaled ticks
|
||||
push!(style, "scaled $(letter) ticks = false")
|
||||
|
||||
# set to supported framestyle
|
||||
framestyle = pgf_framestyle(sp[:framestyle])
|
||||
|
||||
# axis guide
|
||||
kw[Symbol(letter,:label)] = axis[:guide]
|
||||
|
||||
# Add ticklabel rotations
|
||||
push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}")
|
||||
# Add label font
|
||||
cstr, α = pgf_color(plot_color(axis[:guidefontcolor]))
|
||||
push!(style, string(letter, "label style = {font = ", pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:guidefontrotation], "}"))
|
||||
|
||||
# flip/reverse?
|
||||
axis[:flip] && push!(style, "$letter dir=reverse")
|
||||
@@ -292,7 +400,7 @@ function pgf_axis(sp::Subplot, letter)
|
||||
end
|
||||
|
||||
# ticks on or off
|
||||
if axis[:ticks] in (nothing, false) || framestyle == :none
|
||||
if axis[:ticks] in (nothing, false, :none) || framestyle == :none
|
||||
push!(style, "$(letter)majorticks=false")
|
||||
end
|
||||
|
||||
@@ -311,7 +419,7 @@ function pgf_axis(sp::Subplot, letter)
|
||||
kw[Symbol(letter,:max)] = lims[2]
|
||||
end
|
||||
|
||||
if !(axis[:ticks] in (nothing, false, :none)) && framestyle != :none
|
||||
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
|
||||
ticks = get_ticks(axis)
|
||||
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
|
||||
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
|
||||
@@ -326,11 +434,18 @@ function pgf_axis(sp::Subplot, letter)
|
||||
push!(style, string(letter, "ticklabels = {\$", join(tick_labels,"\$,\$"), "\$}"))
|
||||
elseif axis[:showaxis]
|
||||
tick_labels = ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
|
||||
if axis[:formatter] in (:scientific, :auto)
|
||||
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
|
||||
tick_labels = replace.(tick_labels, "×", "\\times")
|
||||
end
|
||||
push!(style, string(letter, "ticklabels = {", join(tick_labels,","), "}"))
|
||||
else
|
||||
push!(style, string(letter, "ticklabels = {}"))
|
||||
end
|
||||
push!(style, string(letter, "tick align = ", (axis[:tick_direction] == :out ? "outside" : "inside")))
|
||||
cstr, α = pgf_color(plot_color(axis[:tickfontcolor]))
|
||||
push!(style, string(letter, "ticklabel style = {font = ", pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:tickfontrotation], "}"))
|
||||
push!(style, string(letter, " grid style = {", pgf_linestyle(pgf_thickness_scaling(sp) * axis[:gridlinewidth], axis[:foreground_color_grid], axis[:gridalpha], axis[:gridstyle]), "}"))
|
||||
end
|
||||
|
||||
# framestyle
|
||||
@@ -343,7 +458,7 @@ function pgf_axis(sp::Subplot, letter)
|
||||
if framestyle == :zerolines
|
||||
push!(style, string("extra ", letter, " ticks = 0"))
|
||||
push!(style, string("extra ", letter, " tick labels = "))
|
||||
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {color = black, draw opacity=1.0, line width=0.5), solid}}"))
|
||||
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}}"))
|
||||
end
|
||||
|
||||
if !axis[:showaxis]
|
||||
@@ -351,6 +466,8 @@ function pgf_axis(sp::Subplot, letter)
|
||||
end
|
||||
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
|
||||
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
|
||||
else
|
||||
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}"))
|
||||
end
|
||||
|
||||
# return the style list and KW args
|
||||
@@ -395,6 +512,8 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
|
||||
if sp[:title] != ""
|
||||
kw[:title] = "$(sp[:title])"
|
||||
cstr, α = pgf_color(plot_color(sp[:titlefontcolor]))
|
||||
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
|
||||
end
|
||||
|
||||
if sp[:aspect_ratio] in (1, :equal)
|
||||
@@ -405,8 +524,13 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
if haskey(_pgfplots_legend_pos, legpos)
|
||||
kw[:legendPos] = _pgfplots_legend_pos[legpos]
|
||||
end
|
||||
cstr, a = pgf_color(plot_color(sp[:background_color_legend]))
|
||||
push!(style, string("legend style = {", pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], 1.0, "solid"), ",", "fill = $cstr,", "font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)), "}"))
|
||||
|
||||
if is3d(sp)
|
||||
if any(s[:seriestype] == :contour for s in series_list(sp))
|
||||
kw[:view] = "{0}{90}"
|
||||
kw[:colorbar] = !(sp[:colorbar] in (:none, :off, :hide, false))
|
||||
elseif is3d(sp)
|
||||
azim, elev = sp[:camera]
|
||||
kw[:view] = "{$(azim)}{$(elev)}"
|
||||
end
|
||||
@@ -430,7 +554,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
# As it is likely that all series within the same axis use the same
|
||||
# colormap this should not cause any problem.
|
||||
for series in series_list(sp)
|
||||
for col in (:markercolor, :fillcolor)
|
||||
for col in (:markercolor, :fillcolor, :linecolor)
|
||||
if typeof(series.d[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.d[col]))}")
|
||||
|
||||
@@ -446,22 +570,22 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
end
|
||||
@label colorbar_end
|
||||
|
||||
o = axisf(; style = style, kw...)
|
||||
o = axisf(; style = join(style, ","), kw...)
|
||||
|
||||
# add the series object to the PGFPlots.Axis
|
||||
for series in series_list(sp)
|
||||
push!(o, pgf_series(sp, series))
|
||||
push!.(o, pgf_series(sp, series))
|
||||
|
||||
# add series annotations
|
||||
anns = series[:series_annotations]
|
||||
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt))
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt), pgf_thickness_scaling(series))
|
||||
end
|
||||
end
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
pgf_add_annotation!(o,ann...)
|
||||
pgf_add_annotation!(o, locate_annotation(sp, ann...)..., pgf_thickness_scaling(sp))
|
||||
end
|
||||
|
||||
|
||||
|
||||
+311
-172
@@ -30,7 +30,7 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :gridalpha, :gridlinewidth,
|
||||
:legend, :colorbar, :colorbar_title,
|
||||
:marker_z, :fill_z, :levels,
|
||||
:marker_z, :fill_z, :line_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
# :overwrite_figure,
|
||||
@@ -45,11 +45,13 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
|
||||
const _plotly_seriestype = [
|
||||
:path, :scatter, :bar, :pie, :heatmap,
|
||||
:path, :scatter, :pie, :heatmap,
|
||||
:contour, :surface, :wireframe, :path3d, :scatter3d, :shape, :scattergl,
|
||||
:straightline
|
||||
]
|
||||
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _plotly_marker = [
|
||||
@@ -86,8 +88,6 @@ const _plotly_js_path_remote = "https://cdn.plot.ly/plotly-latest.min.js"
|
||||
|
||||
function _initialize_backend(::PlotlyBackend; kw...)
|
||||
@eval begin
|
||||
import JSON
|
||||
|
||||
_js_code = open(readstring, _plotly_js_path, "r")
|
||||
|
||||
# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2
|
||||
@@ -147,7 +147,7 @@ function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
|
||||
:text => val,
|
||||
:xref => xref,
|
||||
:x => x,
|
||||
:yref => xref,
|
||||
:yref => yref,
|
||||
:y => y,
|
||||
:showarrow => false,
|
||||
)
|
||||
@@ -233,7 +233,7 @@ function plotly_domain(sp::Subplot, letter)
|
||||
end
|
||||
|
||||
|
||||
function plotly_axis(axis::Axis, sp::Subplot)
|
||||
function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
|
||||
letter = axis[:letter]
|
||||
framestyle = sp[:framestyle]
|
||||
ax = KW(
|
||||
@@ -253,28 +253,37 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
||||
|
||||
if letter in (:x,:y)
|
||||
ax[:domain] = plotly_domain(sp, letter)
|
||||
ax[:anchor] = "$(letter==:x ? :y : :x)$(plotly_subplot_index(sp))"
|
||||
if is3d(sp)
|
||||
# don't link 3d axes for synchronized interactivity
|
||||
x_idx = y_idx = sp[:subplot_index]
|
||||
else
|
||||
x_idx, y_idx = plotly_link_indicies(plt, sp)
|
||||
end
|
||||
ax[:anchor] = "$(letter==:x ? "y$(y_idx)" : "x$(x_idx)")"
|
||||
end
|
||||
|
||||
ax[:tickangle] = -axis[:rotation]
|
||||
ax[:type] = plotly_scale(axis[:scale])
|
||||
lims = axis_limits(axis)
|
||||
ax[:range] = map(scalefunc(axis[:scale]), lims)
|
||||
|
||||
if !(axis[:ticks] in (nothing, :none))
|
||||
|
||||
if axis[:ticks] != :native || axis[:lims] != :auto
|
||||
ax[:range] = map(scalefunc(axis[:scale]), lims)
|
||||
end
|
||||
|
||||
if !(axis[:ticks] in (nothing, :none, false))
|
||||
ax[:titlefont] = plotly_font(guidefont(axis))
|
||||
ax[:type] = plotly_scale(axis[:scale])
|
||||
ax[:tickfont] = plotly_font(tickfont(axis))
|
||||
ax[:tickcolor] = framestyle in (:zerolines, :grid) || !axis[:showaxis] ? rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
|
||||
ax[:linecolor] = rgba_string(axis[:foreground_color_axis])
|
||||
|
||||
# flip
|
||||
if axis[:flip]
|
||||
ax[:autorange] = "reversed"
|
||||
ax[:range] = reverse(ax[:range])
|
||||
end
|
||||
|
||||
# ticks
|
||||
ticks = get_ticks(axis)
|
||||
if ticks != :auto
|
||||
if axis[:ticks] != :native
|
||||
ticks = get_ticks(axis)
|
||||
ttype = ticksType(ticks)
|
||||
if ttype == :ticks
|
||||
ax[:tickmode] = "array"
|
||||
@@ -289,7 +298,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
||||
ax[:showgrid] = false
|
||||
end
|
||||
|
||||
|
||||
|
||||
ax
|
||||
end
|
||||
|
||||
@@ -319,10 +328,11 @@ function plotly_layout(plt::Plot)
|
||||
|
||||
d_out[:annotations] = KW[]
|
||||
|
||||
multiple_subplots = length(plt.subplots) > 1
|
||||
|
||||
for sp in plt.subplots
|
||||
spidx = plotly_subplot_index(sp)
|
||||
|
||||
|
||||
spidx = multiple_subplots ? sp[:subplot_index] : ""
|
||||
x_idx, y_idx = multiple_subplots ? plotly_link_indicies(plt, 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] != ""
|
||||
@@ -350,9 +360,9 @@ function plotly_layout(plt::Plot)
|
||||
azim = sp[:camera][1] - 90 #convert azimuthal to match GR behaviour
|
||||
theta = 90 - sp[:camera][2] #spherical coordinate angle from z axis
|
||||
d_out[:scene] = KW(
|
||||
Symbol("xaxis$spidx") => plotly_axis(sp[:xaxis], sp),
|
||||
Symbol("yaxis$spidx") => plotly_axis(sp[:yaxis], sp),
|
||||
Symbol("zaxis$spidx") => plotly_axis(sp[:zaxis], sp),
|
||||
Symbol("xaxis$(spidx)") => plotly_axis(plt, sp[:xaxis], sp),
|
||||
Symbol("yaxis$(spidx)") => plotly_axis(plt, sp[:yaxis], sp),
|
||||
Symbol("zaxis$(spidx)") => plotly_axis(plt, sp[:zaxis], sp),
|
||||
|
||||
#2.6 multiplier set camera eye such that whole plot can be seen
|
||||
:camera => KW(
|
||||
@@ -364,11 +374,12 @@ function plotly_layout(plt::Plot)
|
||||
),
|
||||
)
|
||||
elseif ispolar(sp)
|
||||
d_out[Symbol("angularaxis$spidx")] = plotly_polaraxis(sp[:xaxis])
|
||||
d_out[Symbol("radialaxis$spidx")] = plotly_polaraxis(sp[:yaxis])
|
||||
d_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp[:xaxis])
|
||||
d_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp[:yaxis])
|
||||
else
|
||||
d_out[Symbol("xaxis$spidx")] = plotly_axis(sp[:xaxis], sp)
|
||||
d_out[Symbol("yaxis$spidx")] = plotly_axis(sp[:yaxis], sp)
|
||||
d_out[Symbol("xaxis$(x_idx)")] = plotly_axis(plt, sp[:xaxis], sp)
|
||||
# don't allow yaxis to be reupdated/reanchored in a linked subplot
|
||||
spidx == y_idx ? d_out[Symbol("yaxis$(y_idx)")] = plotly_axis(plt, sp[:yaxis], sp) : nothing
|
||||
end
|
||||
|
||||
# legend
|
||||
@@ -379,14 +390,16 @@ function plotly_layout(plt::Plot)
|
||||
:bgcolor => rgba_string(sp[:background_color_legend]),
|
||||
:bordercolor => rgba_string(sp[:foreground_color_legend]),
|
||||
:font => plotly_font(legendfont(sp)),
|
||||
:tracegroupgap => 0,
|
||||
: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]])
|
||||
|
||||
for ann in sp[:annotations]
|
||||
append!(d_out[:annotations], KW[plotly_annotation_dict(locate_annotation(sp, ann...)...; xref = "x$(x_idx)", yref = "y$(y_idx)")])
|
||||
end
|
||||
# series_annotations
|
||||
for series in series_list(sp)
|
||||
anns = series[:series_annotations]
|
||||
@@ -394,7 +407,7 @@ function plotly_layout(plt::Plot)
|
||||
push!(d_out[:annotations], plotly_annotation_dict(
|
||||
xi,
|
||||
yi,
|
||||
PlotText(str,fnt); xref = "x$spidx", yref = "y$spidx")
|
||||
PlotText(str,fnt); xref = "x$(x_idx)", yref = "y$(y_idx)")
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -430,9 +443,17 @@ end
|
||||
|
||||
|
||||
function plotly_colorscale(grad::ColorGradient, α)
|
||||
[[grad.values[i], rgb_string(grad.colors[i])] for i in 1:length(grad.colors)]
|
||||
[[grad.values[i], rgba_string(plot_color(grad.colors[i], α))] for i in 1:length(grad.colors)]
|
||||
end
|
||||
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
|
||||
function plotly_colorscale(c::AbstractVector{<:RGBA}, α)
|
||||
if length(c) == 1
|
||||
return [[0.0, rgba_string(plot_color(c[1], α))], [1.0, rgba_string(plot_color(c[1], α))]]
|
||||
else
|
||||
vals = linspace(0.0, 1.0, length(c))
|
||||
return [[vals[i], rgba_string(plot_color(c[i], α))] for i in eachindex(c)]
|
||||
end
|
||||
end
|
||||
# plotly_colorscale(c, alpha = nothing) = plotly_colorscale(cgrad(), alpha)
|
||||
|
||||
|
||||
@@ -447,9 +468,15 @@ const _plotly_markers = KW(
|
||||
:hline => "line-ew",
|
||||
)
|
||||
|
||||
function plotly_subplot_index(sp::Subplot)
|
||||
spidx = sp[:subplot_index]
|
||||
spidx == 1 ? "" : spidx
|
||||
# find indicies of axes to which the supblot links to
|
||||
function plotly_link_indicies(plt::Plot, sp::Subplot)
|
||||
if plt[:link] in (:x, :y, :both)
|
||||
x_idx = sp[:xaxis].sps[1][:subplot_index]
|
||||
y_idx = sp[:yaxis].sps[1][:subplot_index]
|
||||
else
|
||||
x_idx = y_idx = sp[:subplot_index]
|
||||
end
|
||||
x_idx, y_idx
|
||||
end
|
||||
|
||||
|
||||
@@ -464,7 +491,22 @@ function plotly_close_shapes(x, y)
|
||||
nanvcat(xs), nanvcat(ys)
|
||||
end
|
||||
|
||||
plotly_data(v) = collect(v)
|
||||
function plotly_data(series::Series, letter::Symbol, data)
|
||||
axis = series[:subplot][Symbol(letter, :axis)]
|
||||
|
||||
data = if axis[:ticks] == :native && data != nothing
|
||||
plotly_native_data(axis, data)
|
||||
else
|
||||
data
|
||||
end
|
||||
|
||||
if series[:seriestype] in (:heatmap, :contour, :surface, :wireframe)
|
||||
plotly_surface_data(series, data)
|
||||
else
|
||||
plotly_data(data)
|
||||
end
|
||||
end
|
||||
plotly_data(v) = v != nothing ? collect(v) : v
|
||||
plotly_data(surf::Surface) = surf.surf
|
||||
plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v)
|
||||
|
||||
@@ -472,6 +514,28 @@ 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)
|
||||
|
||||
function plotly_native_data(axis::Axis, data::AbstractArray)
|
||||
if !isempty(axis[:discrete_values])
|
||||
construct_categorical_data(data, axis)
|
||||
elseif axis[:formatter] in (datetimeformatter, dateformatter, timeformatter)
|
||||
plotly_convert_to_datetime(data, axis[:formatter])
|
||||
else
|
||||
data
|
||||
end
|
||||
end
|
||||
plotly_native_data(axis::Axis, a::Surface) = Surface(plotly_native_data(axis, a.surf))
|
||||
|
||||
function plotly_convert_to_datetime(x::AbstractArray, formatter::Function)
|
||||
if formatter == datetimeformatter
|
||||
map(xi -> replace(formatter(xi), "T", " "), x)
|
||||
elseif formatter == dateformatter
|
||||
map(xi -> string(formatter(xi), " 00:00:00"), x)
|
||||
elseif formatter == timeformatter
|
||||
map(xi -> string(Dates.Date(Dates.now()), " ", formatter(xi)), x)
|
||||
else
|
||||
error("Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter")
|
||||
end
|
||||
end
|
||||
#ensures that a gradient is called if a single color is supplied where a gradient is needed (e.g. if a series recipe defines marker_z)
|
||||
as_gradient(grad::ColorGradient, α) = grad
|
||||
as_gradient(grad, α) = cgrad(alpha = α)
|
||||
@@ -487,27 +551,30 @@ function plotly_series(plt::Plot, series::Series)
|
||||
d_out = KW()
|
||||
|
||||
# these are the axes that the series should be mapped to
|
||||
spidx = plotly_subplot_index(sp)
|
||||
d_out[:xaxis] = "x$spidx"
|
||||
d_out[:yaxis] = "y$spidx"
|
||||
x_idx, y_idx = plotly_link_indicies(plt, sp)
|
||||
d_out[:xaxis] = "x$(x_idx)"
|
||||
d_out[:yaxis] = "y$(y_idx)"
|
||||
d_out[:showlegend] = should_add_to_legend(series)
|
||||
|
||||
x, y = plotly_data(series[:x]), plotly_data(series[:y])
|
||||
if st == :straightline
|
||||
x, y = straightline_data(series)
|
||||
z = series[:z]
|
||||
else
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
end
|
||||
|
||||
x, y, z = (plotly_data(series, letter, data)
|
||||
for (letter, data) in zip((:x, :y, :z), (x, y, z))
|
||||
)
|
||||
|
||||
d_out[:name] = series[:label]
|
||||
|
||||
isscatter = st in (:scatter, :scatter3d, :scattergl)
|
||||
hasmarker = isscatter || series[:markershape] != :none
|
||||
hasline = st in (:path, :path3d)
|
||||
hasfillrange = st in (:path, :scatter, :scattergl) &&
|
||||
hasline = st in (:path, :path3d, :straightline)
|
||||
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
|
||||
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
|
||||
|
||||
# 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
|
||||
|
||||
d_out[:colorbar] = KW(:title => sp[:colorbar_title])
|
||||
|
||||
clims = sp[:clims]
|
||||
@@ -516,58 +583,34 @@ function plotly_series(plt::Plot, series::Series)
|
||||
end
|
||||
|
||||
# set the "type"
|
||||
if st in (:path, :scatter, :scattergl)
|
||||
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
if series[:fillrange] == true || series[:fillrange] == 0 || isa(series[:fillrange], Tuple)
|
||||
d_out[:fill] = "tozeroy"
|
||||
d_out[:fillcolor] = rgba_string(series[:fillcolor])
|
||||
elseif isa(series[:fillrange], AbstractVector)
|
||||
d_out[:fill] = "tonexty"
|
||||
d_out[:fillcolor] = rgba_string(series[:fillcolor])
|
||||
elseif !(series[:fillrange] in (false, nothing))
|
||||
warn("fillrange ignored... plotly only supports filling to zero and to a vector of values. fillrange: $(series[:fillrange])")
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
|
||||
elseif st == :bar
|
||||
d_out[:type] = "bar"
|
||||
d_out[:x], d_out[:y], d_out[:orientation] = if isvertical(series)
|
||||
x, y, "v"
|
||||
else
|
||||
y, x, "h"
|
||||
end
|
||||
d_out[:width] = series[:bar_width]
|
||||
d_out[:marker] = KW(:color => rgba_string(series[:fillcolor]),
|
||||
:line => KW(:width => series[:linewidth]))
|
||||
if st in (:path, :scatter, :scattergl, :straightline, :path3d, :scatter3d)
|
||||
return plotly_series_segments(series, d_out, x, y, z)
|
||||
|
||||
elseif st == :heatmap
|
||||
x = heatmap_edges(x, sp[:xaxis][:scale])
|
||||
y = heatmap_edges(y, sp[:yaxis][:scale])
|
||||
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] = x, y, z
|
||||
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
d_out[:showscale] = hascolorbar(sp)
|
||||
|
||||
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] = x, y, z
|
||||
# d_out[:showscale] = series[:colorbar] != :none
|
||||
d_out[:ncontours] = series[:levels]
|
||||
d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines")
|
||||
d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
||||
d_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
|
||||
d_out[:showscale] = hascolorbar(sp)
|
||||
|
||||
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[:x], d_out[:y], d_out[:z] = x, y, z
|
||||
if st == :wireframe
|
||||
d_out[:hidesurface] = true
|
||||
wirelines = KW(
|
||||
:show => true,
|
||||
:color => rgba_string(series[:linecolor]),
|
||||
:color => rgba_string(plot_color(series[:linecolor], series[:linealpha])),
|
||||
:highlightwidth => series[:linewidth],
|
||||
)
|
||||
d_out[:contours] = KW(:x => wirelines, :y => wirelines, :z => wirelines)
|
||||
@@ -587,16 +630,6 @@ function plotly_series(plt::Plot, series::Series)
|
||||
d_out[:values] = y
|
||||
d_out[:hoverinfo] = "label+percent+name"
|
||||
|
||||
elseif st in (:path3d, :scatter3d)
|
||||
d_out[:type] = "scatter3d"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = plotly_data(series[:z])
|
||||
|
||||
else
|
||||
warn("Plotly: seriestype $st isn't supported.")
|
||||
return KW()
|
||||
@@ -604,120 +637,229 @@ function plotly_series(plt::Plot, series::Series)
|
||||
|
||||
# add "marker"
|
||||
if hasmarker
|
||||
inds = eachindex(x)
|
||||
d_out[:marker] = KW(
|
||||
:symbol => get(_plotly_markers, series[:markershape], string(series[:markershape])),
|
||||
# :opacity => series[:markeralpha],
|
||||
:size => 2 * series[:markersize],
|
||||
# :color => rgba_string(series[:markercolor]),
|
||||
:size => 2 * _cycle(series[:markersize], inds),
|
||||
:color => rgba_string.(plot_color.(get_markercolor.(series, inds), get_markeralpha.(series, inds))),
|
||||
:line => KW(
|
||||
:color => rgba_string(series[:markerstrokecolor]),
|
||||
:width => series[:markerstrokewidth],
|
||||
:color => rgba_string.(plot_color.(get_markerstrokecolor.(series, inds), get_markerstrokealpha.(series, inds))),
|
||||
:width => _cycle(series[:markerstrokewidth], inds),
|
||||
),
|
||||
)
|
||||
|
||||
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
|
||||
if series[:marker_z] == nothing
|
||||
d_out[:marker][:color] = rgba_string(series[:markercolor])
|
||||
else
|
||||
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
|
||||
# grad = as_gradient(series[:markercolor], series[:markeralpha])
|
||||
cmin, cmax = get_clims(sp)
|
||||
# zrange = zmax == zmin ? 1 : zmax - zmin # if all marker_z values are the same, plot all markers same color (avoids division by zero in next line)
|
||||
d_out[:marker][:color] = [clamp(zi, cmin, cmax) for zi in series[:marker_z]]
|
||||
d_out[:marker][:cmin] = cmin
|
||||
d_out[:marker][:cmax] = cmax
|
||||
d_out[:marker][:colorscale] = plotly_colorscale(series[:markercolor], series[:markeralpha])
|
||||
d_out[:marker][:showscale] = hascolorbar(sp)
|
||||
end
|
||||
end
|
||||
|
||||
# add "line"
|
||||
if hasline
|
||||
d_out[:line] = KW(
|
||||
:color => rgba_string(series[:linecolor]),
|
||||
:width => series[:linewidth],
|
||||
:shape => if st == :steppre
|
||||
"vh"
|
||||
elseif st == :steppost
|
||||
"hv"
|
||||
else
|
||||
"linear"
|
||||
end,
|
||||
:dash => string(series[:linestyle]),
|
||||
# :dash => "solid",
|
||||
)
|
||||
end
|
||||
|
||||
plotly_polar!(d_out, series)
|
||||
plotly_hover!(d_out, series[:hover])
|
||||
|
||||
if hasfillrange
|
||||
# if hasfillrange is true, return two dictionaries (one for original
|
||||
# series, one for series being filled to) instead of one
|
||||
d_out_fillrange = deepcopy(d_out)
|
||||
d_out_fillrange[:showlegend] = false
|
||||
if isa(series[:fillrange], AbstractVector)
|
||||
d_out_fillrange[:y] = series[:fillrange]
|
||||
delete!(d_out_fillrange, :fill)
|
||||
delete!(d_out_fillrange, :fillcolor)
|
||||
else
|
||||
# if fillrange is a tuple with upper and lower limit, d_out_fillrange
|
||||
# is the series that will do the filling
|
||||
d_out_fillrange[:x], d_out_fillrange[:y] =
|
||||
concatenate_fillrange(series[:x], series[:fillrange])
|
||||
d_out_fillrange[:line][:width] = 0
|
||||
delete!(d_out, :fill)
|
||||
delete!(d_out, :fillcolor)
|
||||
end
|
||||
|
||||
return [d_out_fillrange, d_out]
|
||||
else
|
||||
return [d_out]
|
||||
end
|
||||
return [d_out]
|
||||
end
|
||||
|
||||
function plotly_series_shapes(plt::Plot, series::Series)
|
||||
d_outs = []
|
||||
segments = iter_segments(series)
|
||||
d_outs = Vector{KW}(length(segments))
|
||||
|
||||
# TODO: create a d_out for each polygon
|
||||
# x, y = series[:x], series[:y]
|
||||
|
||||
# these are the axes that the series should be mapped to
|
||||
spidx = plotly_subplot_index(series[:subplot])
|
||||
base_d = KW()
|
||||
base_d[:xaxis] = "x$spidx"
|
||||
base_d[:yaxis] = "y$spidx"
|
||||
base_d[:name] = series[:label]
|
||||
# base_d[:legendgroup] = series[:label]
|
||||
x_idx, y_idx = plotly_link_indicies(plt, series[:subplot])
|
||||
d_base = KW(
|
||||
:xaxis => "x$(x_idx)",
|
||||
:yaxis => "y$(y_idx)",
|
||||
:name => series[:label],
|
||||
:legendgroup => series[:label],
|
||||
)
|
||||
|
||||
x, y = plotly_data(series[:x]), plotly_data(series[:y])
|
||||
for (i,rng) in enumerate(iter_segments(x,y))
|
||||
x, y = (plotly_data(series, letter, data)
|
||||
for (letter, data) in zip((:x, :y), shape_data(series))
|
||||
)
|
||||
|
||||
for (i,rng) in enumerate(segments)
|
||||
length(rng) < 2 && continue
|
||||
|
||||
# to draw polygons, we actually draw lines with fill
|
||||
d_out = merge(base_d, KW(
|
||||
d_out = merge(d_base, KW(
|
||||
:type => "scatter",
|
||||
:mode => "lines",
|
||||
:x => vcat(x[rng], x[rng[1]]),
|
||||
:y => vcat(y[rng], y[rng[1]]),
|
||||
:fill => "tozeroy",
|
||||
:fillcolor => rgba_string(_cycle(series[:fillcolor], i)),
|
||||
:fillcolor => rgba_string(plot_color(get_fillcolor(series, i), get_fillalpha(series, i))),
|
||||
))
|
||||
if series[:markerstrokewidth] > 0
|
||||
d_out[:line] = KW(
|
||||
:color => rgba_string(_cycle(series[:linecolor], i)),
|
||||
:width => series[:linewidth],
|
||||
:dash => string(series[:linestyle]),
|
||||
:color => rgba_string(plot_color(get_linecolor(series, i), get_linealpha(series, i))),
|
||||
:width => get_linewidth(series, i),
|
||||
:dash => string(get_linestyle(series, i)),
|
||||
)
|
||||
end
|
||||
d_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
|
||||
plotly_polar!(d_out, series)
|
||||
plotly_hover!(d_out, _cycle(series[:hover], i))
|
||||
push!(d_outs, d_out)
|
||||
d_outs[i] = d_out
|
||||
end
|
||||
if series[:fill_z] != nothing
|
||||
push!(d_outs, plotly_colorbar_hack(series, d_base, :fill))
|
||||
elseif series[:line_z] != nothing
|
||||
push!(d_outs, plotly_colorbar_hack(series, d_base, :line))
|
||||
elseif series[:marker_z] != nothing
|
||||
push!(d_outs, plotly_colorbar_hack(series, d_base, :marker))
|
||||
end
|
||||
d_outs
|
||||
end
|
||||
|
||||
function plotly_series_segments(series::Series, d_base::KW, x, y, z)
|
||||
st = series[:seriestype]
|
||||
sp = series[:subplot]
|
||||
isscatter = st in (:scatter, :scatter3d, :scattergl)
|
||||
hasmarker = isscatter || series[:markershape] != :none
|
||||
hasline = st in (:path, :path3d, :straightline)
|
||||
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
|
||||
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
|
||||
|
||||
segments = iter_segments(series)
|
||||
d_outs = Vector{KW}((hasfillrange ? 2 : 1 ) * length(segments))
|
||||
|
||||
for (i,rng) in enumerate(segments)
|
||||
!isscatter && length(rng) < 2 && continue
|
||||
|
||||
d_out = deepcopy(d_base)
|
||||
d_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
|
||||
d_out[:legendgroup] = series[:label]
|
||||
|
||||
# set the type
|
||||
if st in (:path, :scatter, :scattergl, :straightline)
|
||||
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
if series[:fillrange] == true || series[:fillrange] == 0 || isa(series[:fillrange], Tuple)
|
||||
d_out[:fill] = "tozeroy"
|
||||
d_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, i), get_fillalpha(series, i)))
|
||||
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real}, Real}
|
||||
d_out[:fill] = "tonexty"
|
||||
d_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, i), get_fillalpha(series, i)))
|
||||
elseif !(series[:fillrange] in (false, nothing))
|
||||
warn("fillrange ignored... plotly only supports filling to zero and to a vector of values. fillrange: $(series[:fillrange])")
|
||||
end
|
||||
d_out[:x], d_out[:y] = x[rng], y[rng]
|
||||
|
||||
elseif st in (:path3d, :scatter3d)
|
||||
d_out[:type] = "scatter3d"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
d_out[:x], d_out[:y], d_out[:z] = x[rng], y[rng], z[rng]
|
||||
end
|
||||
|
||||
# add "marker"
|
||||
if hasmarker
|
||||
d_out[:marker] = KW(
|
||||
:symbol => get(_plotly_markers, _cycle(series[:markershape], i), string(_cycle(series[:markershape], i))),
|
||||
# :opacity => series[:markeralpha],
|
||||
:size => 2 * _cycle(series[:markersize], i),
|
||||
:color => rgba_string(plot_color(get_markercolor(series, i), get_markeralpha(series, i))),
|
||||
:line => KW(
|
||||
:color => rgba_string(plot_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i))),
|
||||
:width => _cycle(series[:markerstrokewidth], i),
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
# add "line"
|
||||
if hasline
|
||||
d_out[:line] = KW(
|
||||
:color => rgba_string(plot_color(get_linecolor(series, i), get_linealpha(series, i))),
|
||||
:width => get_linewidth(series, i),
|
||||
:shape => if st == :steppre
|
||||
"vh"
|
||||
elseif st == :steppost
|
||||
"hv"
|
||||
else
|
||||
"linear"
|
||||
end,
|
||||
:dash => string(get_linestyle(series, i)),
|
||||
)
|
||||
end
|
||||
|
||||
plotly_polar!(d_out, series)
|
||||
plotly_hover!(d_out, _cycle(series[:hover], rng))
|
||||
|
||||
if hasfillrange
|
||||
# if hasfillrange is true, return two dictionaries (one for original
|
||||
# series, one for series being filled to) instead of one
|
||||
d_out_fillrange = deepcopy(d_out)
|
||||
d_out_fillrange[:showlegend] = false
|
||||
# if fillrange is provided as real or tuple of real, expand to array
|
||||
if typeof(series[:fillrange]) <: Real
|
||||
series[:fillrange] = fill(series[:fillrange], length(rng))
|
||||
elseif typeof(series[:fillrange]) <: Tuple
|
||||
f1 = typeof(series[:fillrange][1]) <: Real ? fill(series[:fillrange][1], length(rng)) : series[:fillrange][1][rng]
|
||||
f2 = typeof(series[:fillrange][2]) <: Real ? fill(series[:fillrange][2], length(rng)) : series[:fillrange][2][rng]
|
||||
series[:fillrange] = (f1, f2)
|
||||
end
|
||||
if isa(series[:fillrange], AbstractVector)
|
||||
d_out_fillrange[:y] = series[:fillrange][rng]
|
||||
delete!(d_out_fillrange, :fill)
|
||||
delete!(d_out_fillrange, :fillcolor)
|
||||
else
|
||||
# if fillrange is a tuple with upper and lower limit, d_out_fillrange
|
||||
# is the series that will do the filling
|
||||
fillrng = Tuple(series[:fillrange][i][rng] for i in 1:2)
|
||||
d_out_fillrange[:x], d_out_fillrange[:y] = concatenate_fillrange(x[rng], fillrng)
|
||||
d_out_fillrange[:line][:width] = 0
|
||||
delete!(d_out, :fill)
|
||||
delete!(d_out, :fillcolor)
|
||||
end
|
||||
|
||||
d_outs[(2 * i - 1):(2 * i)] = [d_out_fillrange, d_out]
|
||||
else
|
||||
d_outs[i] = d_out
|
||||
end
|
||||
end
|
||||
|
||||
if series[:line_z] != nothing
|
||||
push!(d_outs, plotly_colorbar_hack(series, d_base, :line))
|
||||
elseif series[:fill_z] != nothing
|
||||
push!(d_outs, plotly_colorbar_hack(series, d_base, :fill))
|
||||
elseif series[:marker_z] != nothing
|
||||
push!(d_outs, plotly_colorbar_hack(series, d_base, :marker))
|
||||
end
|
||||
|
||||
d_outs
|
||||
end
|
||||
|
||||
function plotly_colorbar_hack(series::Series, d_base::KW, sym::Symbol)
|
||||
d_out = deepcopy(d_base)
|
||||
cmin, cmax = get_clims(series[:subplot])
|
||||
d_out[:showlegend] = false
|
||||
d_out[:type] = is3d(series) ? :scatter3d : :scatter
|
||||
d_out[:hoverinfo] = :none
|
||||
d_out[:mode] = :markers
|
||||
d_out[:x], d_out[:y] = [series[:x][1]], [series[:y][1]]
|
||||
if is3d(series)
|
||||
d_out[:z] = [series[:z][1]]
|
||||
end
|
||||
# zrange = zmax == zmin ? 1 : zmax - zmin # if all marker_z values are the same, plot all markers same color (avoids division by zero in next line)
|
||||
d_out[:marker] = KW(
|
||||
:size => 0,
|
||||
:opacity => 0,
|
||||
:color => [0.5],
|
||||
:cmin => cmin,
|
||||
:cmax => cmax,
|
||||
:colorscale => plotly_colorscale(series[Symbol("$(sym)color")], 1),
|
||||
:showscale => hascolorbar(series[:subplot]),
|
||||
)
|
||||
return d_out
|
||||
end
|
||||
|
||||
|
||||
function plotly_polar!(d_out::KW, series::Series)
|
||||
# convert polar plots x/y to theta/radius
|
||||
if ispolar(series[:subplot])
|
||||
@@ -738,15 +880,17 @@ function plotly_hover!(d_out::KW, hover)
|
||||
end
|
||||
|
||||
# get a list of dictionaries, each representing the series params
|
||||
function plotly_series_json(plt::Plot)
|
||||
function plotly_series(plt::Plot)
|
||||
slist = []
|
||||
for series in plt.series_list
|
||||
append!(slist, plotly_series(plt, series))
|
||||
end
|
||||
JSON.json(slist)
|
||||
# JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
|
||||
slist
|
||||
end
|
||||
|
||||
# get json string for a list of dictionaries, each representing the series params
|
||||
plotly_series_json(plt::Plot) = JSON.json(plotly_series(plt))
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const _use_remote = Ref(false)
|
||||
@@ -784,12 +928,7 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
function _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
|
||||
# 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})
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
|
||||
write(io, html_head(plt) * html_body(plt))
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "plotlyjs.jl"))
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "plotlyjs.jl"))
|
||||
end
|
||||
|
||||
# https://github.com/spencerlyon2/PlotlyJS.jl
|
||||
@@ -88,8 +88,7 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
|
||||
prepare_output(plt)
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
|
||||
if isijulia() && !_use_remote[]
|
||||
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
|
||||
else
|
||||
@@ -121,6 +120,12 @@ function _display(plt::Plot{PlotlyJSBackend})
|
||||
end
|
||||
end
|
||||
|
||||
@require WebIO begin
|
||||
function WebIO.render(plt::Plot{PlotlyJSBackend})
|
||||
prepare_output(plt)
|
||||
WebIO.render(plt.o)
|
||||
end
|
||||
end
|
||||
|
||||
function closeall(::PlotlyJSBackend)
|
||||
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
|
||||
|
||||
+175
-142
@@ -42,9 +42,10 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pyplot_seriestype = [
|
||||
:path, :steppre, :steppost, :shape,
|
||||
:path, :steppre, :steppost, :shape, :straightline,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
@@ -92,7 +93,7 @@ function _initialize_backend(::PyPlotBackend)
|
||||
pynp["seterr"](invalid="ignore")
|
||||
const pytransforms = PyPlot.pyimport("matplotlib.transforms")
|
||||
const pycollections = PyPlot.pyimport("matplotlib.collections")
|
||||
const pyart3d = PyPlot.pyimport("mpl_toolkits.mplot3d.art3d")
|
||||
const pyart3d = PyPlot.art3D
|
||||
|
||||
# "support" matplotlib v1.5
|
||||
const set_facecolor_sym = if PyPlot.version < v"2"
|
||||
@@ -135,6 +136,7 @@ py_color(s) = py_color(parse(Colorant, string(s)))
|
||||
py_color(c::Colorant) = (red(c), green(c), blue(c), alpha(c))
|
||||
py_color(cs::AVec) = map(py_color, cs)
|
||||
py_color(grad::ColorGradient) = py_color(grad.colors)
|
||||
py_color(c::Colorant, α) = py_color(plot_color(c, α))
|
||||
|
||||
function py_colormap(grad::ColorGradient)
|
||||
pyvals = [(z, py_color(grad[z])) for z in grad.values]
|
||||
@@ -215,6 +217,8 @@ function py_marker(marker::Symbol)
|
||||
marker == :hexagon && return "h"
|
||||
marker == :octagon && return "8"
|
||||
marker == :pixel && return ","
|
||||
marker == :hline && return "_"
|
||||
marker == :vline && return "|"
|
||||
haskey(_shapes, marker) && return py_marker(_shapes[marker])
|
||||
|
||||
warn("Unknown marker $marker")
|
||||
@@ -390,15 +394,15 @@ function py_bbox_title(ax)
|
||||
bb
|
||||
end
|
||||
|
||||
function py_dpi_scale(plt::Plot{PyPlotBackend}, ptsz)
|
||||
ptsz * plt[:dpi] / DPI
|
||||
function py_thickness_scale(plt::Plot{PyPlotBackend}, ptsz)
|
||||
ptsz * plt[:thickness_scaling]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Create the window/figure for this backend.
|
||||
function _create_backend_figure(plt::Plot{PyPlotBackend})
|
||||
w,h = map(px2inch, plt[:size])
|
||||
w,h = map(px2inch, Tuple(s * plt[:dpi] / Plots.DPI for s in plt[:size]))
|
||||
|
||||
# # reuse the current figure?
|
||||
fig = if plt[:overwrite_figure]
|
||||
@@ -450,6 +454,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
# ax = getAxis(plt, series)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
if st == :straightline
|
||||
x, y = straightline_data(series)
|
||||
elseif st == :shape
|
||||
x, y = shape_data(series)
|
||||
end
|
||||
xyargs = (st in _3dTypes ? (x,y,z) : (x,y))
|
||||
|
||||
# handle zcolor and get c/cmap
|
||||
@@ -483,62 +492,52 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# for each plotting command, optionally build and add a series handle to the list
|
||||
|
||||
# line plot
|
||||
if st in (:path, :path3d, :steppre, :steppost)
|
||||
if series[:linewidth] > 0
|
||||
if series[:line_z] == nothing
|
||||
handle = ax[:plot](xyargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
color = py_linecolor(series),
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
linestyle = py_linestyle(st, series[:linestyle]),
|
||||
solid_capstyle = "round",
|
||||
drawstyle = py_stepstyle(st)
|
||||
)[1]
|
||||
push!(handles, handle)
|
||||
|
||||
else
|
||||
# multicolored line segments
|
||||
n = length(x) - 1
|
||||
# segments = Array(Any,n)
|
||||
segments = []
|
||||
kw = KW(
|
||||
:label => series[:label],
|
||||
:zorder => plt.n,
|
||||
:cmap => py_linecolormap(series),
|
||||
:linewidth => py_dpi_scale(plt, series[:linewidth]),
|
||||
:linestyle => py_linestyle(st, series[:linestyle]),
|
||||
:norm => pycolors["Normalize"](; extrakw...)
|
||||
)
|
||||
lz = _cycle(series[:line_z], 1:n)
|
||||
handle = if is3d(st)
|
||||
for rng in iter_segments(x, y, z)
|
||||
length(rng) < 2 && continue
|
||||
for i in rng[1:end-1]
|
||||
push!(segments, [(_cycle(x,i),_cycle(y,i),_cycle(z,i)),
|
||||
(_cycle(x,i+1),_cycle(y,i+1),_cycle(z,i+1))])
|
||||
end
|
||||
end
|
||||
|
||||
lc = pyart3d["Line3DCollection"](segments; kw...)
|
||||
lc[:set_array](lz)
|
||||
ax[:add_collection3d](lc, zs=z) #, zdir='y')
|
||||
lc
|
||||
else
|
||||
for rng in iter_segments(x, y)
|
||||
length(rng) < 2 && continue
|
||||
for i in rng[1:end-1]
|
||||
push!(segments, [(_cycle(x,i),_cycle(y,i)), (_cycle(x,i+1),_cycle(y,i+1))])
|
||||
end
|
||||
end
|
||||
|
||||
lc = pycollections["LineCollection"](segments; kw...)
|
||||
lc[:set_array](lz)
|
||||
ax[:add_collection](lc)
|
||||
lc
|
||||
if st in (:path, :path3d, :steppre, :steppost, :straightline)
|
||||
if maximum(series[:linewidth]) > 0
|
||||
segments = iter_segments(series)
|
||||
# TODO: check LineCollection alternative for speed
|
||||
# if length(segments) > 1 && (any(typeof(series[attr]) <: AbstractVector for attr in (:fillcolor, :fillalpha)) || series[:fill_z] != nothing) && !(typeof(series[:linestyle]) <: AbstractVector)
|
||||
# # multicolored line segments
|
||||
# n = length(segments)
|
||||
# # segments = Array(Any,n)
|
||||
# segments = []
|
||||
# kw = KW(
|
||||
# :label => series[:label],
|
||||
# :zorder => plt.n,
|
||||
# :cmap => py_linecolormap(series),
|
||||
# :linewidths => py_thickness_scale(plt, get_linewidth.(series, 1:n)),
|
||||
# :linestyle => py_linestyle(st, get_linestyle.(series)),
|
||||
# :norm => pycolors["Normalize"](; extrakw...)
|
||||
# )
|
||||
# lz = _cycle(series[:line_z], 1:n)
|
||||
# handle = if is3d(st)
|
||||
# line_segments = [[(x[j], y[j], z[j]) for j in rng] for rng in segments]
|
||||
# lc = pyart3d["Line3DCollection"](line_segments; kw...)
|
||||
# lc[:set_array](lz)
|
||||
# ax[:add_collection3d](lc, zs=z) #, zdir='y')
|
||||
# lc
|
||||
# else
|
||||
# line_segments = [[(x[j], y[j]) for j in rng] for rng in segments]
|
||||
# lc = pycollections["LineCollection"](line_segments; kw...)
|
||||
# lc[:set_array](lz)
|
||||
# ax[:add_collection](lc)
|
||||
# lc
|
||||
# end
|
||||
# push!(handles, handle)
|
||||
# else
|
||||
for (i, rng) in enumerate(iter_segments(series))
|
||||
handle = ax[:plot]((arg[rng] for arg in xyargs)...;
|
||||
label = i == 1 ? series[:label] : "",
|
||||
zorder = series[:series_plotindex],
|
||||
color = py_color(get_linecolor(series, i), get_linealpha(series, i)),
|
||||
linewidth = py_thickness_scale(plt, get_linewidth(series, i)),
|
||||
linestyle = py_linestyle(st, get_linestyle(series, i)),
|
||||
solid_capstyle = "round",
|
||||
drawstyle = py_stepstyle(st)
|
||||
)[1]
|
||||
push!(handles, handle)
|
||||
end
|
||||
push!(handles, handle)
|
||||
end
|
||||
# end
|
||||
|
||||
a = series[:arrow]
|
||||
if a != nothing && !is3d(st) # TODO: handle 3d later
|
||||
@@ -551,8 +550,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
:shrinkB => 0,
|
||||
:edgecolor => py_linecolor(series),
|
||||
:facecolor => py_linecolor(series),
|
||||
:linewidth => py_dpi_scale(plt, series[:linewidth]),
|
||||
:linestyle => py_linestyle(st, series[:linestyle]),
|
||||
:linewidth => py_thickness_scale(plt, get_linewidth(series)),
|
||||
:linestyle => py_linestyle(st, get_linestyle(series)),
|
||||
)
|
||||
add_arrows(x, y) do xyprev, xy
|
||||
ax[:annotate]("",
|
||||
@@ -571,12 +570,12 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
if series[:markershape] != :none && st in (:path, :scatter, :path3d,
|
||||
:scatter3d, :steppre, :steppost,
|
||||
:bar)
|
||||
if series[:marker_z] == nothing
|
||||
extrakw[:c] = py_color_fix(py_markercolor(series), x)
|
||||
markercolor = if any(typeof(series[arg]) <: AVec for arg in (:markercolor, :markeralpha)) || series[:marker_z] != nothing
|
||||
py_color(plot_color.(get_markercolor.(series, eachindex(x)), get_markeralpha.(series, eachindex(x))))
|
||||
else
|
||||
extrakw[:c] = convert(Vector{Float64}, series[:marker_z])
|
||||
extrakw[:cmap] = py_markercolormap(series)
|
||||
py_color(plot_color(series[:markercolor], series[:markeralpha]))
|
||||
end
|
||||
extrakw[:c] = py_color_fix(markercolor, x)
|
||||
xyargs = if st == :bar && !isvertical(series)
|
||||
(y, x)
|
||||
else
|
||||
@@ -590,19 +589,15 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
x,y = xyargs
|
||||
shapes = series[:markershape]
|
||||
msc = py_markerstrokecolor(series)
|
||||
lw = py_dpi_scale(plt, series[:markerstrokewidth])
|
||||
lw = py_thickness_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
|
||||
extrakw[:c] = _cycle(markercolor, i)
|
||||
|
||||
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),
|
||||
s = py_thickness_scale(plt, _cycle(series[:markersize],i) .^ 2),
|
||||
edgecolors = msc,
|
||||
linewidths = lw,
|
||||
extrakw...
|
||||
@@ -615,9 +610,9 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(series[:markershape]),
|
||||
s = py_dpi_scale(plt, series[:markersize] .^ 2),
|
||||
s = py_thickness_scale(plt, series[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(series),
|
||||
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
|
||||
linewidths = py_thickness_scale(plt, series[:markerstrokewidth]),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
@@ -629,7 +624,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
gridsize = series[:bins],
|
||||
linewidths = py_dpi_scale(plt, series[:linewidth]),
|
||||
linewidths = py_thickness_scale(plt, series[:linewidth]),
|
||||
edgecolors = py_linecolor(series),
|
||||
cmap = py_fillcolormap(series), # applies to the pcolorfast object
|
||||
extrakw...
|
||||
@@ -639,20 +634,34 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
if st in (:contour, :contour3d)
|
||||
z = transpose_z(series, z.surf)
|
||||
if typeof(x)<:Plots.Surface
|
||||
x = Plots.transpose_z(series, x.surf)
|
||||
end
|
||||
if typeof(y)<:Plots.Surface
|
||||
y = Plots.transpose_z(series, y.surf)
|
||||
end
|
||||
|
||||
if st == :contour3d
|
||||
extrakw[:extend3d] = true
|
||||
end
|
||||
|
||||
if typeof(series[:linecolor]) <: AbstractArray
|
||||
extrakw[:colors] = py_color.(series[:linecolor])
|
||||
else
|
||||
extrakw[:cmap] = py_linecolormap(series)
|
||||
end
|
||||
|
||||
# contour lines
|
||||
handle = ax[:contour](x, y, z, levelargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
linewidths = py_dpi_scale(plt, series[:linewidth]),
|
||||
linewidths = py_thickness_scale(plt, series[:linewidth]),
|
||||
linestyles = py_linestyle(st, series[:linestyle]),
|
||||
cmap = py_linecolormap(series),
|
||||
extrakw...
|
||||
)
|
||||
if series[:contour_labels] == true
|
||||
PyPlot.clabel(handle, handle[:levels])
|
||||
end
|
||||
push!(handles, handle)
|
||||
|
||||
# contour fills
|
||||
@@ -660,7 +669,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
handle = ax[:contourf](x, y, z, levelargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
cmap = py_fillcolormap(series),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
@@ -689,7 +697,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
zorder = series[:series_plotindex],
|
||||
rstride = series[:stride][1],
|
||||
cstride = series[:stride][2],
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
linewidth = py_thickness_scale(plt, series[:linewidth]),
|
||||
edgecolor = py_linecolor(series),
|
||||
extrakw...
|
||||
)
|
||||
@@ -720,7 +728,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_fillcolormap(series),
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
linewidth = py_thickness_scale(plt, series[:linewidth]),
|
||||
edgecolor = py_linecolor(series),
|
||||
extrakw...
|
||||
)
|
||||
@@ -732,11 +740,12 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
if st == :image
|
||||
# @show typeof(z)
|
||||
xmin, xmax = ignorenan_extrema(series[:x]); ymin, ymax = ignorenan_extrema(series[:y])
|
||||
img = Array(transpose_z(series, z.surf))
|
||||
z = if eltype(img) <: Colors.AbstractGray
|
||||
float(img)
|
||||
elseif eltype(img) <: Colorant
|
||||
map(c -> Float64[red(c),green(c),blue(c)], img)
|
||||
map(c -> Float64[red(c),green(c),blue(c),alpha(c)], img)
|
||||
else
|
||||
z # hopefully it's in a data format that will "just work" with imshow
|
||||
end
|
||||
@@ -744,7 +753,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_colormap([:black, :white]),
|
||||
vmin = 0.0,
|
||||
vmax = 1.0
|
||||
vmax = 1.0,
|
||||
extent = (xmin, xmax, ymax, ymin)
|
||||
)
|
||||
push!(handles, handle)
|
||||
|
||||
@@ -768,6 +778,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_fillcolormap(series),
|
||||
alpha = series[:fillalpha],
|
||||
# edgecolors = (series[:linewidth] > 0 ? py_linecolor(series) : "face"),
|
||||
extrakw...
|
||||
)
|
||||
@@ -776,16 +787,17 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
if st == :shape
|
||||
handle = []
|
||||
for (i,rng) in enumerate(iter_segments(x, y))
|
||||
for (i, rng) in enumerate(iter_segments(series))
|
||||
if length(rng) > 1
|
||||
path = pypath["Path"](hcat(x[rng], y[rng]))
|
||||
patches = pypatches["PathPatch"](
|
||||
path;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
edgecolor = py_color(_cycle(series[:linecolor], i)),
|
||||
facecolor = py_color(_cycle(series[:fillcolor], i)),
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
edgecolor = py_color(get_linecolor(series, i), get_linealpha(series, i)),
|
||||
facecolor = py_color(get_fillcolor(series, i), get_fillalpha(series, i)),
|
||||
linewidth = py_thickness_scale(plt, get_linewidth(series, i)),
|
||||
linestyle = py_linestyle(st, get_linestyle(series, i)),
|
||||
fill = true
|
||||
)
|
||||
push!(handle, ax[:add_patch](patches))
|
||||
@@ -817,24 +829,26 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# handle area filling
|
||||
fillrange = series[:fillrange]
|
||||
if fillrange != nothing && st != :contour
|
||||
f, dim1, dim2 = if isvertical(series)
|
||||
:fill_between, x, y
|
||||
else
|
||||
:fill_betweenx, y, x
|
||||
end
|
||||
n = length(dim1)
|
||||
args = if typeof(fillrange) <: Union{Real, AVec}
|
||||
dim1, expand_data(fillrange, n), dim2
|
||||
elseif is_2tuple(fillrange)
|
||||
dim1, expand_data(fillrange[1], n), expand_data(fillrange[2], n)
|
||||
end
|
||||
for (i, rng) in enumerate(iter_segments(series))
|
||||
f, dim1, dim2 = if isvertical(series)
|
||||
:fill_between, x[rng], y[rng]
|
||||
else
|
||||
:fill_betweenx, y[rng], x[rng]
|
||||
end
|
||||
n = length(dim1)
|
||||
args = if typeof(fillrange) <: Union{Real, AVec}
|
||||
dim1, _cycle(fillrange, rng), dim2
|
||||
elseif is_2tuple(fillrange)
|
||||
dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng)
|
||||
end
|
||||
|
||||
handle = ax[f](args..., trues(n), false, py_fillstepstyle(st);
|
||||
zorder = series[:series_plotindex],
|
||||
facecolor = py_fillcolor(series),
|
||||
linewidths = 0
|
||||
)
|
||||
push!(handles, handle)
|
||||
handle = ax[f](args..., trues(n), false, py_fillstepstyle(st);
|
||||
zorder = series[:series_plotindex],
|
||||
facecolor = py_color(get_fillcolor(series, i), get_fillalpha(series, i)),
|
||||
linewidths = 0
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
end
|
||||
|
||||
# this is all we need to add the series_annotations text
|
||||
@@ -942,9 +956,9 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
fig = plt.o
|
||||
fig[:clear]()
|
||||
dpi = plt[:dpi]
|
||||
fig[:set_size_inches](w/dpi, h/dpi, forward = true)
|
||||
fig[:set_size_inches](w/DPI, h/DPI, forward = true)
|
||||
fig[set_facecolor_sym](py_color(plt[:background_color_outside]))
|
||||
fig[:set_dpi](dpi)
|
||||
fig[:set_dpi](plt[:dpi])
|
||||
|
||||
# resize the window
|
||||
PyPlot.plt[:get_current_fig_manager]()[:resize](w, h)
|
||||
@@ -968,7 +982,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
py_add_annotations(sp, ann...)
|
||||
py_add_annotations(sp, locate_annotation(sp, ann...)...)
|
||||
end
|
||||
|
||||
# title
|
||||
@@ -982,7 +996,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
:title
|
||||
end
|
||||
ax[func][:set_text](sp[:title])
|
||||
ax[func][:set_fontsize](py_dpi_scale(plt, sp[:titlefontsize]))
|
||||
ax[func][:set_fontsize](py_thickness_scale(plt, sp[:titlefontsize]))
|
||||
ax[func][:set_family](sp[:titlefontfamily])
|
||||
ax[func][:set_color](py_color(sp[:titlefontcolor]))
|
||||
# ax[:set_title](sp[:title], loc = loc)
|
||||
@@ -1002,6 +1016,19 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
kw[:ticks] = locator
|
||||
kw[:format] = formatter
|
||||
kw[:boundaries] = vcat(0, kw[:values] + 0.5)
|
||||
elseif any(colorbar_series[attr] != nothing for attr in (:line_z, :fill_z, :marker_z))
|
||||
cmin, cmax = get_clims(sp)
|
||||
norm = pycolors[:Normalize](vmin = cmin, vmax = cmax)
|
||||
f = if colorbar_series[:line_z] != nothing
|
||||
py_linecolormap
|
||||
elseif colorbar_series[:fill_z] != nothing
|
||||
py_fillcolormap
|
||||
else
|
||||
py_markercolormap
|
||||
end
|
||||
cmap = pycmap[:ScalarMappable](norm = norm, cmap = f(colorbar_series))
|
||||
cmap[:set_array]([])
|
||||
handle = cmap
|
||||
end
|
||||
|
||||
# create and store the colorbar object (handle) and the axis that it is drawn on.
|
||||
@@ -1009,9 +1036,9 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
fig = plt.o
|
||||
cbax = fig[:add_axes]([0.8,0.1,0.03,0.8], label = string(gensym()))
|
||||
cb = fig[:colorbar](handle; cax = cbax, kw...)
|
||||
cb[:set_label](sp[:colorbar_title],size=py_dpi_scale(plt, sp[:yaxis][:guidefontsize]),family=sp[:yaxis][:guidefontfamily], color = py_color(sp[:yaxis][:guidefontcolor]))
|
||||
cb[:set_label](sp[:colorbar_title],size=py_thickness_scale(plt, sp[:yaxis][:guidefontsize]),family=sp[:yaxis][:guidefontfamily], color = py_color(sp[:yaxis][:guidefontcolor]))
|
||||
for lab in cb[:ax][:yaxis][:get_ticklabels]()
|
||||
lab[:set_fontsize](py_dpi_scale(plt, sp[:yaxis][:tickfontsize]))
|
||||
lab[:set_fontsize](py_thickness_scale(plt, sp[:yaxis][:tickfontsize]))
|
||||
lab[:set_family](sp[:yaxis][:tickfontfamily])
|
||||
lab[:set_color](py_color(sp[:yaxis][:tickfontcolor]))
|
||||
end
|
||||
@@ -1021,12 +1048,14 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
|
||||
# framestyle
|
||||
if !ispolar(sp) && !is3d(sp)
|
||||
ax[:spines]["left"][:set_linewidth](py_thickness_scale(plt, 1))
|
||||
ax[:spines]["bottom"][:set_linewidth](py_thickness_scale(plt, 1))
|
||||
if sp[:framestyle] == :semi
|
||||
intensity = 0.5
|
||||
ax[:spines]["right"][:set_alpha](intensity)
|
||||
ax[:spines]["top"][:set_alpha](intensity)
|
||||
ax[:spines]["right"][:set_linewidth](intensity)
|
||||
ax[:spines]["top"][:set_linewidth](intensity)
|
||||
ax[:spines]["right"][:set_linewidth](py_thickness_scale(plt, intensity))
|
||||
ax[:spines]["top"][:set_linewidth](py_thickness_scale(plt, intensity))
|
||||
elseif sp[:framestyle] in (:axes, :origin)
|
||||
ax[:spines]["right"][:set_visible](false)
|
||||
ax[:spines]["top"][:set_visible](false)
|
||||
@@ -1039,8 +1068,8 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
spine[:set_visible](false)
|
||||
end
|
||||
if sp[:framestyle] == :zerolines
|
||||
ax[:axhline](y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = 0.75)
|
||||
ax[:axvline](x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = 0.75)
|
||||
ax[:axhline](y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
|
||||
ax[:axvline](x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1058,7 +1087,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
pyaxis[Symbol(:tick_, pos)]() # the tick labels
|
||||
end
|
||||
py_set_scale(ax, axis)
|
||||
py_set_lims(ax, axis)
|
||||
axis[:ticks] != :native ? py_set_lims(ax, axis) : nothing
|
||||
if ispolar(sp) && letter == :y
|
||||
ax[:set_rlabel_position](90)
|
||||
end
|
||||
@@ -1067,16 +1096,16 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
if sp[:framestyle] == :origin && length(ticks) > 1
|
||||
ticks[2][ticks[1] .== 0] = ""
|
||||
end
|
||||
py_set_ticks(ax, ticks, letter)
|
||||
axis[:ticks] != :native ? py_set_ticks(ax, ticks, letter) : nothing
|
||||
pyaxis[:set_tick_params](direction = axis[:tick_direction] == :out ? "out" : "in")
|
||||
ax[Symbol("set_", letter, "label")](axis[:guide])
|
||||
if get(axis.d, :flip, false)
|
||||
ax[Symbol("invert_", letter, "axis")]()
|
||||
end
|
||||
pyaxis[:label][:set_fontsize](py_dpi_scale(plt, axis[:guidefontsize]))
|
||||
pyaxis[:label][:set_fontsize](py_thickness_scale(plt, axis[:guidefontsize]))
|
||||
pyaxis[:label][:set_family](axis[:guidefontfamily])
|
||||
for lab in ax[Symbol("get_", letter, "ticklabels")]()
|
||||
lab[:set_fontsize](py_dpi_scale(plt, axis[:tickfontsize]))
|
||||
lab[:set_fontsize](py_thickness_scale(plt, axis[:tickfontsize]))
|
||||
lab[:set_family](axis[:tickfontfamily])
|
||||
lab[:set_rotation](axis[:rotation])
|
||||
end
|
||||
@@ -1085,7 +1114,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
pyaxis[:grid](true,
|
||||
color = fgcolor,
|
||||
linestyle = py_linestyle(:line, axis[:gridstyle]),
|
||||
linewidth = axis[:gridlinewidth],
|
||||
linewidth = py_thickness_scale(plt, axis[:gridlinewidth]),
|
||||
alpha = axis[:gridalpha])
|
||||
ax[:set_axisbelow](true)
|
||||
else
|
||||
@@ -1137,6 +1166,10 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
# this sets the bg color inside the grid
|
||||
ax[set_facecolor_sym](py_color(sp[:background_color_inside]))
|
||||
|
||||
# link axes
|
||||
x_ax_link, y_ax_link = sp[:xaxis].sps[1].o, sp[:yaxis].sps[1].o
|
||||
ax != x_ax_link && ax[:get_shared_x_axes]()[:join](ax, sp[:xaxis].sps[1].o)
|
||||
ax != y_ax_link && ax[:get_shared_y_axes]()[:join](ax, sp[:yaxis].sps[1].o)
|
||||
end
|
||||
py_drawfig(fig)
|
||||
end
|
||||
@@ -1178,7 +1211,9 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
rightpad += sp[:right_margin]
|
||||
bottompad += sp[:bottom_margin]
|
||||
|
||||
sp.minpad = (leftpad, toppad, rightpad, bottompad)
|
||||
dpi_factor = Plots.DPI / sp.plt[:dpi]
|
||||
|
||||
sp.minpad = Tuple(dpi_factor .* [leftpad, toppad, rightpad, bottompad])
|
||||
end
|
||||
|
||||
|
||||
@@ -1199,7 +1234,7 @@ function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
|
||||
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
|
||||
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
|
||||
rotation = val.font.rotation * 180 / π,
|
||||
size = py_dpi_scale(sp.plt, val.font.pointsize),
|
||||
size = py_thickness_scale(sp.plt, val.font.pointsize),
|
||||
zorder = 999
|
||||
)
|
||||
end
|
||||
@@ -1228,18 +1263,19 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
# add a line/marker and a label
|
||||
push!(handles, if series[:seriestype] == :shape || series[:fillrange] != nothing
|
||||
pypatches[:Patch](
|
||||
edgecolor = py_color(_cycle(series[:linecolor],1)),
|
||||
facecolor = py_color(_cycle(series[:fillcolor],1)),
|
||||
linewidth = py_dpi_scale(plt, clamp(series[:linewidth], 0, 5)),
|
||||
edgecolor = py_color(get_linecolor(series), get_linealpha(series)),
|
||||
facecolor = py_color(get_fillcolor(series), get_fillalpha(series)),
|
||||
linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)),
|
||||
linestyle = py_linestyle(series[:seriestype], get_linestyle(series))
|
||||
)
|
||||
elseif series[:seriestype] == :path
|
||||
elseif series[:seriestype] in (:path, :straightline)
|
||||
PyPlot.plt[:Line2D]((0,1),(0,0),
|
||||
color = py_color(_cycle(series[:fillcolor],1)),
|
||||
linewidth = py_dpi_scale(plt, clamp(series[:linewidth], 0, 5)),
|
||||
linestyle = py_linestyle(:path,series[:linestyle]),
|
||||
color = py_color(get_linecolor(series), get_linealpha(series)),
|
||||
linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)),
|
||||
linestyle = py_linestyle(:path, get_linestyle(series)),
|
||||
marker = py_marker(series[:markershape]),
|
||||
markeredgecolor = py_markerstrokecolor(series),
|
||||
markerfacecolor = py_markercolor(series)
|
||||
markeredgecolor = py_color(get_markerstrokecolor(series), get_markerstrokealpha(series)),
|
||||
markerfacecolor = series[:marker_z] == nothing ? py_color(get_markercolor(series), get_markeralpha(series)) : py_color(series[:markercolor][0.5])
|
||||
)
|
||||
else
|
||||
series[:serieshandle][1]
|
||||
@@ -1254,23 +1290,19 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
labels,
|
||||
loc = get(_pyplot_legend_pos, leg, "best"),
|
||||
scatterpoints = 1,
|
||||
fontsize = py_dpi_scale(plt, sp[:legendfontsize])
|
||||
# family = sp[:legendfont].family
|
||||
# framealpha = 0.6
|
||||
fontsize = py_thickness_scale(plt, sp[:legendfontsize]),
|
||||
facecolor = py_color(sp[:background_color_legend]),
|
||||
edgecolor = py_color(sp[:foreground_color_legend]),
|
||||
framealpha = alpha(plot_color(sp[:background_color_legend])),
|
||||
)
|
||||
frame = leg[:get_frame]()
|
||||
frame[:set_linewidth](py_thickness_scale(plt, 1))
|
||||
leg[:set_zorder](1000)
|
||||
sp[:legendtitle] != nothing && leg[:set_title](sp[:legendtitle])
|
||||
|
||||
fgcolor = py_color(sp[:foreground_color_legend])
|
||||
lfcolor = py_color(sp[:legendfontcolor])
|
||||
for txt in leg[:get_texts]()
|
||||
PyPlot.plt[:setp](txt, color = lfcolor, family = sp[:legendfontfamily])
|
||||
PyPlot.plt[:setp](txt, color = py_color(sp[:legendfontcolor]), family = sp[:legendfontfamily])
|
||||
end
|
||||
|
||||
# set some legend properties
|
||||
frame = leg[:get_frame]()
|
||||
frame[set_facecolor_sym](py_color(sp[:background_color_legend]))
|
||||
frame[:set_edgecolor](fgcolor)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1293,7 +1325,8 @@ function _update_plot_object(plt::Plot{PyPlotBackend})
|
||||
if haskey(sp.attr, :cbar_ax)
|
||||
cbw = sp.attr[:cbar_width]
|
||||
# this is the bounding box of just the colors of the colorbar (not labels)
|
||||
has_toplabel = sp[:zaxis][:extrema].emax >= 1e7
|
||||
ex = sp[:zaxis][:extrema]
|
||||
has_toplabel = !(1e-7 < max(abs(ex.emax), abs(ex.emin)) < 1e7)
|
||||
cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox) + (has_toplabel ? 4mm : 2mm), _cbar_width-1mm, height(sp.bbox) - (has_toplabel ? 6mm : 4mm))
|
||||
pcts = bbox_to_pcts(cb_bbox, figw, figh)
|
||||
sp.attr[:cbar_ax][:set_position](pcts)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# https://github.com/Evizero/UnicodePlots.jl
|
||||
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "unicodeplots.jl"))
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "unicodeplots.jl"))
|
||||
end
|
||||
|
||||
const _unicodeplots_attr = merge_with_base_supported([
|
||||
@@ -17,7 +17,7 @@ const _unicodeplots_attr = merge_with_base_supported([
|
||||
:guide, :lims,
|
||||
])
|
||||
const _unicodeplots_seriestype = [
|
||||
:path, :scatter,
|
||||
:path, :scatter, :straightline,
|
||||
# :bar,
|
||||
:shape,
|
||||
:histogram2d,
|
||||
@@ -142,7 +142,7 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
return
|
||||
end
|
||||
|
||||
if st == :path
|
||||
if st in (:path, :straightline)
|
||||
func = UnicodePlots.lineplot!
|
||||
elseif st == :scatter || d[:markershape] != :none
|
||||
func = UnicodePlots.scatterplot!
|
||||
@@ -155,14 +155,20 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
end
|
||||
|
||||
# get the series data and label
|
||||
x, y = [collect(float(d[s])) for s in (:x, :y)]
|
||||
x, y = if st == :straightline
|
||||
straightline_data(d)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
else
|
||||
[collect(float(d[s])) for s in (:x, :y)]
|
||||
end
|
||||
label = addlegend ? d[:label] : ""
|
||||
|
||||
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
|
||||
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
|
||||
|
||||
# add the series
|
||||
x, y = Plots.unzip(collect(filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
|
||||
x, y = Plots.unzip(collect(Base.Iterators.filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
|
||||
func(o, x, y; color = color, name = label)
|
||||
end
|
||||
|
||||
@@ -206,7 +212,7 @@ end
|
||||
|
||||
function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
|
||||
unicodeplots_rebuild(plt)
|
||||
map(show, plt.o)
|
||||
foreach(x -> show(io, x), plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr
|
||||
<html>
|
||||
<head>
|
||||
<title>$title</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
$(html_head(plt))
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -348,6 +348,7 @@ PlotText(str) = PlotText(string(str), font())
|
||||
Create a PlotText object wrapping a string with font info, for plot annotations
|
||||
"""
|
||||
text(t::PlotText) = t
|
||||
text(t::PlotText, font::Font) = PlotText(t.str, font)
|
||||
text(str::AbstractString, f::Font) = PlotText(str, f)
|
||||
function text(str, args...)
|
||||
PlotText(string(str), font(args...))
|
||||
@@ -535,6 +536,54 @@ annotations(anns::AVec) = anns
|
||||
annotations(anns) = Any[anns]
|
||||
annotations(sa::SeriesAnnotations) = sa
|
||||
|
||||
# Expand arrays of coordinates, positions and labels into induvidual annotations
|
||||
# and make sure labels are of type PlotText
|
||||
function process_annotation(sp::Subplot, xs, ys, labs, font = font())
|
||||
anns = []
|
||||
labs = makevec(labs)
|
||||
for i in 1:max(length(xs), length(ys), length(labs))
|
||||
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
|
||||
if lab == :auto
|
||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||
else
|
||||
push!(anns, (x, y, isa(lab, PlotText) ? lab : text(lab, font)))
|
||||
end
|
||||
end
|
||||
anns
|
||||
end
|
||||
function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol}, labs, font = font())
|
||||
anns = []
|
||||
positions, labs = makevec(positions), makevec(labs)
|
||||
for i in 1:max(length(positions), length(labs))
|
||||
pos, lab = _cycle(positions, i), _cycle(labs, i)
|
||||
pos = get(_positionAliases, pos, pos)
|
||||
if lab == :auto
|
||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||
else
|
||||
push!(anns, (pos, isa(lab, PlotText) ? lab : text(lab, font)))
|
||||
end
|
||||
end
|
||||
anns
|
||||
end
|
||||
|
||||
# Give each annotation coordinates based on specified position
|
||||
function locate_annotation(sp::Subplot, pos::Symbol, lab::PlotText)
|
||||
position_multiplier = Dict{Symbol, Tuple{Float64,Float64}}(
|
||||
:topleft => (0.1, 0.9),
|
||||
:topcenter => (0.5, 0.9),
|
||||
:topright => (0.9, 0.9),
|
||||
:bottomleft => (0.1, 0.1),
|
||||
:bottomcenter => (0.5, 0.1),
|
||||
:bottomright => (0.9, 0.1),
|
||||
)
|
||||
xmin, xmax = ignorenan_extrema(sp[:xaxis])
|
||||
ymin, ymax = ignorenan_extrema(sp[:yaxis])
|
||||
x, y = (xmin, ymin).+ position_multiplier[pos].* (xmax - xmin, ymax - ymin)
|
||||
(x, y, lab)
|
||||
end
|
||||
locate_annotation(sp::Subplot, x, y, label::PlotText) = (x, y, label)
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
"type which represents z-values for colors and sizes (and anything else that might come up)"
|
||||
|
||||
+68
-39
@@ -157,25 +157,16 @@ end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _mimeformats = Dict(
|
||||
"application/eps" => "eps",
|
||||
"image/eps" => "eps",
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg",
|
||||
"text/plain" => "txt",
|
||||
"application/x-tex" => "tex",
|
||||
)
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
:pyplot => :png,
|
||||
:unicodeplots => :txt,
|
||||
:glvisualize => :png
|
||||
:glvisualize => :png,
|
||||
:plotlyjs => :html,
|
||||
:plotly => :html
|
||||
)
|
||||
|
||||
# a backup for html... passes to svg or png depending on the html_output_format arg
|
||||
function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
@@ -189,26 +180,32 @@ function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
elseif output_type == :txt
|
||||
show(io, MIME("text/plain"), plt)
|
||||
else
|
||||
error("only png or svg allowed. got: $output_type")
|
||||
error("only png or svg allowed. got: $(repr(output_type))")
|
||||
end
|
||||
end
|
||||
|
||||
function _show(io::IO, m, plt::Plot{B}) where B
|
||||
# Base.show_backtrace(STDOUT, backtrace())
|
||||
warn("_show is not defined for this backend. m=", string(m))
|
||||
# delegate mimewritable (showable on julia 0.7) to _show instead
|
||||
function Base.mimewritable(m::M, plt::P) where {M<:MIME, P<:Plot}
|
||||
return method_exists(_show, Tuple{IO, M, P})
|
||||
end
|
||||
|
||||
function _display(plt::Plot)
|
||||
warn("_display is not defined for this backend.")
|
||||
end
|
||||
|
||||
# for writing to io streams... first prepare, then callback
|
||||
for mime in keys(_mimeformats)
|
||||
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot{B}) where B
|
||||
for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml",
|
||||
"application/eps", "application/pdf", "application/postscript",
|
||||
"application/x-tex")
|
||||
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
|
||||
prepare_output(plt)
|
||||
_show(io, m, plt)
|
||||
end
|
||||
end
|
||||
|
||||
# default text/plain for all backends
|
||||
_show(io::IO, ::MIME{Symbol("text/plain")}, plt::Plot) = show(io, plt)
|
||||
|
||||
"Close all open gui windows of the current backend"
|
||||
closeall() = closeall(backend())
|
||||
|
||||
@@ -216,9 +213,10 @@ closeall() = closeall(backend())
|
||||
# ---------------------------------------------------------
|
||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||
|
||||
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
||||
if is_installed("FileIO")
|
||||
@eval import FileIO
|
||||
function _show(io::IO, ::MIME"image/png", plt::Plot)
|
||||
function _show(io::IO, ::MIME"image/png", plt::Plot{<:PDFBackends})
|
||||
fn = tempname()
|
||||
|
||||
# first save a pdf file
|
||||
@@ -256,29 +254,56 @@ end
|
||||
# IJulia
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _ijulia_output = String["text/html"]
|
||||
|
||||
@require IJulia begin
|
||||
if IJulia.inited
|
||||
export set_ijulia_output
|
||||
|
||||
function set_ijulia_output(mimestr::AbstractString)
|
||||
# info("Setting IJulia output format to $mimestr")
|
||||
global _ijulia_output
|
||||
_ijulia_output[1] = mimestr
|
||||
"""
|
||||
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
||||
|
||||
The default is nothing, except for plotly based backends, where it
|
||||
adds data for `application/vnd.plotly.v1+json` that is used in
|
||||
frontends like jupyterlab and nteract.
|
||||
"""
|
||||
_extra_mime_info!(plt::Plot, out::Dict) = out
|
||||
function _extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = JSON.lower(plt.o)
|
||||
out
|
||||
end
|
||||
|
||||
function _extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out
|
||||
end
|
||||
|
||||
function IJulia.display_dict(plt::Plot)
|
||||
global _ijulia_output
|
||||
Dict{String, String}(_ijulia_output[1] => sprint(show, _ijulia_output[1], plt))
|
||||
end
|
||||
|
||||
# default text/plain passes to html... handles Interact issues
|
||||
function Base.show(io::IO, m::MIME"text/plain", plt::Plot)
|
||||
show(io, MIME("text/html"), plt)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
end
|
||||
out = Dict()
|
||||
if output_type == :txt
|
||||
mime = "text/plain"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
elseif output_type == :png
|
||||
mime = "image/png"
|
||||
out[mime] = base64encode(show, MIME(mime), plt)
|
||||
elseif output_type == :svg
|
||||
mime = "image/svg+xml"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
elseif output_type == :html
|
||||
mime = "text/html"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
else
|
||||
error("Unsupported output type $output_type")
|
||||
end
|
||||
_extra_mime_info!(plt, out)
|
||||
out
|
||||
end
|
||||
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
set_ijulia_output("text/html")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -291,9 +316,6 @@ end
|
||||
if Juno.isactive()
|
||||
Media.media(Plot, Media.Plot)
|
||||
|
||||
|
||||
_show(io::IO, m::MIME"text/plain", plt::Plot{B}) where {B} = print(io, "Plot{$B}()")
|
||||
|
||||
function Juno.render(e::Juno.Editor, plt::Plot)
|
||||
Juno.render(e, nothing)
|
||||
end
|
||||
@@ -302,13 +324,20 @@ end
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot)
|
||||
# temporarily overwrite size to be Atom.plotsize
|
||||
sz = plt[:size]
|
||||
dpi = plt[:dpi]
|
||||
thickness_scaling = plt[:thickness_scaling]
|
||||
jsize = Juno.plotsize()
|
||||
jsize[1] == 0 && (jsize[1] = 400)
|
||||
jsize[2] == 0 && (jsize[2] = 500)
|
||||
|
||||
plt[:size] = jsize
|
||||
scale = minimum(jsize[i] / sz[i] for i in 1:2)
|
||||
plt[:size] = (s * scale for s in sz)
|
||||
plt[:dpi] = Plots.DPI
|
||||
plt[:thickness_scaling] *= scale
|
||||
Juno.render(pane, HTML(stringmime(MIME("text/html"), plt)))
|
||||
plt[:size] = sz
|
||||
plt[:dpi] = dpi
|
||||
plt[:thickness_scaling] = thickness_scaling
|
||||
end
|
||||
# special handling for PlotlyJS
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot{PlotlyJSBackend})
|
||||
|
||||
+5
-5
@@ -327,7 +327,7 @@ end
|
||||
|
||||
function _override_seriestype_check(d::KW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !is3d(st)
|
||||
if !is3d(st) && !(st in (:contour,:contour3d))
|
||||
z = d[:z]
|
||||
if !isa(z, Void) && (size(d[:x]) == size(d[:y]) == size(z))
|
||||
st = (st == :scatter ? :scatter3d : :path3d)
|
||||
@@ -357,10 +357,9 @@ end
|
||||
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
w, h = size(d[:z])
|
||||
expand_extrema!(sp[:xaxis], (0,w))
|
||||
expand_extrema!(sp[:yaxis], (0,h))
|
||||
sp[:yaxis].d[:flip] = true
|
||||
xmin, xmax = ignorenan_extrema(d[:x]); ymin, ymax = ignorenan_extrema(d[:y])
|
||||
expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
end
|
||||
@@ -399,6 +398,7 @@ function _process_seriesrecipe(plt::Plot, d::KW)
|
||||
sp = _prepare_subplot(plt, d)
|
||||
_prepare_annotations(sp, d)
|
||||
_expand_subplot_extrema(sp, d, st)
|
||||
_update_series_attributes!(d, plt, sp)
|
||||
_add_the_series(plt, sp, d)
|
||||
|
||||
else
|
||||
|
||||
+6
-4
@@ -102,8 +102,13 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
# create the layout and initialize the subplots
|
||||
# create the layout
|
||||
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
# initialize the subplots
|
||||
cmdidx = 1
|
||||
for (idx, sp) in enumerate(plt.subplots)
|
||||
_initialize_subplot(plt, sp)
|
||||
@@ -127,9 +132,6 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
_update_subplot_args(plt, sp, d, idx, false)
|
||||
end
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
# finish up
|
||||
current(plt)
|
||||
_do_plot_show(plt, get(d, :show, default(:show)))
|
||||
|
||||
+88
-27
@@ -79,28 +79,50 @@ function hvline_limits(axis::Axis)
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
xmin, xmax = hvline_limits(plotattributes[:subplot][:xaxis])
|
||||
n = length(y)
|
||||
newx = repmat(Float64[xmin, xmax, NaN], n)
|
||||
newx = repmat(Float64[-1, 1, NaN], n)
|
||||
newy = vec(Float64[yi for i=1:3,yi=y])
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
seriestype := :straightline
|
||||
()
|
||||
end
|
||||
@deps hline path
|
||||
@deps hline straightline
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
ymin, ymax = hvline_limits(plotattributes[:subplot][:yaxis])
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i=1:3,yi=y])
|
||||
newy = repmat(Float64[ymin, ymax, NaN], n)
|
||||
newy = repmat(Float64[-1, 1, NaN], n)
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
seriestype := :straightline
|
||||
()
|
||||
end
|
||||
@deps vline path
|
||||
@deps vline straightline
|
||||
|
||||
@recipe function f(::Type{Val{:hspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
newy = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :shape
|
||||
()
|
||||
end
|
||||
@deps hspan shape
|
||||
|
||||
@recipe function f(::Type{Val{:vspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :shape
|
||||
()
|
||||
end
|
||||
@deps vspan shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# path and scatter
|
||||
@@ -124,27 +146,32 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# steps
|
||||
|
||||
function make_steps(x, y, st)
|
||||
make_steps(x, st) = x
|
||||
function make_steps(x::AbstractArray, st)
|
||||
n = length(x)
|
||||
n == 0 && return zeros(0),zeros(0)
|
||||
newx, newy = zeros(2n-1), zeros(2n-1)
|
||||
for i=1:n
|
||||
idx = 2i-1
|
||||
n == 0 && return zeros(0)
|
||||
newx = zeros(2n - 1)
|
||||
for i in 1:n
|
||||
idx = 2i - 1
|
||||
newx[idx] = x[i]
|
||||
newy[idx] = y[i]
|
||||
if i > 1
|
||||
newx[idx-1] = x[st == :steppre ? i-1 : i]
|
||||
newy[idx-1] = y[st == :steppre ? i : i-1]
|
||||
newx[idx - 1] = x[st == :pre ? i : i - 1]
|
||||
end
|
||||
end
|
||||
newx, newy
|
||||
return newx
|
||||
end
|
||||
make_steps(t::Tuple, st) = Tuple(make_steps(ti, st) for ti in t)
|
||||
|
||||
|
||||
# create a path from steps
|
||||
@recipe function f(::Type{Val{:steppre}}, x, y, z)
|
||||
plotattributes[:x], plotattributes[:y] = make_steps(x, y, :steppre)
|
||||
plotattributes[:x] = make_steps(x, :post)
|
||||
plotattributes[:y] = make_steps(y, :pre)
|
||||
seriestype := :path
|
||||
|
||||
# handle fillrange
|
||||
plotattributes[:fillrange] = make_steps(plotattributes[:fillrange], :pre)
|
||||
|
||||
# create a secondary series for the markers
|
||||
if plotattributes[:markershape] != :none
|
||||
@series begin
|
||||
@@ -163,9 +190,13 @@ end
|
||||
|
||||
# create a path from steps
|
||||
@recipe function f(::Type{Val{:steppost}}, x, y, z)
|
||||
plotattributes[:x], plotattributes[:y] = make_steps(x, y, :steppost)
|
||||
plotattributes[:x] = make_steps(x, :pre)
|
||||
plotattributes[:y] = make_steps(y, :post)
|
||||
seriestype := :path
|
||||
|
||||
# handle fillrange
|
||||
plotattributes[:fillrange] = make_steps(plotattributes[:fillrange], :post)
|
||||
|
||||
# create a secondary series for the markers
|
||||
if plotattributes[:markershape] != :none
|
||||
@series begin
|
||||
@@ -308,7 +339,11 @@ end
|
||||
# compute half-width of bars
|
||||
bw = plotattributes[:bar_width]
|
||||
hw = if bw == nothing
|
||||
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
|
||||
if nx > 1
|
||||
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
|
||||
else
|
||||
0.5 * _bar_width
|
||||
end
|
||||
else
|
||||
Float64[0.5_cycle(bw,i) for i=1:length(procx)]
|
||||
end
|
||||
@@ -354,6 +389,34 @@ end
|
||||
end
|
||||
@deps bar shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plots Heatmap
|
||||
@recipe function f(::Type{Val{:plots_heatmap}}, x, y, z)
|
||||
xe, ye = heatmap_edges(x), heatmap_edges(y)
|
||||
m, n = size(z.surf)
|
||||
x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n)
|
||||
fz = zeros(m * n)
|
||||
for i in 1:m # y
|
||||
for j in 1:n # x
|
||||
k = (j - 1) * m + i
|
||||
inds = (6 * (k - 1) + 1):(6 * k - 1)
|
||||
x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]]
|
||||
y_pts[inds] .= [ye[i], ye[i], ye[i + 1], ye[i + 1], ye[i]]
|
||||
fz[k] = z.surf[i, j]
|
||||
end
|
||||
end
|
||||
ensure_gradient!(plotattributes, :fillcolor, :fillalpha)
|
||||
fill_z := fz
|
||||
line_z := fz
|
||||
x := x_pts
|
||||
y := y_pts
|
||||
z := nothing
|
||||
seriestype := :shape
|
||||
label := ""
|
||||
widen --> false
|
||||
()
|
||||
end
|
||||
@deps plots_heatmap shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
@@ -527,7 +590,7 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
||||
|
||||
# The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation
|
||||
nd = n_samples^(1/(2+N))
|
||||
nd = N == 2 ? nd / (1-cor(first(vs), last(vs))^2)^(3//8) : nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
nd = N == 2 ? min(n_samples^(1/(2+N)), nd / (1-cor(first(vs), last(vs))^2)^(3//8)) : nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
|
||||
v = vs[dim]
|
||||
|
||||
@@ -986,23 +1049,21 @@ end
|
||||
# -------------------------------------------------
|
||||
|
||||
"Adds a+bx... straight line over the current plot, without changing the axis limits"
|
||||
function abline!(plt::Plot, a, b; kw...)
|
||||
xl, yl = xlims(plt), ylims(plt)
|
||||
x1, x2 = max(xl[1], (yl[1] - b)/a), min(xl[2], (yl[2] - b)/a)
|
||||
plot!(plt, x -> b + a*x, x1, x2; kw...)
|
||||
end
|
||||
abline!(plt::Plot, a, b; kw...) = plot!(plt, [0, 1], [b, b+a]; seriestype = :straightline, kw...)
|
||||
|
||||
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
# Dates
|
||||
# Dates & Times
|
||||
|
||||
dateformatter(dt) = string(Date(Dates.UTD(dt)))
|
||||
datetimeformatter(dt) = string(DateTime(Dates.UTM(dt)))
|
||||
timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> Dates.value(dt), dateformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> Dates.value(dt), datetimeformatter)
|
||||
@recipe f(::Type{Dates.Time}, t::Dates.Time) = (t -> Dates.value(t), timeformatter)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
+44
-3
@@ -8,7 +8,7 @@
|
||||
|
||||
const 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))
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(d, :seriestype, :none))
|
||||
|
||||
# unknown
|
||||
convertToAnyVector(x, d::KW) = error("No user recipe defined for $(typeof(x))")
|
||||
@@ -46,7 +46,7 @@ convertToAnyVector(v::Volume, d::KW) = Any[v], nothing
|
||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
|
||||
# # dates
|
||||
# convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
||||
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)
|
||||
@@ -321,10 +321,12 @@ end
|
||||
n, m = size(mat)
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
fillcolor --> ColorGradient([:black, :white])
|
||||
SliceIt, 1:m, 1:n, Surface(convert(Matrix{Float64}, mat))
|
||||
end
|
||||
@@ -337,10 +339,12 @@ end
|
||||
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(z)
|
||||
end
|
||||
@@ -465,6 +469,38 @@ end
|
||||
SliceIt, x, y, Surface(z)
|
||||
end
|
||||
|
||||
# # images - grays
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, x, y, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
fillcolor --> ColorGradient([:black, :white])
|
||||
SliceIt, x, y, Surface(convert(Matrix{Float64}, mat))
|
||||
end
|
||||
end
|
||||
|
||||
# # images - colors
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, x, y, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, x, y, Surface(z)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
@@ -548,9 +584,14 @@ end
|
||||
splittable_kw(key, val, lengthGroup) = false
|
||||
splittable_kw(key, val::AbstractArray, lengthGroup) = (key != :group) && size(val,1) == lengthGroup
|
||||
splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, lengthGroup))
|
||||
splittable_kw(key, val::SeriesAnnotations, lengthGroup) = splittable_kw(key, val.strs, lengthGroup)
|
||||
|
||||
split_kw(key, val::AbstractArray, indices) = val[indices, fill(Colon(), ndims(val)-1)...]
|
||||
split_kw(key, val::Tuple, indices) = Tuple(split_kw(key, v, indices) for v in val)
|
||||
function split_kw(key, val::SeriesAnnotations, indices)
|
||||
split_strs = split_kw(key, val.strs, indices)
|
||||
return SeriesAnnotations(split_strs, val.font, val.baseshape, val.scalefactor)
|
||||
end
|
||||
|
||||
function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1])
|
||||
y_mat = Array{promote_type(eltype(y), typeof(def_val))}(length(keys(x_ind)), length(groupby.groupLabels))
|
||||
@@ -586,7 +627,7 @@ group_as_matrix(t) = false
|
||||
else
|
||||
g = args[1]
|
||||
if length(g.args) == 1
|
||||
x = zeros(Int64, lengthGroup)
|
||||
x = zeros(Int, lengthGroup)
|
||||
for indexes in groupby.groupIds
|
||||
x[indexes] = 1:length(indexes)
|
||||
end
|
||||
|
||||
+4
-5
@@ -77,15 +77,16 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
defaults = _get_defaults(thm)
|
||||
|
||||
# get the gradient
|
||||
gradient_colors = pop!(defaults, :gradient, cgrad(:inferno).colors)
|
||||
gradient_colors = get(defaults, :gradient, cgrad(:inferno).colors)
|
||||
gradient = cgrad(cfunc.(RGB.(gradient_colors)))
|
||||
|
||||
# get the palette
|
||||
palette = pop!(defaults, :palette, get_color_palette(:auto, plot_color(:white), 17))
|
||||
palette = get(defaults, :palette, get_color_palette(:auto, plot_color(:white), 17))
|
||||
palette = cfunc.(RGB.(palette))
|
||||
|
||||
# apply the theme
|
||||
for k in keys(defaults)
|
||||
k in (:gradient, :palette) && continue
|
||||
def = defaults[k]
|
||||
arg = get(_keyAliases, k, k)
|
||||
plotattributes[arg] = if typeof(def) <: Colorant
|
||||
@@ -110,7 +111,6 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
subplot := 1
|
||||
palette := palette
|
||||
seriestype := :path
|
||||
linewidth := 2
|
||||
cumsum(randn(50))
|
||||
end
|
||||
|
||||
@@ -118,8 +118,6 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
subplot := 2
|
||||
seriestype := :scatter
|
||||
palette := palette
|
||||
markersize := 5
|
||||
markerstrokewidth := 0
|
||||
marker := (:circle, :diamond, :star5, :square)[j]
|
||||
randn(10), randn(10)
|
||||
end
|
||||
@@ -142,6 +140,7 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
subplot := 4
|
||||
seriestype := :heatmap
|
||||
seriescolor := gradient
|
||||
ticks := -5:5:5
|
||||
x, y, z
|
||||
end
|
||||
|
||||
|
||||
+200
-27
@@ -192,14 +192,32 @@ function iter_segments(args...)
|
||||
SegmentsIterator(tup, n)
|
||||
end
|
||||
|
||||
function iter_segments(series::Series)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
if has_attribute_segments(series)
|
||||
if series[:seriestype] in (:scatter, :scatter3d)
|
||||
return [[i] for i in 1:length(y)]
|
||||
else
|
||||
return [i:(i + 1) for i in 1:(length(y) - 1)]
|
||||
end
|
||||
else
|
||||
segs = UnitRange{Int}[]
|
||||
args = is3d(series) ? (x, y, z) : (x, y)
|
||||
for seg in iter_segments(args...)
|
||||
push!(segs, seg)
|
||||
end
|
||||
return segs
|
||||
end
|
||||
end
|
||||
|
||||
# helpers to figure out if there are NaN values in a list of array types
|
||||
anynan(i::Int, args::Tuple) = any(a -> !isfinite(_cycle(a,i)), args)
|
||||
anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, args)
|
||||
anynan(istart::Int, iend::Int, args::Tuple) = any(i -> anynan(i, args), istart:iend)
|
||||
allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:iend)
|
||||
|
||||
function Base.start(itr::SegmentsIterator)
|
||||
nextidx = 1
|
||||
if anynan(1, itr.args)
|
||||
if !any(isempty,itr.args) && anynan(1, itr.args)
|
||||
_, nextidx = next(itr, 1)
|
||||
end
|
||||
nextidx
|
||||
@@ -340,19 +358,18 @@ const _scale_base = Dict{Symbol, Real}(
|
||||
:ln => e,
|
||||
)
|
||||
|
||||
"create an (n+1) list of the outsides of heatmap rectangles"
|
||||
function heatmap_edges(v::AVec, scale::Symbol = :identity)
|
||||
function _heatmap_edges(v::AVec)
|
||||
vmin, vmax = ignorenan_extrema(v)
|
||||
extra_min = extra_max = 0.5 * (vmax-vmin) / (length(v)-1)
|
||||
if scale in _logScales
|
||||
vmin > 0 || error("The axis values must be positive for a $scale scale")
|
||||
while vmin - extra_min <= 0
|
||||
extra_min /= _scale_base[scale]
|
||||
end
|
||||
end
|
||||
extra_min = (v[2] - v[1]) / 2
|
||||
extra_max = (v[end] - v[end - 1]) / 2
|
||||
vcat(vmin-extra_min, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra_max)
|
||||
end
|
||||
|
||||
"create an (n+1) list of the outsides of heatmap rectangles"
|
||||
function heatmap_edges(v::AVec, scale::Symbol = :identity)
|
||||
f, invf = scalefunc(scale), invscalefunc(scale)
|
||||
map(invf, _heatmap_edges(map(f,v)))
|
||||
end
|
||||
|
||||
function calc_r_extrema(x, y)
|
||||
xmin, xmax = ignorenan_extrema(x)
|
||||
@@ -553,8 +570,9 @@ zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
|
||||
|
||||
function get_clims(sp::Subplot)
|
||||
zmin, zmax = Inf, -Inf
|
||||
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface)
|
||||
for series in series_list(sp)
|
||||
for vals in (series[:z], series[:line_z], series[:marker_z], series[:fill_z])
|
||||
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
||||
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Real)
|
||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals.surf)...)
|
||||
elseif (vals != nothing) && (eltype(vals) <: Real)
|
||||
@@ -567,14 +585,17 @@ function get_clims(sp::Subplot)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
return zmin < zmax ? (zmin, zmax) : (0.0, 0.0)
|
||||
return zmin < zmax ? (zmin, zmax) : (-0.1, 0.1)
|
||||
end
|
||||
|
||||
_update_clims(zmin, zmax, emin, emax) = min(zmin, emin), max(zmax, emax)
|
||||
|
||||
function hascolorbar(series::Series)
|
||||
st = series[:seriestype]
|
||||
hascbar = st in (:heatmap, :contour)
|
||||
hascbar = st == :heatmap
|
||||
if st == :contour
|
||||
hascbar = (isscalar(series[:levels]) ? (series[:levels] > 1) : (length(series[:levels]) > 1)) && (length(unique(Array(series[:z]))) > 1)
|
||||
end
|
||||
if series[:marker_z] != nothing || series[:line_z] != nothing || series[:fill_z] != nothing
|
||||
hascbar = true
|
||||
end
|
||||
@@ -598,35 +619,84 @@ function hascolorbar(sp::Subplot)
|
||||
hascbar
|
||||
end
|
||||
|
||||
function get_linecolor(sp::Subplot, series::Series, i::Int = 1)
|
||||
function get_linecolor(series, i::Int = 1)
|
||||
lc = series[:linecolor]
|
||||
lz = series[:line_z]
|
||||
if lz == nothing
|
||||
isa(lc, ColorGradient) ? lc : _cycle(lc, i)
|
||||
isa(lc, ColorGradient) ? lc : plot_color(_cycle(lc, i))
|
||||
else
|
||||
cmin, cmax = get_clims(sp)
|
||||
cmin, cmax = get_clims(series[:subplot])
|
||||
grad = isa(lc, ColorGradient) ? lc : cgrad()
|
||||
grad[clamp((_cycle(lz, i) - cmin) / (cmax - cmin), 0, 1)]
|
||||
end
|
||||
end
|
||||
|
||||
function get_fillcolor(sp::Subplot, series::Series, i::Int = 1)
|
||||
function get_linealpha(series, i::Int = 1)
|
||||
_cycle(series[:linealpha], i)
|
||||
end
|
||||
|
||||
function get_linewidth(series, i::Int = 1)
|
||||
_cycle(series[:linewidth], i)
|
||||
end
|
||||
|
||||
function get_linestyle(series, i::Int = 1)
|
||||
_cycle(series[:linestyle], i)
|
||||
end
|
||||
|
||||
function get_fillcolor(series, i::Int = 1)
|
||||
fc = series[:fillcolor]
|
||||
fz = series[:fill_z]
|
||||
lz = series[:line_z]
|
||||
if fz == nothing && lz == nothing
|
||||
isa(fc, ColorGradient) ? fc : _cycle(fc, i)
|
||||
if fz == nothing
|
||||
isa(fc, ColorGradient) ? fc : plot_color(_cycle(fc, i))
|
||||
else
|
||||
cmin, cmax = get_clims(sp)
|
||||
cmin, cmax = get_clims(series[:subplot])
|
||||
grad = isa(fc, ColorGradient) ? fc : cgrad()
|
||||
if fz != nothing
|
||||
grad[clamp((_cycle(fz, i) - cmin) / (cmax - cmin), 0, 1)]
|
||||
elseif lz != nothing
|
||||
grad[clamp((_cycle(lz, i) - cmin) / (cmax - cmin), 0, 1)]
|
||||
end
|
||||
grad[clamp((_cycle(fz, i) - cmin) / (cmax - cmin), 0, 1)]
|
||||
end
|
||||
end
|
||||
|
||||
function get_fillalpha(series, i::Int = 1)
|
||||
_cycle(series[:fillalpha], i)
|
||||
end
|
||||
|
||||
function get_markercolor(series, i::Int = 1)
|
||||
mc = series[:markercolor]
|
||||
mz = series[:marker_z]
|
||||
if mz == nothing
|
||||
isa(mc, ColorGradient) ? mc : plot_color(_cycle(mc, i))
|
||||
else
|
||||
cmin, cmax = get_clims(series[:subplot])
|
||||
grad = isa(mc, ColorGradient) ? mc : cgrad()
|
||||
grad[clamp((_cycle(mz, i) - cmin) / (cmax - cmin), 0, 1)]
|
||||
end
|
||||
end
|
||||
|
||||
function get_markeralpha(series, i::Int = 1)
|
||||
_cycle(series[:markeralpha], i)
|
||||
end
|
||||
|
||||
function get_markerstrokecolor(series, i::Int = 1)
|
||||
msc = series[:markerstrokecolor]
|
||||
isa(msc, ColorGradient) ? msc : _cycle(msc, i)
|
||||
end
|
||||
|
||||
function get_markerstrokealpha(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokealpha], i)
|
||||
end
|
||||
|
||||
function has_attribute_segments(series::Series)
|
||||
# we want to check if a series needs to be split into segments just because
|
||||
# of its attributes
|
||||
for letter in (:x, :y, :z)
|
||||
# If we have NaNs in the data they define the segments and
|
||||
# SegmentsIterator is used
|
||||
series[letter] != nothing && NaN in collect(series[letter]) && return false
|
||||
end
|
||||
series[:seriestype] == :shape && return false
|
||||
# ... else we check relevant attributes if they have multiple inputs
|
||||
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray{<:Real} for attr in (:line_z, :fill_z, :marker_z))
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
makekw(; kw...) = KW(kw)
|
||||
@@ -1047,3 +1117,106 @@ guidefont(ax::Axis) = font(
|
||||
ax[:guidefontrotation],
|
||||
ax[:guidefontcolor],
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# converts unicode scientific notation unsupported by pgfplots and gr
|
||||
# into a format that works
|
||||
|
||||
function convert_sci_unicode(label::AbstractString)
|
||||
unicode_dict = Dict(
|
||||
'⁰' => "0",
|
||||
'¹' => "1",
|
||||
'²' => "2",
|
||||
'³' => "3",
|
||||
'⁴' => "4",
|
||||
'⁵' => "5",
|
||||
'⁶' => "6",
|
||||
'⁷' => "7",
|
||||
'⁸' => "8",
|
||||
'⁹' => "9",
|
||||
'⁻' => "-",
|
||||
"×10" => "×10^{",
|
||||
)
|
||||
for key in keys(unicode_dict)
|
||||
label = replace(label, key, unicode_dict[key])
|
||||
end
|
||||
if contains(label, "10^{")
|
||||
label = string(label, "}")
|
||||
end
|
||||
label
|
||||
end
|
||||
|
||||
function straightline_data(series)
|
||||
sp = series[:subplot]
|
||||
xl, yl = isvertical(series) ? (xlims(sp), ylims(sp)) : (ylims(sp), xlims(sp))
|
||||
x, y = series[:x], series[:y]
|
||||
n = length(x)
|
||||
if n == 2
|
||||
return straightline_data(xl, yl, x, y)
|
||||
else
|
||||
k, r = divrem(n, 3)
|
||||
if r == 0
|
||||
xdata, ydata = fill(NaN, n), fill(NaN, n)
|
||||
for i in 1:k
|
||||
inds = (3 * i - 2):(3 * i - 1)
|
||||
xdata[inds], ydata[inds] = straightline_data(xl, yl, x[inds], y[inds])
|
||||
end
|
||||
return xdata, ydata
|
||||
else
|
||||
error("Misformed data. `straightline_data` either accepts vectors of length 2 or 3k. The provided series has length $n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function straightline_data(xl, yl, x, y)
|
||||
x_vals, y_vals = if y[1] == y[2]
|
||||
if x[1] == x[2]
|
||||
error("Two identical points cannot be used to describe a straight line.")
|
||||
else
|
||||
[xl[1], xl[2]], [y[1], y[2]]
|
||||
end
|
||||
elseif x[1] == x[2]
|
||||
[x[1], x[2]], [yl[1], yl[2]]
|
||||
else
|
||||
# get a and b from the line y = a * x + b through the points given by
|
||||
# the coordinates x and x
|
||||
b = y[1] - (y[1] - y[2]) * x[1] / (x[1] - x[2])
|
||||
a = (y[1] - y[2]) / (x[1] - x[2])
|
||||
# get the data values
|
||||
xdata = [clamp(x[1] + (x[1] - x[2]) * (ylim - y[1]) / (y[1] - y[2]), xl...) for ylim in yl]
|
||||
|
||||
xdata, a .* xdata .+ b
|
||||
end
|
||||
# expand the data outside the axis limits, by a certain factor too improve
|
||||
# plotly(js) and interactive behaviour
|
||||
factor = 100
|
||||
x_vals = x_vals .+ (x_vals[2] - x_vals[1]) .* factor .* [-1, 1]
|
||||
y_vals = y_vals .+ (y_vals[2] - y_vals[1]) .* factor .* [-1, 1]
|
||||
return x_vals, y_vals
|
||||
end
|
||||
|
||||
function shape_data(series)
|
||||
sp = series[:subplot]
|
||||
xl, yl = isvertical(series) ? (xlims(sp), ylims(sp)) : (ylims(sp), xlims(sp))
|
||||
x, y = series[:x], series[:y]
|
||||
factor = 100
|
||||
for i in eachindex(x)
|
||||
if x[i] == -Inf
|
||||
x[i] = xl[1] - factor * (xl[2] - xl[1])
|
||||
elseif x[i] == Inf
|
||||
x[i] = xl[2] + factor * (xl[2] - xl[1])
|
||||
end
|
||||
end
|
||||
for i in eachindex(y)
|
||||
if y[i] == -Inf
|
||||
y[i] = yl[1] - factor * (yl[2] - yl[1])
|
||||
elseif y[i] == Inf
|
||||
y[i] = yl[2] + factor * (yl[2] - yl[1])
|
||||
end
|
||||
end
|
||||
return x, y
|
||||
end
|
||||
|
||||
function construct_categorical_data(x::AbstractArray, axis::Axis)
|
||||
map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x)
|
||||
end
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ StatPlots
|
||||
Images
|
||||
ImageMagick
|
||||
@osx QuartzImageIO
|
||||
GR
|
||||
GR 0.31.0
|
||||
RDatasets
|
||||
VisualRegressionTests
|
||||
UnicodePlots
|
||||
|
||||
+1
-1
@@ -23,7 +23,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.13.1"
|
||||
const _current_plots_version = v"0.17.4"
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||
|
||||
@@ -8,6 +8,7 @@ default(show=false, reuse=true)
|
||||
img_eps = isinteractive() ? 1e-2 : 10e-2
|
||||
|
||||
@testset "GR" begin
|
||||
ENV["PLOTS_TEST"] = "true"
|
||||
ENV["GKSwstype"] = "100"
|
||||
@test gr() == Plots.GRBackend()
|
||||
@test backend() == Plots.GRBackend()
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
Pkg.add("ImageMagick")
|
||||
Pkg.build("ImageMagick")
|
||||
|
||||
Pkg.clone("GR")
|
||||
Pkg.build("GR")
|
||||
# Pkg.clone("GR")
|
||||
# Pkg.build("GR")
|
||||
|
||||
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
|
||||
|
||||
# Pkg.clone("https://github.com/JuliaStats/KernelDensity.jl.git")
|
||||
|
||||
Pkg.clone("StatPlots")
|
||||
Pkg.checkout("PlotUtils")
|
||||
# Pkg.checkout("PlotUtils")
|
||||
|
||||
# Pkg.clone("Blink")
|
||||
# Pkg.build("Blink")
|
||||
|
||||
Reference in New Issue
Block a user