Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6da5bb8822 | ||
|
|
a0a3b42872 | ||
|
|
caf0aa098a | ||
|
|
55ea8b4a70 | ||
|
|
25c53f0e52 | ||
|
|
7c3938f793 | ||
|
|
20a10fcf7d | ||
|
|
6ee49f8430 | ||
|
|
0768c8a658 | ||
|
|
89b2be4ecb | ||
|
|
369d510b3e | ||
|
|
d1dbb1bf2a | ||
|
|
ccf726ddcb | ||
|
|
5b2cd1b8ee | ||
|
|
16b8d01b88 | ||
|
|
8d7b07eab3 | ||
|
|
599d6a2ec8 | ||
|
|
345f5654a3 | ||
|
|
77e04c45db | ||
|
|
ad4d7214e4 | ||
|
|
4051d016b9 | ||
|
|
8610a82a88 | ||
|
|
4832d870dd | ||
|
|
ace915fbfb | ||
|
|
c6593423f5 | ||
|
|
f69daa8cfe | ||
|
|
71246620aa | ||
|
|
6e3181375c | ||
|
|
e7b1dc0cac | ||
|
|
d78bb56316 | ||
|
|
648c5ac587 | ||
|
|
df7d28dc99 | ||
|
|
4d28a49c70 | ||
|
|
ef3e7af329 | ||
|
|
3221b80aeb | ||
|
|
13fc936e1f | ||
|
|
4e5b1aa6e5 | ||
|
|
cb577f0ef3 | ||
|
|
b23dd39e06 | ||
|
|
7498b3c769 | ||
|
|
0c0a2c215c | ||
|
|
b682bc8e96 | ||
|
|
205345a8d3 | ||
|
|
faff9cebe4 | ||
|
|
bcd1bc1009 | ||
|
|
f2ca493a14 | ||
|
|
54301fa97a | ||
|
|
20a290792f | ||
|
|
31f5490517 | ||
|
|
8554e632bd | ||
|
|
70d0d786e2 | ||
|
|
5af5ec7313 | ||
|
|
7fab2f1c6b | ||
|
|
461ef20225 | ||
|
|
4357fde5ea | ||
|
|
f93977b1a4 | ||
|
|
2e77d0df98 | ||
|
|
fc40b0e0ac | ||
|
|
85aca2aa67 | ||
|
|
961d6be9f9 |
@@ -0,0 +1,11 @@
|
||||
name: TagBot
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 * * * *
|
||||
jobs:
|
||||
TagBot:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: JuliaRegistries/TagBot@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
+5
-1
@@ -5,7 +5,7 @@ os:
|
||||
# - osx
|
||||
julia:
|
||||
- 1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
@@ -20,6 +20,10 @@ addons:
|
||||
- xauth
|
||||
- xvfb
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.julia/artifacts
|
||||
|
||||
sudo: required
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
name = "Plots"
|
||||
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
author = ["Tom Breloff (@tbreloff)"]
|
||||
version = "0.29.0"
|
||||
version = "0.29.5"
|
||||
|
||||
[deps]
|
||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
@@ -32,10 +32,10 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
|
||||
[compat]
|
||||
Contour = "0.5"
|
||||
FFMPEG = "0.2"
|
||||
FFMPEG = "0.2, 0.3"
|
||||
FixedPointNumbers = "0.6, 0.7, 0.8"
|
||||
GR = "0.46"
|
||||
GeometryTypes = "0.7"
|
||||
GR = "0.46, 0.47"
|
||||
GeometryTypes = "0.7, 0.8"
|
||||
JSON = "0.21"
|
||||
Measures = "0.3"
|
||||
NaNMath = "0.3"
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/JuliaPlots/Plots.jl)
|
||||
[](https://ci.appveyor.com/project/mkborregaard/plots-jl)
|
||||
[](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<a href="http://docs.juliaplots.org/latest/" target="_blank"><img src="https://img.shields.io/badge/docs-latest-blue.svg" alt="Latest documentation"></a>
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
|
||||
<!-- [](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
|
||||
<!-- [](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
|
||||
[travis-img]: https://img.shields.io/travis/JuliaPlots/Plots.jl?logo=travis
|
||||
[travis-url]: https://travis-ci.org/JuliaPlots/Plots.jl
|
||||
|
||||
[appveyor-img]: https://ci.appveyor.com/api/projects/status/github/juliaplots/plots.jl?branch=master&svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/mkborregaard/plots-jl
|
||||
|
||||
[pkgeval-img]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/P/Plots.svg
|
||||
[pkgeval-url]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html
|
||||
|
||||
[gitter-img]: https://badges.gitter.im/tbreloff/Plots.jl.svg
|
||||
[gitter-url]: https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
[docs-img]: https://img.shields.io/badge/docs-stable-blue.svg
|
||||
[docs-url]: http://docs.juliaplots.org/latest/
|
||||
|
||||
[![][travis-img]][travis-url]
|
||||
[![][appveyor-img]][appveyor-url]
|
||||
[![][pkgeval-img]][pkgeval-url]
|
||||
[![][gitter-img]][gitter-url]
|
||||
[![][docs-img]][docs-url]
|
||||
|
||||
#### Created by Tom Breloff (@tbreloff)
|
||||
|
||||
|
||||
+4
-1
@@ -2,7 +2,7 @@ environment:
|
||||
matrix:
|
||||
# - julia_version: 0.7
|
||||
- julia_version: 1
|
||||
- julia_version: 1.2
|
||||
- julia_version: 1.3
|
||||
- julia_version: nightly
|
||||
|
||||
platform:
|
||||
@@ -20,6 +20,9 @@ branches:
|
||||
- master
|
||||
- /release-.*/
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.julia\artifacts'
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
on_build_success: false
|
||||
|
||||
+3
-3
@@ -163,16 +163,16 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
error("Unsupported animate filter: $args")
|
||||
end
|
||||
|
||||
push!(block.args, :(if $filterexpr; frame($animsym); end))
|
||||
push!(block.args, :(if $filterexpr; Plots.frame($animsym); end))
|
||||
push!(block.args, :(global $countersym += 1))
|
||||
|
||||
# add a final call to `gif(anim)`?
|
||||
retval = callgif ? :(gif($animsym)) : animsym
|
||||
retval = callgif ? :(Plots.gif($animsym)) : animsym
|
||||
|
||||
# full expression:
|
||||
esc(quote
|
||||
$freqassert # if filtering, check frequency is an Integer > 0
|
||||
$animsym = Animation() # init animation object
|
||||
$animsym = Plots.Animation() # init animation object
|
||||
global $countersym = 1 # init iteration counter
|
||||
$forloop # for loop, saving a frame after each iteration
|
||||
$retval # return the animation object, or the gif
|
||||
|
||||
+14
-16
@@ -345,7 +345,7 @@ const _subplot_defaults = KW(
|
||||
:legendtitlefontcolor => :match,
|
||||
:annotations => [], # annotation tuples... list of (x,y,annotation)
|
||||
:projection => :none, # can also be :polar or :3d
|
||||
:aspect_ratio => :none, # choose from :none or :equal
|
||||
:aspect_ratio => :auto, # choose from :none or :equal
|
||||
:margin => 1mm,
|
||||
:left_margin => :match,
|
||||
:top_margin => :match,
|
||||
@@ -906,7 +906,7 @@ _replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
|
||||
_replace_markershape(shapes::AVec) = map(_replace_markershape, shapes)
|
||||
_replace_markershape(shape) = shape
|
||||
|
||||
function _add_markershape(plotattributes::KW)
|
||||
function _add_markershape(plotattributes::AKW)
|
||||
# add the markershape if it needs to be added... hack to allow "m=10" to add a shape,
|
||||
# and still allow overriding in _apply_recipe
|
||||
ms = pop!(plotattributes, :markershape_to_add, :none)
|
||||
@@ -1145,13 +1145,13 @@ end
|
||||
filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
|
||||
filter_data(v, idxfilter) = v
|
||||
|
||||
function filter_data!(plotattributes::KW, idxfilter)
|
||||
function filter_data!(plotattributes::AKW, idxfilter)
|
||||
for s in (:x, :y, :z)
|
||||
plotattributes[s] = filter_data(get(plotattributes, s, nothing), idxfilter)
|
||||
end
|
||||
end
|
||||
|
||||
function _filter_input_data!(plotattributes::KW)
|
||||
function _filter_input_data!(plotattributes::AKW)
|
||||
idxfilter = pop!(plotattributes, :idxfilter, nothing)
|
||||
if idxfilter !== nothing
|
||||
filter_data!(plotattributes, idxfilter)
|
||||
@@ -1200,7 +1200,7 @@ function warnOnUnsupported(pkg::AbstractBackend, plotattributes)
|
||||
end
|
||||
end
|
||||
|
||||
function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::KW)
|
||||
function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::AKW)
|
||||
for k in (:xscale, :yscale, :zscale, :scale)
|
||||
if haskey(plotattributes, k)
|
||||
v = plotattributes[k]
|
||||
@@ -1267,7 +1267,7 @@ end
|
||||
|
||||
# # if the value is `:match` then we take whatever match_color is.
|
||||
# # this is mainly used for cascading defaults for foreground and background colors
|
||||
# function color_or_match!(plotattributes::KW, k::Symbol, match_color)
|
||||
# function color_or_match!(plotattributes::AKW, k::Symbol, match_color)
|
||||
# v = plotattributes[k]
|
||||
# plotattributes[k] = if v == :match
|
||||
# match_color
|
||||
@@ -1379,18 +1379,18 @@ Base.get(series::Series, k::Symbol, v) = get(series.plotattributes, k, v)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function fg_color(plotattributes::Attr)
|
||||
fg = plotattributes[:foreground_color]
|
||||
function fg_color(plotattributes::AKW)
|
||||
fg = get(plotattributes, :foreground_color, :auto)
|
||||
if fg == :auto
|
||||
bg = plot_color(plotattributes[:background_color])
|
||||
bg = plot_color(get(plotattributes, :background_color, :white))
|
||||
fg = isdark(bg) ? colorant"white" : colorant"black"
|
||||
else
|
||||
plot_color(fg)
|
||||
end
|
||||
end
|
||||
|
||||
function fg_color_sp(plotattributes::Attr)
|
||||
fgsp = plotattributes[:foreground_color_subplot]
|
||||
function fg_color_sp(plotattributes::AKW)
|
||||
fgsp = get(plotattributes, :foreground_color_subplot, :match)
|
||||
if fg == :match
|
||||
fg_color(plotattributes)
|
||||
else
|
||||
@@ -1402,11 +1402,9 @@ end
|
||||
|
||||
# update attr from an input dictionary
|
||||
function _update_plot_args(plt::Plot, plotattributes_in::AKW)
|
||||
# TODO do merged kws need to be removed from plotattributes_in?
|
||||
merge!(plt.attr, plotattributes_in)
|
||||
# for (k,v) in _plot_defaults
|
||||
# slice_arg!(plotattributes_in, plt.attr, k, v, 1, true)
|
||||
# end
|
||||
for (k,v) in _plot_defaults
|
||||
slice_arg!(plotattributes_in, plt.attr, k, 1, true)
|
||||
end
|
||||
|
||||
# handle colors
|
||||
plotattributes= plt.attr
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ function get_axis(sp::Subplot, letter::Symbol)
|
||||
end::Axis
|
||||
end
|
||||
|
||||
function process_axis_arg!(plotattributes::KW, arg, letter = "")
|
||||
function process_axis_arg!(plotattributes::AKW, arg, letter = "")
|
||||
T = typeof(arg)
|
||||
arg = get(_scaleAliases, arg, arg)
|
||||
|
||||
|
||||
+51
-74
@@ -232,9 +232,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
sinf = sind.(a)
|
||||
cosf = cosd.(a)
|
||||
rtick_values, rtick_labels = get_ticks(sp, yaxis)
|
||||
if yaxis[:formatter] in (:scientific, :auto) && yaxis[:ticks] in (:auto, :native)
|
||||
rtick_labels = convert_sci_unicode.(rtick_labels)
|
||||
end
|
||||
rtick_labels = gr_tick_label.((yaxis,), rtick_labels)
|
||||
|
||||
#draw angular grid
|
||||
if xaxis[:grid]
|
||||
@@ -307,15 +305,27 @@ end
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# draw ONE Shape
|
||||
function gr_draw_marker(xi, yi, msize, shape::Shape)
|
||||
function gr_draw_marker(series, xi, yi, clims, i, msize, shape::Shape)
|
||||
sx, sy = coords(shape)
|
||||
# convert to ndc coords (percentages of window)
|
||||
GR.selntran(0)
|
||||
w, h = gr_plot_size
|
||||
f = msize / (w + h)
|
||||
xi, yi = GR.wctondc(xi, yi)
|
||||
GR.fillarea(xi .+ sx .* f,
|
||||
yi .+ sy .* f)
|
||||
xs = xi .+ sx .* f
|
||||
ys = yi .+ sy .* f
|
||||
|
||||
# draw the interior
|
||||
mc = get_markercolor(series, clims, i)
|
||||
gr_set_fill(mc)
|
||||
gr_set_transparency(mc, get_markeralpha(series, i))
|
||||
GR.fillarea(xs, ys)
|
||||
|
||||
# draw the shapes
|
||||
msc = get_markerstrokecolor(series, i)
|
||||
gr_set_line(get_markerstrokewidth(series, i), :solid, msc)
|
||||
gr_set_transparency(msc, get_markerstrokealpha(series, i))
|
||||
GR.polyline(xs, ys)
|
||||
GR.selntran(1)
|
||||
end
|
||||
|
||||
@@ -325,7 +335,11 @@ function nominal_size()
|
||||
end
|
||||
|
||||
# draw ONE symbol marker
|
||||
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
|
||||
function gr_draw_marker(series, xi, yi, clims, i, msize::Number, shape::Symbol)
|
||||
GR.setborderwidth(series[:markerstrokewidth]);
|
||||
gr_set_bordercolor(get_markerstrokecolor(series, i));
|
||||
gr_set_markercolor(get_markercolor(series, clims, i));
|
||||
gr_set_transparency(get_markeralpha(series, i))
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
GR.setmarkersize(0.3msize / nominal_size())
|
||||
GR.polymarker([xi], [yi])
|
||||
@@ -343,13 +357,7 @@ function gr_draw_markers(series::Series, x, y, clims, msize = series[:markersize
|
||||
for i=eachindex(x)
|
||||
msi = _cycle(msize, i)
|
||||
shape = _cycle(shapes, i)
|
||||
i
|
||||
GR.setborderwidth(series[:markerstrokewidth]);
|
||||
gr_set_bordercolor(get_markerstrokecolor(series, i));
|
||||
gr_set_markercolor(get_markercolor(series, clims, i));
|
||||
gr_set_transparency(get_markeralpha(series, i))
|
||||
|
||||
gr_draw_marker(x[i], y[i], msi, shape)
|
||||
gr_draw_marker(series, x[i], y[i], clims, i, msi, shape)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -770,6 +778,19 @@ function gr_get_ticks_size(ticks, rot)
|
||||
return w, h
|
||||
end
|
||||
|
||||
gr_tick_label(axis,label) = (axis[:formatter] in (:scientific, :auto)) ?
|
||||
gr_convert_sci_tick_label(label) :
|
||||
label
|
||||
|
||||
function gr_convert_sci_tick_label(label)
|
||||
caret_split = split(label,'^')
|
||||
if length(caret_split) == 2
|
||||
base, exponent = caret_split
|
||||
label = "$base^{$exponent}"
|
||||
end
|
||||
convert_sci_unicode(label)
|
||||
end
|
||||
|
||||
function gr_axis_height(sp, axis)
|
||||
ticks = get_ticks(sp, axis)
|
||||
gr_set_font(tickfont(axis))
|
||||
@@ -960,7 +981,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
xy_lims = data_lims
|
||||
|
||||
ratio = sp[:aspect_ratio]
|
||||
ratio = get_aspect_ratio(sp)
|
||||
if ratio != :none
|
||||
if ratio == :equal
|
||||
ratio = 1
|
||||
@@ -1039,11 +1060,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
if st == :pie
|
||||
draw_axes = false
|
||||
end
|
||||
if st == :heatmap
|
||||
if st in (:heatmap, :image)
|
||||
outside_ticks = true
|
||||
for ax in (sp[:xaxis], sp[:yaxis])
|
||||
v = series[ax[:letter]]
|
||||
end
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(series[:z]))
|
||||
xy_lims = x[1], x[end], y[1], y[end]
|
||||
expand_extrema!(sp[:xaxis], x)
|
||||
@@ -1223,18 +1241,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
for (cv, dv) in zip(xticks...)
|
||||
xi, yi = gr_w3tondc(cv, yt, zt)
|
||||
if xaxis[:ticks] in (:auto, :native)
|
||||
if xaxis[:formatter] in (:scientific, :auto)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if xaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
end
|
||||
dv = convert_sci_unicode(dv)
|
||||
end
|
||||
end
|
||||
xi += (yaxis[:mirror] ? 1 : -1) * 1e-2 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0)
|
||||
yi += (xaxis[:mirror] ? 1 : -1) * 5e-3 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0)
|
||||
gr_text(xi, yi, string(dv))
|
||||
gr_text(xi, yi, gr_tick_label(xaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1262,16 +1271,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
for (cv, dv) in zip(yticks...)
|
||||
xi, yi = gr_w3tondc(xt, cv, zt)
|
||||
if yaxis[:ticks] in (:auto, :native)
|
||||
if xaxis[:formatter] in (:scientific, :auto)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if yaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
end
|
||||
dv = convert_sci_unicode(dv)
|
||||
end
|
||||
end
|
||||
gr_text(xi + (yaxis[:mirror] ? -1 : 1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0), yi + (yaxis[:mirror] ? 1 : -1) * 5e-3 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0), string(dv))
|
||||
gr_text(xi + (yaxis[:mirror] ? -1 : 1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
yi + (yaxis[:mirror] ? 1 : -1) * 5e-3 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
gr_tick_label(yaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1299,16 +1301,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
for (cv, dv) in zip(zticks...)
|
||||
xi, yi = gr_w3tondc(xt, yt, cv)
|
||||
if zaxis[:ticks] in (:auto, :native)
|
||||
if zaxis[:formatter] in (:scientific, :auto)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if zaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
end
|
||||
dv = convert_sci_unicode(dv)
|
||||
end
|
||||
end
|
||||
gr_text(xi + (zaxis[:mirror] ? 1 : -1) * 1e-2 * (zaxis[:tick_direction] == :out ? 1.5 : 1.0), yi, string(dv))
|
||||
gr_text(xi + (zaxis[:mirror] ? 1 : -1) * 1e-2 * (zaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
yi, gr_tick_label(zaxis, dv))
|
||||
end
|
||||
end
|
||||
#
|
||||
@@ -1405,19 +1399,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# x labels
|
||||
flip, mirror = gr_set_xticks_font(sp)
|
||||
for (cv, dv) in zip(xticks...)
|
||||
# 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)
|
||||
if xaxis[:ticks] in (:auto, :native)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if xaxis[:formatter] in (:scientific, :auto)
|
||||
if xaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
end
|
||||
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))
|
||||
gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
gr_tick_label(xaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1425,19 +1409,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# y labels
|
||||
flip, mirror = gr_set_yticks_font(sp)
|
||||
for (cv, dv) in zip(yticks...)
|
||||
# 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
|
||||
if yaxis[:ticks] in (:auto, :native)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if yaxis[:formatter] in (:scientific, :auto)
|
||||
if yaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
end
|
||||
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))
|
||||
gr_text(xi + (mirror ? 1 : -1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
yi,
|
||||
gr_tick_label(yaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1808,8 +1783,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st == :image
|
||||
z = transpose_z(series, series[:z].surf, true)'
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(z))
|
||||
w, h = size(z)
|
||||
xmin, xmax = ignorenan_extrema(series[:x]); ymin, ymax = ignorenan_extrema(series[:y])
|
||||
xmin, xmax = ignorenan_extrema(x)
|
||||
ymin, ymax = ignorenan_extrema(y)
|
||||
rgba = gr_color.(z)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
||||
end
|
||||
|
||||
@@ -109,7 +109,7 @@ _hdf5_datapath(subpath::String) = "$_hdf5_dataroot/$subpath"
|
||||
_hdf5_map_str2telem(k::String) = HDF5PLOT_MAP_STR2TELEM[k]
|
||||
_hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
|
||||
|
||||
function _hdf5_merge!(dest::Dict, src::Dict)
|
||||
function _hdf5_merge!(dest, src)
|
||||
for (k, v) in src
|
||||
if isa(v, Axis)
|
||||
_hdf5_merge!(dest[k].plotattributes, v.plotattributes)
|
||||
@@ -295,7 +295,7 @@ function _hdf5plot_gwrite(grp, k::String, v::Tuple)
|
||||
end
|
||||
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::Dict)
|
||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::AKW)
|
||||
# @warn("Cannot write dict: $k=$plotattributes")
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::AbstractRange)
|
||||
@@ -376,7 +376,7 @@ function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
||||
_hdf5plot_writetype(grp, Subplot)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_write(grp, plotattributes::Dict)
|
||||
function _hdf5plot_write(grp, plotattributes::AKW)
|
||||
for (k, v) in plotattributes
|
||||
kstr = string(k)
|
||||
_hdf5plot_gwrite(grp, kstr, v)
|
||||
@@ -567,7 +567,7 @@ function _hdf5plot_read(grp, k::String)
|
||||
end
|
||||
|
||||
#Read in values in group to populate plotattributes:
|
||||
function _hdf5plot_read(grp, plotattributes::Dict)
|
||||
function _hdf5plot_read(grp, plotattributes::AKW)
|
||||
gnames = names(grp)
|
||||
for k in gnames
|
||||
try
|
||||
|
||||
@@ -470,7 +470,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
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)
|
||||
if get_aspect_ratio(sp) in (1, :equal)
|
||||
kw[:axisEqual] = "true"
|
||||
end
|
||||
|
||||
@@ -482,12 +482,12 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
||||
|
||||
push!(style, string(
|
||||
"legend style = {",
|
||||
pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], fg_alpha, "solid", ), ",",
|
||||
"fill = $cstr,",
|
||||
"fill opacity = $bg_alpha,",
|
||||
"text opacity = $(alpha(plot_color(sp[:legendfontcolor]))),",
|
||||
"font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
|
||||
"legend style = {",
|
||||
pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], fg_alpha, "solid", ), ",",
|
||||
"fill = $cstr,",
|
||||
"fill opacity = $bg_alpha,",
|
||||
"text opacity = $(alpha(plot_color(sp[:legendfontcolor]))),",
|
||||
"font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
|
||||
"}",
|
||||
))
|
||||
|
||||
|
||||
@@ -255,6 +255,9 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
end
|
||||
for (i, rng) in enumerate(segments)
|
||||
segment_opt = PGFPlotsX.Options()
|
||||
if opt[:label] == ""
|
||||
push!(segment_opt, "forget plot" => nothing)
|
||||
end
|
||||
segment_opt = merge(segment_opt, pgfx_linestyle(opt, i))
|
||||
if opt[:markershape] != :none
|
||||
marker = opt[:markershape]
|
||||
|
||||
@@ -108,7 +108,7 @@ function shrink_by(lo, sz, ratio)
|
||||
end
|
||||
|
||||
function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
|
||||
aspect_ratio = sp[:aspect_ratio]
|
||||
aspect_ratio = get_aspect_ratio(sp)
|
||||
if aspect_ratio != :none
|
||||
if aspect_ratio == :equal
|
||||
aspect_ratio = 1.0
|
||||
@@ -780,7 +780,7 @@ end
|
||||
|
||||
function plotly_hover!(plotattributes_out::KW, hover)
|
||||
# hover text
|
||||
if all(in([:none, false]), hover)
|
||||
if hover === nothing || all(in([:none, false]), hover)
|
||||
plotattributes_out[:hoverinfo] = "none"
|
||||
elseif any(!isnothing, hover)
|
||||
plotattributes_out[:hoverinfo] = "text"
|
||||
|
||||
@@ -1155,7 +1155,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
end
|
||||
|
||||
# aspect ratio
|
||||
aratio = sp[:aspect_ratio]
|
||||
aratio = get_aspect_ratio(sp)
|
||||
if aratio != :none
|
||||
ax."set_aspect"(isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
|
||||
end
|
||||
|
||||
+903
-520
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -616,7 +616,7 @@ end
|
||||
# ----------------------------------------------------------------------
|
||||
# @layout macro
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
|
||||
function add_layout_pct!(kw::AKW, v::Expr, idx::Integer, nidx::Integer)
|
||||
# dump(v)
|
||||
# something like {0.2w}?
|
||||
if v.head == :call && v.args[1] == :*
|
||||
@@ -637,7 +637,7 @@ function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
|
||||
error("Couldn't match layout curly (idx=$idx): $v")
|
||||
end
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Number, idx::Integer)
|
||||
function add_layout_pct!(kw::AKW, v::Number, idx::Integer)
|
||||
# kw[idx == 1 ? :w : :h] = v*pct
|
||||
idx == 1 && (kw[:w] = v*pct)
|
||||
(idx == 2 || nidx == 1) && (kw[:h] = v*pct)
|
||||
|
||||
@@ -60,6 +60,13 @@ function html(plt::Plot, fn::AbstractString)
|
||||
end
|
||||
html(fn::AbstractString) = html(current(), fn)
|
||||
|
||||
function txt(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "txt")
|
||||
io = open(fn, "w")
|
||||
show(io, MIME("text/plain"), plt)
|
||||
close(io)
|
||||
end
|
||||
txt(fn::AbstractString) = txt(current(), fn)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -73,6 +80,7 @@ const _savemap = Dict(
|
||||
"tex" => tex,
|
||||
"html" => html,
|
||||
"tikz" => tex,
|
||||
"txt" => txt,
|
||||
)
|
||||
|
||||
function getExtension(fn::AbstractString)
|
||||
|
||||
+12
-12
@@ -3,14 +3,14 @@
|
||||
# ------------------------------------------------------------------
|
||||
# preprocessing
|
||||
|
||||
function series_idx(kw_list::AVec{KW}, kw::KW)
|
||||
function series_idx(kw_list::AVec{KW}, kw::AKW)
|
||||
Int(kw[:series_plotindex]) - Int(kw_list[1][:series_plotindex]) + 1
|
||||
end
|
||||
|
||||
function _expand_seriestype_array(plotattributes::KW, args)
|
||||
function _expand_seriestype_array(plotattributes::AKW, args)
|
||||
sts = get(plotattributes, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(plotattributes, :seriestype)
|
||||
reset_kw!(plotattributes, :seriestype)
|
||||
rd = Vector{RecipeData}(undef, size(sts, 1))
|
||||
for r in axes(sts, 1)
|
||||
dc = copy(plotattributes)
|
||||
@@ -23,7 +23,7 @@ function _expand_seriestype_array(plotattributes::KW, args)
|
||||
end
|
||||
end
|
||||
|
||||
function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{RecipeData})
|
||||
function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{RecipeData})
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
@@ -46,7 +46,7 @@ function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{Rec
|
||||
_axis_defaults_byletter[:y],
|
||||
_axis_defaults_byletter[:z])
|
||||
if haskey(defdict, k)
|
||||
delete!(plotattributes, k)
|
||||
reset_kw!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -59,7 +59,7 @@ end
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(plotattributes, args, still_to_process)
|
||||
|
||||
@@ -110,7 +110,7 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD
|
||||
return
|
||||
end
|
||||
|
||||
function _preprocess_userrecipe(kw::KW)
|
||||
function _preprocess_userrecipe(kw::AKW)
|
||||
_add_markershape(kw)
|
||||
|
||||
# if there was a grouping, filter the data here
|
||||
@@ -134,7 +134,7 @@ function _preprocess_userrecipe(kw::KW)
|
||||
return
|
||||
end
|
||||
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
# handle error bars by creating new recipedata data... these will have
|
||||
# the same recipedata index as the recipedata they are copied from
|
||||
for esym in (:xerror, :yerror)
|
||||
@@ -149,7 +149,7 @@ function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
# handle smoothing by adding a new series
|
||||
if get(kw, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
@@ -174,7 +174,7 @@ end
|
||||
# to generate a list of RecipeData objects (data + attributes).
|
||||
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
|
||||
# otherwise we just add the original KW.
|
||||
function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_process::Vector{KW})
|
||||
function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_process::Vector{KW})
|
||||
if !isa(get(kw, :seriestype, nothing), Symbol)
|
||||
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
|
||||
push!(kw_list, kw)
|
||||
@@ -205,7 +205,7 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
|
||||
function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (plotattributes[k] = pop!(kw, k))
|
||||
@@ -254,7 +254,7 @@ function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
plt[:inset_subplots] = nothing
|
||||
end
|
||||
|
||||
function _subplot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# we'll keep a map of subplot to an attribute override dict.
|
||||
# Subplot/Axis attributes set by a user/series recipe apply only to the
|
||||
# Subplot object which they belong to.
|
||||
|
||||
+1
-1
@@ -163,7 +163,7 @@ end
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
function _plot!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
plotattributes[:plot_object] = plt
|
||||
|
||||
if !isempty(args) && !isdefined(Main, :StatsPlots) &&
|
||||
|
||||
+324
-124
@@ -44,10 +44,10 @@ end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
num_series(x::AMat) = size(x,2)
|
||||
num_series(x::AMat) = size(x, 2)
|
||||
num_series(x) = 1
|
||||
|
||||
RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -55,13 +55,26 @@ RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where
|
||||
# for seriestype `line`, need to sort by x values
|
||||
|
||||
const POTENTIAL_VECTOR_ARGUMENTS = [
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linealpha, :linewidth, :linestyle, :line_z,
|
||||
:fillcolor, :fillalpha, :fill_z,
|
||||
:markercolor, :markeralpha, :markershape, :marker_z,
|
||||
:markerstrokecolor, :markerstrokealpha,
|
||||
:yerror, :yerror,
|
||||
:series_annotations, :fillrange
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linealpha,
|
||||
:linewidth,
|
||||
:linestyle,
|
||||
:line_z,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:fill_z,
|
||||
:markercolor,
|
||||
:markeralpha,
|
||||
:markershape,
|
||||
:marker_z,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:yerror,
|
||||
:yerror,
|
||||
:series_annotations,
|
||||
:fillrange,
|
||||
]
|
||||
|
||||
@recipe function f(::Type{Val{:line}}, x, y, z)
|
||||
@@ -99,7 +112,7 @@ end
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = repeat(Float64[-1, 1, NaN], n)
|
||||
newy = vec(Float64[yi for i=1:3,yi=y])
|
||||
newy = vec(Float64[yi for i = 1:3, yi in y])
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :straightline
|
||||
@@ -109,7 +122,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i=1:3,yi=y])
|
||||
newx = vec(Float64[yi for i = 1:3, yi in y])
|
||||
newy = repeat(Float64[-1, 1, NaN], n)
|
||||
x := newx
|
||||
y := newy
|
||||
@@ -121,7 +134,7 @@ end
|
||||
@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]...)
|
||||
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
@@ -132,7 +145,7 @@ end
|
||||
|
||||
@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]...)
|
||||
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
@@ -156,7 +169,7 @@ end
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
()
|
||||
()
|
||||
end
|
||||
@deps scatterpath path scatter
|
||||
|
||||
@@ -169,7 +182,7 @@ function make_steps(x::AbstractArray, st)
|
||||
n = length(x)
|
||||
n == 0 && return zeros(0)
|
||||
newx = zeros(2n - 1)
|
||||
for i in 1:n
|
||||
for i = 1:n
|
||||
idx = 2i - 1
|
||||
newx[idx] = x[i]
|
||||
if i > 1
|
||||
@@ -249,10 +262,10 @@ end
|
||||
end
|
||||
end
|
||||
newx, newy = zeros(3n), zeros(3n)
|
||||
for i=1:n
|
||||
rng = 3i-2:3i
|
||||
for i = 1:n
|
||||
rng = (3i - 2):(3i)
|
||||
newx[rng] = [x[i], x[i], NaN]
|
||||
newy[rng] = [_cycle(fr,i), y[i], NaN]
|
||||
newy[rng] = [_cycle(fr, i), y[i], NaN]
|
||||
end
|
||||
x := newx
|
||||
y := newy
|
||||
@@ -282,16 +295,16 @@ end
|
||||
# get the value of the curve point at position t
|
||||
function bezier_value(pts::AVec, t::Real)
|
||||
val = 0.0
|
||||
n = length(pts)-1
|
||||
for (i,p) in enumerate(pts)
|
||||
val += p * binomial(n, i-1) * (1-t)^(n-i+1) * t^(i-1)
|
||||
n = length(pts) - 1
|
||||
for (i, p) in enumerate(pts)
|
||||
val += p * binomial(n, i - 1) * (1 - t)^(n - i + 1) * t^(i - 1)
|
||||
end
|
||||
val
|
||||
end
|
||||
|
||||
# create segmented bezier curves in place of line segments
|
||||
@recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30)
|
||||
args = z !== nothing ? (x,y,z) : (x,y)
|
||||
args = z !== nothing ? (x, y, z) : (x, y)
|
||||
newx, newy = zeros(0), zeros(0)
|
||||
fr = plotattributes[:fillrange]
|
||||
newfr = fr !== nothing ? zeros(0) : nothing
|
||||
@@ -304,13 +317,13 @@ end
|
||||
for rng in iter_segments(args...)
|
||||
length(rng) < 2 && continue
|
||||
ts = range(0, stop = 1, length = npoints)
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x,rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y,rng), t), ts))
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x, rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y, rng), t), ts))
|
||||
if z !== nothing
|
||||
nanappend!(newz, map(t -> bezier_value(_cycle(z,rng), t), ts))
|
||||
nanappend!(newz, map(t -> bezier_value(_cycle(z, rng), t), ts))
|
||||
end
|
||||
if fr !== nothing
|
||||
nanappend!(newfr, map(t -> bezier_value(_cycle(fr,rng), t), ts))
|
||||
nanappend!(newfr, map(t -> bezier_value(_cycle(fr, rng), t), ts))
|
||||
end
|
||||
# if lz !== nothing
|
||||
# lzrng = _cycle(lz, rng) # the line_z's for this segment
|
||||
@@ -343,14 +356,15 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y)
|
||||
procx, procy, xscale, yscale, baseline =
|
||||
_preprocess_barlike(plotattributes, x, y)
|
||||
nx, ny = length(procx), length(procy)
|
||||
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=procx]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi in procx]
|
||||
procx = if nx == ny
|
||||
cv
|
||||
elseif nx == ny + 1
|
||||
0.5diff(cv) + cv[1:end-1]
|
||||
0.5 * diff(cv) + cv[1:(end - 1)]
|
||||
else
|
||||
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
|
||||
end
|
||||
@@ -359,12 +373,12 @@ end
|
||||
bw = plotattributes[:bar_width]
|
||||
hw = if bw === nothing
|
||||
if nx > 1
|
||||
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
|
||||
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=eachindex(procx)]
|
||||
Float64[0.5 * _cycle(bw, i) for i in eachindex(procx)]
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
@@ -378,13 +392,20 @@ end
|
||||
|
||||
# create the bar shapes by adding x/y segments
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i=1:ny
|
||||
for i = 1:ny
|
||||
yi = procy[i]
|
||||
if !isnan(yi)
|
||||
center = procx[i]
|
||||
hwi = _cycle(hw,i)
|
||||
fi = _cycle(fillto,i)
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
hwi = _cycle(hw, i)
|
||||
fi = _cycle(fillto, i)
|
||||
push!(
|
||||
xseg,
|
||||
center - hwi,
|
||||
center - hwi,
|
||||
center + hwi,
|
||||
center + hwi,
|
||||
center - hwi,
|
||||
)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
end
|
||||
end
|
||||
@@ -415,8 +436,8 @@ end
|
||||
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
|
||||
for i = 1:m # y
|
||||
for j = 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]]
|
||||
@@ -440,13 +461,17 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
|
||||
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
||||
_bin_centers(v::AVec) = (v[1:(end - 1)] + v[2:end]) / 2
|
||||
|
||||
_is_positive(x) = (x > 0) && !(x ≈ 0)
|
||||
|
||||
_positive_else_nan(::Type{T}, x::Real) where {T} = _is_positive(x) ? T(x) : T(NaN)
|
||||
|
||||
function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) where T<:AbstractFloat
|
||||
function _scale_adjusted_values(
|
||||
::Type{T},
|
||||
V::AbstractVector,
|
||||
scale::Symbol,
|
||||
) where {T<:AbstractFloat}
|
||||
if scale in _logScales
|
||||
[_positive_else_nan(T, x) for x in V]
|
||||
else
|
||||
@@ -455,7 +480,7 @@ function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) whe
|
||||
end
|
||||
|
||||
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
|
||||
if (scale in _logScales)
|
||||
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
|
||||
else
|
||||
@@ -464,7 +489,11 @@ function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
|
||||
end
|
||||
|
||||
|
||||
function _preprocess_binbarlike_weights(::Type{T}, w, wscale::Symbol) where T<:AbstractFloat
|
||||
function _preprocess_binbarlike_weights(
|
||||
::Type{T},
|
||||
w,
|
||||
wscale::Symbol,
|
||||
) where {T<:AbstractFloat}
|
||||
w_adj = _scale_adjusted_values(T, w, wscale)
|
||||
w_min = ignorenan_minimum(w_adj)
|
||||
w_max = ignorenan_maximum(w_adj)
|
||||
@@ -490,7 +519,8 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
if (plotattributes[:bar_width] === nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
@@ -503,8 +533,9 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
xerror := diff(edge)/2
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
xerror := diff(edge) / 2
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
seriestype := :scatter
|
||||
@@ -513,7 +544,13 @@ end
|
||||
@deps scatterbins scatter
|
||||
|
||||
|
||||
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
|
||||
function _stepbins_path(
|
||||
edge,
|
||||
weights,
|
||||
baseline::Real,
|
||||
xscale::Symbol,
|
||||
yscale::Symbol,
|
||||
)
|
||||
log_scale_x = xscale in _logScales
|
||||
log_scale_y = yscale in _logScales
|
||||
|
||||
@@ -538,7 +575,7 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
||||
w, it_state_w = it_tuple_w
|
||||
|
||||
if (log_scale_x && a ≈ 0)
|
||||
a = oftype(a, b/_logScaleBases[xscale]^3)
|
||||
a = oftype(a, b / _logScaleBases[xscale]^3)
|
||||
end
|
||||
|
||||
if isnan(w)
|
||||
@@ -575,9 +612,11 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||
axis = plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
axis =
|
||||
plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
|
||||
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||
if !isvertical(plotattributes)
|
||||
@@ -607,9 +646,19 @@ end
|
||||
end
|
||||
Plots.@deps stepbins path
|
||||
|
||||
wand_edges(x...) = (@warn("Load the StatsPlots package in order to use :wand bins. Defaulting to :auto", once = true); :auto)
|
||||
wand_edges(x...) = (
|
||||
@warn(
|
||||
"Load the StatsPlots package in order to use :wand bins. Defaulting to :auto",
|
||||
once = true
|
||||
);
|
||||
:auto
|
||||
)
|
||||
|
||||
function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto) where N
|
||||
function _auto_binning_nbins(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
dim::Integer;
|
||||
mode::Symbol = :auto,
|
||||
) where {N}
|
||||
max_bins = 10_000
|
||||
_cl(x) = min(ceil(Int, max(x, one(x))), max_bins)
|
||||
_iqr(v) = (q = quantile(v, 0.75) - quantile(v, 0.25); q > 0 ? q : oftype(q, 1))
|
||||
@@ -618,8 +667,13 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
||||
n_samples = length(LinearIndices(first(vs)))
|
||||
|
||||
# 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 ? 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
|
||||
nd = n_samples^(1 / (2 + N))
|
||||
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]
|
||||
|
||||
@@ -644,32 +698,52 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
||||
end
|
||||
end
|
||||
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) where {N} = binning
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} =
|
||||
StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} =
|
||||
_hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
dim::Integer,
|
||||
binning::AbstractVector,
|
||||
) where {N} = binning
|
||||
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N, Any}) where {N} =
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N,Any}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...,))
|
||||
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
|
||||
_hist_edges(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
binning::Union{Integer,Symbol,AbstractVector},
|
||||
) where {N} = map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
|
||||
|
||||
_hist_norm_mode(mode::Symbol) = mode
|
||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
||||
|
||||
_filternans(vs::NTuple{1,AbstractVector}) = filter!.(isfinite, vs)
|
||||
function _filternans(vs::NTuple{N,AbstractVector}) where N
|
||||
_invertedindex(v, not) = [j for (i,j) in enumerate(v) if !(i ∈ not)]
|
||||
function _filternans(vs::NTuple{N,AbstractVector}) where {N}
|
||||
_invertedindex(v, not) = [j for (i, j) in enumerate(v) if !(i ∈ not)]
|
||||
nots = union(Set.(findall.(!isfinite, vs))...)
|
||||
_invertedindex.(vs, Ref(nots))
|
||||
end
|
||||
|
||||
function _make_hist(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing) where N
|
||||
function _make_hist(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
binning;
|
||||
normed = false,
|
||||
weights = nothing,
|
||||
) where {N}
|
||||
localvs = _filternans(vs)
|
||||
edges = _hist_edges(localvs, binning)
|
||||
h = float( weights === nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, StatsBase.Weights(weights), edges, closed = :left)
|
||||
h = float(
|
||||
weights === nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||
StatsBase.fit(
|
||||
StatsBase.Histogram,
|
||||
localvs,
|
||||
StatsBase.Weights(weights),
|
||||
edges,
|
||||
closed = :left,
|
||||
),
|
||||
)
|
||||
normalize!(h, mode = _hist_norm_mode(normed))
|
||||
end
|
||||
@@ -682,7 +756,12 @@ end
|
||||
@deps histogram barhist
|
||||
|
||||
@recipe function f(::Type{Val{:barhist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :barbins
|
||||
@@ -691,7 +770,12 @@ end
|
||||
@deps barhist barbins
|
||||
|
||||
@recipe function f(::Type{Val{:stephist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :stepbins
|
||||
@@ -700,7 +784,12 @@ end
|
||||
@deps stephist stepbins
|
||||
|
||||
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :scatterbins
|
||||
@@ -709,19 +798,23 @@ end
|
||||
@deps scatterhist scatterbins
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T, 1, E}) where {T, E}
|
||||
@recipe function f(h::StatsBase.Histogram{T,1,E}) where {T,E}
|
||||
seriestype --> :barbins
|
||||
|
||||
st_map = Dict(
|
||||
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
|
||||
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
|
||||
:bar => :barbins,
|
||||
:scatter => :scatterbins,
|
||||
:step => :stepbins,
|
||||
:steppost => :stepbins, # :step can be mapped to :steppost in pre-processing
|
||||
)
|
||||
seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
seriestype :=
|
||||
get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
|
||||
if plotattributes[:seriestype] == :scatterbins
|
||||
# Workaround, error bars currently not set correctly by scatterbins
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1])/2
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1]) / 2
|
||||
seriestype := :scatter
|
||||
(Plots._bin_centers(edge), weights)
|
||||
else
|
||||
@@ -730,7 +823,7 @@ end
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(hv::AbstractVector{H}) where H <: StatsBase.Histogram
|
||||
@recipe function f(hv::AbstractVector{H}) where {H<:StatsBase.Histogram}
|
||||
for h in hv
|
||||
@series begin
|
||||
h
|
||||
@@ -769,7 +862,12 @@ Plots.@deps bins2d heatmap
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
h = _make_hist((x, y), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(x, y),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.edges[2]
|
||||
z := Surface(h.weights)
|
||||
@@ -779,7 +877,7 @@ end
|
||||
@deps histogram2d bins2d
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T, 2, E}) where {T, E}
|
||||
@recipe function f(h::StatsBase.Histogram{T,2,E}) where {T,E}
|
||||
seriestype --> :bins2d
|
||||
(h.edges[1], h.edges[2], Surface(h.weights))
|
||||
end
|
||||
@@ -801,6 +899,91 @@ end
|
||||
# note: don't add dependencies because this really isn't a drop-in replacement
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# lens! - magnify a region of a plot
|
||||
lens!(args...;kwargs...) = plot!(args...; seriestype=:lens, kwargs...)
|
||||
export lens!
|
||||
@recipe function f(::Type{Val{:lens}}, plt::AbstractPlot)
|
||||
sp_index, inset_bbox = plotattributes[:inset_subplots]
|
||||
if !(width(inset_bbox) isa Measures.Length{:w,<:Real})
|
||||
throw(ArgumentError("Inset bounding box needs to in relative coordinates."))
|
||||
end
|
||||
sp = plt.subplots[sp_index]
|
||||
xl1, xl2 = xlims(plt.subplots[sp_index])
|
||||
bbx1 = xl1 + left(inset_bbox).value * (xl2 - xl1)
|
||||
bbx2 = bbx1 + width(inset_bbox).value * (xl2 - xl1)
|
||||
yl1, yl2 = ylims(plt.subplots[sp_index])
|
||||
bby1 = yl1 + (1 - bottom(inset_bbox).value) * (yl2 - yl1)
|
||||
bby2 = bby1 + height(inset_bbox).value * (yl2 - yl1)
|
||||
bbx = bbx1 + width(inset_bbox).value * (xl2 - xl1) / 2
|
||||
bby = bby1 + height(inset_bbox).value * (yl2 - yl1) / 2
|
||||
lens_index = last(plt.subplots)[:subplot_index] + 1
|
||||
x1, x2 = plotattributes[:x]
|
||||
y1, y2 = plotattributes[:y]
|
||||
seriestype := :path
|
||||
label := ""
|
||||
linecolor := :lightgray
|
||||
bbx_mag = (x1 + x2) / 2
|
||||
bby_mag = (y1 + y2) / 2
|
||||
xi_lens, yi_lens = intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
|
||||
xi_mag, yi_mag = intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
|
||||
# add lines
|
||||
if xl1 < xi_lens < xl2 &&
|
||||
yl1 < yi_lens < yl2
|
||||
@series begin
|
||||
subplot := sp_index
|
||||
x := [xi_mag, xi_lens]
|
||||
y := [yi_mag, yi_lens]
|
||||
()
|
||||
end
|
||||
end
|
||||
# add magnification shape
|
||||
@series begin
|
||||
subplot := sp_index
|
||||
x := [x1, x1, x2, x2, x1]
|
||||
y := [y1, y2, y2, y1, y1]
|
||||
()
|
||||
end
|
||||
# add subplot
|
||||
for series in sp.series_list
|
||||
@series begin
|
||||
plotattributes = merge(plotattributes, copy(series.plotattributes))
|
||||
subplot := lens_index
|
||||
label := ""
|
||||
xlims := (x1, x2)
|
||||
ylims := (y1, y2)
|
||||
()
|
||||
end
|
||||
end
|
||||
backup = copy(plotattributes)
|
||||
empty!(plotattributes)
|
||||
seriestype := :path
|
||||
series_plotindex := backup[:series_plotindex]
|
||||
end
|
||||
|
||||
function intersection_point(xA, yA, xB, yB, h, w)
|
||||
s = (yA - yB) / (xA - xB)
|
||||
hh = h / 2
|
||||
hw = w / 2
|
||||
# left or right?
|
||||
if -hh <= s * hw <= hh
|
||||
if xA > xB
|
||||
# right
|
||||
return xB + hw, yB + s * hw
|
||||
else # left
|
||||
return xB - hw, yB - s * hw
|
||||
end
|
||||
# top or bot?
|
||||
elseif -hw <= hh/s <= hw
|
||||
if yA > yB
|
||||
# top
|
||||
return xB + hh/s, yB + hh
|
||||
else
|
||||
# bottom
|
||||
return xB - hh/s, yB - hh
|
||||
end
|
||||
end
|
||||
end
|
||||
# ---------------------------------------------------------------------------
|
||||
# contourf - filled contours
|
||||
|
||||
@@ -813,7 +996,7 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# Error Bars
|
||||
|
||||
function error_style!(plotattributes::KW)
|
||||
function error_style!(plotattributes::AKW)
|
||||
plotattributes[:seriestype] = :path
|
||||
plotattributes[:linecolor] = plotattributes[:markerstrokecolor]
|
||||
plotattributes[:linewidth] = plotattributes[:markerstrokewidth]
|
||||
@@ -855,7 +1038,11 @@ end
|
||||
@recipe function f(::Type{Val{:yerror}}, x, y, z)
|
||||
error_style!(plotattributes)
|
||||
markershape := :hline
|
||||
plotattributes[:x], plotattributes[:y] = error_coords(plotattributes[:x], plotattributes[:y], error_zipit(plotattributes[:yerror]))
|
||||
plotattributes[:x], plotattributes[:y] = error_coords(
|
||||
plotattributes[:x],
|
||||
plotattributes[:y],
|
||||
error_zipit(plotattributes[:yerror]),
|
||||
)
|
||||
()
|
||||
end
|
||||
@deps yerror path
|
||||
@@ -863,7 +1050,11 @@ end
|
||||
@recipe function f(::Type{Val{:xerror}}, x, y, z)
|
||||
error_style!(plotattributes)
|
||||
markershape := :vline
|
||||
plotattributes[:y], plotattributes[:x] = error_coords(plotattributes[:y], plotattributes[:x], error_zipit(plotattributes[:xerror]))
|
||||
plotattributes[:y], plotattributes[:x] = error_coords(
|
||||
plotattributes[:y],
|
||||
plotattributes[:x],
|
||||
error_zipit(plotattributes[:xerror]),
|
||||
)
|
||||
()
|
||||
end
|
||||
@deps xerror path
|
||||
@@ -874,8 +1065,8 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# quiver
|
||||
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(plotattributes::KW)
|
||||
# function apply_series_recipe(plotattributes::AKW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(plotattributes::AKW)
|
||||
plotattributes[:label] = ""
|
||||
plotattributes[:seriestype] = :path
|
||||
if !isa(plotattributes[:arrow], Arrow)
|
||||
@@ -898,23 +1089,23 @@ function quiver_using_arrows(plotattributes::KW)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
elseif isa(vi, Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
end
|
||||
|
||||
# add the points
|
||||
nanappend!(x, [xi, xi+vx, NaN])
|
||||
nanappend!(y, [yi, yi+vy, NaN])
|
||||
nanappend!(x, [xi, xi + vx, NaN])
|
||||
nanappend!(y, [yi, yi + vy, NaN])
|
||||
end
|
||||
|
||||
plotattributes[:x], plotattributes[:y] = x, y
|
||||
# KW[plotattributes]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(plotattributes::KW)
|
||||
# function apply_series_recipe(plotattributes::AKW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(plotattributes::AKW)
|
||||
plotattributes[:label] = ""
|
||||
plotattributes[:seriestype] = :shape
|
||||
|
||||
@@ -936,7 +1127,7 @@ function quiver_using_hack(plotattributes::KW)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
elseif isa(vi, Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
@@ -951,15 +1142,18 @@ function quiver_using_hack(plotattributes::KW)
|
||||
U1 *= arrow_h
|
||||
U2 *= arrow_w
|
||||
|
||||
ppv = p+v
|
||||
nanappend!(pts, P2[p, ppv-U1, ppv-U1+U2, ppv, ppv-U1-U2, ppv-U1])
|
||||
ppv = p + v
|
||||
nanappend!(
|
||||
pts,
|
||||
P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1],
|
||||
)
|
||||
end
|
||||
|
||||
plotattributes[:x], plotattributes[:y] = Plots.unzip(pts[2:end])
|
||||
# KW[plotattributes]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
# function apply_series_recipe(plotattributes::AKW, ::Type{Val{:quiver}})
|
||||
@recipe function f(::Type{Val{:quiver}}, x, y, z)
|
||||
if :arrow in supported_attrs()
|
||||
quiver_using_arrows(plotattributes)
|
||||
@@ -977,32 +1171,28 @@ end
|
||||
|
||||
"Represent Open High Low Close data (used in finance)"
|
||||
mutable struct OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
end
|
||||
Base.convert(::Type{OHLC}, tup::Tuple) = OHLC(tup...)
|
||||
# Base.tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close)
|
||||
|
||||
# get one OHLC path
|
||||
function get_xy(o::OHLC, x, xdiff)
|
||||
xl, xm, xr = x-xdiff, x, x+xdiff
|
||||
ox = [xl, xm, NaN,
|
||||
xm, xm, NaN,
|
||||
xm, xr]
|
||||
oy = [o.open, o.open, NaN,
|
||||
o.low, o.high, NaN,
|
||||
o.close, o.close]
|
||||
xl, xm, xr = x - xdiff, x, x + xdiff
|
||||
ox = [xl, xm, NaN, xm, xm, NaN, xm, xr]
|
||||
oy = [o.open, o.open, NaN, o.low, o.high, NaN, o.close, o.close]
|
||||
ox, oy
|
||||
end
|
||||
|
||||
# get the joined vector
|
||||
function get_xy(v::AVec{OHLC}, x = eachindex(v))
|
||||
xdiff = 0.3ignorenan_mean(abs.(diff(x)))
|
||||
xdiff = 0.3 * ignorenan_mean(abs.(diff(x)))
|
||||
x_out, y_out = zeros(0), zeros(0)
|
||||
for (i,ohlc) in enumerate(v)
|
||||
ox,oy = get_xy(ohlc, x[i], xdiff)
|
||||
for (i, ohlc) in enumerate(v)
|
||||
ox, oy = get_xy(ohlc, x[i], xdiff)
|
||||
nanappend!(x_out, ox)
|
||||
nanappend!(y_out, oy)
|
||||
end
|
||||
@@ -1015,10 +1205,17 @@ end
|
||||
|
||||
# to squash ambiguity warnings...
|
||||
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
|
||||
@recipe f(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
|
||||
@recipe f(
|
||||
x::AVec{Function},
|
||||
v::AVec{Tuple{R1,R2,R3,R4}},
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
|
||||
|
||||
# this must be OHLC?
|
||||
@recipe f(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = x, OHLC[OHLC(t...) for t in ohlc]
|
||||
@recipe f(
|
||||
x::AVec,
|
||||
ohlc::AVec{Tuple{R1,R2,R3,R4}},
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} =
|
||||
x, OHLC[OHLC(t...) for t in ohlc]
|
||||
|
||||
@recipe function f(x::AVec, v::AVec{OHLC})
|
||||
seriestype := :path
|
||||
@@ -1056,11 +1253,11 @@ end
|
||||
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
|
||||
seriestype := :spy
|
||||
mat = g.args[1]
|
||||
n,m = axes(mat)
|
||||
n, m = axes(mat)
|
||||
Plots.SliceIt, m, n, Surface(mat)
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:spy}}, x,y,z)
|
||||
@recipe function f(::Type{Val{:spy}}, x, y, z)
|
||||
yflip := true
|
||||
aspect_ratio := 1
|
||||
rs, cs, zs = findnz(z.surf)
|
||||
@@ -1086,7 +1283,8 @@ end
|
||||
# -------------------------------------------------
|
||||
|
||||
"Adds ax+b... straight line over the current plot, without changing the axis limits"
|
||||
abline!(plt::Plot, a, b; kw...) = plot!(plt, [0, 1], [b, b+a]; seriestype = :straightline, kw...)
|
||||
abline!(plt::Plot, a, b; kw...) =
|
||||
plot!(plt, [0, 1], [b, b + a]; seriestype = :straightline, kw...)
|
||||
|
||||
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
|
||||
@@ -1099,9 +1297,11 @@ 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{DateTime}, dt::DateTime) =
|
||||
(dt -> Dates.value(dt), datetimeformatter)
|
||||
@recipe f(::Type{Dates.Time}, t::Dates.Time) = (t -> Dates.value(t), timeformatter)
|
||||
@recipe f(::Type{P}, t::P) where P <: Dates.Period = (t -> Dates.value(t), t -> string(P(t)))
|
||||
@recipe f(::Type{P}, t::P) where {P<:Dates.Period} =
|
||||
(t -> Dates.value(t), t -> string(P(t)))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Characters
|
||||
@@ -1111,7 +1311,7 @@ timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
@recipe function f(A::Array{Complex{T}}) where T<:Number
|
||||
@recipe function f(A::Array{Complex{T}}) where {T<:Number}
|
||||
xguide --> "Re(x)"
|
||||
yguide --> "Im(x)"
|
||||
real.(A), imag.(A)
|
||||
@@ -1120,10 +1320,10 @@ end
|
||||
# Splits a complex matrix to its real and complex parts
|
||||
# Reals defaults solid, imaginary defaults dashed
|
||||
# Label defaults are changed to match the real-imaginary reference / indexing
|
||||
@recipe function f(x::AbstractArray{T},y::Array{Complex{T2}}) where {T<:Real,T2}
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x,real.(y),imag.(y)
|
||||
@recipe function f(x::AbstractArray{T}, y::Array{Complex{T2}}) where {T<:Real,T2}
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x, real.(y), imag.(y)
|
||||
end
|
||||
|
||||
|
||||
@@ -1137,7 +1337,7 @@ end
|
||||
end
|
||||
|
||||
library = PlotUtils.color_libraries[cl.args[1]]
|
||||
z = sqrt.((1:15)*reshape(1:20,1,:))
|
||||
z = sqrt.((1:15) * reshape(1:20, 1, :))
|
||||
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
@@ -1161,7 +1361,7 @@ end
|
||||
if !(length(grad.args) == 1 && isa(grad.args[1], Symbol))
|
||||
error("showgradient takes the name of a color gradient as a Symbol")
|
||||
end
|
||||
z = sqrt.((1:15)*reshape(1:20,1,:))
|
||||
z = sqrt.((1:15) * reshape(1:20, 1, :))
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
legend := false
|
||||
@@ -1184,9 +1384,9 @@ end
|
||||
weights = cumsum(weights, dims = 2)
|
||||
seriestype := :shape
|
||||
|
||||
# create a filled polygon for each item
|
||||
for c=axes(weights,2)
|
||||
sx = vcat(weights[:,c], c==1 ? zeros(n) : reverse(weights[:,c-1]))
|
||||
# create a filled polygon for each item
|
||||
for c in axes(weights, 2)
|
||||
sx = vcat(weights[:, c], c == 1 ? zeros(n) : reverse(weights[:, c - 1]))
|
||||
sy = vcat(returns, reverse(returns))
|
||||
@series Plots.isvertical(plotattributes) ? (sx, sy) : (sy, sx)
|
||||
end
|
||||
@@ -1205,13 +1405,13 @@ julia> areaplot(1:3, [1 2 3; 7 8 9; 4 5 6], seriescolor = [:red :green :blue], f
|
||||
@userplot AreaPlot
|
||||
|
||||
@recipe function f(a::AreaPlot)
|
||||
data = cumsum(a.args[end], dims=2)
|
||||
data = cumsum(a.args[end], dims = 2)
|
||||
x = length(a.args) == 1 ? (axes(data, 1)) : a.args[1]
|
||||
seriestype := :line
|
||||
for i in axes(data, 2)
|
||||
@series begin
|
||||
fillrange := i > 1 ? data[:,i-1] : 0
|
||||
x, data[:,i]
|
||||
fillrange := i > 1 ? data[:, i - 1] : 0
|
||||
x, data[:, i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+2
-1
@@ -267,7 +267,7 @@ end
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# helper function to ensure relevant attributes are wrapped by Surface
|
||||
function wrap_surfaces(plotattributes::KW)
|
||||
function wrap_surfaces(plotattributes::AKW)
|
||||
if haskey(plotattributes, :fill_z)
|
||||
v = plotattributes[:fill_z]
|
||||
if !isa(v, Surface)
|
||||
@@ -347,6 +347,7 @@ end
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
aspect_ratio --> :equal
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, m, n, Surface(z)
|
||||
end
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ function theme(s::Symbol; kw...)
|
||||
_theme(s, defaults; kw...)
|
||||
end
|
||||
|
||||
function _theme(s::Symbol, defaults::KW; kw...)
|
||||
function _theme(s::Symbol, defaults::AKW; kw...)
|
||||
# Reset to defaults to overwrite active theme
|
||||
reset_defaults()
|
||||
|
||||
|
||||
@@ -30,6 +30,18 @@ Base.getindex(attr::Attr, k) = haskey(attr.explicit,k) ?
|
||||
attr.explicit[k] : attr.defaults[k]
|
||||
Base.haskey(attr::Attr, k) = haskey(attr.explicit,k) || haskey(attr.defaults,k)
|
||||
Base.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default
|
||||
function Base.get!(attr::Attr, k, default)
|
||||
v = if haskey(attr, k)
|
||||
attr[k]
|
||||
else
|
||||
attr.defaults[k] = default
|
||||
end
|
||||
return v
|
||||
end
|
||||
function Base.delete!(attr::Attr, k)
|
||||
haskey(attr.explicit, k) && delete!(attr.explicit, k)
|
||||
haskey(attr.defaults, k) && delete!(attr.defaults, k)
|
||||
end
|
||||
Base.length(attr::Attr) = length(union(keys(attr.explicit), keys(attr.defaults)))
|
||||
function Base.iterate(attr::Attr)
|
||||
exp_keys = keys(attr.explicit)
|
||||
|
||||
+21
-17
@@ -116,24 +116,11 @@ end
|
||||
|
||||
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
|
||||
n, m = size(z)
|
||||
# idx = 0
|
||||
colors = ColorGradient(vec(z))
|
||||
newz = reshape(range(0, stop=1, length=n*m), n, m)
|
||||
newz, colors
|
||||
# newz = zeros(n, m)
|
||||
# for i=1:n, j=1:m
|
||||
# push!(colors, T(z[i,j]...))
|
||||
# newz[i,j] = idx / (n*m-1)
|
||||
# idx += 1
|
||||
# end
|
||||
# newz, ColorGradient(colors)
|
||||
end
|
||||
|
||||
function imageHack(plotattributes::KW)
|
||||
is_seriestype_supported(:heatmap) || error("Neither :image or :heatmap are supported!")
|
||||
plotattributes[:seriestype] = :heatmap
|
||||
plotattributes[:z], plotattributes[:fillcolor] = replace_image_with_heatmap(plotattributes[:z].surf)
|
||||
end
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
"Build line segments for plotting"
|
||||
@@ -499,7 +486,7 @@ function make_fillrange_side(y, rib)
|
||||
end
|
||||
|
||||
# turn a ribbon into a fillrange
|
||||
function make_fillrange_from_ribbon(kw::KW)
|
||||
function make_fillrange_from_ribbon(kw::AKW)
|
||||
y, rib = kw[:y], kw[:ribbon]
|
||||
rib = wraptuple(rib)
|
||||
rib1, rib2 = -first(rib), last(rib)
|
||||
@@ -693,6 +680,10 @@ function get_markerstrokealpha(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokealpha], i)
|
||||
end
|
||||
|
||||
function get_markerstrokewidth(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokewidth], 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
|
||||
@@ -706,6 +697,19 @@ function has_attribute_segments(series::Series)
|
||||
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :linestyle, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z))
|
||||
end
|
||||
|
||||
function get_aspect_ratio(sp)
|
||||
aspect_ratio = sp[:aspect_ratio]
|
||||
if aspect_ratio == :auto
|
||||
aspect_ratio = :none
|
||||
for series in series_list(sp)
|
||||
if series[:seriestype] == :image
|
||||
aspect_ratio = :equal
|
||||
end
|
||||
end
|
||||
end
|
||||
return aspect_ratio
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
makekw(; kw...) = KW(kw)
|
||||
@@ -842,8 +846,8 @@ function dumpdict(io::IO, plotattributes::AKW, prefix = "", alwaysshow = false)
|
||||
end
|
||||
println(io)
|
||||
end
|
||||
DD(io::IO, plotattributes::KW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
|
||||
DD(plotattributes::KW, prefix = "") = DD(stdout, plotattributes, prefix)
|
||||
DD(io::IO, plotattributes::AKW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
|
||||
DD(plotattributes::AKW, prefix = "") = DD(stdout, plotattributes, prefix)
|
||||
|
||||
function dumpcallstack()
|
||||
error() # well... you wanted the stacktrace, didn't you?!?
|
||||
@@ -1158,7 +1162,7 @@ function convert_sci_unicode(label::AbstractString)
|
||||
for key in keys(unicode_dict)
|
||||
label = replace(label, key => unicode_dict[key])
|
||||
end
|
||||
if occursin("10^{", label)
|
||||
if occursin("×10^{", label)
|
||||
label = string(label, "}")
|
||||
end
|
||||
label
|
||||
|
||||
+25
-1
@@ -42,7 +42,31 @@ include("imgcomp.jl")
|
||||
Random.seed!(1234)
|
||||
default(show=false, reuse=true)
|
||||
is_ci() = get(ENV, "CI", "false") == "true"
|
||||
img_tol = is_ci() ? 10e-2 : 10e-2
|
||||
img_tol = is_ci() ? 1e-2 : 1e-3
|
||||
|
||||
## Uncomment the following lines to update reference images for different backends
|
||||
|
||||
#=
|
||||
@testset "GR" begin
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
|
||||
end
|
||||
|
||||
plotly()
|
||||
@testset "Plotly" begin
|
||||
image_comparison_facts(:plotly, tol=img_tol, skip = Plots._backend_skips[:plotlyjs])
|
||||
end
|
||||
|
||||
pyplot()
|
||||
@testset "PyPlot" begin
|
||||
image_comparison_facts(:pyplot, tol=img_tol, skip = Plots._backend_skips[:pyplot])
|
||||
end
|
||||
|
||||
pgfplots()
|
||||
@testset "PGFPlots" begin
|
||||
image_comparison_facts(:pgfplots, tol=img_tol, skip = Plots._backend_skips[:pgfplots])
|
||||
end
|
||||
=#
|
||||
##
|
||||
|
||||
@testset "Backends" begin
|
||||
|
||||
|
||||
Reference in New Issue
Block a user