Merge branch 'master' into as/remove-tryrange
This commit is contained in:
commit
fe436cf54f
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ deps/plotly-latest.min.js
|
|||||||
deps/build.log
|
deps/build.log
|
||||||
deps/deps.jl
|
deps/deps.jl
|
||||||
Manifest.toml
|
Manifest.toml
|
||||||
|
dev/
|
||||||
|
test/tmpplotsave.hdf5
|
||||||
|
|||||||
@ -4,8 +4,8 @@ os:
|
|||||||
- linux
|
- linux
|
||||||
# - osx
|
# - osx
|
||||||
julia:
|
julia:
|
||||||
|
- 1.0
|
||||||
- 1
|
- 1
|
||||||
- 1.3
|
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
@ -20,6 +20,9 @@ addons:
|
|||||||
- xauth
|
- xauth
|
||||||
- xvfb
|
- xvfb
|
||||||
|
|
||||||
|
env:
|
||||||
|
- GKS_ENCODING="utf8"
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.julia/artifacts
|
- $HOME/.julia/artifacts
|
||||||
@ -32,6 +35,9 @@ before_install:
|
|||||||
notifications:
|
notifications:
|
||||||
email: true
|
email: true
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())'
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
||||||
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
|
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
|
||||||
|
|||||||
10
Project.toml
10
Project.toml
@ -1,7 +1,7 @@
|
|||||||
name = "Plots"
|
name = "Plots"
|
||||||
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||||
author = ["Tom Breloff (@tbreloff)"]
|
author = ["Tom Breloff (@tbreloff)"]
|
||||||
version = "0.29.7"
|
version = "0.29.9"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||||
@ -34,7 +34,7 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
|||||||
Contour = "0.5"
|
Contour = "0.5"
|
||||||
FFMPEG = "0.2, 0.3"
|
FFMPEG = "0.2, 0.3"
|
||||||
FixedPointNumbers = "0.6, 0.7, 0.8"
|
FixedPointNumbers = "0.6, 0.7, 0.8"
|
||||||
GR = "0.46, 0.47"
|
GR = "0.46, 0.47, 0.48"
|
||||||
GeometryTypes = "0.7, 0.8"
|
GeometryTypes = "0.7, 0.8"
|
||||||
JSON = "0.21"
|
JSON = "0.21"
|
||||||
Measures = "0.3"
|
Measures = "0.3"
|
||||||
@ -55,15 +55,17 @@ GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
|
|||||||
Gtk = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44"
|
Gtk = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44"
|
||||||
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
|
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
|
||||||
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
|
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
|
||||||
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
|
|
||||||
LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
||||||
|
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
|
||||||
PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925"
|
PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925"
|
||||||
|
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
|
||||||
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
|
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
|
||||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
|
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
||||||
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
|
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
|
||||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
|
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
|
||||||
VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92"
|
VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92"
|
||||||
|
|
||||||
[targets]
|
[targets]
|
||||||
test = ["FileIO", "GeometryTypes", "Gtk", "ImageMagick", "Images", "LaTeXStrings", "LibGit2", "PGFPlotsX", "Random", "RDatasets", "StatsPlots", "Test", "UnicodePlots", "VisualRegressionTests"]
|
test = ["FileIO", "GeometryTypes", "Gtk", "ImageMagick", "Images", "LibGit2", "OffsetArrays", "PGFPlotsX", "HDF5", "Random", "RDatasets", "StaticArrays", "StatsPlots", "Test", "UnicodePlots", "VisualRegressionTests"]
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
[![][pkgeval-img]][pkgeval-url]
|
[![][pkgeval-img]][pkgeval-url]
|
||||||
[![][gitter-img]][gitter-url]
|
[![][gitter-img]][gitter-url]
|
||||||
[![][docs-img]][docs-url]
|
[![][docs-img]][docs-url]
|
||||||
|
[](https://codecov.io/gh/JuliaPlots/Plots.jl)
|
||||||
|
|
||||||
#### Created by Tom Breloff (@tbreloff)
|
#### Created by Tom Breloff (@tbreloff)
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
# - julia_version: 0.7
|
- julia_version: 1.0
|
||||||
- julia_version: 1
|
- julia_version: 1
|
||||||
- julia_version: 1.3
|
|
||||||
- julia_version: nightly
|
- julia_version: nightly
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
@ -33,7 +32,7 @@ install:
|
|||||||
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
|
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- echo "%JL_BUILD_SCRIPT%"
|
- echo "%JL_TEST_SCRIPT%"
|
||||||
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
|
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
|
|||||||
10
codecov.yml
Normal file
10
codecov.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
ignore:
|
||||||
|
- "src/backends/inspectdr.jl"
|
||||||
|
- "src/backends/orca.jl"
|
||||||
|
- "src/backends/pgfplots.jl"
|
||||||
|
- "src/backends/plotly.jl"
|
||||||
|
- "src/backends/plotlyjs.jl"
|
||||||
|
- "src/backends/pyplot.jl"
|
||||||
|
- "src/backends/web.jl"
|
||||||
|
- "src/fileio.jl"
|
||||||
|
- "src/ijulia.jl"
|
||||||
6
deps/generateprecompiles.jl
vendored
6
deps/generateprecompiles.jl
vendored
@ -26,13 +26,15 @@
|
|||||||
|
|
||||||
using SnoopCompile
|
using SnoopCompile
|
||||||
|
|
||||||
|
project_flag = string("--project=", joinpath(homedir(), ".julia", "dev", "Plots"))
|
||||||
log_path = joinpath(tempdir(), "compiles.log")
|
log_path = joinpath(tempdir(), "compiles.log")
|
||||||
precompiles_path = joinpath(tempdir(), "precompile")
|
precompiles_path = joinpath(tempdir(), "precompile")
|
||||||
|
|
||||||
# run examples with GR backend, logging what needs to be compiled
|
# run examples with GR backend, logging what needs to be compiled
|
||||||
SnoopCompile.@snoopc log_path begin
|
SnoopCompile.@snoopc project_flag log_path begin
|
||||||
using Plots
|
using Plots
|
||||||
Plots.test_examples(:gr, disp=true)
|
Plots.test_examples(:gr)
|
||||||
|
Plots.test_examples(:plotly, skip = Plots._backend_skips[:plotly])
|
||||||
end
|
end
|
||||||
|
|
||||||
# precompile calls containing the following strings are dropped
|
# precompile calls containing the following strings are dropped
|
||||||
|
|||||||
@ -71,6 +71,7 @@ function buildanimation(anim::Animation, fn::AbstractString,
|
|||||||
is_animated_gif::Bool=true;
|
is_animated_gif::Bool=true;
|
||||||
fps::Real = 20, loop::Integer = 0,
|
fps::Real = 20, loop::Integer = 0,
|
||||||
variable_palette::Bool=false,
|
variable_palette::Bool=false,
|
||||||
|
verbose=false,
|
||||||
show_msg::Bool=true)
|
show_msg::Bool=true)
|
||||||
if length(anim.frames) == 0
|
if length(anim.frames) == 0
|
||||||
throw(ArgumentError("Cannot build empty animations"))
|
throw(ArgumentError("Cannot build empty animations"))
|
||||||
@ -79,20 +80,23 @@ function buildanimation(anim::Animation, fn::AbstractString,
|
|||||||
fn = abspath(expanduser(fn))
|
fn = abspath(expanduser(fn))
|
||||||
animdir = anim.dir
|
animdir = anim.dir
|
||||||
framerate = ffmpeg_framerate(fps)
|
framerate = ffmpeg_framerate(fps)
|
||||||
|
verbose_level = (verbose isa Int ? verbose :
|
||||||
|
verbose ? 32 # "info"
|
||||||
|
: 16) # "error"
|
||||||
|
|
||||||
if is_animated_gif
|
if is_animated_gif
|
||||||
if variable_palette
|
if variable_palette
|
||||||
# generate a colorpalette for each frame for highest quality, but larger filesize
|
# generate a colorpalette for each frame for highest quality, but larger filesize
|
||||||
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
|
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
|
||||||
ffmpeg_exe(`-v 0 -framerate $framerate -loop $loop -i $(animdir)/%06d.png -lavfi "$palette" -y $fn`)
|
ffmpeg_exe(`-v $verbose_level -framerate $framerate -loop $loop -i $(animdir)/%06d.png -lavfi "$palette" -y $fn`)
|
||||||
else
|
else
|
||||||
# generate a colorpalette first so ffmpeg does not have to guess it
|
# generate a colorpalette first so ffmpeg does not have to guess it
|
||||||
ffmpeg_exe(`-v 0 -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
|
ffmpeg_exe(`-v $verbose_level -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
|
||||||
# then apply the palette to get better results
|
# then apply the palette to get better results
|
||||||
ffmpeg_exe(` -v 0 -framerate $framerate -loop $loop -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
|
ffmpeg_exe(`-v $verbose_level -framerate $framerate -loop $loop -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
ffmpeg_exe(`-v 0 -framerate $framerate -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
ffmpeg_exe(`-v $verbose_level -framerate $framerate -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
||||||
end
|
end
|
||||||
|
|
||||||
show_msg && @info("Saved animation to ", fn)
|
show_msg && @info("Saved animation to ", fn)
|
||||||
|
|||||||
@ -89,7 +89,7 @@ const _arg_desc = KW(
|
|||||||
:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.",
|
:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.",
|
||||||
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
|
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
|
||||||
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
|
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
|
||||||
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)",
|
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` , `:inline` (note: only some may be supported in each backend)",
|
||||||
:legendfontfamily => "String or Symbol. Font family of legend entries.",
|
:legendfontfamily => "String or Symbol. Font family of legend entries.",
|
||||||
:legendfontsize => "Integer. Font pointsize of legend entries.",
|
:legendfontsize => "Integer. Font pointsize of legend entries.",
|
||||||
:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center",
|
:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center",
|
||||||
|
|||||||
83
src/args.jl
83
src/args.jl
@ -450,15 +450,33 @@ const _initial_fontsizes = Dict(:titlefontsize => _subplot_defaults[:titlefonts
|
|||||||
:tickfontsize => _axis_defaults[:tickfontsize],
|
:tickfontsize => _axis_defaults[:tickfontsize],
|
||||||
:guidefontsize => _axis_defaults[:guidefontsize])
|
:guidefontsize => _axis_defaults[:guidefontsize])
|
||||||
|
|
||||||
const _all_args = sort(collect(union(map(keys, _all_defaults)...)))
|
const _internal_args =
|
||||||
|
[:plot_object, :series_plotindex, :markershape_to_add, :letter, :idxfilter]
|
||||||
|
|
||||||
is_subplot_attr(k) = haskey(_subplot_defaults, k)
|
const _axis_args = sort(union(collect(keys(_axis_defaults))))
|
||||||
is_series_attr(k) = haskey(_series_defaults, k)
|
const _series_args = sort(union(collect(keys(_series_defaults))))
|
||||||
#is_axis_attr(k) = haskey(_axis_defaults_byletter, k)
|
const _subplot_args = sort(union(collect(keys(_subplot_defaults))))
|
||||||
is_axis_attr(k) = haskey(_axis_defaults, Symbol(chop(string(k); head=1, tail=0)))
|
const _plot_args = sort(union(collect(keys(_plot_defaults))))
|
||||||
is_axis_attr_noletter(k) = haskey(_axis_defaults, k)
|
|
||||||
|
const _magic_axis_args = [:axis, :tickfont, :guidefont, :grid, :minorgrid]
|
||||||
|
const _magic_subplot_args = [:titlefont, :legendfont, :legendtitlefont, ]
|
||||||
|
const _magic_series_args = [:line, :marker, :fill]
|
||||||
|
|
||||||
|
const _all_axis_args = sort(union([_axis_args; _magic_axis_args]))
|
||||||
|
const _all_subplot_args = sort(union([_subplot_args; _magic_subplot_args]))
|
||||||
|
const _all_series_args = sort(union([_series_args; _magic_series_args]))
|
||||||
|
const _all_plot_args = _plot_args
|
||||||
|
|
||||||
|
const _all_args =
|
||||||
|
sort([_all_axis_args; _all_subplot_args; _all_series_args; _all_plot_args])
|
||||||
|
|
||||||
|
is_subplot_attr(k) = k in _all_subplot_args
|
||||||
|
is_series_attr(k) = k in _all_series_args
|
||||||
|
is_axis_attr(k) = Symbol(chop(string(k); head=1, tail=0)) in _all_axis_args
|
||||||
|
is_axis_attr_noletter(k) = k in _all_axis_args
|
||||||
|
|
||||||
RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k)
|
RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k)
|
||||||
|
is_default_attribute(k) = k in _internal_args || k in _all_args || is_axis_attr_noletter(k)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -638,6 +656,7 @@ function default(k::Symbol)
|
|||||||
letter, key = axis_k
|
letter, key = axis_k
|
||||||
return _axis_defaults_byletter[letter][key]
|
return _axis_defaults_byletter[letter][key]
|
||||||
end
|
end
|
||||||
|
k == :letter && return k # for type recipe processing
|
||||||
k in _suppress_warnings || error("Unknown key: ", k)
|
k in _suppress_warnings || error("Unknown key: ", k)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -919,22 +938,6 @@ end
|
|||||||
function preprocessArgs!(plotattributes::AKW)
|
function preprocessArgs!(plotattributes::AKW)
|
||||||
replaceAliases!(plotattributes, _keyAliases)
|
replaceAliases!(plotattributes, _keyAliases)
|
||||||
|
|
||||||
# clear all axis stuff
|
|
||||||
# if haskey(plotattributes, :axis) && plotattributes[:axis] in (:none, nothing, false)
|
|
||||||
# plotattributes[:ticks] = nothing
|
|
||||||
# plotattributes[:foreground_color_border] = RGBA(0,0,0,0)
|
|
||||||
# plotattributes[:foreground_color_axis] = RGBA(0,0,0,0)
|
|
||||||
# plotattributes[:grid] = false
|
|
||||||
# delete!(plotattributes, :axis)
|
|
||||||
# end
|
|
||||||
# for letter in (:x, :y, :z)
|
|
||||||
# asym = Symbol(letter, :axis)
|
|
||||||
# if haskey(plotattributes, asym) || plotattributes[asym] in (:none, nothing, false)
|
|
||||||
# plotattributes[Symbol(letter, :ticks)] = nothing
|
|
||||||
# plotattributes[Symbol(letter, :foreground_color_border)] = RGBA(0,0,0,0)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# handle axis args common to all axis
|
# handle axis args common to all axis
|
||||||
args = pop_kw!(plotattributes, :axis, ())
|
args = pop_kw!(plotattributes, :axis, ())
|
||||||
for arg in wraptuple(args)
|
for arg in wraptuple(args)
|
||||||
@ -992,13 +995,6 @@ function preprocessArgs!(plotattributes::AKW)
|
|||||||
processMinorGridArg!(plotattributes, arg, letter)
|
processMinorGridArg!(plotattributes, arg, letter)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# fonts
|
|
||||||
for fontname in (:titlefont, :legendfont, :legendtitlefont)
|
|
||||||
args = pop_kw!(plotattributes, fontname, ())
|
|
||||||
for arg in wraptuple(args)
|
|
||||||
processFontArg!(plotattributes, fontname, arg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# handle font args common to all axes
|
# handle font args common to all axes
|
||||||
for fontname in (:tickfont, :guidefont)
|
for fontname in (:tickfont, :guidefont)
|
||||||
args = pop_kw!(plotattributes, fontname, ())
|
args = pop_kw!(plotattributes, fontname, ())
|
||||||
@ -1017,6 +1013,26 @@ function preprocessArgs!(plotattributes::AKW)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# handle axes args
|
||||||
|
for k in _axis_args
|
||||||
|
if haskey(plotattributes, k)
|
||||||
|
v = plotattributes[k]
|
||||||
|
for letter in (:x, :y, :z)
|
||||||
|
lk = Symbol(letter, k)
|
||||||
|
if !is_explicit(plotattributes, lk)
|
||||||
|
plotattributes[lk] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# fonts
|
||||||
|
for fontname in (:titlefont, :legendfont, :legendtitlefont)
|
||||||
|
args = pop_kw!(plotattributes, fontname, ())
|
||||||
|
for arg in wraptuple(args)
|
||||||
|
processFontArg!(plotattributes, fontname, arg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# handle line args
|
# handle line args
|
||||||
for arg in wraptuple(pop_kw!(plotattributes, :line, ()))
|
for arg in wraptuple(pop_kw!(plotattributes, :line, ()))
|
||||||
@ -1219,7 +1235,7 @@ function convertLegendValue(val::Symbol)
|
|||||||
:best
|
:best
|
||||||
elseif val in (:no, :none)
|
elseif val in (:no, :none)
|
||||||
:none
|
:none
|
||||||
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend, :topright, :topleft, :bottomleft, :bottomright, :outertopright, :outertopleft, :outertop, :outerright, :outerleft, :outerbottomright, :outerbottomleft, :outerbottom)
|
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend, :topright, :topleft, :bottomleft, :bottomright, :outertopright, :outertopleft, :outertop, :outerright, :outerleft, :outerbottomright, :outerbottomleft, :outerbottom, :inline)
|
||||||
val
|
val
|
||||||
else
|
else
|
||||||
error("Invalid symbol for legend: $val")
|
error("Invalid symbol for legend: $val")
|
||||||
@ -1471,12 +1487,9 @@ function _update_axis(plt::Plot, sp::Subplot, plotattributes_in::AKW, letter::Sy
|
|||||||
end
|
end
|
||||||
|
|
||||||
function _update_axis(axis::Axis, plotattributes_in::AKW, letter::Symbol, subplot_index::Int)
|
function _update_axis(axis::Axis, plotattributes_in::AKW, letter::Symbol, subplot_index::Int)
|
||||||
# grab magic args (for example `xaxis = (:flip, :log)`)
|
|
||||||
args = wraptuple(get(plotattributes_in, Symbol(letter, :axis), ()))
|
|
||||||
|
|
||||||
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
|
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
|
||||||
kw = KW()
|
kw = KW()
|
||||||
for k in keys(_axis_defaults)
|
for k in _all_axis_args
|
||||||
# first get the args without the letter: `tickfont = font(10)`
|
# first get the args without the letter: `tickfont = font(10)`
|
||||||
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
|
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
|
||||||
if haskey(plotattributes_in, k)
|
if haskey(plotattributes_in, k)
|
||||||
@ -1491,7 +1504,7 @@ function _update_axis(axis::Axis, plotattributes_in::AKW, letter::Symbol, subplo
|
|||||||
end
|
end
|
||||||
|
|
||||||
# update the axis
|
# update the axis
|
||||||
attr!(axis, args...; kw...)
|
attr!(axis; kw...)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -84,6 +84,9 @@ function attr!(axis::Axis, args...; kw...)
|
|||||||
process_axis_arg!(plotattributes, arg)
|
process_axis_arg!(plotattributes, arg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# then preprocess keyword arguments
|
||||||
|
preprocessArgs!(KW(kw))
|
||||||
|
|
||||||
# then override for any keywords... only those keywords that already exists in plotattributes
|
# then override for any keywords... only those keywords that already exists in plotattributes
|
||||||
for (k,v) in kw
|
for (k,v) in kw
|
||||||
if haskey(plotattributes, k)
|
if haskey(plotattributes, k)
|
||||||
|
|||||||
@ -1043,6 +1043,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
viewport_plotarea[3] += legendh + 0.04
|
viewport_plotarea[3] += legendh + 0.04
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if sp[:legend] == :inline
|
||||||
|
if sp[:yaxis][:mirror]
|
||||||
|
viewport_plotarea[1] += legendw
|
||||||
|
else
|
||||||
|
viewport_plotarea[2] -= legendw
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# fill in the plot area background
|
# fill in the plot area background
|
||||||
bg = plot_color(sp[:background_color_inside])
|
bg = plot_color(sp[:background_color_inside])
|
||||||
@ -1798,6 +1805,21 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
gr_text(GR.wctondc(xi, yi)..., str)
|
gr_text(GR.wctondc(xi, yi)..., str)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if sp[:legend] == :inline && should_add_to_legend(series)
|
||||||
|
gr_set_font(legendfont(sp))
|
||||||
|
gr_set_textcolor(plot_color(sp[:legendfontcolor]))
|
||||||
|
if sp[:yaxis][:mirror]
|
||||||
|
(_,i) = sp[:xaxis][:flip] ? findmax(x) : findmin(x)
|
||||||
|
GR.settextalign(GR.TEXT_HALIGN_RIGHT, GR.TEXT_VALIGN_HALF)
|
||||||
|
offset = -0.01
|
||||||
|
else
|
||||||
|
(_,i) = sp[:xaxis][:flip] ? findmin(x) : findmax(x)
|
||||||
|
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
|
||||||
|
offset = 0.01
|
||||||
|
end
|
||||||
|
(x_l,y_l) = GR.wctondc(x[i],y[i])
|
||||||
|
gr_text(x_l+offset,y_l,series[:label])
|
||||||
|
end
|
||||||
GR.restorestate()
|
GR.restorestate()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1805,7 +1827,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
hascolorbar(sp) && gr_draw_colorbar(cbar, sp, get_clims(sp))
|
hascolorbar(sp) && gr_draw_colorbar(cbar, sp, get_clims(sp))
|
||||||
|
|
||||||
# add the legend
|
# add the legend
|
||||||
if sp[:legend] != :none
|
if !(sp[:legend] in(:none, :inline))
|
||||||
GR.savestate()
|
GR.savestate()
|
||||||
GR.selntran(0)
|
GR.selntran(0)
|
||||||
GR.setscale(0)
|
GR.setscale(0)
|
||||||
|
|||||||
@ -83,6 +83,7 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
|||||||
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
|
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
|
||||||
|
|
||||||
#Sub-structure types:
|
#Sub-structure types:
|
||||||
|
"ATTR" => Attr,
|
||||||
"FONT" => Font,
|
"FONT" => Font,
|
||||||
"BOUNDINGBOX" => BoundingBox,
|
"BOUNDINGBOX" => BoundingBox,
|
||||||
"GRIDLAYOUT" => GridLayout,
|
"GRIDLAYOUT" => GridLayout,
|
||||||
@ -109,7 +110,7 @@ _hdf5_datapath(subpath::String) = "$_hdf5_dataroot/$subpath"
|
|||||||
_hdf5_map_str2telem(k::String) = HDF5PLOT_MAP_STR2TELEM[k]
|
_hdf5_map_str2telem(k::String) = HDF5PLOT_MAP_STR2TELEM[k]
|
||||||
_hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
|
_hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
|
||||||
|
|
||||||
function _hdf5_merge!(dest, src)
|
function _hdf5_merge!(dest::AKW, src::AKW)
|
||||||
for (k, v) in src
|
for (k, v) in src
|
||||||
if isa(v, Axis)
|
if isa(v, Axis)
|
||||||
_hdf5_merge!(dest[k].plotattributes, v.plotattributes)
|
_hdf5_merge!(dest[k].plotattributes, v.plotattributes)
|
||||||
@ -263,6 +264,15 @@ function _hdf5plot_gwrite(grp, k::String, v::Array{T}) where T<:Number #Default
|
|||||||
grp[k] = v
|
grp[k] = v
|
||||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||||
end
|
end
|
||||||
|
function _hdf5plot_gwrite(grp, k::String, v::Dict)
|
||||||
|
#=
|
||||||
|
tstr = string(Dict)
|
||||||
|
path = HDF5.name(grp) * "/" * k
|
||||||
|
@info("Type not supported: $tstr\npath: $path")
|
||||||
|
=#
|
||||||
|
#No support for structures with Dicts different than KW (plotattributes)
|
||||||
|
end
|
||||||
|
|
||||||
#=
|
#=
|
||||||
function _hdf5plot_gwrite(grp, k::String, v::Array{Any})
|
function _hdf5plot_gwrite(grp, k::String, v::Array{Any})
|
||||||
# @show grp, k
|
# @show grp, k
|
||||||
@ -295,7 +305,8 @@ function _hdf5plot_gwrite(grp, k::String, v::Tuple)
|
|||||||
end
|
end
|
||||||
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
||||||
end
|
end
|
||||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::AKW)
|
function _hdf5plot_gwrite(grp, k::String, plotattributes::KW)
|
||||||
|
#NOTE: Can only write directly to group, not a subi-item
|
||||||
# @warn("Cannot write dict: $k=$plotattributes")
|
# @warn("Cannot write dict: $k=$plotattributes")
|
||||||
end
|
end
|
||||||
function _hdf5plot_gwrite(grp, k::String, v::AbstractRange)
|
function _hdf5plot_gwrite(grp, k::String, v::AbstractRange)
|
||||||
@ -376,13 +387,22 @@ function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
|||||||
_hdf5plot_writetype(grp, Subplot)
|
_hdf5plot_writetype(grp, Subplot)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
function _hdf5plot_write(grp, plotattributes::AKW)
|
|
||||||
|
function _hdf5plot_write(grp, plotattributes::KW)
|
||||||
for (k, v) in plotattributes
|
for (k, v) in plotattributes
|
||||||
kstr = string(k)
|
kstr = string(k)
|
||||||
_hdf5plot_gwrite(grp, kstr, v)
|
_hdf5plot_gwrite(grp, kstr, v)
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
function _hdf5plot_write(grp, plotattributes::Attr)
|
||||||
|
for (k, v) in plotattributes
|
||||||
|
kstr = string(k)
|
||||||
|
_hdf5plot_gwrite(grp, kstr, v)
|
||||||
|
end
|
||||||
|
_hdf5plot_writetype(grp, Attr)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# Write main plot structures:
|
# Write main plot structures:
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
@ -445,20 +465,14 @@ _hdf5plot_convert(T::Type{Extrema}, v) = Extrema(v[1], v[2])
|
|||||||
# Read data structures:
|
# Read data structures:
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
function _hdf5plot_read(grp, k::String, T::Type, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type)
|
||||||
v = HDF5.d_read(grp, k)
|
v = HDF5.d_read(grp, k)
|
||||||
return _hdf5plot_convert(T, v)
|
return _hdf5plot_convert(T, v)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{Length}, dtid::Vector)
|
|
||||||
v = HDF5.d_read(grp, k)
|
|
||||||
TU = Symbol(dtid[2])
|
|
||||||
T = typeof(v)
|
|
||||||
return Length{TU,T}(v)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Read more complex data structures:
|
# Read more complex data structures:
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{Font}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{Font})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
family = _hdf5plot_read(grp, "family")
|
family = _hdf5plot_read(grp, "family")
|
||||||
@ -469,7 +483,7 @@ function _hdf5plot_read(grp, k::String, T::Type{Font}, dtid)
|
|||||||
color = _hdf5plot_read(grp, "color")
|
color = _hdf5plot_read(grp, "color")
|
||||||
return Font(family, pointsize, halign, valign, rotation, color)
|
return Font(family, pointsize, halign, valign, rotation, color)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
|
function _hdf5plot_read(grp, k::String, T::Type{Array}) #ANY
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
sz = _hdf5plot_read(grp, "dim")
|
sz = _hdf5plot_read(grp, "dim")
|
||||||
if [0] == sz; return []; end
|
if [0] == sz; return []; end
|
||||||
@ -488,18 +502,18 @@ function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
|
|||||||
result = [result[iter] for iter in eachindex(result)] #Potentially make more specific
|
result = [result[iter] for iter in eachindex(result)] #Potentially make more specific
|
||||||
return reshape(result, sz)
|
return reshape(result, sz)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple})
|
||||||
v = _hdf5plot_read(grp, k, Array, dtid)
|
v = _hdf5plot_read(grp, k, Array)
|
||||||
return tuple(v...)
|
return tuple(v...)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{PlotText}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{PlotText})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
str = _hdf5plot_read(grp, "str")
|
str = _hdf5plot_read(grp, "str")
|
||||||
font = _hdf5plot_read(grp, "font")
|
font = _hdf5plot_read(grp, "font")
|
||||||
return PlotText(str, font)
|
return PlotText(str, font)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{SeriesAnnotations}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{SeriesAnnotations})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
strs = _hdf5plot_read(grp, "strs")
|
strs = _hdf5plot_read(grp, "strs")
|
||||||
@ -508,29 +522,29 @@ function _hdf5plot_read(grp, k::String, T::Type{SeriesAnnotations}, dtid)
|
|||||||
scalefactor = _hdf5plot_read(grp, "scalefactor")
|
scalefactor = _hdf5plot_read(grp, "scalefactor")
|
||||||
return SeriesAnnotations(strs, font, baseshape, scalefactor)
|
return SeriesAnnotations(strs, font, baseshape, scalefactor)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{Shape}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{Shape})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
x = _hdf5plot_read(grp, "x")
|
x = _hdf5plot_read(grp, "x")
|
||||||
y = _hdf5plot_read(grp, "y")
|
y = _hdf5plot_read(grp, "y")
|
||||||
return Shape(x, y)
|
return Shape(x, y)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
colors = _hdf5plot_read(grp, "colors")
|
colors = _hdf5plot_read(grp, "colors")
|
||||||
values = _hdf5plot_read(grp, "values")
|
values = _hdf5plot_read(grp, "values")
|
||||||
return ColorGradient(colors, values)
|
return ColorGradient(colors, values)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{BoundingBox}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{BoundingBox})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
x0 = _hdf5plot_read(grp, "x0")
|
x0 = _hdf5plot_read(grp, "x0")
|
||||||
a = _hdf5plot_read(grp, "a")
|
a = _hdf5plot_read(grp, "a")
|
||||||
return BoundingBox(x0, a)
|
return BoundingBox(x0, a)
|
||||||
end
|
end
|
||||||
_hdf5plot_read(grp, k::String, T::Type{RootLayout}, dtid) = RootLayout()
|
_hdf5plot_read(grp, k::String, T::Type{RootLayout}) = RootLayout()
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{GridLayout}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{GridLayout})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
# parent = _hdf5plot_read(grp, "parent")
|
# parent = _hdf5plot_read(grp, "parent")
|
||||||
@ -544,22 +558,36 @@ parent = RootLayout()
|
|||||||
|
|
||||||
return GridLayout(parent, minpad, bbox, grid, widths, heights, attr)
|
return GridLayout(parent, minpad, bbox, grid, widths, heights, attr)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid)
|
function _hdf5plot_read(grp, T::Type{Attr})
|
||||||
grp = HDF5.g_open(grp, k)
|
attr = Attr(KW(), _plot_defaults)
|
||||||
kwlist = KW()
|
v = _hdf5plot_read(grp, attr)
|
||||||
_hdf5plot_read(grp, kwlist)
|
return attr
|
||||||
return Axis([], kwlist)
|
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{Surface}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{Axis})
|
||||||
|
grp = HDF5.g_open(grp, k)
|
||||||
|
plotattributes = Attr(KW(), _plot_defaults)
|
||||||
|
_hdf5plot_read(grp, plotattributes)
|
||||||
|
return Axis([], plotattributes)
|
||||||
|
end
|
||||||
|
function _hdf5plot_read(grp, k::String, T::Type{Surface})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
data2d = _hdf5plot_read(grp, "data2d")
|
data2d = _hdf5plot_read(grp, "data2d")
|
||||||
return Surface(data2d)
|
return Surface(data2d)
|
||||||
end
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{Subplot}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{Subplot})
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
idx = _hdf5plot_read(grp, "index")
|
idx = _hdf5plot_read(grp, "index")
|
||||||
return HDF5PLOT_PLOTREF.ref.subplots[idx]
|
return HDF5PLOT_PLOTREF.ref.subplots[idx]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#Most types don't need dtid for read!!:
|
||||||
|
_hdf5plot_read(grp, k::String, T::Type, dtid) = _hdf5plot_read(grp, k, T)
|
||||||
|
function _hdf5plot_read(grp, k::String, T::Type{Length}, dtid::Vector)
|
||||||
|
v = HDF5.d_read(grp, k)
|
||||||
|
TU = Symbol(dtid[2])
|
||||||
|
T = typeof(v)
|
||||||
|
return Length{TU,T}(v)
|
||||||
|
end
|
||||||
function _hdf5plot_read(grp, k::String)
|
function _hdf5plot_read(grp, k::String)
|
||||||
dtid = HDF5.a_read(grp[k], _hdf5plot_datatypeid)
|
dtid = HDF5.a_read(grp[k], _hdf5plot_datatypeid)
|
||||||
T = _hdf5_map_str2telem(dtid) #expect exception
|
T = _hdf5_map_str2telem(dtid) #expect exception
|
||||||
@ -567,7 +595,7 @@ function _hdf5plot_read(grp, k::String)
|
|||||||
end
|
end
|
||||||
|
|
||||||
#Read in values in group to populate plotattributes:
|
#Read in values in group to populate plotattributes:
|
||||||
function _hdf5plot_read(grp, plotattributes::AKW)
|
function _hdf5plot_readattr(grp, plotattributes::AbstractDict)
|
||||||
gnames = names(grp)
|
gnames = names(grp)
|
||||||
for k in gnames
|
for k in gnames
|
||||||
try
|
try
|
||||||
@ -581,6 +609,8 @@ function _hdf5plot_read(grp, plotattributes::AKW)
|
|||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
_hdf5plot_read(grp, plotattributes::KW) = _hdf5plot_readattr(grp, plotattributes)
|
||||||
|
_hdf5plot_read(grp, plotattributes::Attr) = _hdf5plot_readattr(grp, plotattributes)
|
||||||
|
|
||||||
# Read main plot structures:
|
# Read main plot structures:
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
@ -593,17 +623,17 @@ function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
|||||||
|
|
||||||
for i in 1:nseries
|
for i in 1:nseries
|
||||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
|
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
|
||||||
kwlist = KW()
|
seriesinfo = Attr(KW(), _plot_defaults)
|
||||||
_hdf5plot_read(grp, kwlist)
|
_hdf5plot_read(grp, seriesinfo)
|
||||||
plot!(sp, kwlist[:x], kwlist[:y]) #Add data & create data structures
|
plot!(sp, seriesinfo[:x], seriesinfo[:y]) #Add data & create data structures
|
||||||
_hdf5_merge!(sp.series_list[end].plotattributes, kwlist)
|
_hdf5_merge!(sp.series_list[end].plotattributes, seriesinfo)
|
||||||
end
|
end
|
||||||
|
|
||||||
#Perform after adding series... otherwise values get overwritten:
|
#Perform after adding series... otherwise values get overwritten:
|
||||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
|
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
|
||||||
kwlist = KW()
|
attr = Attr(KW(), _plot_defaults)
|
||||||
_hdf5plot_read(grp, kwlist)
|
_hdf5plot_read(grp, attr)
|
||||||
_hdf5_merge!(sp.attr, kwlist)
|
_hdf5_merge!(sp.attr, attr)
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@ -29,11 +29,20 @@ Base.@kwdef mutable struct PGFPlotsXPlot
|
|||||||
PGFPlotsX.push_preamble!(
|
PGFPlotsX.push_preamble!(
|
||||||
pgfx_plot.the_plot,
|
pgfx_plot.the_plot,
|
||||||
raw"""
|
raw"""
|
||||||
\pgfplotsset{
|
\pgfplotsset{%
|
||||||
/pgfplots/layers/axis on top/.define layer set={
|
layers/standard/.define layer set={%
|
||||||
background, axis background,pre main,main,axis grid,axis ticks,axis lines,axis tick labels,
|
background,axis background,axis grid,axis ticks,axis lines,axis tick labels,pre main,main,axis descriptions,axis foreground%
|
||||||
axis descriptions,axis foreground
|
}{grid style= {/pgfplots/on layer=axis grid},%
|
||||||
}{/pgfplots/layers/standard},
|
tick style= {/pgfplots/on layer=axis ticks},%
|
||||||
|
axis line style= {/pgfplots/on layer=axis lines},%
|
||||||
|
label style= {/pgfplots/on layer=axis descriptions},%
|
||||||
|
legend style= {/pgfplots/on layer=axis descriptions},%
|
||||||
|
title style= {/pgfplots/on layer=axis descriptions},%
|
||||||
|
colorbar style= {/pgfplots/on layer=axis descriptions},%
|
||||||
|
ticklabel style= {/pgfplots/on layer=axis tick labels},%
|
||||||
|
axis background@ style={/pgfplots/on layer=axis background},%
|
||||||
|
3d box foreground style={/pgfplots/on layer=axis foreground},%
|
||||||
|
},
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
@ -76,6 +85,7 @@ end
|
|||||||
|
|
||||||
function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||||
if !pgfx_plot.is_created || pgfx_plot.was_shown
|
if !pgfx_plot.is_created || pgfx_plot.was_shown
|
||||||
|
pgfx_sanitize_plot!(plt)
|
||||||
the_plot = PGFPlotsX.TikzPicture(PGFPlotsX.Options())
|
the_plot = PGFPlotsX.TikzPicture(PGFPlotsX.Options())
|
||||||
bgc = plt.attr[:background_color_outside] == :match ?
|
bgc = plt.attr[:background_color_outside] == :match ?
|
||||||
plt.attr[:background_color] : plt.attr[:background_color_outside]
|
plt.attr[:background_color] : plt.attr[:background_color_outside]
|
||||||
@ -107,14 +117,24 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
|||||||
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
||||||
title_cstr = plot_color(sp[:titlefontcolor])
|
title_cstr = plot_color(sp[:titlefontcolor])
|
||||||
title_a = alpha(title_cstr)
|
title_a = alpha(title_cstr)
|
||||||
|
title_loc = sp[:title_location]
|
||||||
bgc_inside = plot_color(sp[:background_color_inside])
|
bgc_inside = plot_color(sp[:background_color_inside])
|
||||||
bgc_inside_a = alpha(bgc_inside)
|
bgc_inside_a = alpha(bgc_inside)
|
||||||
axis_opt = PGFPlotsX.Options(
|
axis_opt = PGFPlotsX.Options(
|
||||||
"title" => sp[:title],
|
"title" => sp[:title],
|
||||||
"title style" => PGFPlotsX.Options(
|
"title style" => PGFPlotsX.Options(
|
||||||
"font" => pgfx_font(
|
"at" => if title_loc == :left
|
||||||
sp[:titlefontsize],
|
"{(0,1)}"
|
||||||
pgfx_thickness_scaling(sp),
|
elseif title_loc == :right
|
||||||
|
"{(1,1)}"
|
||||||
|
elseif title_loc isa Tuple
|
||||||
|
"{$(string(title_loc))}"
|
||||||
|
else
|
||||||
|
"{(0.5,1)}"
|
||||||
|
end,
|
||||||
|
"font" => pgfx_font(
|
||||||
|
sp[:titlefontsize],
|
||||||
|
pgfx_thickness_scaling(sp),
|
||||||
),
|
),
|
||||||
"color" => title_cstr,
|
"color" => title_cstr,
|
||||||
"draw opacity" => title_a,
|
"draw opacity" => title_a,
|
||||||
@ -139,8 +159,6 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
|||||||
"fill" => bgc_inside,
|
"fill" => bgc_inside,
|
||||||
"opacity" => bgc_inside_a,
|
"opacity" => bgc_inside_a,
|
||||||
),
|
),
|
||||||
"axis on top" => nothing,
|
|
||||||
# "framed" => nothing,
|
|
||||||
# These are for layouting
|
# These are for layouting
|
||||||
"anchor" => "north west",
|
"anchor" => "north west",
|
||||||
"xshift" => string(dx),
|
"xshift" => string(dx),
|
||||||
@ -259,7 +277,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
|||||||
if marker isa Shape
|
if marker isa Shape
|
||||||
x = marker.x
|
x = marker.x
|
||||||
y = marker.y
|
y = marker.y
|
||||||
scale_factor = 0.025
|
scale_factor = 0.00125
|
||||||
mark_size = opt[:markersize] * scale_factor
|
mark_size = opt[:markersize] * scale_factor
|
||||||
path = join(
|
path = join(
|
||||||
[
|
[
|
||||||
@ -346,28 +364,28 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
# add to legend?
|
# add to legend?
|
||||||
if sp[:legend] != :none && pgfx_should_add_to_legend(series)
|
if sp[:legend] != :none
|
||||||
leg_entry = if opt[:label] isa AVec
|
leg_entry = if opt[:label] isa AVec
|
||||||
get(opt[:label], i, "")
|
get(opt[:label], i, "")
|
||||||
elseif opt[:label] isa AbstractString
|
elseif opt[:label] isa AbstractString
|
||||||
if i == 1
|
if i == 1
|
||||||
opt[:label]
|
get(opt, :label, "")
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
throw(ArgumentError("Malformed label. label = $(opt[:label])"))
|
throw(ArgumentError("Malformed label. label = $(opt[:label])"))
|
||||||
end
|
end
|
||||||
if leg_entry == ""
|
if leg_entry == "" || !pgfx_should_add_to_legend(series)
|
||||||
push!(segment_plot.options, "forget plot" => nothing)
|
push!(axis.contents[end].options, "forget plot" => nothing)
|
||||||
continue
|
else
|
||||||
|
leg_opt = PGFPlotsX.Options()
|
||||||
|
if ribbon !== nothing
|
||||||
|
pgfx_filllegend!(axis.contents[end - 3].options, opt)
|
||||||
|
end
|
||||||
|
legend = PGFPlotsX.LegendEntry(leg_opt, leg_entry, false)
|
||||||
|
push!(axis, legend)
|
||||||
end
|
end
|
||||||
leg_opt = PGFPlotsX.Options()
|
|
||||||
if ribbon !== nothing
|
|
||||||
pgfx_filllegend!(axis.contents[end - 3].options, opt)
|
|
||||||
end
|
|
||||||
legend = PGFPlotsX.LegendEntry(leg_opt, leg_entry, false)
|
|
||||||
push!(axis, legend)
|
|
||||||
end
|
end
|
||||||
end # for segments
|
end # for segments
|
||||||
# add subplot annotations
|
# add subplot annotations
|
||||||
@ -586,11 +604,22 @@ const _pgfplotsx_markers = KW(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const _pgfplotsx_legend_pos = KW(
|
const _pgfplotsx_legend_pos = KW(
|
||||||
|
:top => "north",
|
||||||
|
:bottom => "south",
|
||||||
|
:left => "west",
|
||||||
|
:right => "east",
|
||||||
:bottomleft => "south west",
|
:bottomleft => "south west",
|
||||||
:bottomright => "south east",
|
:bottomright => "south east",
|
||||||
:topright => "north east",
|
:topright => "north east",
|
||||||
:topleft => "north west",
|
:topleft => "north west",
|
||||||
|
:outertop => "north",
|
||||||
|
:outerbottom => "outer south",
|
||||||
|
:outerleft => "outer west",
|
||||||
|
:outerright => "outer east",
|
||||||
|
:outerbottomleft => "outer south west",
|
||||||
|
:outerbottomright => "outer south east",
|
||||||
:outertopright => "outer north east",
|
:outertopright => "outer north east",
|
||||||
|
:outertopleft => "outer north west",
|
||||||
)
|
)
|
||||||
|
|
||||||
const _pgfx_framestyles = [:box, :axes, :origin, :zerolines, :grid, :none]
|
const _pgfx_framestyles = [:box, :axes, :origin, :zerolines, :grid, :none]
|
||||||
@ -691,7 +720,7 @@ function pgfx_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
|
|||||||
end
|
end
|
||||||
|
|
||||||
function pgfx_should_add_to_legend(series::Series)
|
function pgfx_should_add_to_legend(series::Series)
|
||||||
series.plotattributes[:primary] && series.plotattributes[:label] != "" &&
|
series.plotattributes[:primary] &&
|
||||||
!(
|
!(
|
||||||
series.plotattributes[:seriestype] in (
|
series.plotattributes[:seriestype] in (
|
||||||
:hexbin,
|
:hexbin,
|
||||||
@ -862,6 +891,48 @@ function pgfx_fillrange_args(fillrange, x, y, z)
|
|||||||
z_fill = [z; _cycle(fillrange, n:-1:1); z[1]]
|
z_fill = [z; _cycle(fillrange, n:-1:1); z[1]]
|
||||||
return PGFPlotsX.Coordiantes(x_fill, y_fill, z_fill)
|
return PGFPlotsX.Coordiantes(x_fill, y_fill, z_fill)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function pgfx_sanitize_string(p::PlotText)
|
||||||
|
PlotText(pgfx_sanitize_string(p.str), p.font)
|
||||||
|
end
|
||||||
|
function pgfx_sanitize_string(s::AbstractString)
|
||||||
|
s = replace(s, r"\\?\#" => "\\#")
|
||||||
|
s = replace(s, r"\\?\%" => "\\%")
|
||||||
|
s = replace(s, r"\\?\_" => "\\_")
|
||||||
|
s = replace(s, r"\\?\&" => "\\&")
|
||||||
|
end
|
||||||
|
function pgfx_sanitize_plot!(plt)
|
||||||
|
for (key, value) in plt.attr
|
||||||
|
if value isa Union{AbstractString, AbstractVector{<:AbstractString}}
|
||||||
|
plt.attr[key] = pgfx_sanitize_string.(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for subplot in plt.subplots
|
||||||
|
for (key, value) in subplot.attr
|
||||||
|
if key == :annotations && subplot.attr[:annotations] !== nothing
|
||||||
|
old_ann = subplot.attr[key]
|
||||||
|
for i in eachindex(old_ann)
|
||||||
|
subplot.attr[key][i] = (old_ann[i][1], old_ann[i][2], pgfx_sanitize_string(old_ann[i][3]))
|
||||||
|
end
|
||||||
|
elseif value isa Union{AbstractString, AbstractVector{<:AbstractString}}
|
||||||
|
subplot.attr[key] = pgfx_sanitize_string.(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for series in plt.series_list
|
||||||
|
for (key, value) in series.plotattributes
|
||||||
|
if key == :series_annotations && series.plotattributes[:series_annotations] !== nothing
|
||||||
|
old_ann = series.plotattributes[key].strs
|
||||||
|
for i in eachindex(old_ann)
|
||||||
|
series.plotattributes[key].strs[i] = pgfx_sanitize_string(old_ann[i])
|
||||||
|
end
|
||||||
|
elseif value isa Union{AbstractString, AbstractVector{<:AbstractString}}
|
||||||
|
series.plotattributes[key] = pgfx_sanitize_string.(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
##
|
||||||
|
end
|
||||||
# --------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------
|
||||||
function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||||
axis = sp[Symbol(letter, :axis)]
|
axis = sp[Symbol(letter, :axis)]
|
||||||
@ -872,6 +943,21 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
|||||||
"scaled $(letter) ticks" => "false",
|
"scaled $(letter) ticks" => "false",
|
||||||
string(letter, :label) => axis[:guide],
|
string(letter, :label) => axis[:guide],
|
||||||
)
|
)
|
||||||
|
tick_color = plot_color(axis[:foreground_color_axis])
|
||||||
|
push!(opt,
|
||||||
|
"$(letter) tick style" => PGFPlotsX.Options(
|
||||||
|
"color" => color(tick_color),
|
||||||
|
"opacity" => alpha(tick_color),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
tick_label_color = plot_color(axis[:tickfontcolor])
|
||||||
|
push!(opt,
|
||||||
|
"$(letter) tick label style" => PGFPlotsX.Options(
|
||||||
|
"color" => color(tick_color),
|
||||||
|
"opacity" => alpha(tick_color),
|
||||||
|
"rotate" => axis[:rotation]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# set to supported framestyle
|
# set to supported framestyle
|
||||||
framestyle = pgfx_framestyle(sp[:framestyle] == false ? :none : sp[:framestyle])
|
framestyle = pgfx_framestyle(sp[:framestyle] == false ? :none : sp[:framestyle])
|
||||||
|
|||||||
@ -42,8 +42,8 @@ _display(plt::Plot{PlotlyJSBackend}) = display(plotlyjs_syncplot(plt))
|
|||||||
|
|
||||||
@require WebIO = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" begin
|
@require WebIO = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" begin
|
||||||
function WebIO.render(plt::Plot{PlotlyJSBackend})
|
function WebIO.render(plt::Plot{PlotlyJSBackend})
|
||||||
prepare_output(plt)
|
plt_html = sprint(show, MIME("text/html"), plt)
|
||||||
WebIO.render(plt.o)
|
return WebIO.render(dom"div"(innerHTML=plt_html))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -594,7 +594,9 @@ function process_annotation(sp::Subplot, xs, ys, labs, font = font())
|
|||||||
xlength = length(methods(length, (typeof(xs),))) == 0 ? 1 : length(xs)
|
xlength = length(methods(length, (typeof(xs),))) == 0 ? 1 : length(xs)
|
||||||
ylength = length(methods(length, (typeof(ys),))) == 0 ? 1 : length(ys)
|
ylength = length(methods(length, (typeof(ys),))) == 0 ? 1 : length(ys)
|
||||||
for i in 1:max(xlength, ylength, length(labs))
|
for i in 1:max(xlength, ylength, length(labs))
|
||||||
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
|
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
|
||||||
|
x = typeof(x) <: TimeType ? Dates.value(x) : x
|
||||||
|
y = typeof(y) <: TimeType ? Dates.value(y) : y
|
||||||
if lab == :auto
|
if lab == :auto
|
||||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||||
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||||
@ -661,8 +663,8 @@ Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for yi in y, xi in x])
|
|||||||
|
|
||||||
Base.Array(surf::Surface) = surf.surf
|
Base.Array(surf::Surface) = surf.surf
|
||||||
|
|
||||||
for f in (:length, :size)
|
for f in (:length, :size, :axes)
|
||||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||||
end
|
end
|
||||||
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
||||||
Base.eltype(surf::Surface{T}) where {T} = eltype(T)
|
Base.eltype(surf::Surface{T}) where {T} = eltype(T)
|
||||||
|
|||||||
@ -875,6 +875,65 @@ const _examples = PlotExample[
|
|||||||
end,
|
end,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
PlotExample(
|
||||||
|
"Array Types",
|
||||||
|
"Plots supports different `Array` types that follow the `AbstractArray` interface, like `StaticArrays` and `OffsetArrays.`",
|
||||||
|
[
|
||||||
|
quote
|
||||||
|
begin
|
||||||
|
using StaticArrays, OffsetArrays
|
||||||
|
sv = SVector{10}(rand(10))
|
||||||
|
ov = OffsetVector(rand(10), -2)
|
||||||
|
plot([sv, ov], label = ["StaticArray" "OffsetArray"])
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
PlotExample(
|
||||||
|
"Setting defaults and font arguments",
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
quote
|
||||||
|
begin
|
||||||
|
using Plots
|
||||||
|
default(
|
||||||
|
titlefont = (20, "times"),
|
||||||
|
legendfontsize = 18,
|
||||||
|
guidefont = (18, :darkgreen),
|
||||||
|
tickfont = (12, :orange),
|
||||||
|
guide = "x",
|
||||||
|
framestyle = :zerolines,
|
||||||
|
yminorgrid = true
|
||||||
|
)
|
||||||
|
plot(
|
||||||
|
[sin, cos],
|
||||||
|
-2π,
|
||||||
|
2π,
|
||||||
|
label = ["sin(θ)" "cos(θ)"],
|
||||||
|
title = "Trigonometric Functions",
|
||||||
|
xlabel = "θ",
|
||||||
|
linewidth = 2,
|
||||||
|
legend = :outertopleft,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
PlotExample(
|
||||||
|
"Heatmap with DateTime axis",
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
quote
|
||||||
|
begin
|
||||||
|
using Dates
|
||||||
|
z = rand(5, 5)
|
||||||
|
x = DateTime.(2016:2020)
|
||||||
|
y = 1:5
|
||||||
|
heatmap(x, y, z)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Some constants for PlotDocs and PlotReferenceImages
|
# Some constants for PlotDocs and PlotReferenceImages
|
||||||
@ -887,7 +946,6 @@ _backend_skips = Dict(
|
|||||||
:pgfplots => [2, 5, 6, 10, 16, 20, 22, 23, 25, 28, 30, 31, 34, 37, 38, 39],
|
:pgfplots => [2, 5, 6, 10, 16, 20, 22, 23, 25, 28, 30, 31, 34, 37, 38, 39],
|
||||||
:pgfplotsx => [ 6, # images
|
:pgfplotsx => [ 6, # images
|
||||||
10, # histogram2d
|
10, # histogram2d
|
||||||
20, # annotations due to missing sanitation
|
|
||||||
22, # contourf
|
22, # contourf
|
||||||
23, # pie
|
23, # pie
|
||||||
32, # spy
|
32, # spy
|
||||||
|
|||||||
@ -52,6 +52,14 @@ function tex(plt::Plot, fn::AbstractString)
|
|||||||
end
|
end
|
||||||
tex(fn::AbstractString) = tex(current(), fn)
|
tex(fn::AbstractString) = tex(current(), fn)
|
||||||
|
|
||||||
|
function json(plt::Plot, fn::AbstractString)
|
||||||
|
fn = addExtension(fn, "json")
|
||||||
|
io = open(fn, "w")
|
||||||
|
show(io, MIME("application/vnd.plotly.v1+json"), plt)
|
||||||
|
close(io)
|
||||||
|
end
|
||||||
|
json(fn::AbstractString) = json(current(), fn)
|
||||||
|
|
||||||
function html(plt::Plot, fn::AbstractString)
|
function html(plt::Plot, fn::AbstractString)
|
||||||
fn = addExtension(fn, "html")
|
fn = addExtension(fn, "html")
|
||||||
io = open(fn, "w")
|
io = open(fn, "w")
|
||||||
@ -78,6 +86,7 @@ const _savemap = Dict(
|
|||||||
"ps" => ps,
|
"ps" => ps,
|
||||||
"eps" => eps,
|
"eps" => eps,
|
||||||
"tex" => tex,
|
"tex" => tex,
|
||||||
|
"json" => json,
|
||||||
"html" => html,
|
"html" => html,
|
||||||
"tikz" => tex,
|
"tikz" => tex,
|
||||||
"txt" => txt,
|
"txt" => txt,
|
||||||
|
|||||||
@ -1,4 +1,28 @@
|
|||||||
|
# Error for aliases used in recipes
|
||||||
|
function warn_on_recipe_aliases!(plotattributes, recipe_type, args...)
|
||||||
|
for k in keys(plotattributes)
|
||||||
|
if !is_default_attribute(k)
|
||||||
|
dk = get(_keyAliases, k, k)
|
||||||
|
if k !== dk
|
||||||
|
@warn "Attribute alias `$k` detected in the $recipe_type recipe defined for the signature $(signature_string(Val{recipe_type}, args...)). To ensure expected behavior it is recommended to use the default attribute `$dk`."
|
||||||
|
end
|
||||||
|
plotattributes[dk] = pop_kw!(plotattributes, k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function warn_on_recipe_aliases!(v::AbstractVector, recipe_type, args...)
|
||||||
|
foreach(x -> warn_on_recipe_aliases!(x, recipe_type, args...), v)
|
||||||
|
end
|
||||||
|
function warn_on_recipe_aliases!(rd::RecipeData, recipe_type, args...)
|
||||||
|
warn_on_recipe_aliases!(rd.plotattributes, recipe_type, args...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function signature_string(::Type{Val{:user}}, args...)
|
||||||
|
return string("(::", join(string.(typeof.(args)), ", ::"), ")")
|
||||||
|
end
|
||||||
|
signature_string(::Type{Val{:type}}, T) = "(::Type{$T}, ::$T)"
|
||||||
|
signature_string(::Type{Val{:plot}}, st) = "(::Type{Val{:$st}}, ::AbstractPlot)"
|
||||||
|
signature_string(::Type{Val{:series}}, st) = "(::Type{Val{:$st}}, x, y, z)"
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# preprocessing
|
# preprocessing
|
||||||
@ -40,14 +64,8 @@ function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{Re
|
|||||||
# remove subplot and axis args from plotattributes... they will be passed through in the kw_list
|
# remove subplot and axis args from plotattributes... they will be passed through in the kw_list
|
||||||
if !isempty(args)
|
if !isempty(args)
|
||||||
for (k,v) in plotattributes
|
for (k,v) in plotattributes
|
||||||
for defdict in (_subplot_defaults,
|
if k in _all_subplot_args || k in _all_axis_args
|
||||||
_axis_defaults,
|
reset_kw!(plotattributes, k)
|
||||||
_axis_defaults_byletter[:x],
|
|
||||||
_axis_defaults_byletter[:y],
|
|
||||||
_axis_defaults_byletter[:z])
|
|
||||||
if haskey(defdict, k)
|
|
||||||
reset_kw!(plotattributes, k)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -82,7 +100,11 @@ function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
|
|||||||
if isempty(next_series.args)
|
if isempty(next_series.args)
|
||||||
_process_userrecipe(plt, kw_list, next_series)
|
_process_userrecipe(plt, kw_list, next_series)
|
||||||
else
|
else
|
||||||
rd_list = RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...)
|
rd_list = RecipesBase.apply_recipe(
|
||||||
|
next_series.plotattributes,
|
||||||
|
next_series.args...
|
||||||
|
)
|
||||||
|
warn_on_recipe_aliases!(rd_list, :user, next_series.args...)
|
||||||
prepend!(still_to_process,rd_list)
|
prepend!(still_to_process,rd_list)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -184,6 +206,7 @@ function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_p
|
|||||||
st = kw[:seriestype]
|
st = kw[:seriestype]
|
||||||
st = kw[:seriestype] = get(_typeAliases, st, st)
|
st = kw[:seriestype] = get(_typeAliases, st, st)
|
||||||
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
|
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
|
||||||
|
warn_on_recipe_aliases!(datalist, :plot, st)
|
||||||
for data in datalist
|
for data in datalist
|
||||||
preprocessArgs!(data.plotattributes)
|
preprocessArgs!(data.plotattributes)
|
||||||
if data.plotattributes[:seriestype] == st
|
if data.plotattributes[:seriestype] == st
|
||||||
@ -408,7 +431,9 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
|
|||||||
|
|
||||||
else
|
else
|
||||||
# get a sub list of series for this seriestype
|
# get a sub list of series for this seriestype
|
||||||
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z])
|
x, y, z = plotattributes[:x], plotattributes[:y], plotattributes[:z]
|
||||||
|
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, x, y, z)
|
||||||
|
warn_on_recipe_aliases!(datalist, :series, st)
|
||||||
|
|
||||||
# assuming there was no error, recursively apply the series recipes
|
# assuming there was no error, recursively apply the series recipes
|
||||||
for data in datalist
|
for data in datalist
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1261,8 +1261,8 @@ end
|
|||||||
yflip := true
|
yflip := true
|
||||||
aspect_ratio := 1
|
aspect_ratio := 1
|
||||||
rs, cs, zs = findnz(z.surf)
|
rs, cs, zs = findnz(z.surf)
|
||||||
xlim := ignorenan_extrema(cs)
|
xlims := ignorenan_extrema(cs)
|
||||||
ylim := ignorenan_extrema(rs)
|
ylims := ignorenan_extrema(rs)
|
||||||
if plotattributes[:markershape] == :none
|
if plotattributes[:markershape] == :none
|
||||||
markershape := :circle
|
markershape := :circle
|
||||||
end
|
end
|
||||||
|
|||||||
389
src/series.jl
389
src/series.jl
@ -15,40 +15,43 @@ prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plo
|
|||||||
prepareSeriesData(::Nothing) = nothing
|
prepareSeriesData(::Nothing) = nothing
|
||||||
prepareSeriesData(t::Tuple{T, T}) where {T<:Number} = t
|
prepareSeriesData(t::Tuple{T, T}) where {T<:Number} = t
|
||||||
prepareSeriesData(f::Function) = f
|
prepareSeriesData(f::Function) = f
|
||||||
prepareSeriesData(a::AbstractArray{<:MaybeNumber}) = replace!(
|
prepareSeriesData(ar::AbstractRange{<:Number}) = ar
|
||||||
x -> ismissing(x) || isinf(x) ? NaN : x,
|
function prepareSeriesData(a::AbstractArray{<:MaybeNumber})
|
||||||
map(float,a))
|
f = isimmutable(a) ? replace : replace!
|
||||||
|
a = f(x -> ismissing(x) || isinf(x) ? NaN : x, map(float, a))
|
||||||
|
end
|
||||||
|
prepareSeriesData(a::AbstractArray{<:Missing}) = fill(NaN, axes(a))
|
||||||
prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a)
|
prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a)
|
||||||
prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf))
|
prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf))
|
||||||
prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image
|
prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image
|
||||||
prepareSeriesData(v::Volume) = Volume(prepareSeriesData(v.v), v.x_extents, v.y_extents, v.z_extents)
|
prepareSeriesData(v::Volume) = Volume(prepareSeriesData(v.v), v.x_extents, v.y_extents, v.z_extents)
|
||||||
|
|
||||||
# default: assume x represents a single series
|
# default: assume x represents a single series
|
||||||
convertToAnyVector(x, plotattributes) = Any[prepareSeriesData(x)]
|
series_vector(x, plotattributes) = [prepareSeriesData(x)]
|
||||||
|
|
||||||
# fixed number of blank series
|
# fixed number of blank series
|
||||||
convertToAnyVector(n::Integer, plotattributes) = Any[zeros(0) for i in 1:n]
|
series_vector(n::Integer, plotattributes) = [zeros(0) for i in 1:n]
|
||||||
|
|
||||||
# vector of data points is a single series
|
# vector of data points is a single series
|
||||||
convertToAnyVector(v::AVec{<:DataPoint}, plotattributes) = Any[prepareSeriesData(v)]
|
series_vector(v::AVec{<:DataPoint}, plotattributes) = [prepareSeriesData(v)]
|
||||||
|
|
||||||
# list of things (maybe other vectors, functions, or something else)
|
# list of things (maybe other vectors, functions, or something else)
|
||||||
function convertToAnyVector(v::AVec, plotattributes)
|
function series_vector(v::AVec, plotattributes)
|
||||||
if all(x -> x isa MaybeNumber, v)
|
if all(x -> x isa MaybeNumber, v)
|
||||||
convertToAnyVector(Vector{MaybeNumber}(v), plotattributes)
|
series_vector(Vector{MaybeNumber}(v), plotattributes)
|
||||||
elseif all(x -> x isa MaybeString, v)
|
elseif all(x -> x isa MaybeString, v)
|
||||||
convertToAnyVector(Vector{MaybeString}(v), plotattributes)
|
series_vector(Vector{MaybeString}(v), plotattributes)
|
||||||
else
|
else
|
||||||
vcat((convertToAnyVector(vi, plotattributes) for vi in v)...)
|
vcat((series_vector(vi, plotattributes) for vi in v)...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Matrix is split into columns
|
# Matrix is split into columns
|
||||||
function convertToAnyVector(v::AMat{<:DataPoint}, plotattributes)
|
function series_vector(v::AMat{<:DataPoint}, plotattributes)
|
||||||
if all3D(plotattributes)
|
if all3D(plotattributes)
|
||||||
Any[prepareSeriesData(Surface(v))]
|
[prepareSeriesData(Surface(v))]
|
||||||
else
|
else
|
||||||
Any[prepareSeriesData(v[:, i]) for i in axes(v, 2)]
|
[prepareSeriesData(v[:, i]) for i in axes(v, 2)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -57,34 +60,32 @@ end
|
|||||||
|
|
||||||
|
|
||||||
process_fillrange(range::Number, plotattributes) = [range]
|
process_fillrange(range::Number, plotattributes) = [range]
|
||||||
process_fillrange(range, plotattributes) = convertToAnyVector(range, plotattributes)
|
process_fillrange(range, plotattributes) = series_vector(range, plotattributes)
|
||||||
|
|
||||||
process_ribbon(ribbon::Number, plotattributes) = [ribbon]
|
process_ribbon(ribbon::Number, plotattributes) = [ribbon]
|
||||||
process_ribbon(ribbon, plotattributes) = convertToAnyVector(ribbon, plotattributes)
|
process_ribbon(ribbon, plotattributes) = series_vector(ribbon, plotattributes)
|
||||||
# ribbon as a tuple: (lower_ribbons, upper_ribbons)
|
# ribbon as a tuple: (lower_ribbons, upper_ribbons)
|
||||||
process_ribbon(ribbon::Tuple{Any,Any}, plotattributes) = collect(zip(convertToAnyVector(ribbon[1], plotattributes),
|
process_ribbon(ribbon::Tuple{S, T}, plotattributes) where {S, T} = collect(zip(
|
||||||
convertToAnyVector(ribbon[2], plotattributes)))
|
series_vector(ribbon[1], plotattributes),
|
||||||
|
series_vector(ribbon[2], plotattributes),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
compute_x(x::Nothing, y::Nothing, z) = axes(z,1)
|
||||||
# result in that array being shared. push!, etc will add too many items to that array
|
compute_x(x::Nothing, y, z) = axes(y,1)
|
||||||
|
compute_x(x::Function, y, z) = map(x, y)
|
||||||
|
compute_x(x, y, z) = x
|
||||||
|
|
||||||
compute_x(x::Nothing, y::Nothing, z) = axes(z,1)
|
compute_y(x::Nothing, y::Nothing, z) = axes(z,2)
|
||||||
compute_x(x::Nothing, y, z) = axes(y,1)
|
compute_y(x, y::Function, z) = map(y, x)
|
||||||
compute_x(x::Function, y, z) = map(x, y)
|
compute_y(x, y, z) = y
|
||||||
compute_x(x, y, z) = copy(x)
|
|
||||||
|
|
||||||
# compute_y(x::Void, y::Function, z) = error()
|
compute_z(x, y, z::Function) = map(z, x, y)
|
||||||
compute_y(x::Nothing, y::Nothing, z) = axes(z,2)
|
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||||
compute_y(x, y::Function, z) = map(y, x)
|
compute_z(x, y, z::Nothing) = nothing
|
||||||
compute_y(x, y, z) = copy(y)
|
compute_z(x, y, z) = z
|
||||||
|
|
||||||
compute_z(x, y, z::Function) = map(z, x, y)
|
|
||||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
|
||||||
compute_z(x, y, z::Nothing) = nothing
|
|
||||||
compute_z(x, y, z) = copy(z)
|
|
||||||
|
|
||||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
||||||
nobigs(v::AVec{BigInt}) = map(Int64, v)
|
nobigs(v::AVec{BigInt}) = map(Int64, v)
|
||||||
@ -107,10 +108,16 @@ compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all no
|
|||||||
|
|
||||||
# we are going to build recipes to do the processing and splitting of the args
|
# we are going to build recipes to do the processing and splitting of the args
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# The catch-all SliceIt recipe
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
# ensure we dispatch to the slicer
|
# ensure we dispatch to the slicer
|
||||||
struct SliceIt end
|
struct SliceIt end
|
||||||
|
|
||||||
# the catch-all recipes
|
# The `SliceIt` recipe finishes user and type recipe processing.
|
||||||
|
# It splits processed data into individual series data, stores in copied `plotattributes`
|
||||||
|
# for each series and returns no arguments.
|
||||||
@recipe function f(::Type{SliceIt}, x, y, z)
|
@recipe function f(::Type{SliceIt}, x, y, z)
|
||||||
|
|
||||||
# handle data with formatting attached
|
# handle data with formatting attached
|
||||||
@ -127,10 +134,9 @@ struct SliceIt end
|
|||||||
z = z.data
|
z = z.data
|
||||||
end
|
end
|
||||||
|
|
||||||
xs = convertToAnyVector(x, plotattributes)
|
xs = series_vector(x, plotattributes)
|
||||||
ys = convertToAnyVector(y, plotattributes)
|
ys = series_vector(y, plotattributes)
|
||||||
zs = convertToAnyVector(z, plotattributes)
|
zs = series_vector(z, plotattributes)
|
||||||
|
|
||||||
|
|
||||||
fr = pop!(plotattributes, :fillrange, nothing)
|
fr = pop!(plotattributes, :fillrange, nothing)
|
||||||
fillranges = process_fillrange(fr, plotattributes)
|
fillranges = process_fillrange(fr, plotattributes)
|
||||||
@ -140,8 +146,6 @@ struct SliceIt end
|
|||||||
ribbons = process_ribbon(rib, plotattributes)
|
ribbons = process_ribbon(rib, plotattributes)
|
||||||
mr = length(ribbons)
|
mr = length(ribbons)
|
||||||
|
|
||||||
# @show zs
|
|
||||||
|
|
||||||
mx = length(xs)
|
mx = length(xs)
|
||||||
my = length(ys)
|
my = length(ys)
|
||||||
mz = length(zs)
|
mz = length(zs)
|
||||||
@ -166,56 +170,111 @@ struct SliceIt end
|
|||||||
nothing # don't add a series for the main block
|
nothing # don't add a series for the main block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# Apply type recipes
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
# this is the default "type recipe"... just pass the object through
|
# this is the default "type recipe"... just pass the object through
|
||||||
@recipe f(::Type{T}, v::T) where {T<:Any} = v
|
@recipe f(::Type{T}, v::T) where T = v
|
||||||
|
|
||||||
# this should catch unhandled "series recipes" and error with a nice message
|
# this should catch unhandled "series recipes" and error with a nice message
|
||||||
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
|
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
|
||||||
|
|
||||||
_apply_type_recipe(plotattributes, v) = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
|
function _apply_type_recipe(plotattributes, v, letter)
|
||||||
|
_preprocess_axis_args!(plotattributes, letter)
|
||||||
|
rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v)
|
||||||
|
warn_on_recipe_aliases!(plotattributes, :type, typeof(v))
|
||||||
|
_postprocess_axis_args!(plotattributes, letter)
|
||||||
|
return rdvec[1].args[1]
|
||||||
|
end
|
||||||
|
|
||||||
# Handle type recipes when the recipe is defined on the elements.
|
# Handle type recipes when the recipe is defined on the elements.
|
||||||
# This sort of recipe should return a pair of functions... one to convert to number,
|
# This sort of recipe should return a pair of functions... one to convert to number,
|
||||||
# and one to format tick values.
|
# and one to format tick values.
|
||||||
function _apply_type_recipe(plotattributes, v::AbstractArray)
|
function _apply_type_recipe(plotattributes, v::AbstractArray, letter)
|
||||||
isempty(skipmissing(v)) && return Float64[]
|
_preprocess_axis_args!(plotattributes, letter)
|
||||||
x = first(skipmissing(v))
|
# First we try to apply an array type recipe.
|
||||||
args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args
|
w = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
|
||||||
if length(args) == 2 && typeof(args[1]) <: Function && typeof(args[2]) <: Function
|
warn_on_recipe_aliases!(plotattributes, :type, typeof(v))
|
||||||
numfunc, formatter = args
|
# If the type did not change try it element-wise
|
||||||
Formatted(map(numfunc, v), formatter)
|
if typeof(v) == typeof(w)
|
||||||
|
isempty(skipmissing(v)) && return Float64[]
|
||||||
|
x = first(skipmissing(v))
|
||||||
|
args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args
|
||||||
|
warn_on_recipe_aliases!(plotattributes, :type, typeof(x))
|
||||||
|
_postprocess_axis_args!(plotattributes, letter)
|
||||||
|
if length(args) == 2 && all(arg -> arg isa Function, args)
|
||||||
|
numfunc, formatter = args
|
||||||
|
return Formatted(map(numfunc, v), formatter)
|
||||||
|
else
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_postprocess_axis_args!(plotattributes, letter)
|
||||||
|
return w
|
||||||
|
end
|
||||||
|
|
||||||
|
# special handling for Surface... need to properly unwrap and re-wrap
|
||||||
|
_apply_type_recipe(plotattributes, v::Surface{<:AMat{<:DataPoint}}) = v
|
||||||
|
function _apply_type_recipe(plotattributes, v::Surface)
|
||||||
|
ret = _apply_type_recipe(plotattributes, v.surf)
|
||||||
|
if typeof(ret) <: Formatted
|
||||||
|
Formatted(Surface(ret.data), ret.formatter)
|
||||||
else
|
else
|
||||||
v
|
Surface(ret.data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# # special handling for Surface... need to properly unwrap and re-wrap
|
# don't do anything for datapoints or nothing
|
||||||
# function _apply_type_recipe(plotattributes, v::Surface)
|
_apply_type_recipe(plotattributes, v::AbstractArray{<:DataPoint}, letter) = v
|
||||||
# T = eltype(v.surf)
|
_apply_type_recipe(plotattributes, v::Nothing, letter) = v
|
||||||
# @show T
|
|
||||||
# if T <: Integer || T <: AbstractFloat
|
|
||||||
# v
|
|
||||||
# else
|
|
||||||
# ret = _apply_type_recipe(plotattributes, v.surf)
|
|
||||||
# if typeof(ret) <: Formatted
|
|
||||||
# Formatted(Surface(ret.data), ret.formatter)
|
|
||||||
# else
|
|
||||||
# v
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# don't do anything for ints or floats
|
# axis args before type recipes should still be mapped to all axes
|
||||||
_apply_type_recipe(plotattributes, v::AbstractArray{T}) where {T<:Union{Integer,AbstractFloat}} = v
|
function _preprocess_axis_args!(plotattributes)
|
||||||
|
for (k, v) in plotattributes
|
||||||
|
if is_axis_attr_noletter(k)
|
||||||
|
pop!(plotattributes, k)
|
||||||
|
for l in (:x, :y, :z)
|
||||||
|
lk = Symbol(l, k)
|
||||||
|
haskey(plotattributes, lk) || (plotattributes[lk] = v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function _preprocess_axis_args!(plotattributes, letter)
|
||||||
|
plotattributes[:letter] = letter
|
||||||
|
_preprocess_axis_args!(plotattributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
# axis args in type recipes should only be applied to the current axis
|
||||||
|
function _postprocess_axis_args!(plotattributes, letter)
|
||||||
|
pop!(plotattributes, :letter)
|
||||||
|
if letter in (:x, :y, :z)
|
||||||
|
for (k, v) in plotattributes
|
||||||
|
if is_axis_attr_noletter(k)
|
||||||
|
pop!(plotattributes, k)
|
||||||
|
lk = Symbol(letter, k)
|
||||||
|
haskey(plotattributes, lk) || (plotattributes[lk] = v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# Fallback user recipes calling type recipes
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
# handle "type recipes" by converting inputs, and then either re-calling or slicing
|
# handle "type recipes" by converting inputs, and then either re-calling or slicing
|
||||||
@recipe function f(x, y, z)
|
@recipe function f(x, y, z)
|
||||||
|
wrap_surfaces!(plotattributes, x, y, z)
|
||||||
did_replace = false
|
did_replace = false
|
||||||
newx = _apply_type_recipe(plotattributes, x)
|
newx = _apply_type_recipe(plotattributes, x, :x)
|
||||||
x === newx || (did_replace = true)
|
x === newx || (did_replace = true)
|
||||||
newy = _apply_type_recipe(plotattributes, y)
|
newy = _apply_type_recipe(plotattributes, y, :y)
|
||||||
y === newy || (did_replace = true)
|
y === newy || (did_replace = true)
|
||||||
newz = _apply_type_recipe(plotattributes, z)
|
newz = _apply_type_recipe(plotattributes, z, :z)
|
||||||
z === newz || (did_replace = true)
|
z === newz || (did_replace = true)
|
||||||
if did_replace
|
if did_replace
|
||||||
newx, newy, newz
|
newx, newy, newz
|
||||||
@ -224,10 +283,11 @@ _apply_type_recipe(plotattributes, v::AbstractArray{T}) where {T<:Union{Integer,
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
@recipe function f(x, y)
|
@recipe function f(x, y)
|
||||||
|
wrap_surfaces!(plotattributes, x, y)
|
||||||
did_replace = false
|
did_replace = false
|
||||||
newx = _apply_type_recipe(plotattributes, x)
|
newx = _apply_type_recipe(plotattributes, x, :x)
|
||||||
x === newx || (did_replace = true)
|
x === newx || (did_replace = true)
|
||||||
newy = _apply_type_recipe(plotattributes, y)
|
newy = _apply_type_recipe(plotattributes, y, :y)
|
||||||
y === newy || (did_replace = true)
|
y === newy || (did_replace = true)
|
||||||
if did_replace
|
if did_replace
|
||||||
newx, newy
|
newx, newy
|
||||||
@ -236,7 +296,8 @@ end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
@recipe function f(y)
|
@recipe function f(y)
|
||||||
newy = _apply_type_recipe(plotattributes, y)
|
wrap_surfaces!(plotattributes, y)
|
||||||
|
newy = _apply_type_recipe(plotattributes, y, :y)
|
||||||
if y !== newy
|
if y !== newy
|
||||||
newy
|
newy
|
||||||
else
|
else
|
||||||
@ -249,7 +310,7 @@ end
|
|||||||
@recipe function f(v1, v2, v3, v4, vrest...)
|
@recipe function f(v1, v2, v3, v4, vrest...)
|
||||||
did_replace = false
|
did_replace = false
|
||||||
newargs = map(v -> begin
|
newargs = map(v -> begin
|
||||||
newv = _apply_type_recipe(plotattributes, v)
|
newv = _apply_type_recipe(plotattributes, v, :unknown)
|
||||||
if newv !== v
|
if newv !== v
|
||||||
did_replace = true
|
did_replace = true
|
||||||
end
|
end
|
||||||
@ -262,12 +323,14 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
# # 1 argument
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
|
|
||||||
# helper function to ensure relevant attributes are wrapped by Surface
|
# helper function to ensure relevant attributes are wrapped by Surface
|
||||||
function wrap_surfaces(plotattributes::AKW)
|
function wrap_surfaces!(plotattributes, args...) end
|
||||||
|
wrap_surfaces!(plotattributes, x::AMat, y::AMat, z::AMat) = wrap_surfaces!(plotattributes)
|
||||||
|
wrap_surfaces!(plotattributes, x::AVec, y::AVec, z::AMat) = wrap_surfaces!(plotattributes)
|
||||||
|
function wrap_surfaces!(plotattributes, x::AVec, y::AVec, z::Surface)
|
||||||
|
wrap_surfaces!(plotattributes)
|
||||||
|
end
|
||||||
|
function wrap_surfaces!(plotattributes)
|
||||||
if haskey(plotattributes, :fill_z)
|
if haskey(plotattributes, :fill_z)
|
||||||
v = plotattributes[:fill_z]
|
v = plotattributes[:fill_z]
|
||||||
if !isa(v, Surface)
|
if !isa(v, Surface)
|
||||||
@ -276,42 +339,57 @@ function wrap_surfaces(plotattributes::AKW)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# 1 argument
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||||
|
|
||||||
all3D(plotattributes) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(plotattributes, :seriestype, :none))
|
all3D(plotattributes) = trueOrAllTrue(
|
||||||
|
st -> st in (
|
||||||
|
:contour,
|
||||||
|
:contourf,
|
||||||
|
:heatmap,
|
||||||
|
:surface,
|
||||||
|
:wireframe,
|
||||||
|
:contour3d,
|
||||||
|
:image,
|
||||||
|
:plots_heatmap,
|
||||||
|
),
|
||||||
|
get(plotattributes, :seriestype, :none),
|
||||||
|
)
|
||||||
|
|
||||||
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
||||||
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat,Missing}
|
@recipe function f(mat::AMat)
|
||||||
if all3D(plotattributes)
|
if all3D(plotattributes)
|
||||||
n,m = axes(mat)
|
n, m = axes(mat)
|
||||||
wrap_surfaces(plotattributes)
|
m, n, Surface(mat)
|
||||||
SliceIt, m, n, Surface(mat)
|
|
||||||
else
|
else
|
||||||
SliceIt, nothing, mat, nothing
|
nothing, mat, nothing
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# if a matrix is wrapped by Formatted, do similar logic, but wrap data with Surface
|
# if a matrix is wrapped by Formatted, do similar logic, but wrap data with Surface
|
||||||
@recipe function f(fmt::Formatted{T}) where T<:AbstractMatrix
|
@recipe function f(fmt::Formatted{<:AMat})
|
||||||
if all3D(plotattributes)
|
if all3D(plotattributes)
|
||||||
mat = fmt.data
|
mat = fmt.data
|
||||||
n,m = axes(mat)
|
n, m = axes(mat)
|
||||||
wrap_surfaces(plotattributes)
|
m, n, Formatted(Surface(mat), fmt.formatter)
|
||||||
SliceIt, m, n, Formatted(Surface(mat), fmt.formatter)
|
|
||||||
else
|
else
|
||||||
SliceIt, nothing, fmt, nothing
|
nothing, fmt, nothing
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# assume this is a Volume, so construct one
|
# assume this is a Volume, so construct one
|
||||||
@recipe function f(vol::AbstractArray{T,3}, args...) where T<:Union{Number,Missing}
|
@recipe function f(vol::AbstractArray{<:MaybeNumber, 3}, args...)
|
||||||
seriestype := :volume
|
seriestype := :volume
|
||||||
SliceIt, nothing, Volume(vol, args...), nothing
|
SliceIt, nothing, Volume(vol, args...), nothing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# # images - grays
|
# images - grays
|
||||||
function clamp_greys!(mat::AMat{T}) where T<:Gray
|
function clamp_greys!(mat::AMat{<:Gray})
|
||||||
for i in eachindex(mat)
|
for i in eachindex(mat)
|
||||||
mat[i].val < 0 && (mat[i] = Gray(0))
|
mat[i].val < 0 && (mat[i] = Gray(0))
|
||||||
mat[i].val > 1 && (mat[i] = Gray(1))
|
mat[i].val > 1 && (mat[i] = Gray(1))
|
||||||
@ -319,7 +397,7 @@ function clamp_greys!(mat::AMat{T}) where T<:Gray
|
|||||||
mat
|
mat
|
||||||
end
|
end
|
||||||
|
|
||||||
@recipe function f(mat::AMat{T}) where T<:Gray
|
@recipe function f(mat::AMat{<:Gray})
|
||||||
n, m = axes(mat)
|
n, m = axes(mat)
|
||||||
if is_seriestype_supported(:image)
|
if is_seriestype_supported(:image)
|
||||||
seriestype := :image
|
seriestype := :image
|
||||||
@ -334,8 +412,7 @@ end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# # images - colors
|
# images - colors
|
||||||
|
|
||||||
@recipe function f(mat::AMat{T}) where T<:Colorant
|
@recipe function f(mat::AMat{T}) where T<:Colorant
|
||||||
n, m = axes(mat)
|
n, m = axes(mat)
|
||||||
|
|
||||||
@ -353,8 +430,7 @@ end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
# plotting arbitrary shapes/polygons
|
||||||
# # plotting arbitrary shapes/polygons
|
|
||||||
|
|
||||||
@recipe function f(shape::Shape)
|
@recipe function f(shape::Shape)
|
||||||
seriestype --> :shape
|
seriestype --> :shape
|
||||||
@ -396,14 +472,13 @@ end
|
|||||||
f, xmin, xmax
|
f, xmin, xmax
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# # --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# # 2 arguments
|
# 2 arguments
|
||||||
# # --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
#
|
|
||||||
#
|
# if functions come first, just swap the order (not to be confused with parametric
|
||||||
# # if functions come first, just swap the order (not to be confused with parametric functions...
|
# functions... as there would be more than one function passed in)
|
||||||
# # as there would be more than one function passed in)
|
|
||||||
|
|
||||||
@recipe function f(f::FuncOrFuncs{F}, x) where F<:Function
|
@recipe function f(f::FuncOrFuncs{F}, x) where F<:Function
|
||||||
F2 = typeof(x)
|
F2 = typeof(x)
|
||||||
@ -411,58 +486,25 @@ end
|
|||||||
x, f
|
x, f
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
# # 3 arguments
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # 3d line or scatter
|
|
||||||
|
|
||||||
@recipe function f(x::AVec, y::AVec, z::AVec)
|
# --------------------------------------------------------------------
|
||||||
# st = get(plotattributes, :seriestype, :none)
|
# 3 arguments
|
||||||
# if st == :scatter
|
# --------------------------------------------------------------------
|
||||||
# plotattributes[:seriestype] = :scatter3d
|
|
||||||
# elseif !is3d(st)
|
|
||||||
# plotattributes[:seriestype] = :path3d
|
|
||||||
# end
|
|
||||||
SliceIt, x, y, z
|
|
||||||
end
|
|
||||||
|
|
||||||
@recipe function f(x::AMat, y::AMat, z::AMat)
|
|
||||||
# st = get(plotattributes, :seriestype, :none)
|
|
||||||
# if size(x) == size(y) == size(z)
|
|
||||||
# if !is3d(st)
|
|
||||||
# seriestype := :path3d
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
wrap_surfaces(plotattributes)
|
|
||||||
SliceIt, x, y, z
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# # surface-like... function
|
|
||||||
|
|
||||||
|
# surface-like... function
|
||||||
@recipe function f(x::AVec, y::AVec, zf::Function)
|
@recipe function f(x::AVec, y::AVec, zf::Function)
|
||||||
# x = X <: Number ? sort(x) : x
|
x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||||
# y = Y <: Number ? sort(y) : y
|
|
||||||
wrap_surfaces(plotattributes)
|
|
||||||
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
# surface-like... matrix grid
|
||||||
# # surface-like... matrix grid
|
|
||||||
|
|
||||||
@recipe function f(x::AVec, y::AVec, z::AMat)
|
@recipe function f(x::AVec, y::AVec, z::AMat)
|
||||||
if !like_surface(get(plotattributes, :seriestype, :none))
|
if !like_surface(get(plotattributes, :seriestype, :none))
|
||||||
plotattributes[:seriestype] = :contour
|
plotattributes[:seriestype] = :contour
|
||||||
end
|
end
|
||||||
wrap_surfaces(plotattributes)
|
x, y, Surface(z)
|
||||||
SliceIt, x, y, Surface(z)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# # images - grays
|
# images - grays
|
||||||
|
|
||||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray
|
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray
|
||||||
if is_seriestype_supported(:image)
|
if is_seriestype_supported(:image)
|
||||||
seriestype := :image
|
seriestype := :image
|
||||||
@ -477,8 +519,7 @@ end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# # images - colors
|
# images - colors
|
||||||
|
|
||||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant
|
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant
|
||||||
if is_seriestype_supported(:image)
|
if is_seriestype_supported(:image)
|
||||||
seriestype := :image
|
seriestype := :image
|
||||||
@ -493,27 +534,19 @@ end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
# # Parametric functions
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
|
|
||||||
#
|
# --------------------------------------------------------------------
|
||||||
# # special handling... xmin/xmax with parametric function(s)
|
# Parametric functions
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
|
# special handling... xmin/xmax with parametric function(s)
|
||||||
@recipe function f(f::Function, xmin::Number, xmax::Number)
|
@recipe function f(f::Function, xmin::Number, xmax::Number)
|
||||||
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||||
_scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
_scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||||
end
|
end
|
||||||
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
|
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
|
||||||
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||||
xs = Array{Any}(undef, length(fs))
|
unzip(_scaled_adapted_grid.(fs, xscale, yscale, xmin, xmax))
|
||||||
ys = Array{Any}(undef, length(fs))
|
|
||||||
for (i, (x, y)) in enumerate(_scaled_adapted_grid(f, xscale, yscale, xmin, xmax) for f in fs)
|
|
||||||
xs[i] = x
|
|
||||||
ys[i] = y
|
|
||||||
end
|
|
||||||
xs, ys
|
|
||||||
end
|
end
|
||||||
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||||
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, range(umin, stop = umax, length = n)
|
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, range(umin, stop = umax, length = n)
|
||||||
@ -524,8 +557,7 @@ function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
|||||||
xinv.(xs), yinv.(ys)
|
xinv.(xs), yinv.(ys)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
# special handling... 3D parametric function(s)
|
||||||
# # special handling... 3D parametric function(s)
|
|
||||||
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function}
|
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function}
|
||||||
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
|
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
|
||||||
end
|
end
|
||||||
@ -533,12 +565,10 @@ end
|
|||||||
fx, fy, fz, range(umin, stop = umax, length = numPoints)
|
fx, fy, fz, range(umin, stop = umax, length = numPoints)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
#
|
# --------------------------------------------------------------------
|
||||||
# # --------------------------------------------------------------------
|
# Lists of tuples and GeometryTypes.Points
|
||||||
# # Lists of tuples and GeometryTypes.Points
|
# --------------------------------------------------------------------
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
|
|
||||||
@recipe f(v::AVec{<:Tuple}) = unzip(v)
|
@recipe f(v::AVec{<:Tuple}) = unzip(v)
|
||||||
@recipe f(v::AVec{<:GeometryTypes.Point}) = unzip(v)
|
@recipe f(v::AVec{<:GeometryTypes.Point}) = unzip(v)
|
||||||
@ -548,23 +578,10 @@ end
|
|||||||
# Special case for 4-tuples in :ohlc series
|
# Special case for 4-tuples in :ohlc series
|
||||||
@recipe f(xyuv::AVec{<:Tuple{R1,R2,R3,R4}}) where {R1,R2,R3,R4} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
@recipe f(xyuv::AVec{<:Tuple{R1,R2,R3,R4}}) where {R1,R2,R3,R4} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
||||||
|
|
||||||
#
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
# # handle grouping
|
|
||||||
# # --------------------------------------------------------------------
|
|
||||||
|
|
||||||
# @recipe function f(groupby::GroupBy, args...)
|
# --------------------------------------------------------------------
|
||||||
# for (i,glab) in enumerate(groupby.groupLabels)
|
# handle grouping
|
||||||
# # create a new series, with the label of the group, and an idxfilter (to be applied in slice_and_dice)
|
# --------------------------------------------------------------------
|
||||||
# # TODO: use @series instead
|
|
||||||
# @show i, glab, groupby.groupIds[i]
|
|
||||||
# di = copy(plotattributes)
|
|
||||||
# get!(di, :label, string(glab))
|
|
||||||
# get!(di, :idxfilter, groupby.groupIds[i])
|
|
||||||
# push!(series_list, RecipeData(di, args))
|
|
||||||
# end
|
|
||||||
# nothing
|
|
||||||
# end
|
|
||||||
|
|
||||||
splittable_kw(key, val, lengthGroup) = false
|
splittable_kw(key, val, lengthGroup) = false
|
||||||
splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && length(axes(val,1)) == lengthGroup
|
splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && length(axes(val,1)) == lengthGroup
|
||||||
|
|||||||
@ -271,6 +271,7 @@ Draw a 3D surface plot.
|
|||||||
|
|
||||||
# Example
|
# Example
|
||||||
```julia-repl
|
```julia-repl
|
||||||
|
julia> using LinearAlgebra
|
||||||
julia> x = y = range(-3, 3, length = 100)
|
julia> x = y = range(-3, 3, length = 100)
|
||||||
julia> surface(x, y, (x, y) -> sinc(norm([x, y])))
|
julia> surface(x, y, (x, y) -> sinc(norm([x, y])))
|
||||||
```
|
```
|
||||||
@ -428,8 +429,8 @@ Add annotations to an existing plot.
|
|||||||
|
|
||||||
# Arguments
|
# Arguments
|
||||||
|
|
||||||
- `anns`: An `AbstractVector` of tuples of the form (x,y,text). The text object
|
- `anns`: An `AbstractVector` of tuples of the form `(x,y,text)`. The `text` object
|
||||||
can be an String or PlotText
|
can be a `String` or `PlotText`.
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
```julia-repl
|
```julia-repl
|
||||||
|
|||||||
@ -26,8 +26,9 @@ struct Attr <: AbstractDict{Symbol,Any}
|
|||||||
defaults::KW
|
defaults::KW
|
||||||
end
|
end
|
||||||
|
|
||||||
Base.getindex(attr::Attr, k) = haskey(attr.explicit,k) ?
|
function Base.getindex(attr::Attr, k)
|
||||||
attr.explicit[k] : attr.defaults[k]
|
return haskey(attr.explicit, k) ? attr.explicit[k] : attr.defaults[k]
|
||||||
|
end
|
||||||
Base.haskey(attr::Attr, k) = haskey(attr.explicit,k) || haskey(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
|
Base.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default
|
||||||
function Base.get!(attr::Attr, k, default)
|
function Base.get!(attr::Attr, k, default)
|
||||||
@ -41,7 +42,7 @@ end
|
|||||||
function Base.delete!(attr::Attr, k)
|
function Base.delete!(attr::Attr, k)
|
||||||
haskey(attr.explicit, k) && delete!(attr.explicit, k)
|
haskey(attr.explicit, k) && delete!(attr.explicit, k)
|
||||||
haskey(attr.defaults, k) && delete!(attr.defaults, k)
|
haskey(attr.defaults, k) && delete!(attr.defaults, k)
|
||||||
end
|
end
|
||||||
Base.length(attr::Attr) = length(union(keys(attr.explicit), keys(attr.defaults)))
|
Base.length(attr::Attr) = length(union(keys(attr.explicit), keys(attr.defaults)))
|
||||||
function Base.iterate(attr::Attr)
|
function Base.iterate(attr::Attr)
|
||||||
exp_keys = keys(attr.explicit)
|
exp_keys = keys(attr.explicit)
|
||||||
|
|||||||
469
src/utils.jl
469
src/utils.jl
@ -1,119 +1,4 @@
|
|||||||
|
|
||||||
calcMidpoints(edges::AbstractVector) = Float64[0.5 * (edges[i] + edges[i+1]) for i in 1:length(edges)-1]
|
|
||||||
|
|
||||||
"Make histogram-like bins of data"
|
|
||||||
function binData(data, nbins)
|
|
||||||
lo, hi = ignorenan_extrema(data)
|
|
||||||
edges = collect(range(lo, stop=hi, length=nbins+1))
|
|
||||||
midpoints = calcMidpoints(edges)
|
|
||||||
buckets = Int[max(2, min(searchsortedfirst(edges, x), length(edges)))-1 for x in data]
|
|
||||||
counts = zeros(Int, length(midpoints))
|
|
||||||
for b in buckets
|
|
||||||
counts[b] += 1
|
|
||||||
end
|
|
||||||
edges, midpoints, buckets, counts
|
|
||||||
end
|
|
||||||
|
|
||||||
"""
|
|
||||||
A hacky replacement for a histogram when the backend doesn't support histograms directly.
|
|
||||||
Convert it into a bar chart with the appropriate x/y values.
|
|
||||||
"""
|
|
||||||
function histogramHack(; kw...)
|
|
||||||
plotattributes = KW(kw)
|
|
||||||
|
|
||||||
# we assume that the y kwarg is set with the data to be binned, and nbins is also defined
|
|
||||||
edges, midpoints, buckets, counts = binData(plotattributes[:y], plotattributes[:bins])
|
|
||||||
plotattributes[:x] = midpoints
|
|
||||||
plotattributes[:y] = float(counts)
|
|
||||||
plotattributes[:seriestype] = :bar
|
|
||||||
plotattributes[:fillrange] = plotattributes[:fillrange] === nothing ? 0.0 : plotattributes[:fillrange]
|
|
||||||
plotattributes
|
|
||||||
end
|
|
||||||
|
|
||||||
"""
|
|
||||||
A hacky replacement for a bar graph when the backend doesn't support bars directly.
|
|
||||||
Convert it into a line chart with fillrange set.
|
|
||||||
"""
|
|
||||||
function barHack(; kw...)
|
|
||||||
plotattributes = KW(kw)
|
|
||||||
midpoints = plotattributes[:x]
|
|
||||||
heights = plotattributes[:y]
|
|
||||||
fillrange = plotattributes[:fillrange] === nothing ? 0.0 : plotattributes[:fillrange]
|
|
||||||
|
|
||||||
# estimate the edges
|
|
||||||
dists = diff(midpoints) * 0.5
|
|
||||||
edges = zeros(length(midpoints)+1)
|
|
||||||
for i in eachindex(edges)
|
|
||||||
if i == 1
|
|
||||||
edge = midpoints[1] - dists[1]
|
|
||||||
elseif i == length(edges)
|
|
||||||
edge = midpoints[i-1] + dists[i-2]
|
|
||||||
else
|
|
||||||
edge = midpoints[i-1] + dists[i-1]
|
|
||||||
end
|
|
||||||
edges[i] = edge
|
|
||||||
end
|
|
||||||
|
|
||||||
x = Float64[]
|
|
||||||
y = Float64[]
|
|
||||||
for i in eachindex(heights)
|
|
||||||
e1, e2 = edges[i:i+1]
|
|
||||||
append!(x, [e1, e1, e2, e2])
|
|
||||||
append!(y, [fillrange, heights[i], heights[i], fillrange])
|
|
||||||
end
|
|
||||||
|
|
||||||
plotattributes[:x] = x
|
|
||||||
plotattributes[:y] = y
|
|
||||||
plotattributes[:seriestype] = :path
|
|
||||||
plotattributes[:fillrange] = fillrange
|
|
||||||
plotattributes
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
A hacky replacement for a sticks graph when the backend doesn't support sticks directly.
|
|
||||||
Convert it into a line chart that traces the sticks, and a scatter that sets markers at the points.
|
|
||||||
"""
|
|
||||||
function sticksHack(; kw...)
|
|
||||||
plotattributesLine = KW(kw)
|
|
||||||
plotattributesScatter = copy(plotattributesLine)
|
|
||||||
|
|
||||||
# these are the line vertices
|
|
||||||
x = Float64[]
|
|
||||||
y = Float64[]
|
|
||||||
fillrange = plotattributesLine[:fillrange] === nothing ? 0.0 : plotattributesLine[:fillrange]
|
|
||||||
|
|
||||||
# calculate the vertices
|
|
||||||
yScatter = plotattributesScatter[:y]
|
|
||||||
for (i,xi) in enumerate(plotattributesScatter[:x])
|
|
||||||
yi = yScatter[i]
|
|
||||||
for j in 1:3 push!(x, xi) end
|
|
||||||
append!(y, [fillrange, yScatter[i], fillrange])
|
|
||||||
end
|
|
||||||
|
|
||||||
# change the line args
|
|
||||||
plotattributesLine[:x] = x
|
|
||||||
plotattributesLine[:y] = y
|
|
||||||
plotattributesLine[:seriestype] = :path
|
|
||||||
plotattributesLine[:markershape] = :none
|
|
||||||
plotattributesLine[:fillrange] = nothing
|
|
||||||
|
|
||||||
# change the scatter args
|
|
||||||
plotattributesScatter[:seriestype] = :none
|
|
||||||
|
|
||||||
plotattributesLine, plotattributesScatter
|
|
||||||
end
|
|
||||||
|
|
||||||
function regressionXY(x, y)
|
|
||||||
# regress
|
|
||||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
|
||||||
|
|
||||||
# make a line segment
|
|
||||||
regx = [ignorenan_minimum(x), ignorenan_maximum(x)]
|
|
||||||
regy = β * regx + α
|
|
||||||
regx, regy
|
|
||||||
end
|
|
||||||
|
|
||||||
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
|
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
|
||||||
n, m = size(z)
|
n, m = size(z)
|
||||||
colors = ColorGradient(vec(z))
|
colors = ColorGradient(vec(z))
|
||||||
@ -262,10 +147,10 @@ mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
|||||||
mapFuncOrFuncs(fs::AVec{F}, u::AVec) where {F<:Function} = [map(f, u) for f in fs]
|
mapFuncOrFuncs(fs::AVec{F}, u::AVec) where {F<:Function} = [map(f, u) for f in fs]
|
||||||
|
|
||||||
for i in 2:4
|
for i in 2:4
|
||||||
@eval begin
|
@eval begin
|
||||||
unzip(v::Union{AVec{<:Tuple{Vararg{T,$i} where T}},
|
unzip(v::Union{AVec{<:Tuple{Vararg{T,$i} where T}},
|
||||||
AVec{<:GeometryTypes.Point{$i}}}) = $(Expr(:tuple, (:([t[$j] for t in v]) for j=1:i)...))
|
AVec{<:GeometryTypes.Point{$i}}}) = $(Expr(:tuple, (:([t[$j] for t in v]) for j=1:i)...))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
unzip(v::Union{AVec{<:GeometryTypes.Point{N}},
|
unzip(v::Union{AVec{<:GeometryTypes.Point{N}},
|
||||||
@ -275,15 +160,13 @@ unzip(v::Union{AVec{<:GeometryTypes.Point},
|
|||||||
|
|
||||||
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
||||||
function _expand_limits(lims, x)
|
function _expand_limits(lims, x)
|
||||||
try
|
try
|
||||||
e1, e2 = ignorenan_extrema(x)
|
e1, e2 = ignorenan_extrema(x)
|
||||||
lims[1] = NaNMath.min(lims[1], e1)
|
lims[1] = NaNMath.min(lims[1], e1)
|
||||||
lims[2] = NaNMath.max(lims[2], e2)
|
lims[2] = NaNMath.max(lims[2], e2)
|
||||||
# catch err
|
catch
|
||||||
# @warn(err)
|
end
|
||||||
catch
|
nothing
|
||||||
end
|
|
||||||
nothing
|
|
||||||
end
|
end
|
||||||
|
|
||||||
expand_data(v, n::Integer) = [_cycle(v, i) for i=1:n]
|
expand_data(v, n::Integer) = [_cycle(v, i) for i=1:n]
|
||||||
@ -301,21 +184,21 @@ function addOrReplace(v::AbstractVector, t::DataType, args...; kw...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function replaceType(vec, val)
|
function replaceType(vec, val)
|
||||||
filter!(x -> !isa(x, typeof(val)), vec)
|
filter!(x -> !isa(x, typeof(val)), vec)
|
||||||
push!(vec, val)
|
push!(vec, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function replaceAlias!(plotattributes::AKW, k::Symbol, aliases::Dict{Symbol,Symbol})
|
function replaceAlias!(plotattributes::AKW, k::Symbol, aliases::Dict{Symbol,Symbol})
|
||||||
if haskey(aliases, k)
|
if haskey(aliases, k)
|
||||||
plotattributes[aliases[k]] = pop_kw!(plotattributes, k)
|
plotattributes[aliases[k]] = pop_kw!(plotattributes, k)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function replaceAliases!(plotattributes::AKW, aliases::Dict{Symbol,Symbol})
|
function replaceAliases!(plotattributes::AKW, aliases::Dict{Symbol,Symbol})
|
||||||
ks = collect(keys(plotattributes))
|
ks = collect(keys(plotattributes))
|
||||||
for k in ks
|
for k in ks
|
||||||
replaceAlias!(plotattributes, k, aliases)
|
replaceAlias!(plotattributes, k, aliases)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
createSegments(z) = collect(repeat(reshape(z,1,:),2,1))[2:end]
|
createSegments(z) = collect(repeat(reshape(z,1,:),2,1))[2:end]
|
||||||
@ -334,20 +217,20 @@ const _scale_base = Dict{Symbol, Real}(
|
|||||||
)
|
)
|
||||||
|
|
||||||
function _heatmap_edges(v::AVec, isedges::Bool = false)
|
function _heatmap_edges(v::AVec, isedges::Bool = false)
|
||||||
length(v) == 1 && return v[1] .+ [-0.5, 0.5]
|
length(v) == 1 && return v[1] .+ [-0.5, 0.5]
|
||||||
if isedges return v end
|
if isedges return v end
|
||||||
# `isedges = true` means that v is a vector which already describes edges
|
# `isedges = true` means that v is a vector which already describes edges
|
||||||
# and does not need to be extended.
|
# and does not need to be extended.
|
||||||
vmin, vmax = ignorenan_extrema(v)
|
vmin, vmax = ignorenan_extrema(v)
|
||||||
extra_min = (v[2] - v[1]) / 2
|
extra_min = (v[2] - v[1]) / 2
|
||||||
extra_max = (v[end] - v[end - 1]) / 2
|
extra_max = (v[end] - v[end - 1]) / 2
|
||||||
vcat(vmin-extra_min, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra_max)
|
vcat(vmin-extra_min, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra_max)
|
||||||
end
|
end
|
||||||
|
|
||||||
"create an (n+1) list of the outsides of heatmap rectangles"
|
"create an (n+1) list of the outsides of heatmap rectangles"
|
||||||
function heatmap_edges(v::AVec, scale::Symbol = :identity, isedges::Bool = false)
|
function heatmap_edges(v::AVec, scale::Symbol = :identity, isedges::Bool = false)
|
||||||
f, invf = scalefunc(scale), invscalefunc(scale)
|
f, invf = scalefunc(scale), invscalefunc(scale)
|
||||||
map(invf, _heatmap_edges(map(f,v), isedges))
|
map(invf, _heatmap_edges(map(f,v), isedges))
|
||||||
end
|
end
|
||||||
|
|
||||||
function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size::Tuple{Int, Int})
|
function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size::Tuple{Int, Int})
|
||||||
@ -367,8 +250,8 @@ function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size:
|
|||||||
end
|
end
|
||||||
|
|
||||||
function is_uniformly_spaced(v; tol=1e-6)
|
function is_uniformly_spaced(v; tol=1e-6)
|
||||||
dv = diff(v)
|
dv = diff(v)
|
||||||
maximum(dv) - minimum(dv) < tol * mean(abs.(dv))
|
maximum(dv) - minimum(dv) < tol * mean(abs.(dv))
|
||||||
end
|
end
|
||||||
|
|
||||||
function convert_to_polar(theta, r, r_extrema = ignorenan_extrema(r))
|
function convert_to_polar(theta, r, r_extrema = ignorenan_extrema(r))
|
||||||
@ -380,11 +263,11 @@ function convert_to_polar(theta, r, r_extrema = ignorenan_extrema(r))
|
|||||||
end
|
end
|
||||||
|
|
||||||
function fakedata(sz...)
|
function fakedata(sz...)
|
||||||
y = zeros(sz...)
|
y = zeros(sz...)
|
||||||
for r in 2:size(y,1)
|
for r in 2:size(y,1)
|
||||||
y[r,:] = 0.95 * vec(y[r-1,:]) + randn(size(y,2))
|
y[r,:] = 0.95 * vec(y[r-1,:]) + randn(size(y,2))
|
||||||
end
|
end
|
||||||
y
|
y
|
||||||
end
|
end
|
||||||
|
|
||||||
isijulia() = :IJulia in nameof.(collect(values(Base.loaded_modules)))
|
isijulia() = :IJulia in nameof.(collect(values(Base.loaded_modules)))
|
||||||
@ -752,119 +635,107 @@ function with(f::Function, args...; kw...)
|
|||||||
newdefs[:legend] = false
|
newdefs[:legend] = false
|
||||||
end
|
end
|
||||||
|
|
||||||
# dict to store old and new keyword args for anything that changes
|
# dict to store old and new keyword args for anything that changes
|
||||||
olddefs = KW()
|
olddefs = KW()
|
||||||
for k in keys(newdefs)
|
for k in keys(newdefs)
|
||||||
olddefs[k] = default(k)
|
olddefs[k] = default(k)
|
||||||
end
|
|
||||||
|
|
||||||
# save the backend
|
|
||||||
if CURRENT_BACKEND.sym == :none
|
|
||||||
_pick_default_backend()
|
|
||||||
end
|
|
||||||
oldbackend = CURRENT_BACKEND.sym
|
|
||||||
|
|
||||||
for arg in args
|
|
||||||
|
|
||||||
# change backend?
|
|
||||||
if arg in backends()
|
|
||||||
backend(arg)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# # TODO: generalize this strategy to allow args as much as possible
|
# save the backend
|
||||||
# # as in: with(:gr, :scatter, :legend, :grid) do; ...; end
|
if CURRENT_BACKEND.sym == :none
|
||||||
# # TODO: can we generalize this enough to also do something similar in the plot commands??
|
_pick_default_backend()
|
||||||
|
end
|
||||||
|
oldbackend = CURRENT_BACKEND.sym
|
||||||
|
|
||||||
# k = :seriestype
|
for arg in args
|
||||||
# if arg in _allTypes
|
|
||||||
# olddefs[k] = default(k)
|
|
||||||
# newdefs[k] = arg
|
|
||||||
# elseif haskey(_typeAliases, arg)
|
|
||||||
# olddefs[k] = default(k)
|
|
||||||
# newdefs[k] = _typeAliases[arg]
|
|
||||||
# end
|
|
||||||
|
|
||||||
k = :legend
|
# change backend?
|
||||||
if arg in (k, :leg)
|
if arg in backends()
|
||||||
olddefs[k] = default(k)
|
backend(arg)
|
||||||
newdefs[k] = true
|
end
|
||||||
|
|
||||||
|
# TODO: generalize this strategy to allow args as much as possible
|
||||||
|
# as in: with(:gr, :scatter, :legend, :grid) do; ...; end
|
||||||
|
# TODO: can we generalize this enough to also do something similar in the plot commands??
|
||||||
|
|
||||||
|
# k = :seriestype
|
||||||
|
# if arg in _allTypes
|
||||||
|
# olddefs[k] = default(k)
|
||||||
|
# newdefs[k] = arg
|
||||||
|
# elseif haskey(_typeAliases, arg)
|
||||||
|
# olddefs[k] = default(k)
|
||||||
|
# newdefs[k] = _typeAliases[arg]
|
||||||
|
# end
|
||||||
|
|
||||||
|
k = :legend
|
||||||
|
if arg in (k, :leg)
|
||||||
|
olddefs[k] = default(k)
|
||||||
|
newdefs[k] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
k = :grid
|
||||||
|
if arg == k
|
||||||
|
olddefs[k] = default(k)
|
||||||
|
newdefs[k] = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
k = :grid
|
# display(olddefs)
|
||||||
if arg == k
|
# display(newdefs)
|
||||||
olddefs[k] = default(k)
|
|
||||||
newdefs[k] = true
|
# now set all those defaults
|
||||||
|
default(; newdefs...)
|
||||||
|
|
||||||
|
# call the function
|
||||||
|
ret = f()
|
||||||
|
|
||||||
|
# put the defaults back
|
||||||
|
default(; olddefs...)
|
||||||
|
|
||||||
|
# revert the backend
|
||||||
|
if CURRENT_BACKEND.sym != oldbackend
|
||||||
|
backend(oldbackend)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# display(olddefs)
|
# return the result of the function
|
||||||
# display(newdefs)
|
ret
|
||||||
|
|
||||||
# now set all those defaults
|
|
||||||
default(; newdefs...)
|
|
||||||
|
|
||||||
# call the function
|
|
||||||
ret = f()
|
|
||||||
|
|
||||||
# put the defaults back
|
|
||||||
default(; olddefs...)
|
|
||||||
|
|
||||||
# revert the backend
|
|
||||||
if CURRENT_BACKEND.sym != oldbackend
|
|
||||||
backend(oldbackend)
|
|
||||||
end
|
|
||||||
|
|
||||||
# return the result of the function
|
|
||||||
ret
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
|
|
||||||
mutable struct DebugMode
|
mutable struct DebugMode
|
||||||
on::Bool
|
on::Bool
|
||||||
end
|
end
|
||||||
const _debugMode = DebugMode(false)
|
const _debugMode = DebugMode(false)
|
||||||
|
|
||||||
function debugplots(on = true)
|
function debugplots(on = true)
|
||||||
_debugMode.on = on
|
_debugMode.on = on
|
||||||
end
|
end
|
||||||
|
|
||||||
debugshow(io, x) = show(io, x)
|
debugshow(io, x) = show(io, x)
|
||||||
debugshow(io, x::AbstractArray) = print(io, summary(x))
|
debugshow(io, x::AbstractArray) = print(io, summary(x))
|
||||||
|
|
||||||
function dumpdict(io::IO, plotattributes::AKW, prefix = "", alwaysshow = false)
|
function dumpdict(io::IO, plotattributes::AKW, prefix = "", alwaysshow = false)
|
||||||
_debugMode.on || alwaysshow || return
|
_debugMode.on || alwaysshow || return
|
||||||
println(io)
|
println(io)
|
||||||
if prefix != ""
|
if prefix != ""
|
||||||
println(io, prefix, ":")
|
println(io, prefix, ":")
|
||||||
end
|
end
|
||||||
for k in sort(collect(keys(plotattributes)))
|
for k in sort(collect(keys(plotattributes)))
|
||||||
@printf("%14s: ", k)
|
@printf("%14s: ", k)
|
||||||
debugshow(io, plotattributes[k])
|
debugshow(io, plotattributes[k])
|
||||||
|
println(io)
|
||||||
|
end
|
||||||
println(io)
|
println(io)
|
||||||
end
|
|
||||||
println(io)
|
|
||||||
end
|
end
|
||||||
DD(io::IO, plotattributes::AKW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
|
DD(io::IO, plotattributes::AKW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
|
||||||
DD(plotattributes::AKW, prefix = "") = DD(stdout, plotattributes, prefix)
|
DD(plotattributes::AKW, prefix = "") = DD(stdout, plotattributes, prefix)
|
||||||
|
|
||||||
function dumpcallstack()
|
function dumpcallstack()
|
||||||
error() # well... you wanted the stacktrace, didn't you?!?
|
error() # well... you wanted the stacktrace, didn't you?!?
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# used in updating an existing series
|
|
||||||
|
|
||||||
extendSeriesByOne(v::UnitRange{Int}, n::Int = 1) = isempty(v) ? (1:n) : (minimum(v):maximum(v)+n)
|
|
||||||
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + ignorenan_maximum(v))
|
|
||||||
extendSeriesData(v::AbstractRange{T}, z::Real) where {T} = extendSeriesData(float(collect(v)), z)
|
|
||||||
extendSeriesData(v::AbstractRange{T}, z::AVec) where {T} = extendSeriesData(float(collect(v)), z)
|
|
||||||
extendSeriesData(v::AVec{T}, z::Real) where {T} = (push!(v, convert(T, z)); v)
|
|
||||||
extendSeriesData(v::AVec{T}, z::AVec) where {T} = (append!(v, convert(Vector{T}, z)); v)
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# NOTE: backends should implement the following methods to get/set the x/y/z data objects
|
# NOTE: backends should implement the following methods to get/set the x/y/z data objects
|
||||||
|
|
||||||
@ -908,33 +779,60 @@ Base.setindex!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y} = (setxy!(plt,
|
|||||||
Base.setindex!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z} = (setxyz!(plt, xyz, i); plt)
|
Base.setindex!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z} = (setxyz!(plt, xyz, i); plt)
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
|
|
||||||
# operate on individual series
|
# operate on individual series
|
||||||
|
|
||||||
function push_x!(series::Series, xi)
|
Base.push!(series::Series, args...) = extend_series!(series, args...)
|
||||||
push!(series[:x], xi)
|
Base.append!(series::Series, args...) = extend_series!(series, args...)
|
||||||
expand_extrema!(series[:subplot][:xaxis], xi)
|
|
||||||
return
|
function extend_series!(series::Series, yi)
|
||||||
end
|
y = extend_series_data!(series, yi, :y)
|
||||||
function push_y!(series::Series, yi)
|
x = extend_to_length!(series[:x], length(y))
|
||||||
push!(series[:y], yi)
|
expand_extrema!(series[:subplot][:xaxis], x)
|
||||||
expand_extrema!(series[:subplot][:yaxis], yi)
|
return x, y
|
||||||
return
|
|
||||||
end
|
|
||||||
function push_z!(series::Series, zi)
|
|
||||||
push!(series[:z], zi)
|
|
||||||
expand_extrema!(series[:subplot][:zaxis], zi)
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.push!(series::Series, yi)
|
function extend_series!(series::Series, xi, yi)
|
||||||
x = extendSeriesByOne(series[:x])
|
x = extend_series_data!(series, xi, :x)
|
||||||
expand_extrema!(series[:subplot][:xaxis], x[end])
|
y = extend_series_data!(series, yi, :y)
|
||||||
series[:x] = x
|
return x, y
|
||||||
push_y!(series, yi)
|
end
|
||||||
|
|
||||||
|
function extend_series!(series::Series, xi, yi, zi)
|
||||||
|
x = extend_series_data!(series, xi, :x)
|
||||||
|
y = extend_series_data!(series, yi, :y)
|
||||||
|
z = extend_series_data!(series, zi, :z)
|
||||||
|
return x, y, z
|
||||||
|
end
|
||||||
|
|
||||||
|
function extend_series_data!(series::Series, v, letter)
|
||||||
|
copy_series!(series, letter)
|
||||||
|
d = extend_by_data!(series[letter], v)
|
||||||
|
expand_extrema!(series[:subplot][Symbol(letter, :axis)], d)
|
||||||
|
return d
|
||||||
|
end
|
||||||
|
|
||||||
|
function copy_series!(series, letter)
|
||||||
|
plt = series[:plot_object]
|
||||||
|
for s in plt.series_list
|
||||||
|
for l in (:x, :y, :z)
|
||||||
|
if s !== series || l !== letter
|
||||||
|
if s[l] === series[letter]
|
||||||
|
series[letter] = copy(series[letter])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
extend_to_length!(v::AbstractRange, n) = range(first(v), step = step(v), length = n)
|
||||||
|
function extend_to_length!(v::AbstractVector, n)
|
||||||
|
vmax = isempy(v) ? 0 : ignorenan_maximum(v)
|
||||||
|
extend_by_data!(v, vmax .+ (1:(n - length(v))))
|
||||||
|
end
|
||||||
|
extend_by_data!(v::AbstractVector, x) = isimmutable(v) ? vcat(v, x) : push!(v, x)
|
||||||
|
function extend_by_data!(v::AbstractVector, x::AbstractVector)
|
||||||
|
isimmutable(v) ? vcat(v, x) : append!(v, x)
|
||||||
end
|
end
|
||||||
Base.push!(series::Series, xi, yi) = (push_x!(series,xi); push_y!(series,yi))
|
|
||||||
Base.push!(series::Series, xi, yi, zi) = (push_x!(series,xi); push_y!(series,yi); push_z!(series,zi))
|
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
|
|
||||||
@ -968,59 +866,16 @@ end
|
|||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# push/append for one series
|
# push/append for one series
|
||||||
|
|
||||||
# push value to first series
|
Base.push!(plt::Plot, args::Real...) = push!(plt, 1, args...)
|
||||||
Base.push!(plt::Plot, y::Real) = push!(plt, 1, y)
|
Base.push!(plt::Plot, i::Integer, args::Real...) = push!(plt.series_list[i], args...)
|
||||||
Base.push!(plt::Plot, x::Real, y::Real) = push!(plt, 1, x, y)
|
Base.append!(plt::Plot, args::AbstractVector...) = append!(plt, 1, args...)
|
||||||
Base.push!(plt::Plot, x::Real, y::Real, z::Real) = push!(plt, 1, x, y, z)
|
Base.append!(plt::Plot, i::Integer, args::Real...) = append!(plt.series_list[i], args...)
|
||||||
|
|
||||||
# y only
|
|
||||||
function Base.push!(plt::Plot, i::Integer, y::Real)
|
|
||||||
xdata, ydata = getxy(plt, i)
|
|
||||||
setxy!(plt, (extendSeriesByOne(xdata), extendSeriesData(ydata, y)), i)
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
function Base.append!(plt::Plot, i::Integer, y::AVec)
|
|
||||||
xdata, ydata = plt[i]
|
|
||||||
if !isa(xdata, UnitRange{Int})
|
|
||||||
error("Expected x is a UnitRange since you're trying to push a y value only")
|
|
||||||
end
|
|
||||||
plt[i] = (extendSeriesByOne(xdata, length(y)), extendSeriesData(ydata, y))
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
|
|
||||||
# x and y
|
|
||||||
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real)
|
|
||||||
xdata, ydata = getxy(plt, i)
|
|
||||||
setxy!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y)), i)
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec)
|
|
||||||
@assert length(x) == length(y)
|
|
||||||
xdata, ydata = getxy(plt, i)
|
|
||||||
setxy!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y)), i)
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
|
|
||||||
# x, y, and z
|
|
||||||
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real, z::Real)
|
|
||||||
# @show i, x, y, z
|
|
||||||
xdata, ydata, zdata = getxyz(plt, i)
|
|
||||||
# @show xdata, ydata, zdata
|
|
||||||
setxyz!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y), extendSeriesData(zdata, z)), i)
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec, z::AVec)
|
|
||||||
@assert length(x) == length(y) == length(z)
|
|
||||||
xdata, ydata, zdata = getxyz(plt, i)
|
|
||||||
setxyz!(plt, (extendSeriesData(xdata, x), extendSeriesData(ydata, y), extendSeriesData(zdata, z)), i)
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
|
|
||||||
# tuples
|
# tuples
|
||||||
Base.push!(plt::Plot, xy::Tuple{X,Y}) where {X,Y} = push!(plt, 1, xy...)
|
Base.push!(plt::Plot, t::Tuple) = push!(plt, 1, t...)
|
||||||
Base.push!(plt::Plot, xyz::Tuple{X,Y,Z}) where {X,Y,Z} = push!(plt, 1, xyz...)
|
Base.push!(plt::Plot, i::Integer, t::Tuple) = push!(plt, i, t...)
|
||||||
Base.push!(plt::Plot, i::Integer, xy::Tuple{X,Y}) where {X,Y} = push!(plt, i, xy...)
|
Base.append!(plt::Plot, t::Tuple) = append!(plt, 1, t...)
|
||||||
Base.push!(plt::Plot, i::Integer, xyz::Tuple{X,Y,Z}) where {X,Y,Z} = push!(plt, i, xyz...)
|
Base.append!(plt::Plot, i::Integer, t::Tuple) = append!(plt, i, t...)
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# push/append for all series
|
# push/append for all series
|
||||||
|
|||||||
@ -9,6 +9,7 @@ using LibGit2
|
|||||||
using GeometryTypes
|
using GeometryTypes
|
||||||
using Dates
|
using Dates
|
||||||
|
|
||||||
|
include("test_hdf5plots.jl")
|
||||||
include("test_pgfplotsx.jl")
|
include("test_pgfplotsx.jl")
|
||||||
|
|
||||||
reference_dir(args...) = joinpath(homedir(), ".julia", "dev", "PlotReferenceImages", args...)
|
reference_dir(args...) = joinpath(homedir(), ".julia", "dev", "PlotReferenceImages", args...)
|
||||||
|
|||||||
21
test/test_hdf5plots.jl
Normal file
21
test/test_hdf5plots.jl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Plots, HDF5
|
||||||
|
|
||||||
|
|
||||||
|
@testset "HDF5_Plots" begin
|
||||||
|
fname = "tmpplotsave.hdf5"
|
||||||
|
hdf5()
|
||||||
|
|
||||||
|
x = 1:10
|
||||||
|
psrc=plot(x, x.*x); #Create some plot
|
||||||
|
Plots.hdf5plot_write(psrc, fname)
|
||||||
|
|
||||||
|
#Read back file:
|
||||||
|
gr() #Choose some fast backend likely to work in test environment.
|
||||||
|
pread = Plots.hdf5plot_read(fname)
|
||||||
|
|
||||||
|
#Make sure data made it through:
|
||||||
|
@test psrc.subplots[1].series_list[1][:x] == pread.subplots[1].series_list[1][:x]
|
||||||
|
@test psrc.subplots[1].series_list[1][:y] == pread.subplots[1].series_list[1][:y]
|
||||||
|
|
||||||
|
#display(pread) #Don't display. Regression env might not support
|
||||||
|
end #testset
|
||||||
@ -1,166 +1,295 @@
|
|||||||
using Plots, Test
|
using Plots, Test
|
||||||
pgfplotsx()
|
pgfplotsx()
|
||||||
|
|
||||||
function create_plot( args...; kwargs... )
|
function create_plot(args...; kwargs...)
|
||||||
pgfx_plot = plot(args...; kwargs...)
|
pgfx_plot = plot(args...; kwargs...)
|
||||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||||
end
|
end
|
||||||
|
|
||||||
function create_plot!( args...; kwargs... )
|
function create_plot!(args...; kwargs...)
|
||||||
pgfx_plot = plot!(args...; kwargs...)
|
pgfx_plot = plot!(args...; kwargs...)
|
||||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "PGFPlotsX" begin
|
@testset "PGFPlotsX" begin
|
||||||
pgfx_plot = plot(1:5)
|
pgfx_plot = plot(1:5)
|
||||||
Plots._update_plot_object(pgfx_plot)
|
Plots._update_plot_object(pgfx_plot)
|
||||||
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
|
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
|
||||||
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
|
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
|
||||||
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
|
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
|
||||||
@test count( x-> x isa PGFPlotsX.Plot, axis.contents ) == 1
|
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1
|
||||||
@test !haskey(axis.contents[1].options.dict, "fill")
|
@test !haskey(axis.contents[1].options.dict, "fill")
|
||||||
|
|
||||||
@testset "3D docs example" begin
|
@testset "Legends" begin
|
||||||
n = 100
|
legends_plot = plot( rand(5,2), lab = ["1" ""] )
|
||||||
ts = range(0, stop=8π, length=n)
|
scatter!(legends_plot, rand(5) )
|
||||||
x = ts .* map(cos, ts)
|
Plots._update_plot_object(legends_plot)
|
||||||
y = (0.1ts) .* map(sin, ts)
|
axis_contents = Plots.pgfx_axes(legends_plot.o)[1].contents
|
||||||
z = 1:n
|
leg_entries = filter( x -> x isa PGFPlotsX.LegendEntry, axis_contents )
|
||||||
pl = plot(x, y, z, zcolor=reverse(z), m=(10, 0.8, :blues, Plots.stroke(0)), leg=false, cbar=true, w=5)
|
series = filter( x -> x isa PGFPlotsX.Plot, axis_contents )
|
||||||
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w=10)
|
@test length(leg_entries) == 2
|
||||||
Plots._update_plot_object(pgfx_plot)
|
@test !haskey(series[1].options.dict, "forget plot")
|
||||||
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
|
@test haskey(series[2].options.dict, "forget plot")
|
||||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
@test !haskey(series[3].options.dict, "forget plot")
|
||||||
end
|
end # testset
|
||||||
end # testset
|
|
||||||
@testset "Color docs example" begin
|
|
||||||
y = rand(100)
|
|
||||||
plot(0:10:100, rand(11, 4), lab="lines", w=3, palette=:grays, fill=0, α=0.6)
|
|
||||||
pl = scatter!(y, zcolor=abs.(y .- 0.5), m=(:heat, 0.8, Plots.stroke(1, :green)), ms=10 * abs.(y .- 0.5) .+ 4, lab="grad")
|
|
||||||
Plots._update_plot_object(pl)
|
|
||||||
axis = Plots.pgfx_axes(pl.o)[1]
|
|
||||||
@test count( x->x isa PGFPlotsX.LegendEntry, axis.contents ) == 5
|
|
||||||
@test count( x->x isa PGFPlotsX.Plot, axis.contents ) == 108 # each marker is its own plot, fillranges create 2 plot-objects
|
|
||||||
marker = axis.contents[15]
|
|
||||||
@test marker isa PGFPlotsX.Plot
|
|
||||||
@test marker.options["mark"] == "*"
|
|
||||||
@test marker.options["mark options"]["color"] == RGBA{Float64}( colorant"green", 0.8)
|
|
||||||
@test marker.options["mark options"]["line width"] == 1
|
|
||||||
end # testset
|
|
||||||
@testset "Plot in pieces" begin
|
|
||||||
plot(rand(100) / 3, reg=true, fill=(0, :green))
|
|
||||||
scatter!(rand(100), markersize=6, c=:orange)
|
|
||||||
end # testset
|
|
||||||
@testset "Marker types" begin
|
|
||||||
markers = filter((m->begin
|
|
||||||
m in Plots.supported_markers()
|
|
||||||
end), Plots._shape_keys)
|
|
||||||
markers = reshape(markers, 1, length(markers))
|
|
||||||
n = length(markers)
|
|
||||||
x = (range(0, stop=10, length=n + 2))[2:end - 1]
|
|
||||||
y = repeat(reshape(reverse(x), 1, :), n, 1)
|
|
||||||
scatter(x, y, m=(8, :auto), lab=map(string, markers), bg=:linen, xlim=(0, 10), ylim=(0, 10))
|
|
||||||
end # testset
|
|
||||||
@testset "Layout" begin
|
|
||||||
plot(Plots.fakedata(100, 10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
|
|
||||||
end # testset
|
|
||||||
@testset "Polar plots" begin
|
|
||||||
Θ = range(0, stop=1.5π, length=100)
|
|
||||||
r = abs.(0.1 * randn(100) + sin.(3Θ))
|
|
||||||
plot(Θ, r, proj=:polar, m=2)
|
|
||||||
end # testset
|
|
||||||
@testset "Drawing shapes" begin
|
|
||||||
verts = [(-1.0, 1.0), (-1.28, 0.6), (-0.2, -1.4), (0.2, -1.4), (1.28, 0.6), (1.0, 1.0), (-1.0, 1.0), (-0.2, -0.6), (0.0, -0.2), (-0.4, 0.6), (1.28, 0.6), (0.2, -1.4), (-0.2, -1.4), (0.6, 0.2), (-0.2, 0.2), (0.0, -0.2), (0.2, 0.2), (-0.2, -0.6)]
|
|
||||||
x = 0.1:0.2:0.9
|
|
||||||
y = 0.7 * rand(5) .+ 0.15
|
|
||||||
plot(x, y, line=(3, :dash, :lightblue), marker=(Shape(verts), 30, RGBA(0, 0, 0, 0.2)), bg=:pink, fg=:darkblue, xlim=(0, 1), ylim=(0, 1), leg=false)
|
|
||||||
end # testset
|
|
||||||
@testset "Histogram 2D" begin
|
|
||||||
histogram2d(randn(10000), randn(10000), nbins=20)
|
|
||||||
end # testset
|
|
||||||
@testset "Heatmap-like" begin
|
|
||||||
xs = [string("x", i) for i = 1:10]
|
|
||||||
ys = [string("y", i) for i = 1:4]
|
|
||||||
z = float((1:4) * reshape(1:10, 1, :))
|
|
||||||
pgfx_plot = heatmap(xs, ys, z, aspect_ratio=1)
|
|
||||||
Plots._update_plot_object(pgfx_plot)
|
|
||||||
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
|
|
||||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
|
||||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
|
|
||||||
end
|
|
||||||
|
|
||||||
pgfx_plot = wireframe(xs, ys, z, aspect_ratio=1)
|
@testset "3D docs example" begin
|
||||||
# TODO: clims are wrong
|
n = 100
|
||||||
end # testset
|
ts = range(0, stop = 8π, length = n)
|
||||||
@testset "Contours" begin
|
x = ts .* map(cos, ts)
|
||||||
x = 1:0.5:20
|
y = (0.1ts) .* map(sin, ts)
|
||||||
y = 1:0.5:10
|
z = 1:n
|
||||||
f(x, y) = begin
|
pl = plot(
|
||||||
(3x + y ^ 2) * abs(sin(x) + cos(y))
|
x,
|
||||||
end
|
y,
|
||||||
X = repeat(reshape(x, 1, :), length(y), 1)
|
z,
|
||||||
Y = repeat(y, 1, length(x))
|
zcolor = reverse(z),
|
||||||
Z = map(f, X, Y)
|
m = (10, 0.8, :blues, Plots.stroke(0)),
|
||||||
p2 = contour(x, y, Z)
|
leg = false,
|
||||||
p1 = contour(x, y, f, fill=true)
|
cbar = true,
|
||||||
plot(p1, p2)
|
w = 5,
|
||||||
# TODO: colorbar for filled contours
|
)
|
||||||
end # testset
|
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w = 10)
|
||||||
@testset "Varying colors" begin
|
Plots._update_plot_object(pgfx_plot)
|
||||||
t = range(0, stop=1, length=100)
|
if @test_nowarn(
|
||||||
θ = (6π) .* t
|
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||||
x = t .* cos.(θ)
|
)
|
||||||
y = t .* sin.(θ)
|
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||||
p1 = plot(x, y, line_z=t, linewidth=3, legend=false)
|
end
|
||||||
p2 = scatter(x, y, marker_z=((x, y)->begin
|
end # testset
|
||||||
x + y
|
@testset "Color docs example" begin
|
||||||
end), color=:bluesreds, legend=false)
|
y = rand(100)
|
||||||
plot(p1, p2)
|
plot(
|
||||||
end # testset
|
0:10:100,
|
||||||
@testset "Framestyles" begin
|
rand(11, 4),
|
||||||
scatter(fill(randn(10), 6), fill(randn(10), 6), framestyle=[:box :semi :origin :zerolines :grid :none], title=[":box" ":semi" ":origin" ":zerolines" ":grid" ":none"], color=permutedims(1:6), layout=6, label="", markerstrokewidth=0, ticks=-2:2)
|
lab = "lines",
|
||||||
# TODO: support :semi
|
w = 3,
|
||||||
end # testset
|
palette = :grays,
|
||||||
@testset "Quiver" begin
|
fill = 0,
|
||||||
x = -2pi:0.2:2*pi
|
α = 0.6,
|
||||||
y = sin.(x)
|
)
|
||||||
|
pl = scatter!(
|
||||||
|
y,
|
||||||
|
zcolor = abs.(y .- 0.5),
|
||||||
|
m = (:heat, 0.8, Plots.stroke(1, :green)),
|
||||||
|
ms = 10 * abs.(y .- 0.5) .+ 4,
|
||||||
|
lab = ["grad", "", "ient"],
|
||||||
|
)
|
||||||
|
Plots._update_plot_object(pl)
|
||||||
|
axis = Plots.pgfx_axes(pl.o)[1]
|
||||||
|
@test count(x -> x isa PGFPlotsX.LegendEntry, axis.contents) == 6
|
||||||
|
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 108 # each marker is its own plot, fillranges create 2 plot-objects
|
||||||
|
marker = axis.contents[15]
|
||||||
|
@test marker isa PGFPlotsX.Plot
|
||||||
|
@test marker.options["mark"] == "*"
|
||||||
|
@test marker.options["mark options"]["color"] ==
|
||||||
|
RGBA{Float64}(colorant"green", 0.8)
|
||||||
|
@test marker.options["mark options"]["line width"] == 1
|
||||||
|
end # testset
|
||||||
|
@testset "Plot in pieces" begin
|
||||||
|
pic = plot(rand(100) / 3, reg = true, fill = (0, :green))
|
||||||
|
scatter!(pic, rand(100), markersize = 6, c = :orange)
|
||||||
|
Plots._update_plot_object(pic)
|
||||||
|
axis_contents = Plots.pgfx_axes(pic.o)[1].contents
|
||||||
|
leg_entries = filter( x -> x isa PGFPlotsX.LegendEntry, axis_contents )
|
||||||
|
series = filter( x -> x isa PGFPlotsX.Plot, axis_contents )
|
||||||
|
@test length(leg_entries) == 2
|
||||||
|
@test length(series) == 4
|
||||||
|
@test haskey(series[1].options.dict, "forget plot")
|
||||||
|
@test !haskey(series[2].options.dict, "forget plot")
|
||||||
|
@test haskey(series[3].options.dict, "forget plot")
|
||||||
|
@test !haskey(series[4].options.dict, "forget plot")
|
||||||
|
end # testset
|
||||||
|
@testset "Marker types" begin
|
||||||
|
markers = filter((m -> begin
|
||||||
|
m in Plots.supported_markers()
|
||||||
|
end), Plots._shape_keys)
|
||||||
|
markers = reshape(markers, 1, length(markers))
|
||||||
|
n = length(markers)
|
||||||
|
x = (range(0, stop = 10, length = n + 2))[2:(end - 1)]
|
||||||
|
y = repeat(reshape(reverse(x), 1, :), n, 1)
|
||||||
|
scatter(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
m = (8, :auto),
|
||||||
|
lab = map(string, markers),
|
||||||
|
bg = :linen,
|
||||||
|
xlim = (0, 10),
|
||||||
|
ylim = (0, 10),
|
||||||
|
)
|
||||||
|
end # testset
|
||||||
|
@testset "Layout" begin
|
||||||
|
plot(
|
||||||
|
Plots.fakedata(100, 10),
|
||||||
|
layout = 4,
|
||||||
|
palette = [:grays :blues :heat :lightrainbow],
|
||||||
|
bg_inside = [:orange :pink :darkblue :black],
|
||||||
|
)
|
||||||
|
end # testset
|
||||||
|
@testset "Polar plots" begin
|
||||||
|
Θ = range(0, stop = 1.5π, length = 100)
|
||||||
|
r = abs.(0.1 * randn(100) + sin.(3Θ))
|
||||||
|
plot(Θ, r, proj = :polar, m = 2)
|
||||||
|
end # testset
|
||||||
|
@testset "Drawing shapes" begin
|
||||||
|
verts = [
|
||||||
|
(-1.0, 1.0),
|
||||||
|
(-1.28, 0.6),
|
||||||
|
(-0.2, -1.4),
|
||||||
|
(0.2, -1.4),
|
||||||
|
(1.28, 0.6),
|
||||||
|
(1.0, 1.0),
|
||||||
|
(-1.0, 1.0),
|
||||||
|
(-0.2, -0.6),
|
||||||
|
(0.0, -0.2),
|
||||||
|
(-0.4, 0.6),
|
||||||
|
(1.28, 0.6),
|
||||||
|
(0.2, -1.4),
|
||||||
|
(-0.2, -1.4),
|
||||||
|
(0.6, 0.2),
|
||||||
|
(-0.2, 0.2),
|
||||||
|
(0.0, -0.2),
|
||||||
|
(0.2, 0.2),
|
||||||
|
(-0.2, -0.6),
|
||||||
|
]
|
||||||
|
x = 0.1:0.2:0.9
|
||||||
|
y = 0.7 * rand(5) .+ 0.15
|
||||||
|
plot(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
line = (3, :dash, :lightblue),
|
||||||
|
marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
|
||||||
|
bg = :pink,
|
||||||
|
fg = :darkblue,
|
||||||
|
xlim = (0, 1),
|
||||||
|
ylim = (0, 1),
|
||||||
|
leg = false,
|
||||||
|
)
|
||||||
|
end # testset
|
||||||
|
@testset "Histogram 2D" begin
|
||||||
|
histogram2d(randn(10000), randn(10000), nbins = 20)
|
||||||
|
end # testset
|
||||||
|
@testset "Heatmap-like" begin
|
||||||
|
xs = [string("x", i) for i = 1:10]
|
||||||
|
ys = [string("y", i) for i = 1:4]
|
||||||
|
z = float((1:4) * reshape(1:10, 1, :))
|
||||||
|
pgfx_plot = heatmap(xs, ys, z, aspect_ratio = 1)
|
||||||
|
Plots._update_plot_object(pgfx_plot)
|
||||||
|
if @test_nowarn(
|
||||||
|
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||||
|
)
|
||||||
|
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||||
|
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
|
||||||
|
end
|
||||||
|
|
||||||
u = ones(length(x))
|
pgfx_plot = wireframe(xs, ys, z, aspect_ratio = 1)
|
||||||
v = cos.(x)
|
# TODO: clims are wrong
|
||||||
arrow_plot = plot( x, y, quiver = (u, v), arrow = true )
|
end # testset
|
||||||
# TODO: could adjust limits to fit arrows if too long, but how?
|
@testset "Contours" begin
|
||||||
# TODO: get latex available on CI
|
x = 1:0.5:20
|
||||||
# mktempdir() do path
|
y = 1:0.5:10
|
||||||
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
|
f(x, y) = begin
|
||||||
# end
|
(3x + y^2) * abs(sin(x) + cos(y))
|
||||||
end # testset
|
end
|
||||||
@testset "Annotations" begin
|
X = repeat(reshape(x, 1, :), length(y), 1)
|
||||||
y = rand(10)
|
Y = repeat(y, 1, length(x))
|
||||||
plot(y, annotations=(3, y[3], Plots.text("this is \\#3", :left)), leg=false)
|
Z = map(f, X, Y)
|
||||||
annotate!([(5, y[5], Plots.text("this is \\#5", 16, :red, :center)), (10, y[10], Plots.text("this is \\#10", :right, 20, "courier"))])
|
p2 = contour(x, y, Z)
|
||||||
annotation_plot = scatter!(range(2, stop=8, length=6), rand(6), marker=(50, 0.2, :orange), series_annotations=["series", "annotations", "map", "to", "series", Plots.text("data", :green)])
|
p1 = contour(x, y, f, fill = true)
|
||||||
# mktempdir() do path
|
plot(p1, p2)
|
||||||
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
|
# TODO: colorbar for filled contours
|
||||||
# end
|
end # testset
|
||||||
end # testset
|
@testset "Varying colors" begin
|
||||||
@testset "Ribbon" begin
|
t = range(0, stop = 1, length = 100)
|
||||||
aa = rand(10)
|
θ = (6π) .* t
|
||||||
bb = rand(10)
|
x = t .* cos.(θ)
|
||||||
cc = rand(10)
|
y = t .* sin.(θ)
|
||||||
conf = [aa-cc bb-cc]
|
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
|
||||||
ribbon_plot = plot(collect(1:10),fill(1,10), ribbon=(conf[:,1],conf[:,2]))
|
p2 = scatter(
|
||||||
Plots._update_plot_object(ribbon_plot)
|
x,
|
||||||
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
|
y,
|
||||||
plots = filter(x->x isa PGFPlotsX.Plot, axis.contents)
|
marker_z = ((x, y) -> begin
|
||||||
@test length(plots) == 4
|
x + y
|
||||||
@test !haskey(plots[1].options.dict, "fill")
|
end),
|
||||||
@test !haskey(plots[2].options.dict, "fill")
|
color = :bluesreds,
|
||||||
@test !haskey(plots[3].options.dict, "fill")
|
legend = false,
|
||||||
@test haskey(plots[4].options.dict, "fill")
|
)
|
||||||
@test ribbon_plot.o !== nothing
|
plot(p1, p2)
|
||||||
@test ribbon_plot.o.the_plot !== nothing
|
end # testset
|
||||||
# mktempdir() do path
|
@testset "Framestyles" begin
|
||||||
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
|
scatter(
|
||||||
# end
|
fill(randn(10), 6),
|
||||||
end # testset
|
fill(randn(10), 6),
|
||||||
end # testset
|
framestyle = [:box :semi :origin :zerolines :grid :none],
|
||||||
|
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
|
||||||
|
color = permutedims(1:6),
|
||||||
|
layout = 6,
|
||||||
|
label = "",
|
||||||
|
markerstrokewidth = 0,
|
||||||
|
ticks = -2:2,
|
||||||
|
)
|
||||||
|
# TODO: support :semi
|
||||||
|
end # testset
|
||||||
|
@testset "Quiver" begin
|
||||||
|
x = (-2pi):0.2:(2 * pi)
|
||||||
|
y = sin.(x)
|
||||||
|
|
||||||
|
u = ones(length(x))
|
||||||
|
v = cos.(x)
|
||||||
|
arrow_plot = plot(x, y, quiver = (u, v), arrow = true)
|
||||||
|
# TODO: could adjust limits to fit arrows if too long, but how?
|
||||||
|
# TODO: get latex available on CI
|
||||||
|
# mktempdir() do path
|
||||||
|
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
|
||||||
|
# end
|
||||||
|
end # testset
|
||||||
|
@testset "Annotations" begin
|
||||||
|
y = rand(10)
|
||||||
|
plot(
|
||||||
|
y,
|
||||||
|
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
|
||||||
|
leg = false,
|
||||||
|
)
|
||||||
|
annotate!([
|
||||||
|
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
|
||||||
|
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
|
||||||
|
])
|
||||||
|
annotation_plot = scatter!(
|
||||||
|
range(2, stop = 8, length = 6),
|
||||||
|
rand(6),
|
||||||
|
marker = (50, 0.2, :orange),
|
||||||
|
series_annotations = [
|
||||||
|
"series",
|
||||||
|
"annotations",
|
||||||
|
"map",
|
||||||
|
"to",
|
||||||
|
"series",
|
||||||
|
Plots.text("data", :green),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
# mktempdir() do path
|
||||||
|
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
|
||||||
|
# end
|
||||||
|
end # testset
|
||||||
|
@testset "Ribbon" begin
|
||||||
|
aa = rand(10)
|
||||||
|
bb = rand(10)
|
||||||
|
cc = rand(10)
|
||||||
|
conf = [aa - cc bb - cc]
|
||||||
|
ribbon_plot =
|
||||||
|
plot(collect(1:10), fill(1, 10), ribbon = (conf[:, 1], conf[:, 2]))
|
||||||
|
Plots._update_plot_object(ribbon_plot)
|
||||||
|
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
|
||||||
|
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
|
||||||
|
@test length(plots) == 4
|
||||||
|
@test !haskey(plots[1].options.dict, "fill")
|
||||||
|
@test !haskey(plots[2].options.dict, "fill")
|
||||||
|
@test !haskey(plots[3].options.dict, "fill")
|
||||||
|
@test haskey(plots[4].options.dict, "fill")
|
||||||
|
@test ribbon_plot.o !== nothing
|
||||||
|
@test ribbon_plot.o.the_plot !== nothing
|
||||||
|
# mktempdir() do path
|
||||||
|
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
|
||||||
|
# end
|
||||||
|
end # testset
|
||||||
|
end # testset
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user