Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
babc600915 | ||
|
|
b2f92e246e | ||
|
|
004ad15875 | ||
|
|
d11c6a20bf | ||
|
|
6c396e1d88 | ||
|
|
7cee448d68 | ||
|
|
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 | ||
|
|
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 | ||
|
|
d52b2a9adc | ||
|
|
81782c3927 | ||
|
|
2c2ebbba0e | ||
|
|
3d323a734d | ||
|
|
93263a61b1 | ||
|
|
8b551e5285 | ||
|
|
e11c91ddd9 | ||
|
|
938c83533b | ||
|
|
46136d616f | ||
|
|
8fe902e96c | ||
|
|
349b6e8254 | ||
|
|
ee6dbb9ac7 | ||
|
|
2a819c2849 |
@@ -3,7 +3,7 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- All new development should target 0.14!
|
||||
- All new development should target 0.15!
|
||||
- Minor version 0.11 is the last one to support Julia 0.5!!
|
||||
- Critical bugfixes only
|
||||
- `backports` branch is for Julia 0.5
|
||||
@@ -11,6 +11,40 @@
|
||||
---
|
||||
## (current master)
|
||||
|
||||
## 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
|
||||
|
||||
- fix plotly bar lines bug
|
||||
- allow passing multiple series to `ribbon`
|
||||
- add a new example for `line_z`
|
||||
|
||||
## 0.14.1
|
||||
|
||||
- Add linestyle argument to the legend
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
|
||||
+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
|
||||
|
||||
@@ -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.",
|
||||
|
||||
+40
-8
@@ -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?
|
||||
@@ -559,6 +575,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 +751,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 +1016,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 +1328,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
|
||||
|
||||
|
||||
+17
-12
@@ -234,11 +234,22 @@ 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
|
||||
cv, dv = if !isempty(dvals)
|
||||
# discrete ticks...
|
||||
axis[:continuous_values], dvals
|
||||
elseif ticks == :auto
|
||||
n = length(dvals)
|
||||
rng = if ticks == :auto
|
||||
Int[round(Int,i) for i in linspace(1, n, 15)]
|
||||
elseif ticks == :all
|
||||
1:n
|
||||
elseif typeof(ticks) <: Int
|
||||
Int[round(Int,i) for i in linspace(1, n, ticks)]
|
||||
end
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
elseif typeof(ticks) <: Symbol
|
||||
if ispolar(axis.sps[1]) && axis[:letter] == :x
|
||||
#force theta axis to be full circle
|
||||
(collect(0:pi/4:7pi/4), string.(0:45:315))
|
||||
@@ -257,13 +268,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
|
||||
@@ -552,7 +557,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 +596,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
|
||||
|
||||
@@ -336,7 +336,7 @@ end
|
||||
function extract_stroke(d, kw_args)
|
||||
extract_c(d, kw_args, :line)
|
||||
if haskey(d, :linewidth)
|
||||
kw_args[:thickness] = d[:linewidth] * 3
|
||||
kw_args[:thickness] = Float32(d[:linewidth] * 3)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -404,7 +404,7 @@ function extract_linestyle(d, kw_args)
|
||||
haskey(d, :linestyle) || return
|
||||
ls = d[:linestyle]
|
||||
lw = d[:linewidth]
|
||||
kw_args[:thickness] = lw
|
||||
kw_args[:thickness] = Float32(lw)
|
||||
if ls == :dash
|
||||
points = [0.0, lw, 2lw, 3lw, 4lw]
|
||||
insert_pattern!(points, kw_args)
|
||||
@@ -576,7 +576,7 @@ function draw_grid_lines(sp, grid_segs, thickness, style, model, color)
|
||||
)
|
||||
d = Dict(
|
||||
:linestyle => style,
|
||||
:linewidth => thickness,
|
||||
:linewidth => Float32(thickness),
|
||||
:linecolor => color
|
||||
)
|
||||
Plots.extract_linestyle(d, kw_args)
|
||||
@@ -1215,7 +1215,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
|
||||
|
||||
@@ -1350,7 +1350,7 @@ function gl_surface(x,y,z, kw_args)
|
||||
end
|
||||
color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1))
|
||||
kw_args[:color] = color
|
||||
kw_args[:thickness] = get(kw_args, :stroke_width, 1f0)
|
||||
kw_args[:thickness] = Float32(get(kw_args, :stroke_width, 1f0))
|
||||
kw_args[:indices] = faces
|
||||
delete!(kw_args, :stroke_color)
|
||||
delete!(kw_args, :stroke_width)
|
||||
|
||||
+67
-25
@@ -45,6 +45,7 @@ const _gr_attr = merge_with_base_supported([
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _gr_seriestype = [
|
||||
:path, :scatter,
|
||||
@@ -193,7 +194,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 +206,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,6 +223,9 @@ 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] == :scientific && yaxis[:ticks] in (:auto, :native)
|
||||
rtick_labels = convert_sci_unicode(rtick_labels)
|
||||
end
|
||||
|
||||
#draw angular grid
|
||||
if xaxis[:grid]
|
||||
@@ -543,7 +547,7 @@ 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()
|
||||
|
||||
# collect some monitor/display sizes in meters and pixels
|
||||
@@ -554,17 +558,25 @@ function gr_display(plt::Plot)
|
||||
# compute the viewport_canvas, normalized to the larger dimension
|
||||
viewport_canvas = Float64[0,1,0,1]
|
||||
w, h = plt[:size]
|
||||
if !haskey(ENV, "PLOTS_TEST")
|
||||
dpi_factor = plt[:dpi] / DPI
|
||||
if fmt == "png"
|
||||
dpi_factor *= 6
|
||||
end
|
||||
else
|
||||
dpi_factor = 1
|
||||
end
|
||||
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
|
||||
@@ -638,7 +650,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 +659,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
|
||||
@@ -780,10 +792,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.settransparency(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.settransparency(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.settransparency(zaxis[:gridalpha])
|
||||
GR.grid3d(0, 0, ztick, xmin, ymax, zmin, 0, 0, 2)
|
||||
end
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_axis])
|
||||
GR.settransparency(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)
|
||||
|
||||
@@ -860,9 +885,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] == :scientific
|
||||
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 +904,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] == :scientific
|
||||
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
|
||||
|
||||
@@ -1019,24 +1056,28 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
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)
|
||||
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)
|
||||
@@ -1061,7 +1102,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
zmin, zmax = clims
|
||||
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 +
|
||||
@@ -1163,8 +1204,9 @@ 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)
|
||||
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 +
|
||||
@@ -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)
|
||||
|
||||
@@ -272,6 +272,9 @@ 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])
|
||||
|
||||
@@ -292,7 +295,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 +314,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,6 +329,7 @@ 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]
|
||||
tick_labels = axis[:formatter] == :scientific ? string.("\$", convert_sci_unicode.(tick_labels), "\$") : tick_labels
|
||||
push!(style, string(letter, "ticklabels = {", join(tick_labels,","), "}"))
|
||||
else
|
||||
push!(style, string(letter, "ticklabels = {}"))
|
||||
@@ -446,7 +450,7 @@ 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)
|
||||
@@ -461,7 +465,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
pgf_add_annotation!(o,ann...)
|
||||
pgf_add_annotation!(o, locate_annotation(sp, ann...)...)
|
||||
end
|
||||
|
||||
|
||||
@@ -497,6 +501,7 @@ end
|
||||
|
||||
function _display(plt::Plot{PGFPlotsBackend})
|
||||
# prepare the object
|
||||
PGFPlots.pushPGFPlotsPreamble("\\usepackage{fontspec}")
|
||||
pgfplt = PGFPlots.plot(plt.o)
|
||||
|
||||
# save an svg
|
||||
|
||||
+92
-67
@@ -45,10 +45,11 @@ 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,
|
||||
]
|
||||
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
@@ -86,8 +87,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 +146,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 +232,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,14 +252,20 @@ 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]
|
||||
lims = axis_limits(axis)
|
||||
ax[:range] = map(scalefunc(axis[:scale]), lims)
|
||||
|
||||
if !(axis[:ticks] in (nothing, :none))
|
||||
axis[:ticks] != :native ? ax[:range] = map(scalefunc(axis[:scale]), lims) : nothing
|
||||
|
||||
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))
|
||||
@@ -273,8 +278,8 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
||||
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 +294,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
||||
ax[:showgrid] = false
|
||||
end
|
||||
|
||||
|
||||
|
||||
ax
|
||||
end
|
||||
|
||||
@@ -320,9 +325,8 @@ function plotly_layout(plt::Plot)
|
||||
d_out[:annotations] = KW[]
|
||||
|
||||
for sp in plt.subplots
|
||||
spidx = plotly_subplot_index(sp)
|
||||
|
||||
|
||||
spidx = sp[:subplot_index]
|
||||
x_idx, y_idx = 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 +354,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 +368,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
|
||||
@@ -385,8 +390,9 @@ function plotly_layout(plt::Plot)
|
||||
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 +400,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 +436,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 +461,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 +484,7 @@ function plotly_close_shapes(x, y)
|
||||
nanvcat(xs), nanvcat(ys)
|
||||
end
|
||||
|
||||
plotly_data(v) = collect(v)
|
||||
plotly_data(v) = v != nothing ? collect(v) : v
|
||||
plotly_data(surf::Surface) = surf.surf
|
||||
plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v)
|
||||
|
||||
@@ -487,12 +507,21 @@ 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])
|
||||
|
||||
x, y, z = map(letter -> (axis = sp[Symbol(letter, :axis)];
|
||||
if axis[:ticks] == :native && !isempty(axis[:discrete_values])
|
||||
axis[:discrete_values]
|
||||
elseif st in (:heatmap, :contour, :surface, :wireframe)
|
||||
plotly_surface_data(series, series[letter])
|
||||
else
|
||||
plotly_data(series[letter])
|
||||
end), (:x, :y, :z))
|
||||
|
||||
d_out[:name] = series[:label]
|
||||
|
||||
isscatter = st in (:scatter, :scatter3d, :scattergl)
|
||||
@@ -501,13 +530,6 @@ function plotly_series(plt::Plot, series::Series)
|
||||
hasfillrange = st in (:path, :scatter, :scattergl) &&
|
||||
(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]
|
||||
@@ -526,7 +548,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
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)
|
||||
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real}, Real}
|
||||
d_out[:fill] = "tonexty"
|
||||
d_out[:fillcolor] = rgba_string(series[:fillcolor])
|
||||
elseif !(series[:fillrange] in (false, nothing))
|
||||
@@ -534,35 +556,24 @@ function plotly_series(plt::Plot, series::Series)
|
||||
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]),
|
||||
:width => series[:strokewidth])
|
||||
|
||||
elseif st == :heatmap
|
||||
d_out[:type] = "heatmap"
|
||||
# d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
|
||||
d_out[:x], d_out[:y], d_out[:z] = 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(
|
||||
@@ -594,8 +605,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = plotly_data(series[:z])
|
||||
d_out[:x], d_out[:y], d_out[:z] = x, y, z
|
||||
|
||||
else
|
||||
warn("Plotly: seriestype $st isn't supported.")
|
||||
@@ -610,14 +620,14 @@ function plotly_series(plt::Plot, series::Series)
|
||||
:size => 2 * series[:markersize],
|
||||
# :color => rgba_string(series[:markercolor]),
|
||||
:line => KW(
|
||||
:color => rgba_string(series[:markerstrokecolor]),
|
||||
:color => _cycle(rgba_string.(series[:markerstrokecolor]),eachindex(series[:x])),
|
||||
:width => series[:markerstrokewidth],
|
||||
),
|
||||
)
|
||||
|
||||
# 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])
|
||||
d_out[:marker][:color] = _cycle(rgba_string.(series[:markercolor]),eachindex(series[:x]))
|
||||
else
|
||||
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
|
||||
# grad = as_gradient(series[:markercolor], series[:markeralpha])
|
||||
@@ -656,6 +666,14 @@ function plotly_series(plt::Plot, series::Series)
|
||||
# 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(series[:x]))
|
||||
elseif typeof(series[:fillrange]) <: Tuple
|
||||
f1 = typeof(series[:fillrange][1]) <: Real ? fill(series[:fillrange][1], length(series[:x])) : series[:fillrange][1]
|
||||
f2 = typeof(series[:fillrange][2]) <: Real ? fill(series[:fillrange][2], length(series[:x])) : series[:fillrange][2]
|
||||
series[:fillrange] = (f1, f2)
|
||||
end
|
||||
if isa(series[:fillrange], AbstractVector)
|
||||
d_out_fillrange[:y] = series[:fillrange]
|
||||
delete!(d_out_fillrange, :fill)
|
||||
@@ -683,10 +701,10 @@ function plotly_series_shapes(plt::Plot, series::Series)
|
||||
# x, y = series[:x], series[:y]
|
||||
|
||||
# these are the axes that the series should be mapped to
|
||||
spidx = plotly_subplot_index(series[:subplot])
|
||||
x_idx, y_idx = plotly_link_indicies(plt, series[:subplot])
|
||||
base_d = KW()
|
||||
base_d[:xaxis] = "x$spidx"
|
||||
base_d[:yaxis] = "y$spidx"
|
||||
base_d[:xaxis] = "x$(x_idx)"
|
||||
base_d[:yaxis] = "y$(y_idx)"
|
||||
base_d[:name] = series[:label]
|
||||
# base_d[:legendgroup] = series[:label]
|
||||
|
||||
@@ -738,15 +756,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)
|
||||
@@ -793,6 +813,11 @@ function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
|
||||
write(io, html_head(plt) * html_body(plt))
|
||||
end
|
||||
|
||||
function Base.show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
|
||||
prepare_output(plt)
|
||||
write(io, html_head(plt) * html_body(plt))
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PlotlyBackend})
|
||||
standalone_html_window(plt)
|
||||
end
|
||||
|
||||
+29
-12
@@ -42,6 +42,7 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pyplot_seriestype = [
|
||||
:path, :steppre, :steppost, :shape,
|
||||
@@ -83,7 +84,6 @@ function _initialize_backend(::PyPlotBackend)
|
||||
export PyPlot
|
||||
const pycolors = PyPlot.pyimport("matplotlib.colors")
|
||||
const pypath = PyPlot.pyimport("matplotlib.path")
|
||||
const mplot3d = PyPlot.pyimport("mpl_toolkits.mplot3d")
|
||||
const pypatches = PyPlot.pyimport("matplotlib.patches")
|
||||
const pyfont = PyPlot.pyimport("matplotlib.font_manager")
|
||||
const pyticker = PyPlot.pyimport("matplotlib.ticker")
|
||||
@@ -92,7 +92,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"
|
||||
@@ -215,6 +215,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")
|
||||
@@ -572,7 +574,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
:scatter3d, :steppre, :steppost,
|
||||
:bar)
|
||||
if series[:marker_z] == nothing
|
||||
extrakw[:c] = py_color_fix(py_markercolor(series), x)
|
||||
extrakw[:c] = series[:markershape] in (:+, :x, :hline, :vline) ? py_markerstrokecolor(series) : py_color_fix(py_markercolor(series), x)
|
||||
else
|
||||
extrakw[:c] = convert(Vector{Float64}, series[:marker_z])
|
||||
extrakw[:cmap] = py_markercolormap(series)
|
||||
@@ -644,15 +646,23 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
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]),
|
||||
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 +670,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)
|
||||
@@ -732,11 +741,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 +754,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 +779,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...
|
||||
)
|
||||
@@ -968,7 +980,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
|
||||
@@ -1058,7 +1070,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,7 +1079,7 @@ 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)
|
||||
@@ -1137,6 +1149,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
|
||||
@@ -1234,7 +1250,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
)
|
||||
elseif series[:seriestype] == :path
|
||||
PyPlot.plt[:Line2D]((0,1),(0,0),
|
||||
color = py_color(_cycle(series[:fillcolor],1)),
|
||||
color = py_color(_cycle(series[:linecolor],1)),
|
||||
linewidth = py_dpi_scale(plt, clamp(series[:linewidth], 0, 5)),
|
||||
linestyle = py_linestyle(:path,series[:linestyle]),
|
||||
marker = py_marker(series[:markershape]),
|
||||
@@ -1293,7 +1309,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)
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -416,6 +416,22 @@ attribute. The default framestyle is `:axes`.
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Lines and markers with varying colors",
|
||||
"""
|
||||
You can use the `line_z` and `marker_z` properties to associate a color with
|
||||
each line segment or marker in the plot.
|
||||
""",
|
||||
[:(begin
|
||||
t = linspace(0, 1, 100)
|
||||
θ = 6π .* t
|
||||
x = t .* cos.(θ)
|
||||
y = t .* sin.(θ)
|
||||
p1 = plot(x, y, line_z=t, linewidth=3, legend=false)
|
||||
p2 = scatter(x, y, marker_z=t, color=:bluesreds, legend=false)
|
||||
plot(p1, p2)
|
||||
end)]
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
+42
-11
@@ -171,7 +171,9 @@ const _mimeformats = Dict(
|
||||
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
|
||||
@@ -256,20 +258,50 @@ 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))
|
||||
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 == :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
|
||||
|
||||
# default text/plain passes to html... handles Interact issues
|
||||
@@ -278,7 +310,6 @@ const _ijulia_output = String["text/html"]
|
||||
end
|
||||
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
set_ijulia_output("text/html")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+3
-4
@@ -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
|
||||
|
||||
+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)))
|
||||
|
||||
+33
-14
@@ -124,27 +124,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 +168,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 +317,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
|
||||
@@ -989,20 +1002,26 @@ end
|
||||
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...)
|
||||
if x2 > x1
|
||||
plot!(plt, x -> b + a*x, x1, x2; kw...)
|
||||
else
|
||||
nothing
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
+54
-1
@@ -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)
|
||||
@@ -140,6 +140,14 @@ struct SliceIt end
|
||||
end
|
||||
mf = length(fillranges)
|
||||
|
||||
rib = pop!(plotattributes, :ribbon, nothing)
|
||||
ribbons, _ = if typeof(rib) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(rib, plotattributes)
|
||||
end
|
||||
mr = length(ribbons)
|
||||
|
||||
# @show zs
|
||||
|
||||
mx = length(xs)
|
||||
@@ -156,6 +164,10 @@ struct SliceIt end
|
||||
fr = fillranges[mod1(i,mf)]
|
||||
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
|
||||
|
||||
# handle ribbons
|
||||
rib = ribbons[mod1(i,mr)]
|
||||
di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib
|
||||
|
||||
push!(series_list, RecipeData(di, ()))
|
||||
end
|
||||
end
|
||||
@@ -309,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
|
||||
@@ -325,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
|
||||
@@ -453,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
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
@@ -536,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))
|
||||
|
||||
+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
|
||||
|
||||
|
||||
+36
-4
@@ -199,7 +199,7 @@ allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:i
|
||||
|
||||
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
|
||||
@@ -553,8 +553,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 +568,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
|
||||
@@ -1047,3 +1051,31 @@ 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
|
||||
|
||||
+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.15.0"
|
||||
|
||||
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user