commit
54a6518d59
12
.JuliaFormatter.toml
Normal file
12
.JuliaFormatter.toml
Normal file
@ -0,0 +1,12 @@
|
||||
always_for_in = true
|
||||
import_to_using = false
|
||||
align_pair_arrow = true
|
||||
align_assignment = true
|
||||
align_conditional = true
|
||||
always_use_return = false
|
||||
conditional_to_if = false
|
||||
whitespace_in_kwargs = true
|
||||
remove_extra_newlines = true
|
||||
whitespace_ops_in_indices = true
|
||||
short_to_long_function_def = false
|
||||
annotate_untyped_fields_with_any = false
|
||||
7
.github/workflows/benchmark.yml
vendored
7
.github/workflows/benchmark.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Run benchmarks
|
||||
name: benchmarks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@ -6,9 +6,6 @@ on:
|
||||
jobs:
|
||||
Benchmark:
|
||||
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
||||
env:
|
||||
GKS_ENCODING: "utf8"
|
||||
GKSwstype: "100"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -16,7 +13,7 @@ jobs:
|
||||
with:
|
||||
version: 1
|
||||
|
||||
## Setup
|
||||
# Setup
|
||||
- name: Ubuntu TESTCMD
|
||||
run: echo "TESTCMD=xvfb-run --auto-servernum julia" >> $GITHUB_ENV
|
||||
- name: Install Plots dependencies
|
||||
|
||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -72,9 +72,9 @@ jobs:
|
||||
# Run tests
|
||||
- name: Run Graphical test
|
||||
run: |
|
||||
$TESTCMD --project -e 'using Pkg; Pkg.test(coverage=true);'
|
||||
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("StatsPlots"); Pkg.test("StatsPlots");'
|
||||
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("GraphRecipes"); Pkg.test("GraphRecipes");'
|
||||
$TESTCMD --project -e 'using Pkg; Pkg.test(coverage=true)'
|
||||
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("StatsPlots"); Pkg.test("StatsPlots")'
|
||||
$TESTCMD -e 'using Pkg; Pkg.activate(tempdir()); Pkg.develop(path=abspath(".")); Pkg.add("GraphRecipes"); Pkg.test("GraphRecipes")'
|
||||
|
||||
- name: Codecov
|
||||
uses: julia-actions/julia-uploadcodecov@latest
|
||||
|
||||
26
.github/workflows/format.yml
vendored
Normal file
26
.github/workflows/format.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: format
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
JuliaFormatter:
|
||||
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: julia-actions/setup-julia@latest
|
||||
with:
|
||||
version: 1
|
||||
|
||||
# Setup
|
||||
- name: Ubuntu TESTCMD
|
||||
run: echo "TESTCMD=xvfb-run --auto-servernum julia" >> $GITHUB_ENV
|
||||
|
||||
# Check format
|
||||
- name: Install JuliaFormatter and format
|
||||
run: |
|
||||
git diff --name-only --exit-code
|
||||
$TESTCMD -e 'using Pkg; pkg"add JuliaFormatter CSTParser#master"'
|
||||
$TESTCMD -e 'using JuliaFormatter; format(["src", "test"])'
|
||||
git diff --exit-code
|
||||
29
src/Plots.jl
29
src/Plots.jl
@ -4,7 +4,17 @@ if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@optle
|
||||
@eval Base.Experimental.@optlevel 1
|
||||
end
|
||||
|
||||
const _current_plots_version = VersionNumber(split(first(filter(line -> occursin("version", line), readlines(normpath(@__DIR__, "..", "Project.toml")))), "\"")[2])
|
||||
const _current_plots_version = VersionNumber(
|
||||
split(
|
||||
first(
|
||||
filter(
|
||||
line -> occursin("version", line),
|
||||
readlines(normpath(@__DIR__, "..", "Project.toml")),
|
||||
),
|
||||
),
|
||||
"\"",
|
||||
)[2],
|
||||
)
|
||||
|
||||
using Reexport
|
||||
|
||||
@ -25,6 +35,7 @@ import JSON
|
||||
|
||||
using Requires
|
||||
|
||||
#! format: off
|
||||
export
|
||||
grid,
|
||||
bbox,
|
||||
@ -117,7 +128,7 @@ export
|
||||
scalefontsize,
|
||||
scalefontsizes,
|
||||
resetfontsizes
|
||||
|
||||
#! format: on
|
||||
# ---------------------------------------------------------
|
||||
|
||||
import NaNMath # define functions that ignores NaNs. To overcome the destructive effects of https://github.com/JuliaLang/julia/pull/12563
|
||||
@ -136,7 +147,6 @@ ignorenan_extrema(x) = Base.extrema(x)
|
||||
# This makes it impossible to create row vectors of String and Symbol with the transpose operator.
|
||||
# This solves this issue, internally in Plots at least.
|
||||
|
||||
|
||||
# commented out on the insistence of the METADATA maintainers
|
||||
|
||||
#Base.transpose(x::Symbol) = x
|
||||
@ -147,7 +157,8 @@ ignorenan_extrema(x) = Base.extrema(x)
|
||||
import Measures
|
||||
module PlotMeasures
|
||||
import Measures
|
||||
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
|
||||
import Measures:
|
||||
Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
|
||||
const BBox = Measures.Absolute2DBox
|
||||
|
||||
# allow pixels and percentages
|
||||
@ -167,7 +178,8 @@ import .PlotMeasures: Length, AbsoluteLength, Measure, width, height
|
||||
# ---------------------------------------------------------
|
||||
|
||||
import RecipesPipeline
|
||||
import RecipesPipeline: SliceIt,
|
||||
import RecipesPipeline:
|
||||
SliceIt,
|
||||
DefaultsDict,
|
||||
Formatted,
|
||||
AbstractSurface,
|
||||
@ -231,10 +243,8 @@ let PlotOrSubplot = Union{Plot, Subplot}
|
||||
global zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin, zmax), kw...)
|
||||
global xticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) = plot!(plt; xticks = ticks, kw...)
|
||||
global yticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) = plot!(plt; yticks = ticks, kw...)
|
||||
global xticks!(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; xticks = (ticks,labels), kw...)
|
||||
global yticks!(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; yticks = (ticks,labels), kw...)
|
||||
global xticks!(plt::PlotOrSubplot, ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; xticks = (ticks, labels), kw...)
|
||||
global yticks!(plt::PlotOrSubplot, ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; yticks = (ticks, labels), kw...)
|
||||
global xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
|
||||
global ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
|
||||
global annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
@ -245,7 +255,6 @@ let PlotOrSubplot = Union{Plot, Subplot}
|
||||
global yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
@ -14,7 +14,7 @@ end
|
||||
|
||||
Add a plot (the current plot if not specified) to an existing animation
|
||||
"""
|
||||
function frame(anim::Animation, plt::P=current()) where P<:AbstractPlot
|
||||
function frame(anim::Animation, plt::P = current()) where {P<:AbstractPlot}
|
||||
i = length(anim.frames) + 1
|
||||
filename = @sprintf("%06d.png", i)
|
||||
png(plt, joinpath(anim.dir, filename))
|
||||
@ -69,12 +69,16 @@ webm(anim::Animation, fn = webmfn(); kw...) = buildanimation(anim, fn, false; kw
|
||||
ffmpeg_framerate(fps) = "$fps"
|
||||
ffmpeg_framerate(fps::Rational) = "$(fps.num)/$(fps.den)"
|
||||
|
||||
function buildanimation(anim::Animation, fn::AbstractString,
|
||||
function buildanimation(
|
||||
anim::Animation,
|
||||
fn::AbstractString,
|
||||
is_animated_gif::Bool = true;
|
||||
fps::Real = 20, loop::Integer = 0,
|
||||
fps::Real = 20,
|
||||
loop::Integer = 0,
|
||||
variable_palette::Bool = false,
|
||||
verbose = false,
|
||||
show_msg::Bool=true)
|
||||
show_msg::Bool = true,
|
||||
)
|
||||
if length(anim.frames) == 0
|
||||
throw(ArgumentError("Cannot build empty animations"))
|
||||
end
|
||||
@ -82,39 +86,47 @@ function buildanimation(anim::Animation, fn::AbstractString,
|
||||
fn = abspath(expanduser(fn))
|
||||
animdir = anim.dir
|
||||
framerate = ffmpeg_framerate(fps)
|
||||
verbose_level = (verbose isa Int ? verbose :
|
||||
verbose ? 32 # "info"
|
||||
: 16) # "error"
|
||||
verbose_level = (verbose isa Int ? verbose : verbose ? 32 : 16) # "error"
|
||||
|
||||
if is_animated_gif
|
||||
if variable_palette
|
||||
# generate a colorpalette for each frame for highest quality, but larger filesize
|
||||
palette = "palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
|
||||
ffmpeg_exe(`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -lavfi "$palette" -loop $loop -y $fn`)
|
||||
ffmpeg_exe(
|
||||
`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -lavfi "$palette" -loop $loop -y $fn`,
|
||||
)
|
||||
else
|
||||
# generate a colorpalette first so ffmpeg does not have to guess it
|
||||
ffmpeg_exe(`-v $verbose_level -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
|
||||
ffmpeg_exe(`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -loop $loop -y $fn`)
|
||||
ffmpeg_exe(
|
||||
`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -loop $loop -y $fn`,
|
||||
)
|
||||
end
|
||||
else
|
||||
ffmpeg_exe(`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -vf format=yuv420p -loop $loop -y $fn`)
|
||||
ffmpeg_exe(
|
||||
`-v $verbose_level -framerate $framerate -i $(animdir)/%06d.png -vf format=yuv420p -loop $loop -y $fn`,
|
||||
)
|
||||
end
|
||||
|
||||
show_msg && @info("Saved animation to ", fn)
|
||||
AnimatedGif(fn)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# write out html to view the gif
|
||||
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
||||
ext = file_extension(agif.filename)
|
||||
if ext == "gif"
|
||||
html = "<img src=\"data:image/gif;base64," * base64encode(read(agif.filename)) * "\" />"
|
||||
html =
|
||||
"<img src=\"data:image/gif;base64," *
|
||||
base64encode(read(agif.filename)) *
|
||||
"\" />"
|
||||
elseif ext in ("mov", "mp4", "webm")
|
||||
mimetype = ext == "mov" ? "video/quicktime" : "video/$ext"
|
||||
html = "<video controls><source src=\"data:$mimetype;base64," *
|
||||
html =
|
||||
"<video controls><source src=\"data:$mimetype;base64," *
|
||||
base64encode(read(agif.filename)) *
|
||||
"\" type = \"$mimetype\"></video>"
|
||||
else
|
||||
@ -125,7 +137,6 @@ function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
# Only gifs can be shown via image/gif
|
||||
Base.showable(::MIME"image/gif", agif::AnimatedGif) = file_extension(agif.filename) == "gif"
|
||||
|
||||
@ -133,7 +144,6 @@ function Base.show(io::IO, ::MIME"image/gif", agif::AnimatedGif)
|
||||
open(fio -> write(io, fio), agif.filename)
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
function _animate(forloop::Expr, args...; callgif = false)
|
||||
@ -169,7 +179,11 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
error("Unsupported animate filter: $args")
|
||||
end
|
||||
|
||||
push!(block.args, :(if $filterexpr; Plots.frame($animsym); end))
|
||||
push!(block.args, :(
|
||||
if $filterexpr
|
||||
Plots.frame($animsym)
|
||||
end
|
||||
))
|
||||
push!(block.args, :($countersym += 1))
|
||||
|
||||
# add a final call to `gif(anim)`?
|
||||
|
||||
708
src/args.jl
708
src/args.jl
File diff suppressed because it is too large
Load Diff
170
src/axes.jl
170
src/axes.jl
@ -1,5 +1,4 @@
|
||||
|
||||
|
||||
# xaxis(args...; kw...) = Axis(:x, args...; kw...)
|
||||
# yaxis(args...; kw...) = Axis(:y, args...; kw...)
|
||||
# zaxis(args...; kw...) = Axis(:z, args...; kw...)
|
||||
@ -71,7 +70,6 @@ function process_axis_arg!(plotattributes::AKW, arg, letter = "")
|
||||
|
||||
elseif !handleColors!(plotattributes, arg, Symbol(letter, :foreground_color_axis))
|
||||
@warn("Skipped $(letter)axis arg $arg")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@ -122,11 +120,8 @@ Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.plotattributes, v,
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.plotattributes, k)
|
||||
ignorenan_extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
|
||||
|
||||
const _label_func = Dict{Symbol,Function}(
|
||||
:log10 => x -> "10^$x",
|
||||
:log2 => x -> "2^$x",
|
||||
:ln => x -> "e^$x",
|
||||
)
|
||||
const _label_func =
|
||||
Dict{Symbol,Function}(:log10 => x -> "10^$x", :log2 => x -> "2^$x", :ln => x -> "e^$x")
|
||||
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
|
||||
|
||||
const _label_func_tex = Dict{Symbol,Function}(
|
||||
@ -136,7 +131,6 @@ const _label_func_tex = Dict{Symbol,Function}(
|
||||
)
|
||||
labelfunc_tex(scale::Symbol) = get(_label_func_tex, scale, convert_sci_unicode)
|
||||
|
||||
|
||||
function optimal_ticks_and_labels(ticks, alims, scale, formatter)
|
||||
amin, amax = alims
|
||||
|
||||
@ -158,8 +152,8 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter)
|
||||
# are converted to 'DateTime integers' (actually floats) before
|
||||
# being passed to optimize_datetime_ticks.
|
||||
# (convert(Int, convert(DateTime, convert(Date, i))) == 87600000*i)
|
||||
ticks, labels = optimize_datetime_ticks(864e5 * amin, 864e5 * amax;
|
||||
k_min = 2, k_max = 4)
|
||||
ticks, labels =
|
||||
optimize_datetime_ticks(864e5 * amin, 864e5 * amax; k_min = 2, k_max = 4)
|
||||
# Now the ticks are converted back to floats corresponding to Dates.
|
||||
return ticks / 864e5, labels
|
||||
elseif formatter == RecipesPipeline.datetimeformatter
|
||||
@ -198,7 +192,10 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter)
|
||||
if formatter in (:auto, :plain, :scientific, :engineering)
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, formatter))
|
||||
elseif formatter == :latex
|
||||
map(x -> string("\$", replace(convert_sci_unicode(x), '×' => "\\times"), "\$"), Showoff.showoff(unscaled_ticks, :auto))
|
||||
map(
|
||||
x -> string("\$", replace(convert_sci_unicode(x), '×' => "\\times"), "\$"),
|
||||
Showoff.showoff(unscaled_ticks, :auto),
|
||||
)
|
||||
else
|
||||
# there was an override for the formatter... use that on the unscaled ticks
|
||||
map(formatter, unscaled_ticks)
|
||||
@ -223,9 +220,15 @@ function get_ticks(sp::Subplot, axis::Axis; update = true)
|
||||
if update || !haskey(axis.plotattributes, :optimized_ticks)
|
||||
dvals = axis[:discrete_values]
|
||||
ticks = _transform_ticks(axis[:ticks])
|
||||
axis.plotattributes[:optimized_ticks] = if ticks isa Symbol && ticks !== :none &&
|
||||
ispolar(sp) && axis[:letter] === :x && !isempty(dvals)
|
||||
collect(0:pi/4:7pi/4), string.(0:45:315)
|
||||
axis.plotattributes[:optimized_ticks] =
|
||||
if (
|
||||
ticks isa Symbol &&
|
||||
ticks !== :none &&
|
||||
ispolar(sp) &&
|
||||
axis[:letter] === :x &&
|
||||
!isempty(dvals)
|
||||
)
|
||||
collect(0:(pi / 4):(7pi / 4)), string.(0:45:315)
|
||||
else
|
||||
cvals = axis[:continuous_values]
|
||||
alims = axis_limits(sp, axis[:letter])
|
||||
@ -297,7 +300,7 @@ end
|
||||
get_ticks(sp::Subplot, s::Symbol) = get_ticks(sp, sp[Symbol(s, :axis)])
|
||||
get_ticks(p::Plot, s::Symbol) = [get_ticks(sp, s) for sp in p.subplots]
|
||||
|
||||
function get_ticks(ticks::Symbol, cvals::T, dvals, args...) where T
|
||||
function get_ticks(ticks::Symbol, cvals::T, dvals, args...) where {T}
|
||||
if ticks === :none
|
||||
return T[], String[]
|
||||
elseif !isempty(dvals)
|
||||
@ -323,13 +326,13 @@ function get_ticks(ticks::Int, dvals, cvals, args...)
|
||||
end
|
||||
end
|
||||
get_ticks(ticks::NTuple{2,Any}, args...) = ticks
|
||||
get_ticks(::Nothing, cvals::T, args...) where T = T[], String[]
|
||||
get_ticks(::Nothing, cvals::T, args...) where {T} = T[], String[]
|
||||
get_ticks(ticks::Bool, args...) =
|
||||
ticks ? get_ticks(:auto, args...) : get_ticks(nothing, args...)
|
||||
get_ticks(::T, args...) where T = error("Unknown ticks type in get_ticks: $T")
|
||||
get_ticks(::T, args...) where {T} = error("Unknown ticks type in get_ticks: $T")
|
||||
|
||||
_transform_ticks(ticks) = ticks
|
||||
_transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks)
|
||||
_transform_ticks(ticks::AbstractArray{T}) where {T<:Dates.TimeType} = Dates.value.(ticks)
|
||||
_transform_ticks(ticks::NTuple{2,Any}) = (_transform_ticks(ticks[1]), ticks[2])
|
||||
|
||||
function get_minor_ticks(sp, axis, ticks)
|
||||
@ -356,24 +359,30 @@ function get_minor_ticks(sp, axis, ticks)
|
||||
|
||||
# default to 9 intervals between major ticks for log10 scale and 5 intervals otherwise
|
||||
n_default = (scale == :log10) ? 9 : 5
|
||||
n = typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] : n_default
|
||||
n =
|
||||
typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] :
|
||||
n_default
|
||||
|
||||
minorticks = typeof(ticks[1])[]
|
||||
for (i, hi) ∈ enumerate(ticks[2:end])
|
||||
for (i, hi) in enumerate(ticks[2:end])
|
||||
lo = ticks[i]
|
||||
if isfinite(lo) && isfinite(hi) && hi > lo
|
||||
if log_scaled
|
||||
for e ∈ 1:sub
|
||||
for e in 1:sub
|
||||
lo_ = lo * base^(e - 1)
|
||||
hi_ = lo_ * base
|
||||
step = (hi_ - lo_) / n
|
||||
append!(minorticks, collect(
|
||||
lo_ + (e > 1 ? 0 : step) : step : hi_ - (e < sub ? 0 : step / 2)
|
||||
))
|
||||
append!(
|
||||
minorticks,
|
||||
collect(
|
||||
(lo_ + (e > 1 ? 0 : step)):step:(hi_ - (e < sub ? 0 :
|
||||
step / 2)),
|
||||
),
|
||||
)
|
||||
end
|
||||
else
|
||||
step = (hi - lo) / n
|
||||
append!(minorticks, collect(lo + step : step : hi - step / 2))
|
||||
append!(minorticks, collect((lo + step):step:(hi - step / 2)))
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -382,7 +391,6 @@ end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
function reset_extrema!(sp::Subplot)
|
||||
for asym in (:x, :y, :z)
|
||||
sp[Symbol(asym, :axis)][:extrema] = Extrema()
|
||||
@ -392,7 +400,6 @@ function reset_extrema!(sp::Subplot)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(ex::Extrema, v::Number)
|
||||
ex.emin = isfinite(v) ? min(v, ex.emin) : ex.emin
|
||||
ex.emax = isfinite(v) ? max(v, ex.emax) : ex.emax
|
||||
@ -407,14 +414,13 @@ end
|
||||
expand_extrema!(axis::Axis, ::Nothing) = axis[:extrema]
|
||||
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
|
||||
|
||||
|
||||
function expand_extrema!(axis::Axis, v::Tuple{MIN,MAX}) where {MIN<:Number,MAX<:Number}
|
||||
ex = axis[:extrema]
|
||||
ex.emin = isfinite(v[1]) ? min(v[1], ex.emin) : ex.emin
|
||||
ex.emax = isfinite(v[2]) ? max(v[2], ex.emax) : ex.emax
|
||||
ex
|
||||
end
|
||||
function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
|
||||
function expand_extrema!(axis::Axis, v::AVec{N}) where {N<:Number}
|
||||
ex = axis[:extrema]
|
||||
for vi in v
|
||||
expand_extrema!(ex, vi)
|
||||
@ -422,7 +428,6 @@ function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
|
||||
ex
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
vert = isvertical(plotattributes)
|
||||
|
||||
@ -433,7 +438,12 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
else
|
||||
letter == :x ? :y : letter == :y ? :x : :z
|
||||
end]
|
||||
if letter != :z && plotattributes[:seriestype] == :straightline && any(series[:seriestype] != :straightline for series in series_list(sp)) && data[1] != data[2]
|
||||
if (
|
||||
letter != :z &&
|
||||
plotattributes[:seriestype] == :straightline &&
|
||||
any(series[:seriestype] != :straightline for series in series_list(sp)) &&
|
||||
data[1] != data[2]
|
||||
)
|
||||
data = [NaN]
|
||||
end
|
||||
axis = sp[Symbol(letter, "axis")]
|
||||
@ -442,7 +452,8 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
expand_extrema!(sp[:xaxis], data.x_extents)
|
||||
expand_extrema!(sp[:yaxis], data.y_extents)
|
||||
expand_extrema!(sp[:zaxis], data.z_extents)
|
||||
elseif eltype(data) <: Number || (isa(data, Surface) && all(di -> isa(di, Number), data.surf))
|
||||
elseif eltype(data) <: Number ||
|
||||
(isa(data, Surface) && all(di -> isa(di, Number), data.surf))
|
||||
if !(eltype(data) <: Number)
|
||||
# huh... must have been a mis-typed surface? lets swap it out
|
||||
data = plotattributes[letter] = Surface(Matrix{Float64}(data.surf))
|
||||
@ -452,7 +463,8 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
# TODO: need more here... gotta track the discrete reference value
|
||||
# as well as any coord offset (think of boxplot shape coords... they all
|
||||
# correspond to the same x-value)
|
||||
plotattributes[letter], plotattributes[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
|
||||
plotattributes[letter], plotattributes[Symbol(letter, "_discrete_indices")] =
|
||||
discrete_value!(axis, data)
|
||||
expand_extrema!(axis, plotattributes[letter])
|
||||
end
|
||||
end
|
||||
@ -486,7 +498,9 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
|
||||
bw = plotattributes[:bar_width]
|
||||
if bw === nothing
|
||||
bw = plotattributes[:bar_width] = _bar_width * ignorenan_minimum(filter(x->x>0,diff(sort(data))))
|
||||
bw =
|
||||
plotattributes[:bar_width] =
|
||||
_bar_width * ignorenan_minimum(filter(x -> x > 0, diff(sort(data))))
|
||||
end
|
||||
axis = sp.attr[Symbol(dsym, :axis)]
|
||||
expand_extrema!(axis, ignorenan_maximum(data) + 0.5maximum(bw))
|
||||
@ -521,7 +535,28 @@ function widen(lmin, lmax, scale=:identity)
|
||||
end
|
||||
|
||||
# figure out if widening is a good idea.
|
||||
const _widen_seriestypes = (:line, :path, :steppre, :stepmid, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d)
|
||||
const _widen_seriestypes = (
|
||||
:line,
|
||||
:path,
|
||||
:steppre,
|
||||
:stepmid,
|
||||
:steppost,
|
||||
:sticks,
|
||||
:scatter,
|
||||
:barbins,
|
||||
:barhist,
|
||||
:histogram,
|
||||
:scatterbins,
|
||||
:scatterhist,
|
||||
:stepbins,
|
||||
:stephist,
|
||||
:bins2d,
|
||||
:histogram2d,
|
||||
:bar,
|
||||
:shape,
|
||||
:path3d,
|
||||
:scatter3d,
|
||||
)
|
||||
|
||||
function default_should_widen(axis::Axis)
|
||||
if axis[:widen] isa Bool
|
||||
@ -540,7 +575,7 @@ function default_should_widen(axis::Axis)
|
||||
end
|
||||
|
||||
function round_limits(amin, amax, scale)
|
||||
base = get(_logScaleBases, scale, 10.)
|
||||
base = get(_logScaleBases, scale, 10.0)
|
||||
factor = base^(1 - round(log(base, amax - amin)))
|
||||
amin = floor(amin * factor) / factor
|
||||
amax = ceil(amax * factor) / factor
|
||||
@ -548,7 +583,12 @@ function round_limits(amin, amax, scale)
|
||||
end
|
||||
|
||||
# using the axis extrema and limit overrides, return the min/max value for this axis
|
||||
function axis_limits(sp, letter, should_widen = default_should_widen(sp[Symbol(letter, :axis)]), consider_aspect = true)
|
||||
function axis_limits(
|
||||
sp,
|
||||
letter,
|
||||
should_widen = default_should_widen(sp[Symbol(letter, :axis)]),
|
||||
consider_aspect = true,
|
||||
)
|
||||
axis = sp[Symbol(letter, :axis)]
|
||||
ex = axis[:extrema]
|
||||
amin, amax = ex.emin, ex.emax
|
||||
@ -594,7 +634,9 @@ function axis_limits(sp, letter, should_widen = default_should_widen(sp[Symbol(l
|
||||
end
|
||||
|
||||
if (
|
||||
!has_user_lims && consider_aspect && letter in (:x, :y) &&
|
||||
!has_user_lims &&
|
||||
consider_aspect &&
|
||||
letter in (:x, :y) &&
|
||||
!(sp[:aspect_ratio] in (:none, :auto) || RecipesPipeline.is3d(:sp))
|
||||
)
|
||||
aspect_ratio = isa(sp[:aspect_ratio], Number) ? sp[:aspect_ratio] : 1
|
||||
@ -711,7 +753,11 @@ function axis_drawing_info(sp, letter)
|
||||
if sp[:framestyle] != :grid
|
||||
push!(segments, reverse_if((amin, oa1), isy), reverse_if((amax, oa1), isy))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(ticks in (:none, nothing, false)) && length(ticks) > 1
|
||||
if (
|
||||
sp[:framestyle] == :origin &&
|
||||
!(ticks in (:none, nothing, false)) &&
|
||||
length(ticks) > 1
|
||||
)
|
||||
i = findfirst(==(0), ticks[1])
|
||||
if i !== nothing
|
||||
deleteat!(ticks[1], i)
|
||||
@ -765,11 +811,23 @@ function axis_drawing_info(sp, letter)
|
||||
ax_length = letter === :x ? height(sp.plotarea).value : width(sp.plotarea).value
|
||||
|
||||
# add major grid segments
|
||||
add_major_or_minor_segments(ticks[1], ax[:grid], grid_segments, 1.2 / ax_length, ax[:tick_direction] !== :none)
|
||||
add_major_or_minor_segments(
|
||||
ticks[1],
|
||||
ax[:grid],
|
||||
grid_segments,
|
||||
1.2 / ax_length,
|
||||
ax[:tick_direction] !== :none,
|
||||
)
|
||||
|
||||
# add minor grid segments
|
||||
if ax[:minorticks] ∉ (:none, nothing, false) || ax[:minorgrid]
|
||||
add_major_or_minor_segments(minor_ticks, ax[:minorgrid], minorgrid_segments, 0.6 / ax_length, true)
|
||||
add_major_or_minor_segments(
|
||||
minor_ticks,
|
||||
ax[:minorgrid],
|
||||
minorgrid_segments,
|
||||
0.6 / ax_length,
|
||||
true,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -780,7 +838,7 @@ function axis_drawing_info(sp, letter)
|
||||
tick_segments = tick_segments,
|
||||
grid_segments = grid_segments,
|
||||
minorgrid_segments = minorgrid_segments,
|
||||
border_segments = border_segments
|
||||
border_segments = border_segments,
|
||||
)
|
||||
end
|
||||
|
||||
@ -816,7 +874,6 @@ function axis_drawing_info_3d(sp, letter)
|
||||
minorgrid_segments = Segments(3)
|
||||
border_segments = Segments(3)
|
||||
|
||||
|
||||
if sp[:framestyle] != :none # && letter === :x
|
||||
na0, na1 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0, 0
|
||||
@ -837,7 +894,11 @@ function axis_drawing_info_3d(sp, letter)
|
||||
sort_3d_axes(amax, na0, fa0, letter),
|
||||
)
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(ticks in (:none, nothing, false)) && length(ticks) > 1
|
||||
if (
|
||||
sp[:framestyle] == :origin &&
|
||||
!(ticks in (:none, nothing, false)) &&
|
||||
length(ticks) > 1
|
||||
)
|
||||
i0 = findfirst(==(0), ticks[1])
|
||||
if i0 !== nothing
|
||||
deleteat!(ticks[1], i0)
|
||||
@ -857,7 +918,8 @@ function axis_drawing_info_3d(sp, letter)
|
||||
if ax[:ticks] ∉ (:none, nothing, false)
|
||||
f = RecipesPipeline.scale_func(nax[:scale])
|
||||
invf = RecipesPipeline.inverse_scale_func(nax[:scale])
|
||||
ga0, ga1 = sp[:framestyle] in (:origin, :zerolines) ? (namin, namax) : (na0, na1)
|
||||
ga0, ga1 =
|
||||
sp[:framestyle] in (:origin, :zerolines) ? (namin, namax) : (na0, na1)
|
||||
|
||||
add_major_or_minor_segments(ticks, grid, segments, factor, cond) = begin
|
||||
ticks === nothing && return
|
||||
@ -898,11 +960,23 @@ function axis_drawing_info_3d(sp, letter)
|
||||
end
|
||||
|
||||
# add major grid segments
|
||||
add_major_or_minor_segments(ticks[1], ax[:grid], grid_segments, 0.012, ax[:tick_direction] !== :none)
|
||||
add_major_or_minor_segments(
|
||||
ticks[1],
|
||||
ax[:grid],
|
||||
grid_segments,
|
||||
0.012,
|
||||
ax[:tick_direction] !== :none,
|
||||
)
|
||||
|
||||
# add minor grid segments
|
||||
if ax[:minorticks] ∉ (:none, nothing, false) || ax[:minorgrid]
|
||||
add_major_or_minor_segments(minor_ticks, ax[:minorgrid], minorgrid_segments, 0.006, true)
|
||||
add_major_or_minor_segments(
|
||||
minor_ticks,
|
||||
ax[:minorgrid],
|
||||
minorgrid_segments,
|
||||
0.006,
|
||||
true,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -913,7 +987,7 @@ function axis_drawing_info_3d(sp, letter)
|
||||
tick_segments = tick_segments,
|
||||
grid_segments = grid_segments,
|
||||
minorgrid_segments = minorgrid_segments,
|
||||
border_segments = border_segments
|
||||
border_segments = border_segments,
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
648
src/backends.jl
648
src/backends.jl
@ -69,7 +69,8 @@ function text_size(lablen::Int, sz::Number, rot::Number = 0)
|
||||
width = abs(sind(rot + 90)) * width + abs(cosd(rot + 90)) * ptsz
|
||||
width, height
|
||||
end
|
||||
text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(lab), sz, rot)
|
||||
text_size(lab::AbstractString, sz::Number, rot::Number = 0) =
|
||||
text_size(length(lab), sz, rot)
|
||||
text_size(lab::PlotText, sz::Number, rot::Number = 0) = text_size(length(lab.str), sz, rot)
|
||||
|
||||
# account for the size/length/rotation of tick labels
|
||||
@ -125,7 +126,6 @@ _update_plot_object(plt::Plot) = nothing
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
mutable struct CurrentBackend
|
||||
sym::Symbol
|
||||
pkg::AbstractBackend
|
||||
@ -143,8 +143,10 @@ function _pick_default_backend()
|
||||
if sym in _backends
|
||||
backend(sym)
|
||||
else
|
||||
@warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t" *
|
||||
join(sort(_backends), "\n\t"))
|
||||
@warn(
|
||||
"You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t" *
|
||||
join(sort(_backends), "\n\t")
|
||||
)
|
||||
_fallback_default_backend()
|
||||
end
|
||||
else
|
||||
@ -152,7 +154,6 @@ function _pick_default_backend()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
"""
|
||||
@ -189,7 +190,8 @@ function backend(sym::Symbol)
|
||||
end
|
||||
end
|
||||
|
||||
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse, :glvisualize, :pgfplots]
|
||||
const _deprecated_backends =
|
||||
[:qwt, :winston, :bokeh, :gadfly, :immerse, :glvisualize, :pgfplots]
|
||||
|
||||
function warn_on_deprecated_backend(bsym::Symbol)
|
||||
if bsym in _deprecated_backends
|
||||
@ -201,23 +203,29 @@ function warn_on_deprecated_backend(bsym::Symbol)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# these are args which every backend supports because they're not used in the backend code
|
||||
const _base_supported_args = [
|
||||
:color_palette,
|
||||
:background_color, :background_color_subplot,
|
||||
:foreground_color, :foreground_color_subplot,
|
||||
:background_color,
|
||||
:background_color_subplot,
|
||||
:foreground_color,
|
||||
:foreground_color_subplot,
|
||||
:group,
|
||||
:seriestype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:smooth,
|
||||
:xerror, :yerror, :zerror,
|
||||
:xerror,
|
||||
:yerror,
|
||||
:zerror,
|
||||
:subplot,
|
||||
:x, :y, :z,
|
||||
:show, :size,
|
||||
:x,
|
||||
:y,
|
||||
:z,
|
||||
:show,
|
||||
:size,
|
||||
:margin,
|
||||
:left_margin,
|
||||
:right_margin,
|
||||
@ -231,7 +239,7 @@ const _base_supported_args = [
|
||||
:subplot_index,
|
||||
:discrete_values,
|
||||
:projection,
|
||||
:show_empty_bins
|
||||
:show_empty_bins,
|
||||
]
|
||||
|
||||
function merge_with_base_supported(v::AVec)
|
||||
@ -246,8 +254,6 @@ function merge_with_base_supported(v::AVec)
|
||||
Set(v)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@init_backend PyPlot
|
||||
@init_backend UnicodePlots
|
||||
@init_backend Plotly
|
||||
@ -286,7 +292,6 @@ end
|
||||
# is_subplot_supported(::AbstractBackend) = false
|
||||
# is_subplot_supported() = is_subplot_supported(backend())
|
||||
|
||||
|
||||
################################################################################
|
||||
# initialize the backends
|
||||
|
||||
@ -305,39 +310,91 @@ _initialize_backend(pkg::GRBackend) = nothing
|
||||
|
||||
const _gr_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
:background_color_outside,
|
||||
:foreground_color_legend,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_text,
|
||||
:foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:bins,
|
||||
:layout,
|
||||
:title, :window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip,
|
||||
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
|
||||
:titlefontrotation, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfonthalign, :legendfontvalign,
|
||||
:legendfontrotation, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfonthalign, :tickfontvalign,
|
||||
:tickfontrotation, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefonthalign, :guidefontvalign,
|
||||
:guidefontrotation, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:colorbar_titlefontfamily, :colorbar_titlefontsize,
|
||||
:colorbar_titlefontvalign, :colorbar_titlefonthalign,
|
||||
:colorbar_titlefontrotation, :colorbar_titlefontcolor,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:titlefontfamily,
|
||||
:titlefontsize,
|
||||
:titlefonthalign,
|
||||
:titlefontvalign,
|
||||
:titlefontrotation,
|
||||
:titlefontcolor,
|
||||
:legendfontfamily,
|
||||
:legendfontsize,
|
||||
:legendfonthalign,
|
||||
:legendfontvalign,
|
||||
:legendfontrotation,
|
||||
:legendfontcolor,
|
||||
:tickfontfamily,
|
||||
:tickfontsize,
|
||||
:tickfonthalign,
|
||||
:tickfontvalign,
|
||||
:tickfontrotation,
|
||||
:tickfontcolor,
|
||||
:guidefontfamily,
|
||||
:guidefontsize,
|
||||
:guidefonthalign,
|
||||
:guidefontvalign,
|
||||
:guidefontrotation,
|
||||
:guidefontcolor,
|
||||
:grid,
|
||||
:gridalpha,
|
||||
:gridstyle,
|
||||
:gridlinewidth,
|
||||
:legend,
|
||||
:legendtitle,
|
||||
:colorbar,
|
||||
:colorbar_title,
|
||||
:colorbar_entry,
|
||||
:colorbar_titlefontfamily,
|
||||
:colorbar_titlefontsize,
|
||||
:colorbar_titlefontvalign,
|
||||
:colorbar_titlefonthalign,
|
||||
:colorbar_titlefontrotation,
|
||||
:colorbar_titlefontcolor,
|
||||
:fill_z,
|
||||
:line_z,
|
||||
:marker_z,
|
||||
:levels,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:aspect_ratio,
|
||||
:normalize, :weights,
|
||||
:normalize,
|
||||
:weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:arrow,
|
||||
@ -381,35 +438,79 @@ end
|
||||
|
||||
const _plotly_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_legend, :foreground_color_guide,
|
||||
:foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
:background_color_outside,
|
||||
:foreground_color_legend,
|
||||
:foreground_color_guide,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_text,
|
||||
:foreground_color_border,
|
||||
:foreground_color_title,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:markerstrokestyle,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:bins,
|
||||
:title, :titlelocation,
|
||||
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
|
||||
:title,
|
||||
:titlelocation,
|
||||
:titlefontfamily,
|
||||
:titlefontsize,
|
||||
:titlefonthalign,
|
||||
:titlefontvalign,
|
||||
:titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:legendfontfamily,
|
||||
:legendfontsize,
|
||||
:legendfontcolor,
|
||||
:tickfontfamily,
|
||||
:tickfontsize,
|
||||
:tickfontcolor,
|
||||
:guidefontfamily,
|
||||
:guidefontsize,
|
||||
:guidefontcolor,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :gridalpha, :gridlinewidth,
|
||||
:legend, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:marker_z, :fill_z, :line_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:guide,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:rotation,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:gridalpha,
|
||||
:gridlinewidth,
|
||||
:legend,
|
||||
:colorbar,
|
||||
:colorbar_title,
|
||||
:colorbar_entry,
|
||||
:marker_z,
|
||||
:fill_z,
|
||||
:line_z,
|
||||
:levels,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
# :overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
:normalize,
|
||||
:weights,
|
||||
# :contours,
|
||||
:aspect_ratio,
|
||||
:hover,
|
||||
@ -434,12 +535,24 @@ const _plotly_seriestype = [
|
||||
:shape,
|
||||
:scattergl,
|
||||
:straightline,
|
||||
:mesh3d
|
||||
:mesh3d,
|
||||
]
|
||||
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _plotly_marker = [
|
||||
:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle,
|
||||
:cross, :xcross, :pentagon, :hexagon, :octagon, :vline, :hline
|
||||
:none,
|
||||
:auto,
|
||||
:circle,
|
||||
:rect,
|
||||
:diamond,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:cross,
|
||||
:xcross,
|
||||
:pentagon,
|
||||
:hexagon,
|
||||
:octagon,
|
||||
:vline,
|
||||
:hline,
|
||||
]
|
||||
const _plotly_scale = [:identity, :log10]
|
||||
|
||||
@ -454,23 +567,50 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
# :foreground_color_legend,
|
||||
:foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_text,
|
||||
:foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:markerstrokestyle,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:bins,
|
||||
# :bar_width, :bar_edges,
|
||||
:title,
|
||||
# :window_title,
|
||||
:guide, :guide_position, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
:colorbar, :colorbar_title,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
:guide,
|
||||
:guide_position,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:rotation,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:legend,
|
||||
:colorbar,
|
||||
:colorbar_title,
|
||||
:fill_z,
|
||||
:line_z,
|
||||
:marker_z,
|
||||
:levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
@ -482,9 +622,36 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape, :straightline,]
|
||||
const _pgfplots_seriestype = [
|
||||
:path,
|
||||
:path3d,
|
||||
:scatter,
|
||||
:steppre,
|
||||
:stepmid,
|
||||
:steppost,
|
||||
:histogram2d,
|
||||
:ysticks,
|
||||
:xsticks,
|
||||
:contour,
|
||||
:shape,
|
||||
:straightline,
|
||||
]
|
||||
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline, :vline] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_marker = [
|
||||
:none,
|
||||
:auto,
|
||||
:circle,
|
||||
:rect,
|
||||
:diamond,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:cross,
|
||||
:xcross,
|
||||
:star5,
|
||||
:pentagon,
|
||||
:hline,
|
||||
:vline,
|
||||
] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@ -519,36 +686,89 @@ end
|
||||
|
||||
const _pyplot_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
:background_color_outside,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_legend,
|
||||
:foreground_color_title,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_border,
|
||||
:foreground_color_guide,
|
||||
:foreground_color_text,
|
||||
:label,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :titlelocation, :titlefont,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:bins,
|
||||
:bar_width,
|
||||
:bar_edges,
|
||||
:bar_position,
|
||||
:title,
|
||||
:titlelocation,
|
||||
:titlefont,
|
||||
:window_title,
|
||||
:guide, :guide_position, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:colorbar_ticks, :colorbar_tickfontfamily, :colorbar_tickfontsize,
|
||||
:colorbar_tickfonthalign, :colorbar_tickfontvalign,
|
||||
:colorbar_tickfontrotation, :colorbar_tickfontcolor,
|
||||
:guide,
|
||||
:guide_position,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:rotation,
|
||||
:titlefontfamily,
|
||||
:titlefontsize,
|
||||
:titlefontcolor,
|
||||
:legendfontfamily,
|
||||
:legendfontsize,
|
||||
:legendfontcolor,
|
||||
:tickfontfamily,
|
||||
:tickfontsize,
|
||||
:tickfontcolor,
|
||||
:guidefontfamily,
|
||||
:guidefontsize,
|
||||
:guidefontcolor,
|
||||
:grid,
|
||||
:gridalpha,
|
||||
:gridstyle,
|
||||
:gridlinewidth,
|
||||
:legend,
|
||||
:legendtitle,
|
||||
:colorbar,
|
||||
:colorbar_title,
|
||||
:colorbar_entry,
|
||||
:colorbar_ticks,
|
||||
:colorbar_tickfontfamily,
|
||||
:colorbar_tickfontsize,
|
||||
:colorbar_tickfonthalign,
|
||||
:colorbar_tickfontvalign,
|
||||
:colorbar_tickfontrotation,
|
||||
:colorbar_tickfontcolor,
|
||||
:colorbar_scale,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:marker_z,
|
||||
:line_z,
|
||||
:fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:arrow,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
:contours, :aspect_ratio,
|
||||
:normalize,
|
||||
:weights,
|
||||
:contours,
|
||||
:aspect_ratio,
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
@ -598,18 +818,35 @@ const _gaston_attr = merge_with_base_supported([
|
||||
# :foreground_color_legend,
|
||||
# :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
:label, :seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:label,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||
# :fillrange, :fillcolor, :fillalpha,
|
||||
# :bins,
|
||||
# :bar_width, :bar_edges,
|
||||
:title, :window_title,
|
||||
:guide, :guide_position,
|
||||
:lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont,
|
||||
:legendfont, :grid, :legend,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide,
|
||||
:guide_position,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:rotation,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:legend,
|
||||
# :colorbar, :colorbar_title,
|
||||
# :fill_z, :line_z, :marker_z, :levels,
|
||||
# :ribbon,
|
||||
@ -632,7 +869,8 @@ const _gaston_seriestype = [
|
||||
:steppre,
|
||||
:stepmid,
|
||||
:steppost,
|
||||
:ysticks, :xsticks,
|
||||
:ysticks,
|
||||
:xsticks,
|
||||
:contour,
|
||||
:shape,
|
||||
:straightline,
|
||||
@ -645,14 +883,7 @@ const _gaston_seriestype = [
|
||||
:image,
|
||||
]
|
||||
|
||||
const _gaston_style = [
|
||||
:auto,
|
||||
:solid,
|
||||
:dash,
|
||||
:dot,
|
||||
:dashdot,
|
||||
:dashdotdot
|
||||
]
|
||||
const _gaston_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
|
||||
const _gaston_marker = [
|
||||
:none,
|
||||
@ -687,14 +918,17 @@ const _unicodeplots_attr = merge_with_base_supported([
|
||||
:markershape,
|
||||
:bins,
|
||||
:title,
|
||||
:guide, :lims,
|
||||
:guide,
|
||||
:lims,
|
||||
])
|
||||
const _unicodeplots_seriestype = [
|
||||
:path, :scatter, :straightline,
|
||||
:path,
|
||||
:scatter,
|
||||
:straightline,
|
||||
# :bar,
|
||||
:shape,
|
||||
:histogram2d,
|
||||
:spy
|
||||
:spy,
|
||||
]
|
||||
const _unicodeplots_style = [:auto, :solid]
|
||||
const _unicodeplots_marker = [:none, :auto, :circle]
|
||||
@ -708,28 +942,65 @@ const _canvas_type = Ref(:auto)
|
||||
|
||||
const _hdf5_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
:background_color_outside,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_legend,
|
||||
:foreground_color_title,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_border,
|
||||
:foreground_color_guide,
|
||||
:foreground_color_text,
|
||||
:label,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :titlelocation, :titlefont,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:bins,
|
||||
:bar_width,
|
||||
:bar_edges,
|
||||
:bar_position,
|
||||
:title,
|
||||
:titlelocation,
|
||||
:titlefont,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:guide,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:rotation,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:legend,
|
||||
:colorbar,
|
||||
:marker_z,
|
||||
:line_z,
|
||||
:fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:arrow,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
:contours, :aspect_ratio,
|
||||
:normalize,
|
||||
:weights,
|
||||
:contours,
|
||||
:aspect_ratio,
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
@ -768,32 +1039,59 @@ mutable struct HDF5Plot_PlotRef
|
||||
end
|
||||
const HDF5PLOT_PLOTREF = HDF5Plot_PlotRef(nothing)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# inspectdr
|
||||
|
||||
const _inspectdr_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
:background_color_outside,
|
||||
# :foreground_color_grid,
|
||||
:foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:foreground_color_legend,
|
||||
:foreground_color_title,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_border,
|
||||
:foreground_color_guide,
|
||||
:foreground_color_text,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:markerstrokestyle, #Causes warning not to have it... what is this?
|
||||
:fillcolor, :fillalpha, #:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha, #:fillrange,
|
||||
# :bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :titlelocation,
|
||||
:title,
|
||||
:titlelocation,
|
||||
:window_title,
|
||||
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
||||
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :legend, #:colorbar,
|
||||
:guide,
|
||||
:lims,
|
||||
:scale, #:ticks, :flip, :rotation,
|
||||
:titlefontfamily,
|
||||
:titlefontsize,
|
||||
:titlefontcolor,
|
||||
:legendfontfamily,
|
||||
:legendfontsize,
|
||||
:legendfontcolor,
|
||||
:tickfontfamily,
|
||||
:tickfontsize,
|
||||
:tickfontcolor,
|
||||
:guidefontfamily,
|
||||
:guidefontsize,
|
||||
:guidefontcolor,
|
||||
:grid,
|
||||
:legend, #:colorbar,
|
||||
# :marker_z,
|
||||
# :line_z,
|
||||
# :levels,
|
||||
@ -810,17 +1108,37 @@ const _inspectdr_attr = merge_with_base_supported([
|
||||
])
|
||||
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _inspectdr_seriestype = [
|
||||
:path, :scatter, :shape, :straightline, #, :steppre, :stepmid, :steppost
|
||||
:path,
|
||||
:scatter,
|
||||
:shape,
|
||||
:straightline, #, :steppre, :stepmid, :steppost
|
||||
]
|
||||
#see: _allMarkers, _shape_keys
|
||||
const _inspectdr_marker = Symbol[
|
||||
:none, :auto,
|
||||
:circle, :rect, :diamond,
|
||||
:cross, :xcross,
|
||||
:utriangle, :dtriangle, :rtriangle, :ltriangle,
|
||||
:pentagon, :hexagon, :heptagon, :octagon,
|
||||
:star4, :star5, :star6, :star7, :star8,
|
||||
:vline, :hline, :+, :x,
|
||||
:none,
|
||||
:auto,
|
||||
:circle,
|
||||
:rect,
|
||||
:diamond,
|
||||
:cross,
|
||||
:xcross,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:rtriangle,
|
||||
:ltriangle,
|
||||
:pentagon,
|
||||
:hexagon,
|
||||
:heptagon,
|
||||
:octagon,
|
||||
:star4,
|
||||
:star5,
|
||||
:star6,
|
||||
:star7,
|
||||
:star8,
|
||||
:vline,
|
||||
:hline,
|
||||
:+,
|
||||
:x,
|
||||
]
|
||||
|
||||
const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
@ -27,7 +27,7 @@ const _pgfplots_markers = KW(
|
||||
:diamond => "diamond*",
|
||||
:pentagon => "pentagon*",
|
||||
:hline => "-",
|
||||
:vline => "|"
|
||||
:vline => "|",
|
||||
)
|
||||
|
||||
const _pgfplots_legend_pos = KW(
|
||||
@ -38,7 +38,6 @@ const _pgfplots_legend_pos = KW(
|
||||
:outertopright => "outer north east",
|
||||
)
|
||||
|
||||
|
||||
const _pgf_series_extrastyle = KW(
|
||||
:steppre => "const plot mark right",
|
||||
:stepmid => "const plot mark mid",
|
||||
@ -50,11 +49,7 @@ const _pgf_series_extrastyle = KW(
|
||||
|
||||
# PGFPlots uses the anchors to define orientations for example to align left
|
||||
# one needs to use the right edge as anchor
|
||||
const _pgf_annotation_halign = KW(
|
||||
:center => "",
|
||||
:left => "right",
|
||||
:right => "left"
|
||||
)
|
||||
const _pgf_annotation_halign = KW(:center => "", :left => "right", :right => "left")
|
||||
|
||||
const _pgf_framestyles = [:box, :axes, :origin, :zerolines, :grid, :none]
|
||||
const _pgf_framestyle_defaults = Dict(:semi => :box)
|
||||
@ -63,7 +58,9 @@ function pgf_framestyle(style::Symbol)
|
||||
return style
|
||||
else
|
||||
default_style = get(_pgf_framestyle_defaults, style, :axes)
|
||||
@warn("Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead.")
|
||||
@warn(
|
||||
"Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead."
|
||||
)
|
||||
default_style
|
||||
end
|
||||
end
|
||||
@ -126,8 +123,15 @@ end
|
||||
|
||||
function pgf_marker(plotattributes, i = 1)
|
||||
shape = _cycle(plotattributes[:markershape], i)
|
||||
cstr, a = pgf_color(plot_color(get_markercolor(plotattributes, i), get_markeralpha(plotattributes, i)))
|
||||
cstr_stroke, a_stroke = pgf_color(plot_color(get_markerstrokecolor(plotattributes, i), get_markerstrokealpha(plotattributes, i)))
|
||||
cstr, a = pgf_color(
|
||||
plot_color(get_markercolor(plotattributes, i), get_markeralpha(plotattributes, i)),
|
||||
)
|
||||
cstr_stroke, a_stroke = pgf_color(
|
||||
plot_color(
|
||||
get_markerstrokecolor(plotattributes, i),
|
||||
get_markerstrokealpha(plotattributes, i),
|
||||
),
|
||||
)
|
||||
return string(
|
||||
"mark = $(get(_pgfplots_markers, shape, "*")),\n",
|
||||
"mark size = $(pgf_thickness_scaling(plotattributes) * 0.5 * _cycle(plotattributes[:markersize], i)),\n",
|
||||
@ -138,7 +142,7 @@ function pgf_marker(plotattributes, i = 1)
|
||||
line width = $(pgf_thickness_scaling(plotattributes) * _cycle(plotattributes[:markerstrokewidth], i)),
|
||||
rotate = $(shape == :dtriangle ? 180 : 0),
|
||||
$(get(_pgfplots_linestyles, _cycle(plotattributes[:markerstrokestyle], i), "solid"))
|
||||
}"
|
||||
}",
|
||||
)
|
||||
end
|
||||
|
||||
@ -146,14 +150,20 @@ function pgf_add_annotation!(o, x, y, val, thickness_scaling = 1)
|
||||
# Construct the style string.
|
||||
# Currently supports color and orientation
|
||||
cstr, a = pgf_color(val.font.color)
|
||||
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
|
||||
x, y,
|
||||
push!(
|
||||
o,
|
||||
PGFPlots.Plots.Node(
|
||||
val.str, # Annotation Text
|
||||
x,
|
||||
y,
|
||||
style = """
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation),
|
||||
font=$(pgf_font(val.font.pointsize, thickness_scaling))
|
||||
"""))
|
||||
""",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
@ -241,7 +251,15 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
|
||||
# add fillrange
|
||||
if series[:fillrange] !== nothing && st != :shape
|
||||
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
|
||||
push!(
|
||||
series_collection,
|
||||
pgf_fillrange_series(
|
||||
series,
|
||||
i,
|
||||
_cycle(series[:fillrange], rng),
|
||||
seg_args...,
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
# build/return the series object
|
||||
@ -336,7 +354,23 @@ function pgf_axis(sp::Subplot, letter)
|
||||
|
||||
# Add label font
|
||||
cstr, α = pgf_color(plot_color(axis[:guidefontcolor]))
|
||||
push!(style, string(letter, "label style = {", labelpos ,"font = ", pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:guidefontrotation], "}"))
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
letter,
|
||||
"label style = {",
|
||||
labelpos,
|
||||
"font = ",
|
||||
pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)),
|
||||
", color = ",
|
||||
cstr,
|
||||
", draw opacity = ",
|
||||
α,
|
||||
", rotate = ",
|
||||
axis[:guidefontrotation],
|
||||
"}",
|
||||
),
|
||||
)
|
||||
|
||||
# flip/reverse?
|
||||
axis[:flip] && push!(style, "$letter dir=reverse")
|
||||
@ -363,7 +397,9 @@ function pgf_axis(sp::Subplot, letter)
|
||||
# limits
|
||||
# TODO: support zlims
|
||||
if letter != :z
|
||||
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) : axis_limits(sp, letter)
|
||||
lims =
|
||||
ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
|
||||
axis_limits(sp, letter)
|
||||
kw[Symbol(letter, :min)] = lims[1]
|
||||
kw[Symbol(letter, :max)] = lims[2]
|
||||
end
|
||||
@ -371,7 +407,9 @@ function pgf_axis(sp::Subplot, letter)
|
||||
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
|
||||
ticks = get_ticks(sp, axis)
|
||||
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
|
||||
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
|
||||
tick_values =
|
||||
ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] :
|
||||
ticks[1]
|
||||
push!(style, string(letter, "tick = {", join(tick_values, ","), "}"))
|
||||
if axis[:showaxis] && axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
|
||||
# wrap the power part of label with }
|
||||
@ -381,9 +419,13 @@ function pgf_axis(sp::Subplot, letter)
|
||||
power = string("{", power, "}")
|
||||
tick_labels[i] = string(base, "^", power)
|
||||
end
|
||||
push!(style, string(letter, "ticklabels = {\$", join(tick_labels,"\$,\$"), "\$}"))
|
||||
push!(
|
||||
style,
|
||||
string(letter, "ticklabels = {\$", join(tick_labels, "\$,\$"), "\$}"),
|
||||
)
|
||||
elseif axis[:showaxis]
|
||||
tick_labels = ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
|
||||
tick_labels =
|
||||
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
|
||||
if axis[:formatter] in (:scientific, :auto)
|
||||
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
|
||||
tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
|
||||
@ -392,10 +434,44 @@ function pgf_axis(sp::Subplot, letter)
|
||||
else
|
||||
push!(style, string(letter, "ticklabels = {}"))
|
||||
end
|
||||
push!(style, string(letter, "tick align = ", (axis[:tick_direction] == :out ? "outside" : "inside")))
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
letter,
|
||||
"tick align = ",
|
||||
(axis[:tick_direction] == :out ? "outside" : "inside"),
|
||||
),
|
||||
)
|
||||
cstr, α = pgf_color(plot_color(axis[:tickfontcolor]))
|
||||
push!(style, string(letter, "ticklabel style = {font = ", pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:tickfontrotation], "}"))
|
||||
push!(style, string(letter, " grid style = {", pgf_linestyle(pgf_thickness_scaling(sp) * axis[:gridlinewidth], axis[:foreground_color_grid], axis[:gridalpha], axis[:gridstyle]), "}"))
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
letter,
|
||||
"ticklabel style = {font = ",
|
||||
pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)),
|
||||
", color = ",
|
||||
cstr,
|
||||
", draw opacity = ",
|
||||
α,
|
||||
", rotate = ",
|
||||
axis[:tickfontrotation],
|
||||
"}",
|
||||
),
|
||||
)
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
letter,
|
||||
" grid style = {",
|
||||
pgf_linestyle(
|
||||
pgf_thickness_scaling(sp) * axis[:gridlinewidth],
|
||||
axis[:foreground_color_grid],
|
||||
axis[:gridalpha],
|
||||
axis[:gridstyle],
|
||||
),
|
||||
"}",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
# framestyle
|
||||
@ -412,7 +488,20 @@ function pgf_axis(sp::Subplot, letter)
|
||||
if framestyle == :zerolines
|
||||
push!(style, string("extra ", letter, " ticks = 0"))
|
||||
push!(style, string("extra ", letter, " tick labels = "))
|
||||
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}}"))
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
"extra ",
|
||||
letter,
|
||||
" tick style = {grid = major, major grid style = {",
|
||||
pgf_linestyle(
|
||||
pgf_thickness_scaling(sp),
|
||||
axis[:foreground_color_border],
|
||||
1.0,
|
||||
),
|
||||
"}}",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
if !axis[:showaxis]
|
||||
@ -421,7 +510,19 @@ function pgf_axis(sp::Subplot, letter)
|
||||
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
|
||||
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
|
||||
else
|
||||
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}"))
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
letter,
|
||||
" axis line style = {",
|
||||
pgf_linestyle(
|
||||
pgf_thickness_scaling(sp),
|
||||
axis[:foreground_color_border],
|
||||
1.0,
|
||||
),
|
||||
"}",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
# return the style list and KW args
|
||||
@ -430,7 +531,6 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
plt.o = PGFPlots.Axis[]
|
||||
# Obtain the total height of the plot by extracting the maximal bottom
|
||||
@ -456,18 +556,34 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
# A round on 2 decimal places should be enough precision for 300 dpi
|
||||
# plots.
|
||||
bb = bbox(sp)
|
||||
push!(style, """
|
||||
push!(
|
||||
style,
|
||||
"""
|
||||
xshift = $(left(bb).value)mm,
|
||||
yshift = $(round((total_height - (bottom(bb))).value, digits=2))mm,
|
||||
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
|
||||
""")
|
||||
""",
|
||||
)
|
||||
kw[:width] = "$(width(bb).value)mm"
|
||||
kw[:height] = "$(height(bb).value)mm"
|
||||
|
||||
if sp[:title] != ""
|
||||
kw[:title] = "$(sp[:title])"
|
||||
cstr, α = pgf_color(plot_color(sp[:titlefontcolor]))
|
||||
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
"title style = {font = ",
|
||||
pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)),
|
||||
", color = ",
|
||||
cstr,
|
||||
", draw opacity = ",
|
||||
α,
|
||||
", rotate = ",
|
||||
sp[:titlefontrotation],
|
||||
"}",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
if get_aspect_ratio(sp) in (1, :equal)
|
||||
@ -481,15 +597,25 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
cstr, bg_alpha = pgf_color(plot_color(sp[:background_color_legend]))
|
||||
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
||||
|
||||
push!(style, string(
|
||||
push!(
|
||||
style,
|
||||
string(
|
||||
"legend style = {",
|
||||
pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], fg_alpha, "solid", ), ",",
|
||||
pgf_linestyle(
|
||||
pgf_thickness_scaling(sp),
|
||||
sp[:foreground_color_legend],
|
||||
fg_alpha,
|
||||
"solid",
|
||||
),
|
||||
",",
|
||||
"fill = $cstr,",
|
||||
"fill opacity = $bg_alpha,",
|
||||
"text opacity = $(alpha(plot_color(sp[:legendfontcolor]))),",
|
||||
"font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
|
||||
"font = ",
|
||||
pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
|
||||
"}",
|
||||
))
|
||||
),
|
||||
)
|
||||
|
||||
if any(s[:seriestype] == :contour for s in series_list(sp))
|
||||
kw[:view] = "{0}{90}"
|
||||
@ -520,7 +646,10 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
for series in series_list(sp)
|
||||
for col in (:markercolor, :fillcolor, :linecolor)
|
||||
if typeof(series.plotattributes[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.plotattributes[col]))}")
|
||||
push!(
|
||||
style,
|
||||
"colormap={plots}{$(pgf_colormap(series.plotattributes[col]))}",
|
||||
)
|
||||
|
||||
if sp[:colorbar] == :none
|
||||
kw[:colorbar] = "false"
|
||||
@ -544,16 +673,25 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
# add series annotations
|
||||
anns = series[:series_annotations]
|
||||
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt), pgf_thickness_scaling(series))
|
||||
pgf_add_annotation!(
|
||||
o,
|
||||
xi,
|
||||
yi,
|
||||
PlotText(str, fnt),
|
||||
pgf_thickness_scaling(series),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
pgf_add_annotation!(o, locate_annotation(sp, ann...)..., pgf_thickness_scaling(sp))
|
||||
pgf_add_annotation!(
|
||||
o,
|
||||
locate_annotation(sp, ann...)...,
|
||||
pgf_thickness_scaling(sp),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
# add the PGFPlots.Axis to the list
|
||||
push!(plt.o, o)
|
||||
end
|
||||
@ -580,7 +718,11 @@ end
|
||||
|
||||
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
|
||||
fn = tempname() * ".tex"
|
||||
PGFPlots.save(fn, backend_object(plt), include_preamble=plt.attr[:tex_output_standalone])
|
||||
PGFPlots.save(
|
||||
fn,
|
||||
backend_object(plt),
|
||||
include_preamble = plt.attr[:tex_output_standalone],
|
||||
)
|
||||
write(io, read(open(fn), String))
|
||||
end
|
||||
|
||||
|
||||
@ -31,11 +31,11 @@ function _before_layout_calcs(plt::Plot{GastonBackend})
|
||||
plt.o.layout = gaston_init_subplots(plt, sps)
|
||||
|
||||
# Then add the series (curves in gaston)
|
||||
for series ∈ plt.series_list
|
||||
for series in plt.series_list
|
||||
gaston_add_series(plt, series)
|
||||
end
|
||||
|
||||
for sp ∈ plt.subplots
|
||||
for sp in plt.subplots
|
||||
sp === nothing && continue
|
||||
for ann in sp[:annotations]
|
||||
x, y, val = locate_annotation(sp, ann...)
|
||||
@ -57,7 +57,7 @@ function _update_plot_object(plt::Plot{GastonBackend})
|
||||
nothing
|
||||
end
|
||||
|
||||
for (mime, term) ∈ (
|
||||
for (mime, term) in (
|
||||
"application/eps" => "epscairo",
|
||||
"image/eps" => "epslatex",
|
||||
"application/pdf" => "pdfcairo",
|
||||
@ -69,10 +69,17 @@ for (mime, term) ∈ (
|
||||
"text/plain" => "dumb",
|
||||
)
|
||||
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GastonBackend})
|
||||
term = String($term); tmpfile = "$(Gaston.tempname()).$term"
|
||||
term = String($term)
|
||||
tmpfile = "$(Gaston.tempname()).$term"
|
||||
|
||||
Gaston.save(term=term, output=tmpfile, handle=plt.o.handle, saveopts=gaston_saveopts(plt))
|
||||
while !isfile(tmpfile) end # avoid race condition with read in next line
|
||||
Gaston.save(
|
||||
term = term,
|
||||
output = tmpfile,
|
||||
handle = plt.o.handle,
|
||||
saveopts = gaston_saveopts(plt),
|
||||
)
|
||||
while !isfile(tmpfile)
|
||||
end # avoid race condition with read in next line
|
||||
write(io, read(tmpfile))
|
||||
rm(tmpfile, force = true)
|
||||
nothing
|
||||
@ -88,7 +95,16 @@ _display(plt::Plot{GastonBackend}) = display(plt.o)
|
||||
function gaston_saveopts(plt::Plot{GastonBackend})
|
||||
saveopts = String["size $(join(plt.attr[:size], ","))"]
|
||||
|
||||
push!(saveopts, gaston_font(plottitlefont(plt), rot=false, align=false, color=false, scale=1))
|
||||
push!(
|
||||
saveopts,
|
||||
gaston_font(
|
||||
plottitlefont(plt),
|
||||
rot = false,
|
||||
align = false,
|
||||
color = false,
|
||||
scale = 1,
|
||||
),
|
||||
)
|
||||
|
||||
push!(saveopts, "background $(gaston_color(plt.attr[:background_color]))")
|
||||
|
||||
@ -104,7 +120,7 @@ end
|
||||
function gaston_get_subplots(n, plt_subplots, layout)
|
||||
nr, nc = size(layout)
|
||||
sps = Array{Any}(nothing, nr, nc)
|
||||
for r ∈ 1:nr, c ∈ 1:nc # NOTE: col major
|
||||
for r in 1:nr, c in 1:nc # NOTE: col major
|
||||
l = layout[r, c]
|
||||
if l isa GridLayout
|
||||
n, sub = gaston_get_subplots(n, plt_subplots, l)
|
||||
@ -118,7 +134,7 @@ end
|
||||
|
||||
function gaston_init_subplots(plt, sps)
|
||||
sz = nr, nc = size(sps)
|
||||
for c ∈ 1:nc, r ∈ 1:nr # NOTE: row major
|
||||
for c in 1:nc, r in 1:nr # NOTE: row major
|
||||
sp = sps[r, c]
|
||||
if sp isa Subplot || sp === nothing
|
||||
gaston_init_subplot(plt, sp)
|
||||
@ -130,20 +146,28 @@ function gaston_init_subplots(plt, sps)
|
||||
return sz
|
||||
end
|
||||
|
||||
function gaston_init_subplot(plt::Plot{GastonBackend}, sp::Union{Nothing,Subplot{GastonBackend}})
|
||||
function gaston_init_subplot(
|
||||
plt::Plot{GastonBackend},
|
||||
sp::Union{Nothing,Subplot{GastonBackend}},
|
||||
)
|
||||
if sp === nothing
|
||||
push!(plt.o.subplots, sp)
|
||||
else
|
||||
dims = RecipesPipeline.is3d(sp) || sp.attr[:projection] == "3d" || needs_any_3d_axes(sp) ? 3 : 2
|
||||
dims =
|
||||
RecipesPipeline.is3d(sp) ||
|
||||
sp.attr[:projection] == "3d" ||
|
||||
needs_any_3d_axes(sp) ? 3 : 2
|
||||
any_label = false
|
||||
for series ∈ series_list(sp)
|
||||
for series in series_list(sp)
|
||||
if dims == 2 && series[:seriestype] ∈ (:heatmap, :contour)
|
||||
dims = 3 # we need heatmap/contour to use splot, not plot
|
||||
end
|
||||
any_label |= should_add_to_legend(series)
|
||||
end
|
||||
sp.o = Gaston.Plot(
|
||||
dims=dims, curves=[], axesconf=gaston_parse_axes_args(plt, sp, dims, any_label),
|
||||
dims = dims,
|
||||
curves = [],
|
||||
axesconf = gaston_parse_axes_args(plt, sp, dims, any_label),
|
||||
)
|
||||
push!(plt.o.subplots, sp.o)
|
||||
end
|
||||
@ -153,7 +177,7 @@ end
|
||||
function gaston_multiplot_pos_size(layout, parent_xy_wh)
|
||||
nr, nc = size(layout)
|
||||
dat = Array{Any}(nothing, nr, nc)
|
||||
for r ∈ 1:nr, c ∈ 1:nc
|
||||
for r in 1:nr, c in 1:nc
|
||||
l = layout[r, c]
|
||||
# width and height (pct) are multiplicative (parent)
|
||||
w = layout.widths[c].value * parent_xy_wh[3]
|
||||
@ -181,7 +205,7 @@ end
|
||||
|
||||
function gaston_multiplot_pos_size!(dat)
|
||||
nr, nc = size(dat)
|
||||
for r ∈ 1:nr, c ∈ 1:nc
|
||||
for r in 1:nr, c in 1:nc
|
||||
xy_wh_sp = dat[r, c]
|
||||
if xy_wh_sp isa Array
|
||||
gaston_multiplot_pos_size!(xy_wh_sp)
|
||||
@ -197,15 +221,16 @@ function gaston_multiplot_pos_size!(dat)
|
||||
end
|
||||
|
||||
function gaston_add_series(plt::Plot{GastonBackend}, series::Series)
|
||||
sp = series[:subplot]; gsp = sp.o
|
||||
sp = series[:subplot]
|
||||
gsp = sp.o
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
st = series[:seriestype]
|
||||
|
||||
curves = []
|
||||
if gsp.dims == 2 && z === nothing
|
||||
for (n, seg) ∈ enumerate(series_segments(series, st; check=true))
|
||||
for (n, seg) in enumerate(series_segments(series, st; check = true))
|
||||
i, rng = seg.attr_index, seg.range
|
||||
for sc ∈ gaston_seriesconf!(sp, series, i, n == 1)
|
||||
for sc in gaston_seriesconf!(sp, series, i, n == 1)
|
||||
push!(curves, Gaston.Curve(x[rng], y[rng], nothing, nothing, sc))
|
||||
end
|
||||
end
|
||||
@ -222,26 +247,32 @@ function gaston_add_series(plt::Plot{GastonBackend}, series::Series)
|
||||
x = collect(range(x[1], x[2], length = nc))
|
||||
end
|
||||
elseif st == :heatmap
|
||||
length(x) == size(z, 2) + 1 && (x = @view x[1:end-1])
|
||||
length(y) == size(z, 1) + 1 && (y = @view y[1:end-1])
|
||||
length(x) == size(z, 2) + 1 && (x = @view x[1:(end - 1)])
|
||||
length(y) == size(z, 1) + 1 && (y = @view y[1:(end - 1)])
|
||||
end
|
||||
end
|
||||
if st == :mesh3d
|
||||
x, y, z = mesh3d_triangles(x, y, z, series[:connections])
|
||||
end
|
||||
for sc ∈ gaston_seriesconf!(sp, series, 1, true)
|
||||
for sc in gaston_seriesconf!(sp, series, 1, true)
|
||||
push!(curves, Gaston.Curve(x, y, z, nothing, sc))
|
||||
end
|
||||
end
|
||||
|
||||
for c ∈ curves
|
||||
append = length(gsp.curves) > 0; push!(gsp.curves, c)
|
||||
for c in curves
|
||||
append = length(gsp.curves) > 0
|
||||
push!(gsp.curves, c)
|
||||
Gaston.write_data(c, gsp.dims, gsp.datafile, append = append)
|
||||
end
|
||||
nothing
|
||||
end
|
||||
|
||||
function gaston_seriesconf!(sp::Subplot{GastonBackend}, series::Series, i::Int, add_to_legend::Bool)
|
||||
function gaston_seriesconf!(
|
||||
sp::Subplot{GastonBackend},
|
||||
series::Series,
|
||||
i::Int,
|
||||
add_to_legend::Bool,
|
||||
)
|
||||
#=
|
||||
gnuplot abbreviations (see gnuplot/src/set.c)
|
||||
---------------------------------------------
|
||||
@ -261,7 +292,9 @@ function gaston_seriesconf!(sp::Subplot{GastonBackend}, series::Series, i::Int,
|
||||
tc: textcolor
|
||||
w: with
|
||||
=#
|
||||
gsp = sp.o; st = series[:seriestype]; extra = []
|
||||
gsp = sp.o
|
||||
st = series[:seriestype]
|
||||
extra = []
|
||||
add_to_legend &= should_add_to_legend(series)
|
||||
curveconf = String[add_to_legend ? "title '$(series[:label])'" : "notitle"]
|
||||
|
||||
@ -320,18 +353,24 @@ function gaston_seriesconf!(sp::Subplot{GastonBackend}, series::Series, i::Int,
|
||||
end
|
||||
|
||||
function gaston_parse_axes_args(
|
||||
plt::Plot{GastonBackend}, sp::Subplot{GastonBackend}, dims::Int, any_label::Bool
|
||||
plt::Plot{GastonBackend},
|
||||
sp::Subplot{GastonBackend},
|
||||
dims::Int,
|
||||
any_label::Bool,
|
||||
)
|
||||
# axesconf = String["set margins 2, 2, 2, 2"] # left, right, bottom, top
|
||||
axesconf = String[]
|
||||
|
||||
polar = ispolar(sp) && dims == 2 # cannot splot in polar coordinates
|
||||
|
||||
for letter ∈ (:x, :y, :z)
|
||||
for letter in (:x, :y, :z)
|
||||
(letter == :z && dims == 2) && continue
|
||||
axis = sp.attr[Symbol(letter, :axis)]
|
||||
# label names
|
||||
push!(axesconf, "set $(letter)label '$(axis[:guide])' $(gaston_font(guidefont(axis)))")
|
||||
push!(
|
||||
axesconf,
|
||||
"set $(letter)label '$(axis[:guide])' $(gaston_font(guidefont(axis)))",
|
||||
)
|
||||
mirror = axis[:mirror] ? "mirror" : "nomirror"
|
||||
|
||||
if axis[:scale] == :identity
|
||||
@ -349,7 +388,10 @@ function gaston_parse_axes_args(
|
||||
if polar
|
||||
push!(axesconf, "set size square\nunset $(letter)tics")
|
||||
else
|
||||
push!(axesconf, "set $(letter)tics $(mirror) $(axis[:tick_direction]) $(gaston_font(tickfont(axis)))")
|
||||
push!(
|
||||
axesconf,
|
||||
"set $(letter)tics $(mirror) $(axis[:tick_direction]) $(gaston_font(tickfont(axis)))",
|
||||
)
|
||||
|
||||
# major tick locations
|
||||
if axis[:ticks] != :native
|
||||
@ -372,7 +414,8 @@ function gaston_parse_axes_args(
|
||||
|
||||
if axis[:grid]
|
||||
push!(axesconf, "set grid " * (polar ? "polar" : "$(letter)tics"))
|
||||
axis[:minorgrid] && push!(axesconf, "set grid " * (polar ? "polar" : "m$(letter)tics"))
|
||||
axis[:minorgrid] &&
|
||||
push!(axesconf, "set grid " * (polar ? "polar" : "m$(letter)tics"))
|
||||
end
|
||||
|
||||
ratio = get_aspect_ratio(sp)
|
||||
@ -399,12 +442,20 @@ function gaston_parse_axes_args(
|
||||
if (ttype = ticksType(rticks)) == :ticks
|
||||
gaston_ticks = string.(rticks)
|
||||
elseif ttype == :ticks_and_labels
|
||||
gaston_ticks = String["'$l' $t" for (t, l) ∈ zip(rticks...)]
|
||||
gaston_ticks = String["'$l' $t" for (t, l) in zip(rticks...)]
|
||||
end
|
||||
push!(axesconf, "set rtics ( " * join(gaston_ticks, ", ") * " ) $(gaston_font(tickfont(sp.attr[:yaxis])))")
|
||||
push!(
|
||||
axesconf,
|
||||
"set rtics ( " *
|
||||
join(gaston_ticks, ", ") *
|
||||
" ) $(gaston_font(tickfont(sp.attr[:yaxis])))",
|
||||
)
|
||||
push!(axesconf, "set trange [$(min(0, tmin)):$(max(2π, tmax))]")
|
||||
push!(axesconf, "set rrange [$rmin:$rmax]")
|
||||
push!(axesconf, "set ttics 0,30 format \"%g\".GPVAL_DEGREE_SIGN $(gaston_font(tickfont(sp.attr[:xaxis])))")
|
||||
push!(
|
||||
axesconf,
|
||||
"set ttics 0,30 format \"%g\".GPVAL_DEGREE_SIGN $(gaston_font(tickfont(sp.attr[:xaxis])))",
|
||||
)
|
||||
push!(axesconf, "set mttics 3")
|
||||
end
|
||||
|
||||
@ -421,7 +472,7 @@ function gaston_set_ticks!(axesconf, ticks, letter, maj_min, add)
|
||||
gaston_ticks = String[]
|
||||
if (ttype = ticksType(ticks)) == :ticks
|
||||
tick_locs = @view ticks[:]
|
||||
for i ∈ eachindex(tick_locs)
|
||||
for i in eachindex(tick_locs)
|
||||
tick = if maj_min == "m"
|
||||
"'' $(tick_locs[i]) 1" # see gnuplot manual 'Mxtics'
|
||||
else
|
||||
@ -432,7 +483,7 @@ function gaston_set_ticks!(axesconf, ticks, letter, maj_min, add)
|
||||
elseif ttype == :ticks_and_labels
|
||||
tick_locs = @view ticks[1][:]
|
||||
tick_labels = @view ticks[2][:]
|
||||
for i ∈ eachindex(tick_locs)
|
||||
for i in eachindex(tick_locs)
|
||||
lab = gaston_enclose_tick_string(tick_labels[i])
|
||||
push!(gaston_ticks, "'$lab' $(tick_locs[i])")
|
||||
end
|
||||
@ -451,8 +502,11 @@ function gaston_set_legend!(axesconf, sp, any_label)
|
||||
if sp[:legend] ∉ (:none, :inline) && any_label
|
||||
leg == :best && (leg = :topright)
|
||||
|
||||
push!(axesconf, "set key " * (occursin("outer", string(leg)) ? "outside" : "inside"))
|
||||
for position ∈ ("top", "bottom", "left", "right")
|
||||
push!(
|
||||
axesconf,
|
||||
"set key " * (occursin("outer", string(leg)) ? "outside" : "inside"),
|
||||
)
|
||||
for position in ("top", "bottom", "left", "right")
|
||||
occursin(position, string(leg)) && push!(axesconf, "set key $position")
|
||||
end
|
||||
push!(axesconf, "set key $(gaston_font(legendfont(sp), rot=false, align=false))")
|
||||
@ -498,8 +552,9 @@ function gaston_font(f; rot=true, align=true, color=true, scale=1)
|
||||
end
|
||||
|
||||
function gaston_palette(gradient)
|
||||
palette = String[]; n = -1
|
||||
for rgba ∈ gradient # FIXME: naive conversion, inefficient ?
|
||||
palette = String[]
|
||||
n = -1
|
||||
for rgba in gradient # FIXME: naive conversion, inefficient ?
|
||||
push!(palette, "$(n += 1) $(rgba.r) $(rgba.g) $(rgba.b)")
|
||||
end
|
||||
return '(' * join(palette, ", ") * ')'
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
import GR
|
||||
export GR
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
gr_linetype(k) = (auto = 1, solid = 1, dash = 2, dot = 3, dashdot = 4, dashdotdot = -1)[k]
|
||||
@ -27,7 +26,7 @@ gr_markertype(k) = (
|
||||
octagon = -24,
|
||||
cross = 2,
|
||||
xcross = 5,
|
||||
+ = 2,
|
||||
(+) = 2,
|
||||
x = 5,
|
||||
star4 = -25,
|
||||
star5 = -26,
|
||||
@ -92,10 +91,12 @@ const gr_font_family = Dict(
|
||||
|
||||
gr_color(c) = gr_color(c, color_type(c))
|
||||
|
||||
gr_color(c, ::Type{<:AbstractRGB}) = UInt32( round(UInt, clamp(alpha(c) * 255, 0, 255)) << 24 +
|
||||
gr_color(c, ::Type{<:AbstractRGB}) = UInt32(
|
||||
round(UInt, clamp(alpha(c) * 255, 0, 255)) << 24 +
|
||||
round(UInt, clamp(blue(c) * 255, 0, 255)) << 16 +
|
||||
round(UInt, clamp(green(c) * 255, 0, 255)) << 8 +
|
||||
round(UInt, clamp(red(c) * 255, 0, 255)) )
|
||||
round(UInt, clamp(red(c) * 255, 0, 255)),
|
||||
)
|
||||
function gr_color(c, ::Type{<:AbstractGray})
|
||||
g = round(UInt, clamp(gray(c) * 255, 0, 255))
|
||||
α = round(UInt, clamp(alpha(c) * 255, 0, 255))
|
||||
@ -119,7 +120,8 @@ gr_set_transparency(c, α) = gr_set_transparency(α)
|
||||
gr_set_transparency(c::Colorant, ::Nothing) = gr_set_transparency(c)
|
||||
gr_set_transparency(c::Colorant) = GR.settransparency(alpha(c))
|
||||
|
||||
gr_set_arrowstyle(s::Symbol) = GR.setarrowstyle(get(
|
||||
gr_set_arrowstyle(s::Symbol) = GR.setarrowstyle(
|
||||
get(
|
||||
(
|
||||
simple = 1,
|
||||
hollow = 3,
|
||||
@ -131,11 +133,11 @@ gr_set_arrowstyle(s::Symbol) = GR.setarrowstyle(get(
|
||||
),
|
||||
s,
|
||||
1,
|
||||
))
|
||||
),
|
||||
)
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# draw line segments, splitting x/y into contiguous/finite segments
|
||||
# note: this can be used for shapes by passing func `GR.fillarea`
|
||||
function gr_polyline(x, y, func = GR.polyline; arrowside = :none, arrowstyle = :simple)
|
||||
@ -144,7 +146,7 @@ function gr_polyline(x, y, func = GR.polyline; arrowside = :none, arrowstyle = :
|
||||
while iend < n - 1
|
||||
# set istart to the first index that is finite
|
||||
istart = -1
|
||||
for j = iend+1:n
|
||||
for j in (iend + 1):n
|
||||
if isfinite(x[j]) && isfinite(y[j])
|
||||
istart = j
|
||||
break
|
||||
@ -154,7 +156,7 @@ function gr_polyline(x, y, func = GR.polyline; arrowside = :none, arrowstyle = :
|
||||
if istart > 0
|
||||
# iend is the last finite index
|
||||
iend = -1
|
||||
for j = istart+1:n
|
||||
for j in (istart + 1):n
|
||||
if isfinite(x[j]) && isfinite(y[j])
|
||||
iend = j
|
||||
else
|
||||
@ -186,7 +188,7 @@ function gr_polyline3d(x, y, z, func = GR.polyline3d)
|
||||
while iend < n - 1
|
||||
# set istart to the first index that is finite
|
||||
istart = -1
|
||||
for j = iend+1:n
|
||||
for j in (iend + 1):n
|
||||
if isfinite(x[j]) && isfinite(y[j]) && isfinite(z[j])
|
||||
istart = j
|
||||
break
|
||||
@ -196,7 +198,7 @@ function gr_polyline3d(x, y, z, func = GR.polyline3d)
|
||||
if istart > 0
|
||||
# iend is the last finite index
|
||||
iend = -1
|
||||
for j = istart+1:n
|
||||
for j in (istart + 1):n
|
||||
if isfinite(x[j]) && isfinite(y[j]) && isfinite(z[j])
|
||||
iend = j
|
||||
else
|
||||
@ -218,7 +220,7 @@ gr_inqtext(x, y, s) = gr_inqtext(x, y, string(s))
|
||||
|
||||
function gr_inqtext(x, y, s::AbstractString)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.inqmathtex(x, y, s[2:end-1])
|
||||
GR.inqmathtex(x, y, s[2:(end - 1)])
|
||||
elseif occursin('\\', s) || occursin("10^{", s)
|
||||
GR.inqtextext(x, y, s)
|
||||
else
|
||||
@ -230,7 +232,7 @@ gr_text(x, y, s) = gr_text(x, y, string(s))
|
||||
|
||||
function gr_text(x, y, s::AbstractString)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.mathtex(x, y, s[2:end-1])
|
||||
GR.mathtex(x, y, s[2:(end - 1)])
|
||||
elseif occursin('\\', s) || occursin("10^{", s)
|
||||
GR.textext(x, y, s)
|
||||
else
|
||||
@ -252,7 +254,10 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
#draw angular grid
|
||||
if xaxis[:grid]
|
||||
gr_set_line(
|
||||
xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid], sp
|
||||
xaxis[:gridlinewidth],
|
||||
xaxis[:gridstyle],
|
||||
xaxis[:foreground_color_grid],
|
||||
sp,
|
||||
)
|
||||
gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:gridalpha])
|
||||
for i in eachindex(α)
|
||||
@ -263,7 +268,10 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
#draw radial grid
|
||||
if yaxis[:grid]
|
||||
gr_set_line(
|
||||
yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid], sp
|
||||
yaxis[:gridlinewidth],
|
||||
yaxis[:gridstyle],
|
||||
yaxis[:foreground_color_grid],
|
||||
sp,
|
||||
)
|
||||
gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:gridalpha])
|
||||
for i in eachindex(rtick_values)
|
||||
@ -302,14 +310,12 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
GR.restorestate()
|
||||
end
|
||||
|
||||
|
||||
# using the axis extrema and limit overrides, return the min/max value for this axis
|
||||
gr_x_axislims(sp::Subplot) = axis_limits(sp, :x)
|
||||
gr_y_axislims(sp::Subplot) = axis_limits(sp, :y)
|
||||
gr_z_axislims(sp::Subplot) = axis_limits(sp, :z)
|
||||
gr_xy_axislims(sp::Subplot) = gr_x_axislims(sp)..., gr_y_axislims(sp)...
|
||||
|
||||
|
||||
function gr_fill_viewport(vp::AVec{Float64}, c)
|
||||
GR.savestate()
|
||||
GR.selntran(0)
|
||||
@ -365,7 +371,6 @@ function gr_draw_marker(series, xi, yi, clims, i, msize, strokewidth, shape::Sym
|
||||
GR.polymarker([xi], [yi])
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function gr_set_line(lw, style, c, s) # s can be Subplot or Series
|
||||
@ -374,7 +379,6 @@ function gr_set_line(lw, style, c, s) # s can be Subplot or Series
|
||||
gr_set_linecolor(c)
|
||||
end
|
||||
|
||||
|
||||
function gr_set_fill(c) #, a)
|
||||
gr_set_fillcolor(c) #, a)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
@ -385,15 +389,21 @@ end
|
||||
gr_point_mult(s) = 1.5 * get_thickness_scaling(s) * px / pt / maximum(get_size(s))
|
||||
|
||||
# set the font attributes.
|
||||
function gr_set_font(f::Font, s; halign = f.halign, valign = f.valign,
|
||||
color = f.color, rotation = f.rotation)
|
||||
function gr_set_font(
|
||||
f::Font,
|
||||
s;
|
||||
halign = f.halign,
|
||||
valign = f.valign,
|
||||
color = f.color,
|
||||
rotation = f.rotation,
|
||||
)
|
||||
family = lowercase(f.family)
|
||||
GR.setcharheight(gr_point_mult(s) * f.pointsize)
|
||||
GR.setcharup(sind(-rotation), cosd(-rotation))
|
||||
if haskey(gr_font_family, family)
|
||||
GR.settextfontprec(
|
||||
gr_font_family[family],
|
||||
gr_font_family[family] >= 200 ? 3 : GR.TEXT_PRECISION_STRING
|
||||
gr_font_family[family] >= 200 ? 3 : GR.TEXT_PRECISION_STRING,
|
||||
)
|
||||
end
|
||||
gr_set_textcolor(color)
|
||||
@ -408,7 +418,13 @@ end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# viewport plot area
|
||||
function gr_viewport_from_bbox(sp::Subplot{GRBackend}, bb::BoundingBox, w, h, viewport_canvas)
|
||||
function gr_viewport_from_bbox(
|
||||
sp::Subplot{GRBackend},
|
||||
bb::BoundingBox,
|
||||
w,
|
||||
h,
|
||||
viewport_canvas,
|
||||
)
|
||||
viewport = zeros(4)
|
||||
viewport[1] = viewport_canvas[2] * (left(bb) / w)
|
||||
viewport[2] = viewport_canvas[2] * (right(bb) / w)
|
||||
@ -426,7 +442,7 @@ function gr_set_viewport_cmap(sp::Subplot, viewport_plotarea)
|
||||
viewport_plotarea[2] + (RecipesPipeline.is3d(sp) ? 0.07 : 0.02),
|
||||
viewport_plotarea[2] + (RecipesPipeline.is3d(sp) ? 0.10 : 0.05),
|
||||
viewport_plotarea[3],
|
||||
viewport_plotarea[4]
|
||||
viewport_plotarea[4],
|
||||
)
|
||||
end
|
||||
|
||||
@ -451,10 +467,10 @@ end
|
||||
function gr_update_colorbar!(cbar::GRColorbar, series::Series)
|
||||
style = colorbar_style(series)
|
||||
style === nothing && return
|
||||
list = style == cbar_gradient ? cbar.gradients :
|
||||
list =
|
||||
style == cbar_gradient ? cbar.gradients :
|
||||
style == cbar_fill ? cbar.fills :
|
||||
style == cbar_lines ? cbar.lines :
|
||||
error("Unknown colorbar style: $style.")
|
||||
style == cbar_lines ? cbar.lines : error("Unknown colorbar style: $style.")
|
||||
push!(list, series)
|
||||
end
|
||||
|
||||
@ -462,7 +478,7 @@ function gr_contour_levels(series::Series, clims)
|
||||
levels = contour_levels(series, clims)
|
||||
if isfilledcontour(series)
|
||||
# GR implicitly uses the maximal z value as the highest level
|
||||
levels = levels[1:end-1]
|
||||
levels = levels[1:(end - 1)]
|
||||
end
|
||||
levels
|
||||
end
|
||||
@ -484,7 +500,6 @@ function gr_colorbar_colors(series::Series, clims)
|
||||
round.(Int, colors)
|
||||
end
|
||||
|
||||
|
||||
function _cbar_unique(values, propname)
|
||||
out = last(values)
|
||||
if any(x != out for x in values)
|
||||
@ -520,10 +535,10 @@ function gr_draw_colorbar(cbar::GRColorbar, sp::Subplot, clims, viewport_plotare
|
||||
@warn "GR: highest contour level less than maximal z value is not supported."
|
||||
# replace levels, rather than assign to levels[end], to ensure type
|
||||
# promotion in case levels is an integer array
|
||||
levels = [levels[1:end-1]; clims[2]]
|
||||
levels = [levels[1:(end - 1)]; clims[2]]
|
||||
end
|
||||
colors = gr_colorbar_colors(last(series), clims)
|
||||
for (from, to, color) in zip(levels[1:end-1], levels[2:end], colors)
|
||||
for (from, to, color) in zip(levels[1:(end - 1)], levels[2:end], colors)
|
||||
GR.setfillcolorind(color)
|
||||
GR.fillrect(xmin, xmax, from, to)
|
||||
end
|
||||
@ -559,9 +574,7 @@ function gr_draw_colorbar(cbar::GRColorbar, sp::Subplot, clims, viewport_plotare
|
||||
gr_set_font(title.font, sp)
|
||||
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
|
||||
GR.setcharup(-1, 0)
|
||||
gr_text(
|
||||
viewport_plotarea[2] + 0.1, gr_view_ycenter(viewport_plotarea), title.str
|
||||
)
|
||||
gr_text(viewport_plotarea[2] + 0.1, gr_view_ycenter(viewport_plotarea), title.str)
|
||||
|
||||
GR.restorestate()
|
||||
end
|
||||
@ -569,8 +582,10 @@ end
|
||||
gr_view_xcenter(viewport_plotarea) = 0.5 * (viewport_plotarea[1] + viewport_plotarea[2])
|
||||
gr_view_ycenter(viewport_plotarea) = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
|
||||
|
||||
gr_view_xposition(viewport_plotarea, position) = viewport_plotarea[1] + position * (viewport_plotarea[2] - viewport_plotarea[1])
|
||||
gr_view_yposition(viewport_plotarea, position) = viewport_plotarea[3] + position * (viewport_plotarea[4] - viewport_plotarea[3])
|
||||
gr_view_xposition(viewport_plotarea, position) =
|
||||
viewport_plotarea[1] + position * (viewport_plotarea[2] - viewport_plotarea[1])
|
||||
gr_view_yposition(viewport_plotarea, position) =
|
||||
viewport_plotarea[3] + position * (viewport_plotarea[4] - viewport_plotarea[3])
|
||||
|
||||
function position(symb)
|
||||
if symb == :top || symb == :right
|
||||
@ -613,7 +628,8 @@ function gr_display(plt::Plot, fmt="")
|
||||
dpi_factor = fmt == "png" ? plt[:dpi] / Plots.DPI : 1
|
||||
|
||||
# collect some monitor/display sizes in meters and pixels
|
||||
display_width_meters, display_height_meters, display_width_px, display_height_px = GR.inqdspsize()
|
||||
display_width_meters, display_height_meters, display_width_px, display_height_px =
|
||||
GR.inqdspsize()
|
||||
display_width_ratio = display_width_meters / display_width_px
|
||||
display_height_ratio = display_height_meters / display_height_px
|
||||
|
||||
@ -718,7 +734,10 @@ function gr_axis_height(sp, axis)
|
||||
GR.savestate()
|
||||
ticks = get_ticks(sp, axis, update = false)
|
||||
gr_set_font(tickfont(axis), sp)
|
||||
h = (ticks in (nothing, false, :none) ? 0 : last(gr_get_ticks_size(ticks, axis[:rotation])))
|
||||
h = (
|
||||
ticks in (nothing, false, :none) ? 0 :
|
||||
last(gr_get_ticks_size(ticks, axis[:rotation]))
|
||||
)
|
||||
if axis[:guide] != ""
|
||||
gr_set_font(guidefont(axis), sp)
|
||||
h += last(gr_text_size(axis[:guide]))
|
||||
@ -731,7 +750,10 @@ function gr_axis_width(sp, axis)
|
||||
GR.savestate()
|
||||
ticks = get_ticks(sp, axis, update = false)
|
||||
gr_set_font(tickfont(axis), sp)
|
||||
w = (ticks in (nothing, false, :none) ? 0 : first(gr_get_ticks_size(ticks, axis[:rotation])))
|
||||
w = (
|
||||
ticks in (nothing, false, :none) ? 0 :
|
||||
first(gr_get_ticks_size(ticks, axis[:rotation]))
|
||||
)
|
||||
if axis[:guide] != ""
|
||||
gr_set_font(guidefont(axis), sp)
|
||||
w += last(gr_text_size(axis[:guide]))
|
||||
@ -763,16 +785,17 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
|
||||
if RecipesPipeline.is3d(sp)
|
||||
xaxis, yaxis, zaxis = sp[:xaxis], sp[:yaxis], sp[:zaxis]
|
||||
xticks, yticks, zticks = get_ticks(sp, xaxis), get_ticks(sp, yaxis), get_ticks(sp, zaxis)
|
||||
xticks, yticks, zticks =
|
||||
get_ticks(sp, xaxis), get_ticks(sp, yaxis), get_ticks(sp, zaxis)
|
||||
# Add margin for x and y ticks
|
||||
h = 0mm
|
||||
if !isempty(first(xticks))
|
||||
gr_set_font(
|
||||
tickfont(xaxis),
|
||||
sp,
|
||||
halign = (:left, :hcenter, :right)[sign(xaxis[:rotation]) + 2],
|
||||
valign = (xaxis[:mirror] ? :bottom : :top),
|
||||
rotation = xaxis[:rotation],
|
||||
sp
|
||||
)
|
||||
l = 0.01 + last(gr_get_ticks_size(xticks, xaxis[:rotation]))
|
||||
h = max(h, 1mm + get_size(sp)[2] * l * px)
|
||||
@ -780,10 +803,10 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
if !isempty(first(yticks))
|
||||
gr_set_font(
|
||||
tickfont(yaxis),
|
||||
sp,
|
||||
halign = (:left, :hcenter, :right)[sign(yaxis[:rotation]) + 2],
|
||||
valign = (yaxis[:mirror] ? :bottom : :top),
|
||||
rotation = yaxis[:rotation],
|
||||
sp
|
||||
)
|
||||
l = 0.01 + last(gr_get_ticks_size(yticks, yaxis[:rotation]))
|
||||
h = max(h, 1mm + get_size(sp)[2] * l * px)
|
||||
@ -800,11 +823,11 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
if !isempty(first(zticks))
|
||||
gr_set_font(
|
||||
tickfont(zaxis),
|
||||
sp,
|
||||
halign = (zaxis[:mirror] ? :left : :right),
|
||||
valign = (:top, :vcenter, :bottom)[sign(zaxis[:rotation]) + 2],
|
||||
rotation = zaxis[:rotation],
|
||||
color = zaxis[:tickfontcolor],
|
||||
sp
|
||||
)
|
||||
l = 0.01 + first(gr_get_ticks_size(zticks, zaxis[:rotation]))
|
||||
w = 1mm + get_size(sp)[1] * l * px
|
||||
@ -828,7 +851,10 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
h = max(h, 1mm + get_size(sp)[2] * l * px)
|
||||
end
|
||||
if h > 0mm
|
||||
if xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
if (
|
||||
xaxis[:guide_position] == :top ||
|
||||
(xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
)
|
||||
toppad += h
|
||||
else
|
||||
bottompad += h
|
||||
@ -839,7 +865,10 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
gr_set_font(guidefont(sp[:zaxis]), sp)
|
||||
l = last(gr_text_size(sp[:zaxis][:guide]))
|
||||
w = 1mm + get_size(sp)[2] * l * px
|
||||
if zaxis[:guide_position] == :right || (zaxis[:guide_position] == :auto && zaxis[:mirror] == true)
|
||||
if (
|
||||
zaxis[:guide_position] == :right ||
|
||||
(zaxis[:guide_position] == :auto && zaxis[:mirror] == true)
|
||||
)
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
@ -874,7 +903,10 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
gr_set_font(guidefont(sp[:xaxis]), sp)
|
||||
l = last(gr_text_size(sp[:xaxis][:guide]))
|
||||
h = 1mm + get_size(sp)[2] * l * px
|
||||
if sp[:xaxis][:guide_position] == :top || (sp[:xaxis][:guide_position] == :auto && sp[:xaxis][:mirror] == true)
|
||||
if (
|
||||
sp[:xaxis][:guide_position] == :top ||
|
||||
(sp[:xaxis][:guide_position] == :auto && sp[:xaxis][:mirror] == true)
|
||||
)
|
||||
toppad += h
|
||||
else
|
||||
bottompad += h
|
||||
@ -885,7 +917,10 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
gr_set_font(guidefont(sp[:yaxis]), sp)
|
||||
l = last(gr_text_size(sp[:yaxis][:guide]))
|
||||
w = 1mm + get_size(sp)[2] * l * px
|
||||
if sp[:yaxis][:guide_position] == :right || (sp[:yaxis][:guide_position] == :auto && sp[:yaxis][:mirror] == true)
|
||||
if (
|
||||
sp[:yaxis][:guide_position] == :right ||
|
||||
(sp[:yaxis][:guide_position] == :auto && sp[:yaxis][:mirror] == true)
|
||||
)
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
@ -899,7 +934,7 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
end
|
||||
|
||||
function is_equally_spaced(v)
|
||||
d = collect(v[2:end] .- v[1:end-1])
|
||||
d = collect(v[2:end] .- v[1:(end - 1)])
|
||||
all(d .≈ d[1])
|
||||
end
|
||||
|
||||
@ -978,7 +1013,6 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
## Legend
|
||||
|
||||
function gr_add_legend(sp, leg, viewport_plotarea)
|
||||
@ -992,13 +1026,17 @@ function gr_add_legend(sp, leg, viewport_plotarea)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
gr_set_fillcolor(sp[:background_color_legend])
|
||||
GR.fillrect(
|
||||
xpos - leg.leftw, xpos + leg.textw + leg.rightw,
|
||||
ypos + leg.dy, ypos - leg.h
|
||||
xpos - leg.leftw,
|
||||
xpos + leg.textw + leg.rightw,
|
||||
ypos + leg.dy,
|
||||
ypos - leg.h,
|
||||
) # Allocating white space for actual legend width here
|
||||
gr_set_line(1, :solid, sp[:foreground_color_legend], sp)
|
||||
GR.drawrect(
|
||||
xpos - leg.leftw, xpos + leg.textw + leg.rightw,
|
||||
ypos + leg.dy, ypos - leg.h
|
||||
xpos - leg.leftw,
|
||||
xpos + leg.textw + leg.rightw,
|
||||
ypos + leg.dy,
|
||||
ypos - leg.h,
|
||||
) # Drawing actual legend width here
|
||||
i = 0
|
||||
if sp[:legendtitle] !== nothing
|
||||
@ -1015,7 +1053,10 @@ function gr_add_legend(sp, leg, viewport_plotarea)
|
||||
lc = get_linecolor(series, clims)
|
||||
gr_set_line(sp[:legendfontsize] / 8, get_linestyle(series), lc, sp)
|
||||
|
||||
if (st == :shape || series[:fillrange] !== nothing) && series[:ribbon] === nothing
|
||||
if (
|
||||
(st == :shape || series[:fillrange] !== nothing) &&
|
||||
series[:ribbon] === nothing
|
||||
)
|
||||
fc = get_fillcolor(series, clims)
|
||||
gr_set_fill(fc) #, series[:fillalpha])
|
||||
l, r = xpos - leg.width_factor * 3.5, xpos - leg.width_factor / 2
|
||||
@ -1077,8 +1118,12 @@ function gr_legend_pos(sp::Subplot, leg, viewport_plotarea)
|
||||
end
|
||||
|
||||
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
|
||||
xmirror = xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
ymirror = yaxis[:guide_position] == :right || (yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
|
||||
xmirror =
|
||||
xaxis[:guide_position] == :top ||
|
||||
(xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
ymirror =
|
||||
yaxis[:guide_position] == :right ||
|
||||
(yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
|
||||
axisclearance = [
|
||||
!ymirror * gr_axis_width(sp, sp[:yaxis]),
|
||||
ymirror * gr_axis_width(sp, sp[:yaxis]),
|
||||
@ -1094,40 +1139,60 @@ function gr_legend_pos(sp::Subplot, leg, viewport_plotarea)
|
||||
end
|
||||
if occursin("outer", str)
|
||||
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
|
||||
xmirror = xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
ymirror = yaxis[:guide_position] == :right || (yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
|
||||
xmirror =
|
||||
xaxis[:guide_position] == :top ||
|
||||
(xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
ymirror =
|
||||
yaxis[:guide_position] == :right ||
|
||||
(yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
|
||||
end
|
||||
if occursin("right", str)
|
||||
if occursin("outer", str)
|
||||
# As per https://github.com/jheinen/GR.jl/blob/master/src/jlgr.jl#L525
|
||||
xpos = viewport_plotarea[2] + leg.xoffset + leg.leftw + ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
xpos =
|
||||
viewport_plotarea[2] +
|
||||
leg.xoffset +
|
||||
leg.leftw +
|
||||
ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
else
|
||||
xpos = viewport_plotarea[2] - leg.rightw - leg.textw - leg.xoffset
|
||||
end
|
||||
elseif occursin("left", str)
|
||||
if occursin("outer", str)
|
||||
xpos = viewport_plotarea[1] - !ymirror * gr_axis_width(sp, sp[:yaxis]) - leg.xoffset * 2 - leg.rightw - leg.textw
|
||||
xpos =
|
||||
viewport_plotarea[1] - !ymirror * gr_axis_width(sp, sp[:yaxis]) -
|
||||
leg.xoffset * 2 - leg.rightw - leg.textw
|
||||
else
|
||||
xpos = viewport_plotarea[1] + leg.leftw + leg.xoffset
|
||||
end
|
||||
else
|
||||
xpos = (viewport_plotarea[2] - viewport_plotarea[1]) / 2 + viewport_plotarea[1] + leg.leftw - leg.rightw - leg.textw - leg.xoffset * 2
|
||||
xpos =
|
||||
(viewport_plotarea[2] - viewport_plotarea[1]) / 2 +
|
||||
viewport_plotarea[1] +
|
||||
leg.leftw - leg.rightw - leg.textw - leg.xoffset * 2
|
||||
end
|
||||
if occursin("top", str)
|
||||
if s == :outertop
|
||||
ypos = viewport_plotarea[4] + leg.yoffset + leg.h + xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
ypos =
|
||||
viewport_plotarea[4] +
|
||||
leg.yoffset +
|
||||
leg.h +
|
||||
xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
else
|
||||
ypos = viewport_plotarea[4] - leg.yoffset - leg.dy
|
||||
end
|
||||
elseif occursin("bottom", str)
|
||||
if s == :outerbottom
|
||||
ypos = viewport_plotarea[3] - leg.yoffset - leg.dy - !xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
ypos =
|
||||
viewport_plotarea[3] - leg.yoffset - leg.dy -
|
||||
!xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
else
|
||||
ypos = viewport_plotarea[3] + leg.yoffset + leg.h
|
||||
end
|
||||
else
|
||||
# Adding min y to shift legend pos to correct graph (#2377)
|
||||
ypos = (viewport_plotarea[4] - viewport_plotarea[3] + leg.h) / 2 + viewport_plotarea[3]
|
||||
ypos =
|
||||
(viewport_plotarea[4] - viewport_plotarea[3] + leg.h) / 2 + viewport_plotarea[3]
|
||||
end
|
||||
return xpos, ypos
|
||||
end
|
||||
@ -1151,7 +1216,8 @@ function gr_legend_pos(theta::Real, leg, viewport_plotarea; axisclearance=nothin
|
||||
ymax = viewport_plotarea[4] - leg.yoffset - leg.dy
|
||||
else
|
||||
# Outer
|
||||
xmin = viewport_plotarea[1] - leg.xoffset - leg.rightw - leg.textw - axisclearance[1]
|
||||
xmin =
|
||||
viewport_plotarea[1] - leg.xoffset - leg.rightw - leg.textw - axisclearance[1]
|
||||
xmax = viewport_plotarea[2] + leg.xoffset + leg.leftw + axisclearance[2]
|
||||
ymin = viewport_plotarea[3] - leg.yoffset - leg.dy - axisclearance[3]
|
||||
ymax = viewport_plotarea[4] + leg.yoffset + leg.h + axisclearance[4]
|
||||
@ -1212,26 +1278,35 @@ function gr_get_legend_geometry(viewport_plotarea, sp)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
## Viewport, window and scale
|
||||
|
||||
function gr_update_viewport_legend!(viewport_plotarea, sp, leg)
|
||||
s = sp[:legend]
|
||||
|
||||
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
|
||||
xmirror = xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
ymirror = yaxis[:guide_position] == :right || (yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
|
||||
xmirror =
|
||||
xaxis[:guide_position] == :top ||
|
||||
(xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
ymirror =
|
||||
yaxis[:guide_position] == :right ||
|
||||
(yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
|
||||
|
||||
if s isa Tuple{<:Real,Symbol}
|
||||
if s[2] === :outer
|
||||
(x, y) = gr_legend_pos(sp, leg, viewport_plotarea) # Dry run, to figure out
|
||||
if x < viewport_plotarea[1]
|
||||
viewport_plotarea[1] += leg.leftw + leg.textw + leg.rightw + leg.xoffset + !ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
viewport_plotarea[1] +=
|
||||
leg.leftw +
|
||||
leg.textw +
|
||||
leg.rightw +
|
||||
leg.xoffset +
|
||||
!ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
elseif x > viewport_plotarea[2]
|
||||
viewport_plotarea[2] -= leg.leftw + leg.textw + leg.rightw + leg.xoffset
|
||||
end
|
||||
if y < viewport_plotarea[3]
|
||||
viewport_plotarea[3] += leg.h + leg.dy + leg.yoffset + !xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
viewport_plotarea[3] +=
|
||||
leg.h + leg.dy + leg.yoffset + !xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
elseif y > viewport_plotarea[4]
|
||||
viewport_plotarea[4] -= leg.h + leg.dy + leg.yoffset
|
||||
end
|
||||
@ -1242,11 +1317,17 @@ function gr_update_viewport_legend!(viewport_plotarea, sp, leg)
|
||||
if occursin("right", leg_str)
|
||||
viewport_plotarea[2] -= leg.leftw + leg.textw + leg.rightw + leg.xoffset
|
||||
elseif occursin("left", leg_str)
|
||||
viewport_plotarea[1] += leg.leftw + leg.textw + leg.rightw + leg.xoffset + !ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
viewport_plotarea[1] +=
|
||||
leg.leftw +
|
||||
leg.textw +
|
||||
leg.rightw +
|
||||
leg.xoffset +
|
||||
!ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
elseif occursin("top", leg_str)
|
||||
viewport_plotarea[4] -= leg.h + leg.dy + leg.yoffset
|
||||
elseif occursin("bottom", leg_str)
|
||||
viewport_plotarea[3] += leg.h + leg.dy + leg.yoffset + !xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
viewport_plotarea[3] +=
|
||||
leg.h + leg.dy + leg.yoffset + !xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
end
|
||||
end
|
||||
if s === :inline
|
||||
@ -1265,16 +1346,22 @@ function gr_update_viewport_ratio!(viewport_plotarea, sp)
|
||||
if ratio == :equal
|
||||
ratio = 1
|
||||
end
|
||||
viewport_ratio = (viewport_plotarea[2] - viewport_plotarea[1]) / (viewport_plotarea[4] - viewport_plotarea[3])
|
||||
viewport_ratio =
|
||||
(viewport_plotarea[2] - viewport_plotarea[1]) /
|
||||
(viewport_plotarea[4] - viewport_plotarea[3])
|
||||
window_ratio = (xmax - xmin) / (ymax - ymin) / ratio
|
||||
if window_ratio < viewport_ratio
|
||||
viewport_center = 0.5 * (viewport_plotarea[1] + viewport_plotarea[2])
|
||||
viewport_size = (viewport_plotarea[2] - viewport_plotarea[1]) * window_ratio / viewport_ratio
|
||||
viewport_size =
|
||||
(viewport_plotarea[2] - viewport_plotarea[1]) * window_ratio /
|
||||
viewport_ratio
|
||||
viewport_plotarea[1] = viewport_center - 0.5 * viewport_size
|
||||
viewport_plotarea[2] = viewport_center + 0.5 * viewport_size
|
||||
elseif window_ratio > viewport_ratio
|
||||
viewport_center = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
|
||||
viewport_size = (viewport_plotarea[4] - viewport_plotarea[3]) * viewport_ratio / window_ratio
|
||||
viewport_size =
|
||||
(viewport_plotarea[4] - viewport_plotarea[3]) * viewport_ratio /
|
||||
window_ratio
|
||||
viewport_plotarea[3] = viewport_center - 0.5 * viewport_size
|
||||
viewport_plotarea[4] = viewport_center + 0.5 * viewport_size
|
||||
end
|
||||
@ -1315,7 +1402,6 @@ function gr_fill_plotarea(sp, viewport_plotarea)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
## Axes
|
||||
|
||||
function gr_draw_axes(sp, viewport_plotarea)
|
||||
@ -1328,7 +1414,10 @@ function gr_draw_axes(sp, viewport_plotarea)
|
||||
|
||||
camera = round.(Int, sp[:camera])
|
||||
|
||||
warn_invalid(val) = if val < 0 || val > 90 @warn "camera: $(val)° ∉ [0°, 90°]" end
|
||||
warn_invalid(val) =
|
||||
if val < 0 || val > 90
|
||||
@warn "camera: $(val)° ∉ [0°, 90°]"
|
||||
end
|
||||
warn_invalid.(camera)
|
||||
|
||||
GR.setspace(zmin, zmax, camera...)
|
||||
@ -1338,7 +1427,8 @@ function gr_draw_axes(sp, viewport_plotarea)
|
||||
plot_area_x = [xmin, xmin, xmin, xmax, xmax, xmax, xmin]
|
||||
plot_area_y = [ymin, ymin, ymax, ymax, ymax, ymin, ymin]
|
||||
plot_area_z = [zmin, zmax, zmax, zmax, zmin, zmin, zmin]
|
||||
x_bg, y_bg = RecipesPipeline.unzip(GR.wc3towc.(plot_area_x, plot_area_y, plot_area_z))
|
||||
x_bg, y_bg =
|
||||
RecipesPipeline.unzip(GR.wc3towc.(plot_area_x, plot_area_y, plot_area_z))
|
||||
GR.fillarea(x_bg, y_bg)
|
||||
|
||||
for letter in (:x, :y, :z)
|
||||
@ -1396,7 +1486,7 @@ function gr_draw_grid(sp, axis, segments, func = gr_polyline)
|
||||
axis[:gridlinewidth],
|
||||
axis[:gridstyle],
|
||||
axis[:foreground_color_grid],
|
||||
sp
|
||||
sp,
|
||||
)
|
||||
gr_set_transparency(axis[:foreground_color_grid], axis[:gridalpha])
|
||||
func(coords(segments)...)
|
||||
@ -1409,7 +1499,7 @@ function gr_draw_minorgrid(sp, axis, segments, func = gr_polyline)
|
||||
axis[:minorgridlinewidth],
|
||||
axis[:minorgridstyle],
|
||||
axis[:foreground_color_minor_grid],
|
||||
sp
|
||||
sp,
|
||||
)
|
||||
gr_set_transparency(axis[:foreground_color_minor_grid], axis[:minorgridalpha])
|
||||
func(coords(segments)...)
|
||||
@ -1488,7 +1578,6 @@ function gr_label_ticks_3d(sp, letter, ticks)
|
||||
famin, famax = axis_limits(sp, far_letter)
|
||||
n0, n1 = letter === :y ? (namax, namin) : (namin, namax)
|
||||
|
||||
|
||||
# find out which axes we are dealing with
|
||||
i = findfirst(==(letter), (:x, :y, :z))
|
||||
letters = axes_shift((:x, :y, :z), 1 - i)
|
||||
@ -1538,7 +1627,7 @@ function gr_label_axis(sp, letter, viewport_plotarea)
|
||||
guide_position = axis[:guide_position]
|
||||
angle = float(axis[:guidefontrotation]) # github.com/JuliaPlots/Plots.jl/issues/3089
|
||||
if letter === :y
|
||||
angle += 180. # default angle = 0. should yield GR.setcharup(-1, 0) i.e. 180°
|
||||
angle += 180.0 # default angle = 0. should yield GR.setcharup(-1, 0) i.e. 180°
|
||||
GR.setcharup(cosd(angle), sind(angle))
|
||||
ypos = gr_view_yposition(viewport_plotarea, position(axis[:guidefontvalign]))
|
||||
yalign = alignment(axis[:guidefontvalign])
|
||||
@ -1550,16 +1639,21 @@ function gr_label_axis(sp, letter, viewport_plotarea)
|
||||
xpos = viewport_plotarea[1] - 0.03 - !mirror * gr_axis_width(sp, axis)
|
||||
end
|
||||
else
|
||||
angle += 90. # default angle = 0. should yield GR.setcharup(0, 1) i.e. 90°
|
||||
angle += 90.0 # default angle = 0. should yield GR.setcharup(0, 1) i.e. 90°
|
||||
GR.setcharup(cosd(angle), sind(angle))
|
||||
xpos = gr_view_xposition(viewport_plotarea, position(axis[:guidefonthalign]))
|
||||
xalign = alignment(axis[:guidefonthalign])
|
||||
if guide_position === :top || (guide_position == :auto && mirror)
|
||||
GR.settextalign(xalign, GR.TEXT_VALIGN_TOP)
|
||||
ypos = viewport_plotarea[4] + 0.015 + (mirror ? gr_axis_height(sp, axis) : 0.015)
|
||||
ypos =
|
||||
viewport_plotarea[4] +
|
||||
0.015 +
|
||||
(mirror ? gr_axis_height(sp, axis) : 0.015)
|
||||
else
|
||||
GR.settextalign(xalign, GR.TEXT_VALIGN_BOTTOM)
|
||||
ypos = viewport_plotarea[3] - 0.015 - (mirror ? 0.015 : gr_axis_height(sp, axis))
|
||||
ypos =
|
||||
viewport_plotarea[3] - 0.015 -
|
||||
(mirror ? 0.015 : gr_axis_height(sp, axis))
|
||||
end
|
||||
end
|
||||
gr_text(xpos, ypos, axis[:guide])
|
||||
@ -1630,7 +1724,6 @@ function gr_add_title(sp, viewport_plotarea, viewport_subplot)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
## Series
|
||||
|
||||
function gr_add_series(sp, series)
|
||||
@ -1743,9 +1836,7 @@ function gr_draw_segments(series, x, y, fillrange, clims)
|
||||
for segment in segments
|
||||
i, rng = segment.attr_index, segment.range
|
||||
lc = get_linecolor(series, clims, i)
|
||||
gr_set_line(
|
||||
get_linewidth(series, i), get_linestyle(series, i), lc, series
|
||||
)
|
||||
gr_set_line(get_linewidth(series, i), get_linestyle(series, i), lc, series)
|
||||
arrowside = isa(series[:arrow], Arrow) ? series[:arrow].side : :none
|
||||
arrowstyle = isa(series[:arrow], Arrow) ? series[:arrow].style : :simple
|
||||
gr_set_fillcolor(lc)
|
||||
@ -1763,9 +1854,7 @@ function gr_draw_segments_3d(series, x, y, z, clims)
|
||||
for segment in segments
|
||||
i, rng = segment.attr_index, segment.range
|
||||
lc = get_linecolor(series, clims, i)
|
||||
gr_set_line(
|
||||
get_linewidth(series, i), get_linestyle(series, i), lc, series
|
||||
)
|
||||
gr_set_line(get_linewidth(series, i), get_linestyle(series, i), lc, series)
|
||||
gr_set_transparency(lc, get_linealpha(series, i))
|
||||
GR.polyline3d(x[rng], y[rng], z[rng])
|
||||
end
|
||||
@ -1780,7 +1869,6 @@ function gr_draw_markers(
|
||||
msize = series[:markersize],
|
||||
strokewidth = series[:markerstrokewidth],
|
||||
)
|
||||
|
||||
isempty(x) && return
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
|
||||
@ -1794,7 +1882,16 @@ function gr_draw_markers(
|
||||
msw = get_thickness_scaling(series) * _cycle(strokewidth, i)
|
||||
shape = _cycle(shapes, i)
|
||||
for j in rng
|
||||
gr_draw_marker(series, _cycle(x, j), _cycle(y, j), clims, i, ms, msw, shape)
|
||||
gr_draw_marker(
|
||||
series,
|
||||
_cycle(x, j),
|
||||
_cycle(y, j),
|
||||
clims,
|
||||
i,
|
||||
ms,
|
||||
msw,
|
||||
shape,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1869,7 +1966,12 @@ function gr_draw_surface(series, x, y, z, clims)
|
||||
GR.surface(x, y, z, get(e_kwargs, :display_option, GR.OPTION_FILLED_MESH))
|
||||
elseif st === :mesh3d
|
||||
@warn "GR: mesh3d is experimental (no face colors)"
|
||||
gr_set_line(get_linewidth(series), get_linestyle(series), get_linecolor(series), series)
|
||||
gr_set_line(
|
||||
get_linewidth(series),
|
||||
get_linestyle(series),
|
||||
get_linecolor(series),
|
||||
series,
|
||||
)
|
||||
GR.polyline3d(mesh3d_triangles(x, y, z, series[:connections])...)
|
||||
else
|
||||
throw(ArgumentError("Not handled !"))
|
||||
@ -1916,7 +2018,6 @@ function gr_draw_image(series, x, y, z, clims)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
for (mime, fmt) in (
|
||||
@ -1953,7 +2054,11 @@ function _display(plt::Plot{GRBackend})
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
GR.emergencyclosegks()
|
||||
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(read, filepath)), "\a")
|
||||
content = string(
|
||||
"\033]1337;File=inline=1;preserveAspectRatio=0:",
|
||||
base64encode(open(read, filepath)),
|
||||
"\a",
|
||||
)
|
||||
println(content)
|
||||
rm(filepath)
|
||||
else
|
||||
|
||||
@ -13,7 +13,6 @@ Read from .hdf5 file using:
|
||||
display(pread)
|
||||
==#
|
||||
|
||||
|
||||
#==TODO
|
||||
===============================================================================
|
||||
1. Support more features.
|
||||
@ -56,7 +55,8 @@ import ..HDF5PLOT_MAP_STR2TELEM, ..HDF5PLOT_MAP_TELEM2STR
|
||||
import ..HDF5Plot_PlotRef, ..HDF5PLOT_PLOTREF
|
||||
import ..BoundingBox, ..Extrema, ..Length
|
||||
import ..RecipesPipeline.datetimeformatter
|
||||
import ..PlotUtils.ColorPalette, ..PlotUtils.CategoricalColorGradient, ..PlotUtils.ContinuousColorGradient
|
||||
import ..PlotUtils.ColorPalette,
|
||||
..PlotUtils.CategoricalColorGradient, ..PlotUtils.ContinuousColorGradient
|
||||
import ..Surface, ..Shape, ..Arrow
|
||||
import ..GridLayout, ..RootLayout
|
||||
import ..Font, ..PlotText, ..SeriesAnnotations
|
||||
@ -70,10 +70,9 @@ const HDF5_SupportedTypes = Union{Number, String}
|
||||
#TODO: Types_HDF5Support
|
||||
|
||||
#Dispatch types:
|
||||
struct CplxTuple; end #Identifies a "complex" tuple structure (not merely numbers)
|
||||
struct CplxTuple end #Identifies a "complex" tuple structure (not merely numbers)
|
||||
#HDF5 reader will auto-detect type correctly:
|
||||
struct HDF5_AutoDetect; end #See HDF5_SupportedTypes
|
||||
|
||||
struct HDF5_AutoDetect end #See HDF5_SupportedTypes
|
||||
|
||||
#==
|
||||
===============================================================================#
|
||||
@ -87,8 +86,7 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
"SYMBOL" => Symbol,
|
||||
"RGBA" => Colorant, #Write out any Colorant to an #RRGGBBAA string
|
||||
"TUPLE" => Tuple,
|
||||
"CTUPLE" => CplxTuple, #Tuple of complex structures
|
||||
|
||||
"CTUPLE" => CplxTuple,
|
||||
"EXTREMA" => Extrema,
|
||||
"LENGTH" => Length,
|
||||
"ARRAY" => Array, #Array{Any} (because Array{T<:Union{Number, String}} natively supported by HDF5)
|
||||
@ -115,10 +113,12 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
"SUBPLOT" => Subplot,
|
||||
)
|
||||
merge!(HDF5PLOT_MAP_STR2TELEM, _telem2str) #Faster to create than push!()??
|
||||
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
|
||||
merge!(
|
||||
HDF5PLOT_MAP_TELEM2STR,
|
||||
Dict{Type,String}(v => k for (k, v) in HDF5PLOT_MAP_STR2TELEM),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
#==Helper functions
|
||||
===============================================================================#
|
||||
|
||||
@ -151,15 +151,14 @@ function _hdf5_merge!(dest::AKW, src::AKW)
|
||||
end
|
||||
|
||||
#_type_for_map returns the type to use with HDF5PLOT_MAP_TELEM2STR[], in case it is not concrete:
|
||||
_type_for_map(::Type{T}) where T = T #Catch-all
|
||||
_type_for_map(::Type{T}) where T<:BoundingBox = BoundingBox
|
||||
_type_for_map(::Type{T}) where T<:ColorScheme = ColorScheme
|
||||
_type_for_map(::Type{T}) where T<:Surface = Surface
|
||||
|
||||
_type_for_map(::Type{T}) where {T} = T #Catch-all
|
||||
_type_for_map(::Type{T}) where {T<:BoundingBox} = BoundingBox
|
||||
_type_for_map(::Type{T}) where {T<:ColorScheme} = ColorScheme
|
||||
_type_for_map(::Type{T}) where {T<:Surface} = Surface
|
||||
|
||||
#==Read/write things like type name in attributes
|
||||
===============================================================================#
|
||||
function _write_datatype_attr(ds::Union{Group, Dataset}, ::Type{T}) where T
|
||||
function _write_datatype_attr(ds::Union{Group,Dataset}, ::Type{T}) where {T}
|
||||
typestr = HDF5PLOT_MAP_TELEM2STR[T]
|
||||
HDF5.attributes(ds)["TYPE"] = typestr
|
||||
end
|
||||
@ -173,7 +172,7 @@ function _read_datatype_attr(ds::Union{Group, Dataset})
|
||||
end
|
||||
|
||||
#Type parameter attributes:
|
||||
function _write_typeparam_attr(ds::Dataset, v::Length{T}) where T
|
||||
function _write_typeparam_attr(ds::Dataset, v::Length{T}) where {T}
|
||||
HDF5.attributes(ds)["TYPEPARAM"] = string(T) #Need to add units for Length
|
||||
end
|
||||
_read_typeparam_attr(ds::Dataset) = HDF5.read(HDF5.attributes(ds)["TYPEPARAM"])
|
||||
@ -186,8 +185,8 @@ _read_length_attr(::Type{Vector}, grp::Group) = HDF5.read(HDF5.attributes(grp)["
|
||||
function _write_size_attr(grp::Group, v::Array) #of an array
|
||||
HDF5.attributes(grp)["SIZE"] = [size(v)...]
|
||||
end
|
||||
_read_size_attr(::Type{Array}, grp::Group) = tuple(HDF5.read(HDF5.attributes(grp)["SIZE"])...)
|
||||
|
||||
_read_size_attr(::Type{Array}, grp::Group) =
|
||||
tuple(HDF5.read(HDF5.attributes(grp)["SIZE"])...)
|
||||
|
||||
#==_write_typed(): Simple (leaf) datatypes. (Labels with type name.)
|
||||
===============================================================================#
|
||||
@ -230,7 +229,7 @@ function _write_typed(grp::Group, name::String, v::typeof(datetimeformatter))
|
||||
grp[name] = string(v) #Just write something that helps reader
|
||||
_write_datatype_attr(grp[name], typeof(datetimeformatter))
|
||||
end
|
||||
function _write_typed(grp::Group, name::String, v::Array{T}) where T<:Number #Default for arrays
|
||||
function _write_typed(grp::Group, name::String, v::Array{T}) where {T<:Number} #Default for arrays
|
||||
grp[name] = v
|
||||
return #No need to _write_datatype_attr
|
||||
end
|
||||
@ -238,8 +237,6 @@ function _write_typed(grp::Group, name::String, v::AbstractRange)
|
||||
_write_typed(grp, name, collect(v)) #For now
|
||||
end
|
||||
|
||||
|
||||
|
||||
#== Helper functions for writing complex data structures
|
||||
===============================================================================#
|
||||
|
||||
@ -270,7 +267,7 @@ function _write(grp::Group, name::String, d::AbstractDict)
|
||||
end
|
||||
|
||||
#Write out arbitrary `struct`s:
|
||||
function _writestructgeneric(grp::Group, obj::T) where T
|
||||
function _writestructgeneric(grp::Group, obj::T) where {T}
|
||||
for fname in fieldnames(T)
|
||||
v = getfield(obj, fname)
|
||||
_write_typed(grp, String(fname), v)
|
||||
@ -278,12 +275,11 @@ function _writestructgeneric(grp::Group, obj::T) where T
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
#==_write_typed(): More complex structures. (Labels with type name.)
|
||||
===============================================================================#
|
||||
|
||||
#Catch-all (default behaviour for `struct`s):
|
||||
function _write_typed(grp::Group, name::String, v::T) where T
|
||||
function _write_typed(grp::Group, name::String, v::T) where {T}
|
||||
#NOTE: need "name" parameter so that call signature is same with built-ins
|
||||
MT = _type_for_map(T)
|
||||
try #Check to see if type is supported
|
||||
@ -299,12 +295,12 @@ function _write_typed(grp::Group, name::String, v::T) where T
|
||||
_writestructgeneric(objgrp, v)
|
||||
end
|
||||
|
||||
function _write_typed(grp::Group, name::String, v::Array{T}) where T
|
||||
function _write_typed(grp::Group, name::String, v::Array{T}) where {T}
|
||||
_write_harray(grp, name, v)
|
||||
_write_datatype_attr(grp[name], Array) #{Any}
|
||||
end
|
||||
|
||||
function _write_typed(grp::Group, name::String, v::Tuple, ::Type{ELT}) where ELT<: Number #Basic Tuple
|
||||
function _write_typed(grp::Group, name::String, v::Tuple, ::Type{ELT}) where {ELT<:Number} #Basic Tuple
|
||||
_write_typed(grp, name, [v...])
|
||||
_write_datatype_attr(grp[name], Tuple)
|
||||
end
|
||||
@ -347,7 +343,6 @@ function _write_typed(grp::Group, name::String, v::Plot)
|
||||
#Don't write plot references
|
||||
end
|
||||
|
||||
|
||||
#==_write(): Write out more complex structures
|
||||
NOTE: No need to write out type information (inferred from hierarchy)
|
||||
===============================================================================#
|
||||
@ -378,7 +373,11 @@ function _write(grp::Group, plt::Plot{HDF5Backend})
|
||||
return
|
||||
end
|
||||
|
||||
function hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString; name::String="_unnamed")
|
||||
function hdf5plot_write(
|
||||
plt::Plot{HDF5Backend},
|
||||
path::AbstractString;
|
||||
name::String = "_unnamed",
|
||||
)
|
||||
HDF5.h5open(path, "w") do file
|
||||
HDF5.write_dataset(file, "VERSION_INFO", _get_Plots_versionstr())
|
||||
grp = HDF5.create_group(file, h5plotpath(name))
|
||||
@ -386,7 +385,6 @@ function hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString; name::Stri
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#== _read(): Read data, but not type information.
|
||||
===============================================================================#
|
||||
|
||||
@ -417,7 +415,6 @@ function _read(::Type{Length}, ds::Dataset)
|
||||
end
|
||||
_read(::Type{typeof(datetimeformatter)}, ds::Dataset) = datetimeformatter
|
||||
|
||||
|
||||
#== Helper functions for reading in complex data structures
|
||||
===============================================================================#
|
||||
|
||||
@ -429,7 +426,7 @@ function _read_typed(grp::Group, name::String)
|
||||
end
|
||||
|
||||
#_readstructgeneric: Needs object values to be written out with _write_typed():
|
||||
function _readstructgeneric(::Type{T}, grp::Group) where T
|
||||
function _readstructgeneric(::Type{T}, grp::Group) where {T}
|
||||
vlist = Array{Any}(nothing, fieldcount(T))
|
||||
for (i, fname) in enumerate(fieldnames(T))
|
||||
vlist[i] = _read_typed(grp, String(fname))
|
||||
@ -454,7 +451,6 @@ function _read(::Type{KW}, grp::Group)
|
||||
return d
|
||||
end
|
||||
|
||||
|
||||
#== _read(): More complex structures.
|
||||
===============================================================================#
|
||||
|
||||
@ -463,7 +459,9 @@ _read(T::Type, grp::Group) = _readstructgeneric(T, grp)
|
||||
|
||||
function _read(::Type{Array}, grp::Group) #Array{Any}
|
||||
sz = _read_size_attr(Array, grp)
|
||||
if tuple(0) == sz; return []; end
|
||||
if tuple(0) == sz
|
||||
return []
|
||||
end
|
||||
result = Array{Any}(undef, sz)
|
||||
lidx = LinearIndices(sz)
|
||||
|
||||
@ -498,7 +496,9 @@ function _read(::Type{DefaultsDict}, grp::Group)
|
||||
#User should set DefaultsDict.defaults to one of:
|
||||
# _plot_defaults, _subplot_defaults, _axis_defaults, _series_defaults
|
||||
path = HDF5.name(ds)
|
||||
@warn("Cannot yet read DefaultsDict using _read_typed():\n $path\nCannot fully reconstruct plot.")
|
||||
@warn(
|
||||
"Cannot yet read DefaultsDict using _read_typed():\n $path\nCannot fully reconstruct plot."
|
||||
)
|
||||
end
|
||||
function _read(::Type{Axis}, grp::Group)
|
||||
#1st arg appears to be ref to subplots. Seems to work without it.
|
||||
@ -510,7 +510,6 @@ function _read(::Type{Subplot}, grp::Group)
|
||||
return HDF5PLOT_PLOTREF.ref.subplots[idx]
|
||||
end
|
||||
|
||||
|
||||
#== _read(): Main plot structures
|
||||
===============================================================================#
|
||||
|
||||
@ -559,10 +558,8 @@ function hdf5plot_read(path::AbstractString; name::String="_unnamed")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end #module _hdf5_implementation
|
||||
|
||||
|
||||
#==Implement Plots.jl backend interface for HDF5Backend
|
||||
===============================================================================#
|
||||
|
||||
@ -640,7 +637,8 @@ end
|
||||
#==Interface actually required to use HDF5Backend
|
||||
===============================================================================#
|
||||
|
||||
hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString) = _hdf5_implementation.hdf5plot_write(plt, path)
|
||||
hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString) =
|
||||
_hdf5_implementation.hdf5plot_write(plt, path)
|
||||
hdf5plot_write(path::AbstractString) = _hdf5_implementation.hdf5plot_write(current(), path)
|
||||
hdf5plot_read(path::AbstractString) = _hdf5_implementation.hdf5plot_read(path)
|
||||
|
||||
|
||||
@ -64,11 +64,18 @@ function _inspectdr_add_annotations(plot, x, y, val::PlotText)
|
||||
vmap = Dict{Symbol,Symbol}(:top => :t, :bottom => :b) #:vcenter
|
||||
hmap = Dict{Symbol,Symbol}(:left => :l, :right => :r) #:hcenter
|
||||
align = Symbol(get(vmap, val.font.valign, :c), get(hmap, val.font.halign, :c))
|
||||
fnt = InspectDR.Font(val.font.family, val.font.pointsize,
|
||||
color =_inspectdr_mapcolor(val.font.color)
|
||||
fnt = InspectDR.Font(
|
||||
val.font.family,
|
||||
val.font.pointsize,
|
||||
color = _inspectdr_mapcolor(val.font.color),
|
||||
)
|
||||
ann = InspectDR.atext(val.str, x=x, y=y,
|
||||
font=fnt, angle=val.font.rotation, align=align
|
||||
ann = InspectDR.atext(
|
||||
val.str,
|
||||
x = x,
|
||||
y = y,
|
||||
font = fnt,
|
||||
angle = val.font.rotation,
|
||||
align = align,
|
||||
)
|
||||
InspectDR.add(plot, ann)
|
||||
return
|
||||
@ -84,7 +91,9 @@ function _inspectdr_getaxisticks(ticks, gridlines, xfrm)
|
||||
if ticks == :native
|
||||
#keep current
|
||||
elseif ttype == :ticks_and_labels
|
||||
pos = ticks[1]; labels = ticks[2]; nticks = length(ticks[1])
|
||||
pos = ticks[1]
|
||||
labels = ticks[2]
|
||||
nticks = length(ticks[1])
|
||||
newticks = TickCustom[TickCustom(_xfrm(pos[i]), labels[i]) for i in 1:nticks]
|
||||
gridlines = InspectDR.GridLinesCustom(gridlines)
|
||||
gridlines.major = newticks
|
||||
@ -145,9 +154,8 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
#Glyph used when plotting "Shape"s:
|
||||
INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
|
||||
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
|
||||
)
|
||||
INSPECTDR_GLYPH_SHAPE =
|
||||
InspectDR.GlyphPolyline(2 * InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y)
|
||||
|
||||
mutable struct InspecDRPlotRef
|
||||
mplot::Union{Nothing,InspectDR.Multiplot}
|
||||
@ -200,7 +208,9 @@ end
|
||||
function _initialize_subplot(plt::Plot{InspectDRBackend}, sp::Subplot{InspectDRBackend})
|
||||
plot = sp.o
|
||||
#Don't do anything without a "subplot" object: Will process later.
|
||||
if nothing == plot; return; end
|
||||
if nothing == plot
|
||||
return
|
||||
end
|
||||
plot.data = []
|
||||
plot.userannot = [] #Clear old markers/text annotation/polyline "annotation"
|
||||
return plot
|
||||
@ -219,7 +229,9 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series)
|
||||
clims = get_clims(sp, series)
|
||||
|
||||
#Don't do anything without a "subplot" object: Will process later.
|
||||
if nothing == plot; return; end
|
||||
if nothing == plot
|
||||
return
|
||||
end
|
||||
|
||||
_vectorize(v) = isa(v, Vector) ? v : collect(v) #InspectDR only supports vectors
|
||||
x, y = if st == :straightline
|
||||
@ -230,16 +242,19 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series)
|
||||
|
||||
#No support for polar grid... but can still perform polar transformation:
|
||||
if ispolar(sp)
|
||||
Θ = x; r = y
|
||||
x = r.*cos.(Θ); y = r.*sin.(Θ)
|
||||
Θ = x
|
||||
r = y
|
||||
x = r .* cos.(Θ)
|
||||
y = r .* sin.(Θ)
|
||||
end
|
||||
|
||||
# doesn't handle mismatched x/y - wrap data (pyplot behaviour):
|
||||
nx = length(x); ny = length(y)
|
||||
nx = length(x)
|
||||
ny = length(y)
|
||||
if nx < ny
|
||||
series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
|
||||
series[:x] = Float64[x[mod1(i, nx)] for i in 1:ny]
|
||||
elseif ny > nx
|
||||
series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
|
||||
series[:y] = Float64[y[mod1(i, ny)] for i in 1:nx]
|
||||
end
|
||||
|
||||
#= TODO: Eventually support
|
||||
@ -261,11 +276,12 @@ For st in :shape:
|
||||
linecolor = _inspectdr_mapcolor(_cycle(c, i))
|
||||
c = plot_color(get_fillcolor(series), get_fillalpha(series))
|
||||
fillcolor = _inspectdr_mapcolor(_cycle(c, i))
|
||||
line = InspectDR.line(
|
||||
style=:solid, width=linewidth, color=linecolor
|
||||
)
|
||||
line = InspectDR.line(style = :solid, width = linewidth, color = linecolor)
|
||||
apline = InspectDR.PolylineAnnotation(
|
||||
x[rng], y[rng], line=line, fillcolor=fillcolor
|
||||
x[rng],
|
||||
y[rng],
|
||||
line = line,
|
||||
fillcolor = fillcolor,
|
||||
)
|
||||
InspectDR.add(plot, apline)
|
||||
end
|
||||
@ -280,11 +296,14 @@ For st in :shape:
|
||||
fillcolor = _inspectdr_mapcolor(_cycle(c, i))
|
||||
wfrm = InspectDR.add(plot, Float64[], Float64[], id = series[:label])
|
||||
wfrm.line = InspectDR.line(
|
||||
style=:none, width=linewidth, #linewidth affects glyph
|
||||
style = :none,
|
||||
width = linewidth, #linewidth affects glyph
|
||||
)
|
||||
wfrm.glyph = InspectDR.glyph(
|
||||
shape = INSPECTDR_GLYPH_SHAPE, size = 8,
|
||||
color = linecolor, fillcolor = fillcolor
|
||||
shape = INSPECTDR_GLYPH_SHAPE,
|
||||
size = 8,
|
||||
color = linecolor,
|
||||
fillcolor = fillcolor,
|
||||
)
|
||||
end
|
||||
elseif st in (:path, :scatter, :straightline) #, :steppre, :stepmid, :steppost)
|
||||
@ -306,8 +325,12 @@ For st in :shape:
|
||||
wfrm.glyph = InspectDR.glyph(
|
||||
shape = _inspectdr_mapglyph(series[:markershape]),
|
||||
size = _inspectdr_mapglyphsize(series[:markersize]),
|
||||
color = _inspectdr_mapcolor(plot_color(get_markerstrokecolor(series), get_markerstrokealpha(series))),
|
||||
fillcolor = _inspectdr_mapcolor(plot_color(get_markercolor(series, clims), get_markeralpha(series))),
|
||||
color = _inspectdr_mapcolor(
|
||||
plot_color(get_markerstrokecolor(series), get_markerstrokealpha(series)),
|
||||
),
|
||||
fillcolor = _inspectdr_mapcolor(
|
||||
plot_color(get_markercolor(series, clims), get_markeralpha(series)),
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
@ -333,7 +356,8 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
plot = sp.o
|
||||
strip = plot.strips[1] #Only 1 strip supported with Plots.jl
|
||||
|
||||
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
|
||||
xaxis = sp[:xaxis]
|
||||
yaxis = sp[:yaxis]
|
||||
xgrid_show = xaxis[:grid]
|
||||
ygrid_show = yaxis[:grid]
|
||||
|
||||
@ -361,7 +385,8 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
|
||||
a = plot.annotation
|
||||
a.title = sp[:title]
|
||||
a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]]
|
||||
a.xlabel = xaxis[:guide]
|
||||
a.ylabels = [yaxis[:guide]]
|
||||
|
||||
#Modify base layout of new object:
|
||||
l = plot.layout.defaults = deepcopy(InspectDR.defaults.plotlayout)
|
||||
@ -370,24 +395,28 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
l.frame_canvas.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
|
||||
l.frame_data.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
|
||||
l.frame_data.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
|
||||
l.font_title = InspectDR.Font(sp[:titlefontfamily],
|
||||
l.font_title = InspectDR.Font(
|
||||
sp[:titlefontfamily],
|
||||
_inspectdr_mapptsize(sp[:titlefontsize]),
|
||||
color = _inspectdr_mapcolor(sp[:titlefontcolor])
|
||||
color = _inspectdr_mapcolor(sp[:titlefontcolor]),
|
||||
)
|
||||
#Cannot independently control fonts of axes with InspectDR:
|
||||
l.font_axislabel = InspectDR.Font(xaxis[:guidefontfamily],
|
||||
l.font_axislabel = InspectDR.Font(
|
||||
xaxis[:guidefontfamily],
|
||||
_inspectdr_mapptsize(xaxis[:guidefontsize]),
|
||||
color = _inspectdr_mapcolor(xaxis[:guidefontcolor])
|
||||
color = _inspectdr_mapcolor(xaxis[:guidefontcolor]),
|
||||
)
|
||||
l.font_ticklabel = InspectDR.Font(xaxis[:tickfontfamily],
|
||||
l.font_ticklabel = InspectDR.Font(
|
||||
xaxis[:tickfontfamily],
|
||||
_inspectdr_mapptsize(xaxis[:tickfontsize]),
|
||||
color = _inspectdr_mapcolor(xaxis[:tickfontcolor])
|
||||
color = _inspectdr_mapcolor(xaxis[:tickfontcolor]),
|
||||
)
|
||||
l.enable_legend = (sp[:legend] != :none)
|
||||
#l.halloc_legend = 150 #TODO: compute???
|
||||
l.font_legend = InspectDR.Font(sp[:legendfontfamily],
|
||||
l.font_legend = InspectDR.Font(
|
||||
sp[:legendfontfamily],
|
||||
_inspectdr_mapptsize(sp[:legendfontsize]),
|
||||
color = _inspectdr_mapcolor(sp[:legendfontcolor])
|
||||
color = _inspectdr_mapcolor(sp[:legendfontcolor]),
|
||||
)
|
||||
l.frame_legend.fillcolor = _inspectdr_mapcolor(sp[:background_color_legend])
|
||||
|
||||
@ -400,7 +429,9 @@ end
|
||||
# for the calcs
|
||||
function _before_layout_calcs(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
if nothing == mplot
|
||||
return
|
||||
end
|
||||
|
||||
mplot.title = plt[:plot_title]
|
||||
if "" == mplot.title
|
||||
@ -453,7 +484,9 @@ end
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot{InspectDRBackend})
|
||||
plot = sp.o
|
||||
if !isa(plot, InspectDR.Plot2D); return sp.minpad; end
|
||||
if !isa(plot, InspectDR.Plot2D)
|
||||
return sp.minpad
|
||||
end
|
||||
#Computing plotbounds with 0-BoundingBox returns required padding:
|
||||
bb = InspectDR.plotbounds(plot.layout.values, InspectDR.BoundingBox(0, 0, 0, 0))
|
||||
#NOTE: plotbounds always pads for titles, legends, etc. even if not in use.
|
||||
@ -472,7 +505,9 @@ end
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
|
||||
function _update_plot_object(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
if nothing == mplot
|
||||
return
|
||||
end
|
||||
mplot.bblist = InspectDR.BoundingBox[]
|
||||
|
||||
for (i, sp) in enumerate(plt.subplots)
|
||||
@ -486,7 +521,9 @@ function _update_plot_object(plt::Plot{InspectDRBackend})
|
||||
end
|
||||
|
||||
gplot = _inspectdr_getgui(plt.o)
|
||||
if nothing == gplot; return; end
|
||||
if nothing == gplot
|
||||
return
|
||||
end
|
||||
|
||||
gplot.src = mplot #Ensure still references current plot
|
||||
InspectDR.refresh(gplot)
|
||||
@ -522,7 +559,9 @@ end
|
||||
# Display/show the plot (open a GUI window, or browser page, for example).
|
||||
function _display(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
if nothing == mplot
|
||||
return
|
||||
end
|
||||
gplot = _inspectdr_getgui(plt.o)
|
||||
|
||||
if nothing == gplot
|
||||
|
||||
@ -8,23 +8,11 @@ Base.@kwdef mutable struct PGFPlotsXPlot
|
||||
function PGFPlotsXPlot(is_created, was_shown, the_plot)
|
||||
pgfx_plot = new(is_created, was_shown, the_plot)
|
||||
# tikz libraries
|
||||
PGFPlotsX.push_preamble!(
|
||||
pgfx_plot.the_plot,
|
||||
"\\usetikzlibrary{arrows.meta}",
|
||||
)
|
||||
PGFPlotsX.push_preamble!(
|
||||
pgfx_plot.the_plot,
|
||||
"\\usetikzlibrary{backgrounds}",
|
||||
)
|
||||
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{arrows.meta}")
|
||||
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{backgrounds}")
|
||||
# pgfplots libraries
|
||||
PGFPlotsX.push_preamble!(
|
||||
pgfx_plot.the_plot,
|
||||
"\\usepgfplotslibrary{patchplots}",
|
||||
)
|
||||
PGFPlotsX.push_preamble!(
|
||||
pgfx_plot.the_plot,
|
||||
"\\usepgfplotslibrary{fillbetween}",
|
||||
)
|
||||
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{patchplots}")
|
||||
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{fillbetween}")
|
||||
# compatibility fixes
|
||||
# add background layer to standard layers
|
||||
PGFPlotsX.push_preamble!(
|
||||
@ -101,8 +89,9 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
extra_plot = wraptuple(extra_plot)
|
||||
push!(the_plot, extra_plot...)
|
||||
end
|
||||
bgc = plt.attr[:background_color_outside] == :match ?
|
||||
plt.attr[:background_color] : plt.attr[:background_color_outside]
|
||||
bgc =
|
||||
plt.attr[:background_color_outside] == :match ? plt.attr[:background_color] :
|
||||
plt.attr[:background_color_outside]
|
||||
if bgc isa Colors.Colorant
|
||||
cstr = plot_color(bgc)
|
||||
a = alpha(cstr)
|
||||
@ -125,9 +114,14 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
if sp[:subplot_index] == plt[:plot_titleindex]
|
||||
x = dx + sp_width / 2 - 10mm # FIXME: get rid of magic constant
|
||||
y = dy + sp_height / 2
|
||||
pgfx_add_annotation!(the_plot, x, y, PlotText(plt[:plot_title], plottitlefont(plt)), pgfx_thickness_scaling(plt);
|
||||
pgfx_add_annotation!(
|
||||
the_plot,
|
||||
x,
|
||||
y,
|
||||
PlotText(plt[:plot_title], plottitlefont(plt)),
|
||||
pgfx_thickness_scaling(plt);
|
||||
cs = "",
|
||||
options = PGFPlotsX.Options("anchor" => "center")
|
||||
options = PGFPlotsX.Options("anchor" => "center"),
|
||||
)
|
||||
continue
|
||||
end
|
||||
@ -153,28 +147,21 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
"title" => sp[:title],
|
||||
"title style" => PGFPlotsX.Options(
|
||||
pgfx_get_title_pos(title_loc)...,
|
||||
"font" => pgfx_font(
|
||||
sp[:titlefontsize],
|
||||
pgfx_thickness_scaling(sp),
|
||||
),
|
||||
"font" => pgfx_font(sp[:titlefontsize], pgfx_thickness_scaling(sp)),
|
||||
"color" => title_cstr,
|
||||
"draw opacity" => title_a,
|
||||
"rotate" => sp[:titlefontrotation],
|
||||
),
|
||||
"legend style" => pgfx_get_legend_style(sp),
|
||||
"axis background/.style" => PGFPlotsX.Options(
|
||||
"fill" => bgc_inside,
|
||||
"opacity" => bgc_inside_a,
|
||||
),
|
||||
"axis background/.style" =>
|
||||
PGFPlotsX.Options("fill" => bgc_inside, "opacity" => bgc_inside_a),
|
||||
# These are for layouting
|
||||
"anchor" => "north west",
|
||||
"xshift" => string(dx),
|
||||
"yshift" => string(-dy),
|
||||
)
|
||||
sp_width > 0 * mm ? push!(axis_opt, "width" => string(axis_width)) :
|
||||
nothing
|
||||
sp_height > 0 * mm ? push!(axis_opt, "height" => string(axis_height)) :
|
||||
nothing
|
||||
sp_width > 0 * mm ? push!(axis_opt, "width" => string(axis_width)) : nothing
|
||||
sp_height > 0 * mm ? push!(axis_opt, "height" => string(axis_height)) : nothing
|
||||
for letter in (:x, :y, :z)
|
||||
if letter != :z || RecipesPipeline.is3d(sp)
|
||||
pgfx_axis!(axis_opt, sp, letter)
|
||||
@ -216,11 +203,10 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
|
||||
if hascolorbar(sp)
|
||||
cticks = get_colorbar_ticks(sp)[2]
|
||||
colorbar_style = PGFPlotsX.Options(
|
||||
"title" => sp[:colorbar_title],
|
||||
)
|
||||
colorbar_style = PGFPlotsX.Options("title" => sp[:colorbar_title])
|
||||
if sp[:colorbar] === :top
|
||||
push!(colorbar_style,
|
||||
push!(
|
||||
colorbar_style,
|
||||
"at" => string((0.5, 1.05)),
|
||||
"anchor" => "south",
|
||||
"xtick" => string("{", join(cticks, ","), "}"),
|
||||
@ -228,7 +214,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
"xticklabel style" => pgfx_get_colorbar_ticklabel_style(sp),
|
||||
)
|
||||
else
|
||||
push!(colorbar_style,
|
||||
push!(
|
||||
colorbar_style,
|
||||
"ytick" => string("{", join(cticks, ","), "}"),
|
||||
"yticklabel style" => pgfx_get_colorbar_ticklabel_style(sp),
|
||||
)
|
||||
@ -279,15 +266,19 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
"color" => single_color(opt[:linecolor]),
|
||||
"name path" => string(series_id),
|
||||
)
|
||||
extra_series, extra_series_opt = pgfx_split_extra_opts(series[:extra_kwargs])
|
||||
extra_series, extra_series_opt =
|
||||
pgfx_split_extra_opts(series[:extra_kwargs])
|
||||
series_opt = merge(series_opt, PGFPlotsX.Options(extra_series_opt...))
|
||||
if RecipesPipeline.is3d(series) || st in (:heatmap, :contour) || (st == :quiver && opt[:z] !== nothing)
|
||||
if (
|
||||
RecipesPipeline.is3d(series) ||
|
||||
st in (:heatmap, :contour) ||
|
||||
(st == :quiver && opt[:z] !== nothing)
|
||||
)
|
||||
series_func = PGFPlotsX.Plot3
|
||||
else
|
||||
series_func = PGFPlotsX.Plot
|
||||
end
|
||||
if sf !== nothing &&
|
||||
!isfilledcontour(series) && series[:ribbon] === nothing
|
||||
if sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing
|
||||
push!(series_opt, "area legend" => nothing)
|
||||
end
|
||||
pgfx_add_series!(Val(st), axis, series_opt, series, series_func, opt)
|
||||
@ -351,10 +342,7 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
|
||||
scale_factor = 0.00125
|
||||
mark_size = opt[:markersize] * scale_factor
|
||||
path = join(
|
||||
[
|
||||
"($(x[i] * mark_size), $(y[i] * mark_size))"
|
||||
for i in eachindex(x)
|
||||
],
|
||||
["($(x[i] * mark_size), $(y[i] * mark_size))" for i in eachindex(x)],
|
||||
" -- ",
|
||||
)
|
||||
c = get_markercolor(series, i)
|
||||
@ -372,17 +360,9 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
|
||||
segment_opt = merge(segment_opt, pgfx_marker(opt, i))
|
||||
end
|
||||
# add fillrange
|
||||
if sf !== nothing &&
|
||||
!isfilledcontour(series)
|
||||
if sf !== nothing && !isfilledcontour(series)
|
||||
if sf isa Number || sf isa AVec
|
||||
pgfx_fillrange_series!(
|
||||
axis,
|
||||
series,
|
||||
series_func,
|
||||
i,
|
||||
_cycle(sf, rng),
|
||||
rng,
|
||||
)
|
||||
pgfx_fillrange_series!(axis, series, series_func, i, _cycle(sf, rng), rng)
|
||||
elseif sf isa Tuple && series[:ribbon] !== nothing
|
||||
for sfi in sf
|
||||
pgfx_fillrange_series!(
|
||||
@ -395,8 +375,11 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
|
||||
)
|
||||
end
|
||||
end
|
||||
if k == 1 &&
|
||||
series[:subplot][:legend] != :none && pgfx_should_add_to_legend(series)
|
||||
if (
|
||||
k == 1 &&
|
||||
series[:subplot][:legend] != :none &&
|
||||
pgfx_should_add_to_legend(series)
|
||||
)
|
||||
pgfx_filllegend!(series_opt, opt)
|
||||
end
|
||||
end
|
||||
@ -405,18 +388,19 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
|
||||
if arrow isa Arrow
|
||||
arrow_opt = merge(
|
||||
segment_opt,
|
||||
PGFPlotsX.Options("quiver" => PGFPlotsX.Options(
|
||||
PGFPlotsX.Options(
|
||||
"quiver" => PGFPlotsX.Options(
|
||||
"u" => "\\thisrow{u}",
|
||||
"v" => "\\thisrow{v}",
|
||||
pgfx_arrow(arrow, :head) => nothing,
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
if arrow.side == :head
|
||||
x_arrow = opt[:x][rng][end-1:end]
|
||||
y_arrow = opt[:y][rng][end-1:end]
|
||||
x_path = opt[:x][rng][1:end-1]
|
||||
y_path = opt[:y][rng][1:end-1]
|
||||
x_arrow = opt[:x][rng][(end - 1):end]
|
||||
y_arrow = opt[:y][rng][(end - 1):end]
|
||||
x_path = opt[:x][rng][1:(end - 1)]
|
||||
y_path = opt[:y][rng][1:(end - 1)]
|
||||
elseif arrow.side == :tail
|
||||
x_arrow = opt[:x][rng][2:-1:1]
|
||||
y_arrow = opt[:y][rng][2:-1:1]
|
||||
@ -425,26 +409,23 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
|
||||
elseif arrow.side == :both
|
||||
x_arrow = opt[:x][rng][[2, 1, end - 1, end]]
|
||||
y_arrow = opt[:y][rng][[2, 1, end - 1, end]]
|
||||
x_path = opt[:x][rng][2:end-1]
|
||||
y_path = opt[:y][rng][2:end-1]
|
||||
x_path = opt[:x][rng][2:(end - 1)]
|
||||
y_path = opt[:y][rng][2:(end - 1)]
|
||||
end
|
||||
coordinates = PGFPlotsX.Table([
|
||||
:x => x_arrow[1:2:end-1],
|
||||
:y => y_arrow[1:2:end-1],
|
||||
:x => x_arrow[1:2:(end - 1)],
|
||||
:y => y_arrow[1:2:(end - 1)],
|
||||
:u => [x_arrow[i] - x_arrow[i - 1] for i in 2:2:lastindex(x_arrow)],
|
||||
:v => [y_arrow[i] - y_arrow[i - 1] for i in 2:2:lastindex(y_arrow)],
|
||||
])
|
||||
arrow_plot =
|
||||
series_func(merge(series_opt, arrow_opt), coordinates)
|
||||
arrow_plot = series_func(merge(series_opt, arrow_opt), coordinates)
|
||||
push!(axis, arrow_plot)
|
||||
coordinates = PGFPlotsX.Table(x_path, y_path)
|
||||
segment_plot =
|
||||
series_func(merge(series_opt, segment_opt), coordinates)
|
||||
segment_plot = series_func(merge(series_opt, segment_opt), coordinates)
|
||||
push!(axis, segment_plot)
|
||||
else
|
||||
coordinates = PGFPlotsX.Table(pgfx_series_arguments(series, opt, rng)...)
|
||||
segment_plot =
|
||||
series_func(merge(series_opt, segment_opt), coordinates)
|
||||
segment_plot = series_func(merge(series_opt, segment_opt), coordinates)
|
||||
push!(axis, segment_plot)
|
||||
end
|
||||
# fill between functions
|
||||
@ -466,7 +447,10 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o
|
||||
end # for segments
|
||||
# get that last marker
|
||||
if !isnothing(opt[:y]) && !any(isnan, opt[:y]) && opt[:markershape] isa AVec
|
||||
additional_plot = PGFPlotsX.PlotInc(pgfx_marker(opt, length(segments) + 1), PGFPlotsX.Coordinates(tuple((last(opt[:x]), last(opt[:y])))))
|
||||
additional_plot = PGFPlotsX.PlotInc(
|
||||
pgfx_marker(opt, length(segments) + 1),
|
||||
PGFPlotsX.Coordinates(tuple((last(opt[:x]), last(opt[:y])))),
|
||||
)
|
||||
push!(axis, additional_plot)
|
||||
end
|
||||
end
|
||||
@ -503,9 +487,7 @@ function pgfx_add_series!(::Val{:surface}, axis, series_opt, series, series_func
|
||||
end
|
||||
|
||||
function pgfx_add_series!(::Val{:wireframe}, axis, series_opt, series, series_func, opt)
|
||||
push!(series_opt, "mesh" => nothing,
|
||||
"mesh/rows" => length(opt[:x])
|
||||
)
|
||||
push!(series_opt, "mesh" => nothing, "mesh/rows" => length(opt[:x]))
|
||||
pgfx_add_series!(axis, series_opt, series, series_func, opt)
|
||||
end
|
||||
|
||||
@ -530,12 +512,15 @@ function pgfx_add_series!(::Val{:heatmap}, axis, series_opt, series, series_func
|
||||
end
|
||||
|
||||
function pgfx_add_series!(::Val{:mesh3d}, axis, series_opt, series, series_func, opt)
|
||||
ptable = join([ string(i, " ", j, " ", k, "\\\\") for (i, j, k) in zip(opt[:connections]...) ], "\n ")
|
||||
ptable = join(
|
||||
[string(i, " ", j, " ", k, "\\\\") for (i, j, k) in zip(opt[:connections]...)],
|
||||
"\n ",
|
||||
)
|
||||
push!(
|
||||
series_opt,
|
||||
"patch" => nothing,
|
||||
"table/row sep" => "\\\\",
|
||||
"patch table" => ptable
|
||||
"patch table" => ptable,
|
||||
)
|
||||
pgfx_add_series!(axis, series_opt, series, series_func, opt)
|
||||
end
|
||||
@ -572,7 +557,8 @@ function pgfx_add_series!(::Val{:contour3d}, axis, series_opt, series, series_fu
|
||||
)
|
||||
series_opt = merge(series_opt, pgfx_linestyle(opt))
|
||||
args = pgfx_series_arguments(series, opt)
|
||||
series_plot = series_func(series_opt, PGFPlotsX.Table(Contour.contours(args..., opt[:levels])))
|
||||
series_plot =
|
||||
series_func(series_opt, PGFPlotsX.Table(Contour.contours(args..., opt[:levels])))
|
||||
push!(axis, series_plot)
|
||||
pgfx_add_legend!(axis, series, opt)
|
||||
end
|
||||
@ -670,7 +656,8 @@ function pgfx_add_legend!(axis, series, opt, i = 1)
|
||||
return nothing
|
||||
end
|
||||
|
||||
pgfx_series_arguments(series, opt, range) = (arg[range] for arg in pgfx_series_arguments(series, opt))
|
||||
pgfx_series_arguments(series, opt, range) =
|
||||
(arg[range] for arg in pgfx_series_arguments(series, opt))
|
||||
function pgfx_series_arguments(series, opt)
|
||||
st = series[:seriestype]
|
||||
return if st in (:contour, :contour3d)
|
||||
@ -708,7 +695,7 @@ pgfx_get_marker(k) = get(
|
||||
none = "none",
|
||||
cross = "+",
|
||||
xcross = "x",
|
||||
+ = "+",
|
||||
(+) = "+",
|
||||
x = "x",
|
||||
utriangle = "triangle*",
|
||||
dtriangle = "triangle*",
|
||||
@ -734,7 +721,7 @@ pgfx_get_xguide_pos(k) = get(
|
||||
left = "at={(ticklabel* cs:-0.02)}, anchor=east,",
|
||||
),
|
||||
k,
|
||||
"at={(ticklabel cs:0.5)}, anchor=near ticklabel"
|
||||
"at={(ticklabel cs:0.5)}, anchor=near ticklabel",
|
||||
)
|
||||
|
||||
pgfx_get_yguide_pos(k) = get(
|
||||
@ -744,7 +731,7 @@ pgfx_get_yguide_pos(k) = get(
|
||||
bottom = "at={(ticklabel* cs:-0.02)}, anchor=north,",
|
||||
),
|
||||
k,
|
||||
"at={(ticklabel cs:0.5)}, anchor=near ticklabel"
|
||||
"at={(ticklabel cs:0.5)}, anchor=near ticklabel",
|
||||
)
|
||||
|
||||
pgfx_get_legend_pos(k) = get(
|
||||
@ -769,15 +756,17 @@ pgfx_get_legend_pos(k) = get(
|
||||
Symbol(k),
|
||||
("at" => string((1.02, 1)), "anchor" => "north west"),
|
||||
)
|
||||
pgfx_get_legend_pos(t::Tuple{S,T}) where {S<:Real,T<:Real} = ("at" => "{$(string(t))}", "anchor" => "north west")
|
||||
pgfx_get_legend_pos(nt::NamedTuple) = ("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
|
||||
pgfx_get_legend_pos(t::Tuple{S,T}) where {S<:Real,T<:Real} =
|
||||
("at" => "{$(string(t))}", "anchor" => "north west")
|
||||
pgfx_get_legend_pos(nt::NamedTuple) =
|
||||
("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
|
||||
pgfx_get_legend_pos(theta::Real) = pgfx_get_legend_pos((theta, :inner))
|
||||
function pgfx_get_legend_pos(v::Tuple{S,Symbol}) where S <: Real
|
||||
function pgfx_get_legend_pos(v::Tuple{S,Symbol}) where {S<:Real}
|
||||
(s, c) = sincosd(v[1])
|
||||
anchors = [
|
||||
"south west" "south" "south east";
|
||||
"west" "center" "east";
|
||||
"north west" "north" "north east";
|
||||
"south west" "south" "south east"
|
||||
"west" "center" "east"
|
||||
"north west" "north" "north east"
|
||||
]
|
||||
|
||||
if v[2] === :inner
|
||||
@ -805,12 +794,15 @@ function pgfx_get_legend_style(sp)
|
||||
"fill" => cstr,
|
||||
"fill opacity" => a,
|
||||
"text opacity" => alpha(plot_color(sp[:legendfontcolor])),
|
||||
"font" => pgfx_font(
|
||||
sp[:legendfontsize],
|
||||
pgfx_thickness_scaling(sp),
|
||||
),
|
||||
"font" => pgfx_font(sp[:legendfontsize], pgfx_thickness_scaling(sp)),
|
||||
"text" => plot_color(sp[:legendfontcolor]),
|
||||
"cells" => PGFPlotsX.Options("anchor" => get((left = "west", right = "east", hcenter = "center"), legfont.halign, "west")),
|
||||
"cells" => PGFPlotsX.Options(
|
||||
"anchor" => get(
|
||||
(left = "west", right = "east", hcenter = "center"),
|
||||
legfont.halign,
|
||||
"west",
|
||||
),
|
||||
),
|
||||
pgfx_get_legend_pos(sp[:legend])...,
|
||||
)
|
||||
end
|
||||
@ -819,21 +811,23 @@ pgfx_get_colorbar_pos(s) =
|
||||
get((left = " left", bottom = " horizontal", top = " horizontal"), s, "")
|
||||
pgfx_get_colorbar_pos(b::Bool) = ""
|
||||
|
||||
pgfx_get_title_pos(s) =
|
||||
get((
|
||||
pgfx_get_title_pos(s) = get(
|
||||
(
|
||||
left = ("at" => "{(0,1)}", "anchor" => "south west"),
|
||||
right = ("at" => "{(1,1)}", "anchor" => "south east"),
|
||||
), s,
|
||||
("at" => "{(0.5,1)}", "anchor" => "south"))
|
||||
),
|
||||
s,
|
||||
("at" => "{(0.5,1)}", "anchor" => "south"),
|
||||
)
|
||||
pgfx_get_title_pos(t::Tuple) = ("at" => "{$(string(t))}", "anchor" => "south")
|
||||
pgfx_get_title_pos(nt::NamedTuple) = ("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
|
||||
pgfx_get_title_pos(nt::NamedTuple) =
|
||||
("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
|
||||
|
||||
function pgfx_get_ticklabel_style(sp, axis)
|
||||
cstr = plot_color(axis[:tickfontcolor])
|
||||
return PGFPlotsX.Options(
|
||||
"font" => pgfx_font(
|
||||
axis[:tickfontsize], pgfx_thickness_scaling(sp)
|
||||
), "color" => cstr,
|
||||
"font" => pgfx_font(axis[:tickfontsize], pgfx_thickness_scaling(sp)),
|
||||
"color" => cstr,
|
||||
"draw opacity" => alpha(cstr),
|
||||
"rotate" => axis[:tickfontrotation],
|
||||
)
|
||||
@ -842,9 +836,8 @@ end
|
||||
function pgfx_get_colorbar_ticklabel_style(sp)
|
||||
cstr = plot_color(sp[:colorbar_tickfontcolor])
|
||||
return PGFPlotsX.Options(
|
||||
"font" => pgfx_font(
|
||||
sp[:colorbar_tickfontsize], pgfx_thickness_scaling(sp)
|
||||
), "color" => cstr,
|
||||
"font" => pgfx_font(sp[:colorbar_tickfontsize], pgfx_thickness_scaling(sp)),
|
||||
"color" => cstr,
|
||||
"draw opacity" => alpha(cstr),
|
||||
"rotate" => sp[:colorbar_tickfontrotation],
|
||||
)
|
||||
@ -888,12 +881,20 @@ function pgfx_colormap(v::Vector{<:Colorant})
|
||||
end, "\n")
|
||||
end
|
||||
function pgfx_colormap(cg::ColorGradient)
|
||||
join(map(1:length(cg)) do i
|
||||
@sprintf("rgb(%.8f)=(%.8f,%.8f,%.8f)", cg.values[i], red(cg.colors[i]), green(cg.colors[i]), blue(cg.colors[i]))
|
||||
end, "\n")
|
||||
join(
|
||||
map(1:length(cg)) do i
|
||||
@sprintf(
|
||||
"rgb(%.8f)=(%.8f,%.8f,%.8f)",
|
||||
cg.values[i],
|
||||
red(cg.colors[i]),
|
||||
green(cg.colors[i]),
|
||||
blue(cg.colors[i])
|
||||
)
|
||||
end,
|
||||
"\n",
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
function pgfx_framestyle(style::Symbol)
|
||||
if style in (:box, :axes, :origin, :zerolines, :grid, :none)
|
||||
return style
|
||||
@ -980,7 +981,8 @@ function pgfx_marker(plotattributes, i = 1)
|
||||
pgfx_thickness_scaling(plotattributes) *
|
||||
0.75 *
|
||||
_cycle(plotattributes[:markersize], i)
|
||||
mark_freq = !any(isnan, plotattributes[:y]) && plotattributes[:markershape] isa AVec ?
|
||||
mark_freq =
|
||||
!any(isnan, plotattributes[:y]) && plotattributes[:markershape] isa AVec ?
|
||||
length(plotattributes[:markershape]) : 1
|
||||
return PGFPlotsX.Options(
|
||||
"mark" => shape isa Shape ? "PlotsShape$i" : pgfx_get_marker(shape),
|
||||
@ -1004,12 +1006,21 @@ function pgfx_marker(plotattributes, i = 1)
|
||||
else
|
||||
0
|
||||
end,
|
||||
pgfx_get_linestyle(_cycle(plotattributes[:markerstrokestyle], i)) => nothing,
|
||||
pgfx_get_linestyle(_cycle(plotattributes[:markerstrokestyle], i)) =>
|
||||
nothing,
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
function pgfx_add_annotation!(o, x, y, val, thickness_scaling = 1; cs = "axis cs:", options = PGFPlotsX.Options())
|
||||
function pgfx_add_annotation!(
|
||||
o,
|
||||
x,
|
||||
y,
|
||||
val,
|
||||
thickness_scaling = 1;
|
||||
cs = "axis cs:",
|
||||
options = PGFPlotsX.Options(),
|
||||
)
|
||||
# Construct the style string.
|
||||
cstr = val.font.color
|
||||
a = alpha(cstr)
|
||||
@ -1017,17 +1028,23 @@ function pgfx_add_annotation!(o, x, y, val, thickness_scaling = 1; cs = "axis cs
|
||||
o,
|
||||
join([
|
||||
"\\node",
|
||||
sprint(PGFPlotsX.print_tex, merge(
|
||||
sprint(
|
||||
PGFPlotsX.print_tex,
|
||||
merge(
|
||||
PGFPlotsX.Options(
|
||||
get((hcenter = "", left = "right", right = "left"), val.font.halign, "") =>
|
||||
nothing,
|
||||
get(
|
||||
(hcenter = "", left = "right", right = "left"),
|
||||
val.font.halign,
|
||||
"",
|
||||
) => nothing,
|
||||
"color" => cstr,
|
||||
"draw opacity" => convert(Float16, a),
|
||||
"rotate" => val.font.rotation,
|
||||
"font" => pgfx_font(val.font.pointsize, thickness_scaling),
|
||||
),
|
||||
options
|
||||
)),
|
||||
options,
|
||||
),
|
||||
),
|
||||
string(" at (", cs, x, ",", y, ") {", val.str, "};"),
|
||||
]),
|
||||
)
|
||||
@ -1097,12 +1114,10 @@ function pgfx_fillrange_series!(axis, series, series_func, i, fillrange, rng)
|
||||
push!(fillrange_opt, "mark" => "none") # no markers on fillranges
|
||||
push!(fillrange_opt, "forget plot" => nothing)
|
||||
opt = series.plotattributes
|
||||
args = RecipesPipeline.is3d(series) ? (opt[:x][rng], opt[:y][rng], opt[:z][rng]) :
|
||||
args =
|
||||
RecipesPipeline.is3d(series) ? (opt[:x][rng], opt[:y][rng], opt[:z][rng]) :
|
||||
(opt[:x][rng], opt[:y][rng])
|
||||
push!(
|
||||
axis,
|
||||
PGFPlotsX.PlotInc(fillrange_opt, pgfx_fillrange_args(fillrange, args...)),
|
||||
)
|
||||
push!(axis, PGFPlotsX.PlotInc(fillrange_opt, pgfx_fillrange_args(fillrange, args...)))
|
||||
return axis
|
||||
end
|
||||
|
||||
@ -1154,7 +1169,8 @@ function pgfx_sanitize_plot!(plt)
|
||||
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]))
|
||||
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)
|
||||
@ -1163,7 +1179,8 @@ function pgfx_sanitize_plot!(plt)
|
||||
end
|
||||
for series in plt.series_list
|
||||
for (key, value) in series.plotattributes
|
||||
if key == :series_annotations && series.plotattributes[:series_annotations] !== nothing
|
||||
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])
|
||||
@ -1180,24 +1197,20 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
axis = sp[Symbol(letter, :axis)]
|
||||
|
||||
# turn off scaled ticks
|
||||
push!(opt, "scaled $(letter) ticks" => "false", string(letter, :label) => axis[:guide])
|
||||
tick_color = plot_color(axis[:foreground_color_axis])
|
||||
push!(
|
||||
opt,
|
||||
"scaled $(letter) ticks" => "false",
|
||||
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),
|
||||
),
|
||||
"$(letter) tick style" =>
|
||||
PGFPlotsX.Options("color" => color(tick_color), "opacity" => alpha(tick_color)),
|
||||
)
|
||||
tick_label_color = plot_color(axis[:tickfontcolor])
|
||||
push!(opt,
|
||||
push!(
|
||||
opt,
|
||||
"$(letter) tick label style" => PGFPlotsX.Options(
|
||||
"color" => color(tick_color),
|
||||
"opacity" => alpha(tick_color),
|
||||
"rotate" => axis[:rotation]
|
||||
"rotate" => axis[:rotation],
|
||||
),
|
||||
)
|
||||
|
||||
@ -1233,8 +1246,7 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
scale = axis[:scale]
|
||||
if scale in (:log2, :ln, :log10)
|
||||
push!(opt, string(letter, :mode) => "log")
|
||||
scale == :ln ||
|
||||
push!(opt, "log basis $letter" => "$(scale == :log2 ? 2 : 10)")
|
||||
scale == :ln || push!(opt, "log basis $letter" => "$(scale == :log2 ? 2 : 10)")
|
||||
end
|
||||
|
||||
# ticks on or off
|
||||
@ -1252,7 +1264,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
end
|
||||
|
||||
# limits
|
||||
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
|
||||
lims =
|
||||
ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
|
||||
axis_limits(sp, letter)
|
||||
push!(opt, string(letter, :min) => lims[1], string(letter, :max) => lims[2])
|
||||
|
||||
@ -1263,12 +1276,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
tick_values =
|
||||
ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] :
|
||||
ticks[1]
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "tick") => string("{", join(tick_values, ","), "}"),
|
||||
)
|
||||
if axis[:showaxis] &&
|
||||
axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
|
||||
push!(opt, string(letter, "tick") => string("{", join(tick_values, ","), "}"))
|
||||
if axis[:showaxis] && axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
|
||||
# wrap the power part of label with }
|
||||
tick_labels = Vector{String}(undef, length(ticks[2]))
|
||||
for (i, label) in enumerate(ticks[2])
|
||||
@ -1283,25 +1292,20 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
)
|
||||
elseif axis[:showaxis]
|
||||
tick_labels =
|
||||
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] :
|
||||
ticks[2]
|
||||
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
|
||||
if axis[:formatter] in (:scientific, :auto)
|
||||
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
|
||||
tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
|
||||
end
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "ticklabels") =>
|
||||
string("{", join(tick_labels, ","), "}"),
|
||||
string(letter, "ticklabels") => string("{", join(tick_labels, ","), "}"),
|
||||
)
|
||||
else
|
||||
push!(opt, string(letter, "ticklabels") => "{}")
|
||||
end
|
||||
if axis[:tick_direction] === :none
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "tick style") => "draw=none",
|
||||
)
|
||||
push!(opt, string(letter, "tick style") => "draw=none")
|
||||
else
|
||||
push!(
|
||||
opt,
|
||||
@ -1309,9 +1313,7 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
(axis[:tick_direction] == :out ? "outside" : "inside"),
|
||||
)
|
||||
end
|
||||
push!(
|
||||
opt, string(letter, "ticklabel style") => pgfx_get_ticklabel_style(sp, axis)
|
||||
)
|
||||
push!(opt, string(letter, "ticklabel style") => pgfx_get_ticklabel_style(sp, axis))
|
||||
push!(
|
||||
opt,
|
||||
string(letter, " grid style") => pgfx_linestyle(
|
||||
@ -1335,7 +1337,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
minor_ticks
|
||||
push!(
|
||||
opt,
|
||||
string("extra ", letter, " ticks") => string("{", join(minor_ticks, ","), "}"),
|
||||
string("extra ", letter, " ticks") =>
|
||||
string("{", join(minor_ticks, ","), "}"),
|
||||
)
|
||||
push!(opt, string("extra ", letter, " tick labels") => "")
|
||||
push!(
|
||||
@ -1348,7 +1351,9 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
axis[:minorgridalpha],
|
||||
axis[:minorgridstyle],
|
||||
),
|
||||
"major tick length" => typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 || axis[:minorticks] ? "0.1cm" : "0"
|
||||
"major tick length" =>
|
||||
typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ||
|
||||
axis[:minorticks] ? "0.1cm" : "0",
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -1405,7 +1410,8 @@ end
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot{PGFPlotsXBackend})
|
||||
leg = sp[:legend]
|
||||
if leg in (:best, :outertopright, :outerright, :outerbottomright) || (leg isa Tuple && leg[1] >= 1)
|
||||
if leg in (:best, :outertopright, :outerright, :outerbottomright) ||
|
||||
(leg isa Tuple && leg[1] >= 1)
|
||||
sp.minpad = (0mm, 0mm, 5mm, 0mm)
|
||||
else
|
||||
sp.minpad = (0mm, 0mm, 0mm, 0mm)
|
||||
@ -1425,21 +1431,13 @@ function _update_plot_object(plt::Plot{PGFPlotsXBackend})
|
||||
end
|
||||
|
||||
for mime in ("application/pdf", "image/svg+xml")
|
||||
@eval function _show(
|
||||
io::IO,
|
||||
mime::MIME{Symbol($mime)},
|
||||
plt::Plot{PGFPlotsXBackend},
|
||||
)
|
||||
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{PGFPlotsXBackend})
|
||||
plt.o.was_shown = true
|
||||
show(io, mime, plt.o.the_plot)
|
||||
end
|
||||
end
|
||||
|
||||
function _show(
|
||||
io::IO,
|
||||
mime::MIME{Symbol("image/png")},
|
||||
plt::Plot{PGFPlotsXBackend},
|
||||
)
|
||||
function _show(io::IO, mime::MIME{Symbol("image/png")}, plt::Plot{PGFPlotsXBackend})
|
||||
plt.o.was_shown = true
|
||||
plt_file = tempname() * ".png"
|
||||
PGFPlotsX.pgfsave(plt_file, plt.o.the_plot; dpi = plt[:dpi])
|
||||
@ -1447,11 +1445,7 @@ end
|
||||
rm(plt_file; force = true)
|
||||
end
|
||||
|
||||
function _show(
|
||||
io::IO,
|
||||
mime::MIME{Symbol("application/x-tex")},
|
||||
plt::Plot{PGFPlotsXBackend},
|
||||
)
|
||||
function _show(io::IO, mime::MIME{Symbol("application/x-tex")}, plt::Plot{PGFPlotsXBackend})
|
||||
plt.o.was_shown = true
|
||||
PGFPlotsX.print_tex(
|
||||
io,
|
||||
|
||||
@ -7,12 +7,13 @@ function _plotly_framestyle(style::Symbol)
|
||||
return style
|
||||
else
|
||||
default_style = get((semi = :box, origin = :zerolines), style, :axes)
|
||||
@warn("Framestyle :$style is not supported by Plotly and PlotlyJS. :$default_style was chosen instead.")
|
||||
@warn(
|
||||
"Framestyle :$style is not supported by Plotly and PlotlyJS. :$default_style was chosen instead."
|
||||
)
|
||||
default_style
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
using UUIDs
|
||||
@ -37,25 +38,20 @@ function plotly_font(font::Font, color = font.color)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
function plotly_annotation_dict(x, y, val; xref = "paper", yref = "paper")
|
||||
KW(
|
||||
:text => val,
|
||||
:xref => xref,
|
||||
:x => x,
|
||||
:yref => yref,
|
||||
:y => y,
|
||||
:showarrow => false,
|
||||
)
|
||||
KW(:text => val, :xref => xref, :x => x, :yref => yref, :y => y, :showarrow => false)
|
||||
end
|
||||
|
||||
function plotly_annotation_dict(x, y, ptxt::PlotText; xref = "paper", yref = "paper")
|
||||
merge(plotly_annotation_dict(x, y, ptxt.str; xref=xref, yref=yref), KW(
|
||||
merge(
|
||||
plotly_annotation_dict(x, y, ptxt.str; xref = xref, yref = yref),
|
||||
KW(
|
||||
:font => plotly_font(ptxt.font),
|
||||
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
|
||||
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
|
||||
:rotation => -ptxt.font.rotation,
|
||||
))
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
# function get_annotation_dict_for_arrow(plotattributes::KW, xyprev::Tuple, xy::Tuple, a::Arrow)
|
||||
@ -118,7 +114,6 @@ function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
|
||||
pcts
|
||||
end
|
||||
|
||||
|
||||
# this method gets the start/end in percentage of the canvas for this axis direction
|
||||
function plotly_domain(sp::Subplot)
|
||||
figw, figh = sp.plt[:size]
|
||||
@ -129,7 +124,6 @@ function plotly_domain(sp::Subplot)
|
||||
return x_domain, y_domain
|
||||
end
|
||||
|
||||
|
||||
function plotly_axis(axis, sp, anchor = nothing, domain = nothing)
|
||||
letter = axis[:letter]
|
||||
framestyle = sp[:framestyle]
|
||||
@ -137,13 +131,15 @@ function plotly_axis(axis, sp, anchor = nothing, domain = nothing)
|
||||
:visible => framestyle != :none,
|
||||
:title => axis[:guide],
|
||||
:showgrid => axis[:grid],
|
||||
:gridcolor => rgba_string(plot_color(axis[:foreground_color_grid], axis[:gridalpha])),
|
||||
:gridcolor =>
|
||||
rgba_string(plot_color(axis[:foreground_color_grid], axis[:gridalpha])),
|
||||
:gridwidth => axis[:gridlinewidth],
|
||||
:zeroline => framestyle == :zerolines,
|
||||
:zerolinecolor => rgba_string(axis[:foreground_color_axis]),
|
||||
:showline => framestyle in (:box, :axes) && axis[:showaxis],
|
||||
:linecolor => rgba_string(plot_color(axis[:foreground_color_axis])),
|
||||
:ticks => axis[:tick_direction] === :out ? "outside" :
|
||||
:ticks =>
|
||||
axis[:tick_direction] === :out ? "outside" :
|
||||
axis[:tick_direction] === :in ? "inside" : "",
|
||||
:mirror => framestyle == :box,
|
||||
:showticklabels => axis[:showaxis],
|
||||
@ -166,7 +162,9 @@ function plotly_axis(axis, sp, anchor = nothing, domain = nothing)
|
||||
if !(axis[:ticks] in (nothing, :none, false))
|
||||
ax[:titlefont] = plotly_font(guidefont(axis))
|
||||
ax[:tickfont] = plotly_font(tickfont(axis))
|
||||
ax[:tickcolor] = framestyle in (:zerolines, :grid) || !axis[:showaxis] ? rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
|
||||
ax[:tickcolor] =
|
||||
framestyle in (:zerolines, :grid) || !axis[:showaxis] ?
|
||||
rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
|
||||
ax[:linecolor] = rgba_string(axis[:foreground_color_axis])
|
||||
|
||||
# ticks
|
||||
@ -195,10 +193,7 @@ function plotly_axis(axis, sp, anchor = nothing, domain = nothing)
|
||||
end
|
||||
|
||||
function plotly_polaraxis(sp::Subplot, axis::Axis)
|
||||
ax = KW(
|
||||
:visible => axis[:showaxis],
|
||||
:showline => axis[:grid],
|
||||
)
|
||||
ax = KW(:visible => axis[:showaxis], :showline => axis[:grid])
|
||||
|
||||
if axis[:letter] == :x
|
||||
ax[:range] = rad2deg.(axis_limits(sp, :x))
|
||||
@ -239,7 +234,10 @@ function plotly_layout(plt::Plot)
|
||||
end
|
||||
titlex, titley = xy_mm_to_pcts(xmm, top(bbox(sp)), w * px, h * px)
|
||||
title_font = font(titlefont(sp), :top)
|
||||
push!(plotattributes_out[:annotations], plotly_annotation_dict(titlex, titley, text(sp[:title], title_font)))
|
||||
push!(
|
||||
plotattributes_out[:annotations],
|
||||
plotly_annotation_dict(titlex, titley, text(sp[:title], title_font)),
|
||||
)
|
||||
end
|
||||
|
||||
plotattributes_out[:plot_bgcolor] = rgba_string(sp[:background_color_inside])
|
||||
@ -248,8 +246,10 @@ function plotly_layout(plt::Plot)
|
||||
sp[:framestyle] = _plotly_framestyle(sp[:framestyle])
|
||||
|
||||
if ispolar(sp)
|
||||
plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp, sp[:xaxis])
|
||||
plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp, sp[:yaxis])
|
||||
plotattributes_out[Symbol("angularaxis$(spidx)")] =
|
||||
plotly_polaraxis(sp, sp[:xaxis])
|
||||
plotattributes_out[Symbol("radialaxis$(spidx)")] =
|
||||
plotly_polaraxis(sp, sp[:yaxis])
|
||||
else
|
||||
x_domain, y_domain = plotly_domain(sp)
|
||||
if RecipesPipeline.is3d(sp)
|
||||
@ -286,16 +286,28 @@ function plotly_layout(plt::Plot)
|
||||
|
||||
# annotations
|
||||
for ann in sp[:annotations]
|
||||
append!(plotattributes_out[:annotations], KW[plotly_annotation_dict(locate_annotation(sp, ann...)...; xref = "x$(x_idx)", yref = "y$(y_idx)")])
|
||||
append!(
|
||||
plotattributes_out[:annotations],
|
||||
KW[plotly_annotation_dict(
|
||||
locate_annotation(sp, ann...)...;
|
||||
xref = "x$(x_idx)",
|
||||
yref = "y$(y_idx)",
|
||||
)],
|
||||
)
|
||||
end
|
||||
# series_annotations
|
||||
for series in series_list(sp)
|
||||
anns = series[:series_annotations]
|
||||
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
push!(plotattributes_out[:annotations], plotly_annotation_dict(
|
||||
push!(
|
||||
plotattributes_out[:annotations],
|
||||
plotly_annotation_dict(
|
||||
xi,
|
||||
yi,
|
||||
PlotText(str,fnt); xref = "x$(x_idx)", yref = "y$(y_idx)")
|
||||
PlotText(str, fnt);
|
||||
xref = "x$(x_idx)",
|
||||
yref = "y$(y_idx)",
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -325,7 +337,6 @@ function plotly_layout(plt::Plot)
|
||||
plotattributes_out = recursive_merge(plotattributes_out, plt.attr[:extra_plot_kwargs])
|
||||
end
|
||||
|
||||
|
||||
function plotly_add_legend!(plotattributes_out::KW, sp::Subplot)
|
||||
plotattributes_out[:showlegend] = sp[:legend] != :none
|
||||
legend_position = plotly_legend_pos(sp[:legend])
|
||||
@ -374,20 +385,34 @@ function plotly_legend_pos(pos::Symbol)
|
||||
outerleft = (coords = [xouterleft, center], xanchor = "left", yanchor = "top"),
|
||||
outerright = (coords = [xouterright, center], xanchor = "right", yanchor = "top"),
|
||||
outertopleft = (coords = [xouterleft, ytop], xanchor = "upper", yanchor = "left"),
|
||||
outertopright = (coords = [xouterright, ytop], xanchor = "upper", yanchor = "right"),
|
||||
outerbottomleft =(coords = [xouterleft, ybot], xanchor = "lower", yanchor = "left"),
|
||||
outerbottomright =(coords = [xouterright, ybot], xanchor = "lower", yanchor = "right"),
|
||||
default = (coords = [1.0, 1.0], xanchor = "auto", yanchor = "auto")
|
||||
outertopright = (
|
||||
coords = [xouterright, ytop],
|
||||
xanchor = "upper",
|
||||
yanchor = "right",
|
||||
),
|
||||
outerbottomleft = (
|
||||
coords = [xouterleft, ybot],
|
||||
xanchor = "lower",
|
||||
yanchor = "left",
|
||||
),
|
||||
outerbottomright = (
|
||||
coords = [xouterright, ybot],
|
||||
xanchor = "lower",
|
||||
yanchor = "right",
|
||||
),
|
||||
default = (coords = [1.0, 1.0], xanchor = "auto", yanchor = "auto"),
|
||||
)
|
||||
|
||||
legend_position = get(plotly_legend_position_mapping, pos, plotly_legend_position_mapping.default)
|
||||
legend_position =
|
||||
get(plotly_legend_position_mapping, pos, plotly_legend_position_mapping.default)
|
||||
end
|
||||
|
||||
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real, T<:Real} = (coords=v, xanchor="left", yanchor="top")
|
||||
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real,T<:Real} =
|
||||
(coords = v, xanchor = "left", yanchor = "top")
|
||||
|
||||
plotly_legend_pos(theta::Real) = plotly_legend_pos((theta, :inner))
|
||||
|
||||
function plotly_legend_pos(v::Tuple{S,Symbol}) where S<:Real
|
||||
function plotly_legend_pos(v::Tuple{S,Symbol}) where {S<:Real}
|
||||
(s, c) = sincosd(v[1])
|
||||
xanchors = ["left", "center", "right"]
|
||||
yanchors = ["bottom", "middle", "top"]
|
||||
@ -401,15 +426,17 @@ function plotly_legend_pos(v::Tuple{S,Symbol}) where S<:Real
|
||||
xanchor = xanchors[4 - legend_anchor_index(c)]
|
||||
yanchor = yanchors[4 - legend_anchor_index(s)]
|
||||
end
|
||||
return (coords=legend_pos_from_angle(v[1],rect...), xanchor=xanchor, yanchor=yanchor)
|
||||
return (
|
||||
coords = legend_pos_from_angle(v[1], rect...),
|
||||
xanchor = xanchor,
|
||||
yanchor = yanchor,
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
function plotly_layout_json(plt::Plot)
|
||||
JSON.json(plotly_layout(plt), 4)
|
||||
end
|
||||
|
||||
|
||||
plotly_colorscale(cg::ColorGradient, α = nothing) =
|
||||
[[v, rgba_string(plot_color(cg.colors[v], α))] for v in cg.values]
|
||||
function plotly_colorscale(c::AbstractVector{<:Colorant}, α = nothing)
|
||||
@ -428,13 +455,12 @@ function plotly_colorscale(cg::PlotUtils.CategoricalColorGradient, α = nothing)
|
||||
cinds = repeat(1:n, inner = 2)
|
||||
vinds = vcat((i:(i + 1) for i in 1:n)...)
|
||||
return [
|
||||
[cg.values[vinds[i]], rgba_string(plot_color(color_list(cg)[cinds[i]], α))]
|
||||
for i in eachindex(cinds)
|
||||
[cg.values[vinds[i]], rgba_string(plot_color(color_list(cg)[cinds[i]], α))] for
|
||||
i in eachindex(cinds)
|
||||
]
|
||||
end
|
||||
plotly_colorscale(c, α = nothing) = plotly_colorscale(_as_gradient(c), α)
|
||||
|
||||
|
||||
get_plotly_marker(k, def) = get(
|
||||
(
|
||||
rect = "square",
|
||||
@ -461,12 +487,11 @@ function plotly_link_indicies(plt::Plot, sp::Subplot)
|
||||
x_idx, y_idx
|
||||
end
|
||||
|
||||
|
||||
# the Shape contructor will automatically close the shape. since we need it closed,
|
||||
# we split by NaNs and then construct/destruct the shapes to get the closed coords
|
||||
function plotly_close_shapes(x, y)
|
||||
xs, ys = nansplit(x), nansplit(y)
|
||||
for i=eachindex(xs)
|
||||
for i in eachindex(xs)
|
||||
shape = Shape(xs[i], ys[i])
|
||||
xs[i], ys[i] = coords(shape)
|
||||
end
|
||||
@ -512,7 +537,9 @@ function plotly_convert_to_datetime(x::AbstractArray, formatter::Function)
|
||||
elseif formatter == timeformatter
|
||||
map(xi -> string(Dates.Date(Dates.now()), " ", formatter(xi)), x)
|
||||
else
|
||||
error("Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter")
|
||||
error(
|
||||
"Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter",
|
||||
)
|
||||
end
|
||||
end
|
||||
#ensures that a gradient is called if a single color is supplied where a gradient is needed (e.g. if a series recipe defines marker_z)
|
||||
@ -553,8 +580,9 @@ function plotly_series(plt::Plot, series::Series)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
end
|
||||
|
||||
x, y, z = (plotly_data(series, letter, data)
|
||||
for (letter, data) in zip((:x, :y, :z), (x, y, z))
|
||||
x, y, z = (
|
||||
plotly_data(series, letter, data) for
|
||||
(letter, data) in zip((:x, :y, :z), (x, y, z))
|
||||
)
|
||||
|
||||
plotattributes_out[:name] = series[:label]
|
||||
@ -562,7 +590,8 @@ function plotly_series(plt::Plot, series::Series)
|
||||
isscatter = st in (:scatter, :scatter3d, :scattergl)
|
||||
hasmarker = isscatter || series[:markershape] != :none
|
||||
hasline = st in (:path, :path3d, :straightline)
|
||||
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
|
||||
hasfillrange =
|
||||
st in (:path, :scatter, :scattergl, :straightline) &&
|
||||
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
|
||||
|
||||
plotattributes_out[:colorbar] = KW(:title => sp[:colorbar_title])
|
||||
@ -580,7 +609,8 @@ function plotly_series(plt::Plot, series::Series)
|
||||
y = heatmap_edges(y, sp[:yaxis][:scale])
|
||||
plotattributes_out[:type] = "heatmap"
|
||||
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:colorscale] =
|
||||
plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||
|
||||
elseif st == :contour
|
||||
@ -588,8 +618,12 @@ function plotly_series(plt::Plot, series::Series)
|
||||
plotattributes_out[:type] = "contour"
|
||||
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
|
||||
plotattributes_out[:ncontours] = series[:levels] + 2
|
||||
plotattributes_out[:contours] = KW(:coloring => filled ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
|
||||
plotattributes_out[:contours] = KW(
|
||||
:coloring => filled ? "fill" : "lines",
|
||||
:showlabels => series[:contour_labels] == true,
|
||||
)
|
||||
plotattributes_out[:colorscale] =
|
||||
plotly_colorscale(series[:linecolor], series[:linealpha])
|
||||
plotattributes_out[:showscale] = hascolorbar(sp) && hascolorbar(series)
|
||||
|
||||
elseif st in (:surface, :wireframe)
|
||||
@ -599,13 +633,16 @@ function plotly_series(plt::Plot, series::Series)
|
||||
plotattributes_out[:hidesurface] = true
|
||||
wirelines = KW(
|
||||
:show => true,
|
||||
:color => rgba_string(plot_color(series[:linecolor], series[:linealpha])),
|
||||
:color =>
|
||||
rgba_string(plot_color(series[:linecolor], series[:linealpha])),
|
||||
:highlightwidth => series[:linewidth],
|
||||
)
|
||||
plotattributes_out[:contours] = KW(:x => wirelines, :y => wirelines, :z => wirelines)
|
||||
plotattributes_out[:contours] =
|
||||
KW(:x => wirelines, :y => wirelines, :z => wirelines)
|
||||
plotattributes_out[:showscale] = false
|
||||
else
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:colorscale] =
|
||||
plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:opacity] = series[:fillalpha]
|
||||
if series[:fill_z] !== nothing
|
||||
plotattributes_out[:surfacecolor] = handle_surface(series[:fill_z])
|
||||
@ -620,17 +657,27 @@ function plotly_series(plt::Plot, series::Series)
|
||||
if typeof(series[:connections]) <: Tuple{Array,Array,Array}
|
||||
i, j, k = series[:connections]
|
||||
if !(length(i) == length(j) == length(k))
|
||||
throw(ArgumentError("Argument connections must consist of equally sized arrays."))
|
||||
throw(
|
||||
ArgumentError(
|
||||
"Argument connections must consist of equally sized arrays.",
|
||||
),
|
||||
)
|
||||
end
|
||||
plotattributes_out[:i] = i
|
||||
plotattributes_out[:j] = j
|
||||
plotattributes_out[:k] = k
|
||||
else
|
||||
throw(ArgumentError("Argument connections has to be a tuple of three arrays."))
|
||||
throw(
|
||||
ArgumentError(
|
||||
"Argument connections has to be a tuple of three arrays.",
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:color] = rgba_string(plot_color(series[:fillcolor], series[:fillalpha]))
|
||||
plotattributes_out[:colorscale] =
|
||||
plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:color] =
|
||||
rgba_string(plot_color(series[:fillcolor], series[:fillalpha]))
|
||||
plotattributes_out[:opacity] = series[:fillalpha]
|
||||
if series[:fill_z] !== nothing
|
||||
plotattributes_out[:surfacecolor] = handle_surface(series[:fill_z])
|
||||
@ -645,12 +692,25 @@ function plotly_series(plt::Plot, series::Series)
|
||||
if hasmarker
|
||||
inds = eachindex(x)
|
||||
plotattributes_out[:marker] = KW(
|
||||
:symbol => get_plotly_marker(series[:markershape], string(series[:markershape])),
|
||||
:symbol =>
|
||||
get_plotly_marker(series[:markershape], string(series[:markershape])),
|
||||
# :opacity => series[:markeralpha],
|
||||
:size => 2 * _cycle(series[:markersize], inds),
|
||||
:color => rgba_string.(plot_color.(get_markercolor.(series, inds), get_markeralpha.(series, inds))),
|
||||
:color =>
|
||||
rgba_string.(
|
||||
plot_color.(
|
||||
get_markercolor.(series, inds),
|
||||
get_markeralpha.(series, inds),
|
||||
),
|
||||
),
|
||||
:line => KW(
|
||||
:color => rgba_string.(plot_color.(get_markerstrokecolor.(series, inds), get_markerstrokealpha.(series, inds))),
|
||||
:color =>
|
||||
rgba_string.(
|
||||
plot_color.(
|
||||
get_markerstrokecolor.(series, inds),
|
||||
get_markerstrokealpha.(series, inds),
|
||||
),
|
||||
),
|
||||
:width => _cycle(series[:markerstrokewidth], inds),
|
||||
),
|
||||
)
|
||||
@ -678,8 +738,9 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
:legendgroup => series[:label],
|
||||
)
|
||||
|
||||
x, y = (plotly_data(series, letter, data)
|
||||
for (letter, data) in zip((:x, :y), shape_data(series, 100))
|
||||
x, y = (
|
||||
plotly_data(series, letter, data) for
|
||||
(letter, data) in zip((:x, :y), shape_data(series, 100))
|
||||
)
|
||||
|
||||
for (k, segment) in enumerate(segments)
|
||||
@ -687,17 +748,24 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
length(rng) < 2 && continue
|
||||
|
||||
# to draw polygons, we actually draw lines with fill
|
||||
plotattributes_out = merge(plotattributes_base, KW(
|
||||
plotattributes_out = merge(
|
||||
plotattributes_base,
|
||||
KW(
|
||||
:type => "scatter",
|
||||
:mode => "lines",
|
||||
:x => vcat(x[rng], x[rng[1]]),
|
||||
:y => vcat(y[rng], y[rng[1]]),
|
||||
:fill => "tozeroy",
|
||||
:fillcolor => rgba_string(plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i))),
|
||||
))
|
||||
:fillcolor => rgba_string(
|
||||
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
|
||||
),
|
||||
),
|
||||
)
|
||||
if series[:markerstrokewidth] > 0
|
||||
plotattributes_out[:line] = KW(
|
||||
:color => rgba_string(plot_color(get_linecolor(series, clims, i), get_linealpha(series, i))),
|
||||
:color => rgba_string(
|
||||
plot_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
|
||||
),
|
||||
:width => get_linewidth(series, i),
|
||||
:dash => string(get_linestyle(series, i)),
|
||||
)
|
||||
@ -712,7 +780,10 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
elseif series[:line_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line))
|
||||
elseif series[:marker_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
|
||||
push!(
|
||||
plotattributes_outs,
|
||||
plotly_colorbar_hack(series, plotattributes_base, :marker),
|
||||
)
|
||||
end
|
||||
plotattributes_outs
|
||||
end
|
||||
@ -723,7 +794,8 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
isscatter = st in (:scatter, :scatter3d, :scattergl)
|
||||
hasmarker = isscatter || series[:markershape] != :none
|
||||
hasline = st in (:path, :path3d, :straightline)
|
||||
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
|
||||
hasfillrange =
|
||||
st in (:path, :scatter, :scattergl, :straightline) &&
|
||||
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
|
||||
|
||||
segments = collect(series_segments(series, st))
|
||||
@ -746,14 +818,22 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
if series[:fillrange] == true || series[:fillrange] == 0 || isa(series[:fillrange], Tuple)
|
||||
if series[:fillrange] == true ||
|
||||
series[:fillrange] == 0 ||
|
||||
isa(series[:fillrange], Tuple)
|
||||
plotattributes_out[:fill] = "tozeroy"
|
||||
plotattributes_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)))
|
||||
plotattributes_out[:fillcolor] = rgba_string(
|
||||
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
|
||||
)
|
||||
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real},Real}
|
||||
plotattributes_out[:fill] = "tonexty"
|
||||
plotattributes_out[:fillcolor] = rgba_string(plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)))
|
||||
plotattributes_out[:fillcolor] = rgba_string(
|
||||
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
|
||||
)
|
||||
elseif !(series[:fillrange] in (false, nothing))
|
||||
@warn("fillrange ignored... plotly only supports filling to zero and to a vector of values. fillrange: $(series[:fillrange])")
|
||||
@warn(
|
||||
"fillrange ignored... plotly only supports filling to zero and to a vector of values. fillrange: $(series[:fillrange])"
|
||||
)
|
||||
end
|
||||
plotattributes_out[:x], plotattributes_out[:y] = x[rng], y[rng]
|
||||
|
||||
@ -764,20 +844,32 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x[rng], y[rng], z[rng]
|
||||
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] =
|
||||
x[rng], y[rng], z[rng]
|
||||
end
|
||||
|
||||
# add "marker"
|
||||
if hasmarker
|
||||
mcolor = rgba_string(plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i)))
|
||||
lcolor = rgba_string(plot_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i)))
|
||||
mcolor = rgba_string(
|
||||
plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i)),
|
||||
)
|
||||
lcolor = rgba_string(
|
||||
plot_color(
|
||||
get_markerstrokecolor(series, i),
|
||||
get_markerstrokealpha(series, i),
|
||||
),
|
||||
)
|
||||
plotattributes_out[:marker] = KW(
|
||||
:symbol => get_plotly_marker(_cycle(series[:markershape], i), string(_cycle(series[:markershape], i))),
|
||||
:symbol => get_plotly_marker(
|
||||
_cycle(series[:markershape], i),
|
||||
string(_cycle(series[:markershape], i)),
|
||||
),
|
||||
# :opacity => needs_scatter_fix ? [1, 0] : 1,
|
||||
:size => 2 * _cycle(series[:markersize], i),
|
||||
:color => needs_scatter_fix ? [mcolor, "rgba(0, 0, 0, 0.000)"] : mcolor,
|
||||
:line => KW(
|
||||
:color => needs_scatter_fix ? [lcolor, "rgba(0, 0, 0, 0.000)"] : lcolor,
|
||||
:color =>
|
||||
needs_scatter_fix ? [lcolor, "rgba(0, 0, 0, 0.000)"] : lcolor,
|
||||
:width => _cycle(series[:markerstrokewidth], i),
|
||||
),
|
||||
)
|
||||
@ -786,7 +878,9 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
# add "line"
|
||||
if hasline
|
||||
plotattributes_out[:line] = KW(
|
||||
:color => rgba_string(plot_color(get_linecolor(series, clims, i), get_linealpha(series, i))),
|
||||
:color => rgba_string(
|
||||
plot_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
|
||||
),
|
||||
:width => get_linewidth(series, i),
|
||||
:shape => if st == :steppre
|
||||
"vh"
|
||||
@ -813,8 +907,12 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
if typeof(series[:fillrange]) <: Real
|
||||
series[:fillrange] = fill(series[:fillrange], length(rng))
|
||||
elseif typeof(series[:fillrange]) <: Tuple
|
||||
f1 = typeof(series[:fillrange][1]) <: Real ? fill(series[:fillrange][1], length(rng)) : series[:fillrange][1][rng]
|
||||
f2 = typeof(series[:fillrange][2]) <: Real ? fill(series[:fillrange][2], length(rng)) : series[:fillrange][2][rng]
|
||||
f1 =
|
||||
typeof(series[:fillrange][1]) <: Real ?
|
||||
fill(series[:fillrange][1], length(rng)) : series[:fillrange][1][rng]
|
||||
f2 =
|
||||
typeof(series[:fillrange][2]) <: Real ?
|
||||
fill(series[:fillrange][2], length(rng)) : series[:fillrange][2][rng]
|
||||
series[:fillrange] = (f1, f2)
|
||||
end
|
||||
if isa(series[:fillrange], AbstractVector)
|
||||
@ -824,13 +922,15 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
else
|
||||
# if fillrange is a tuple with upper and lower limit, plotattributes_out_fillrange
|
||||
# is the series that will do the filling
|
||||
plotattributes_out_fillrange[:x], plotattributes_out_fillrange[:y] = concatenate_fillrange(x[rng], series[:fillrange])
|
||||
plotattributes_out_fillrange[:x], plotattributes_out_fillrange[:y] =
|
||||
concatenate_fillrange(x[rng], series[:fillrange])
|
||||
plotattributes_out_fillrange[:line][:width] = 0
|
||||
delete!(plotattributes_out, :fill)
|
||||
delete!(plotattributes_out, :fillcolor)
|
||||
end
|
||||
|
||||
plotattributes_outs[(2k-1):(2k)] = [plotattributes_out_fillrange, plotattributes_out]
|
||||
plotattributes_outs[(2k - 1):(2k)] =
|
||||
[plotattributes_out_fillrange, plotattributes_out]
|
||||
else
|
||||
plotattributes_outs[k] = plotattributes_out
|
||||
end
|
||||
@ -841,7 +941,10 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
elseif series[:fill_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill))
|
||||
elseif series[:marker_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
|
||||
push!(
|
||||
plotattributes_outs,
|
||||
plotly_colorbar_hack(series, plotattributes_base, :marker),
|
||||
)
|
||||
end
|
||||
|
||||
plotattributes_outs
|
||||
@ -871,7 +974,6 @@ function plotly_colorbar_hack(series::Series, plotattributes_base::KW, sym::Symb
|
||||
return plotattributes_out
|
||||
end
|
||||
|
||||
|
||||
function plotly_polar!(plotattributes_out::KW, series::Series)
|
||||
# convert polar plots x/y to theta/radius
|
||||
if ispolar(series[:subplot])
|
||||
@ -910,11 +1012,15 @@ html_body(plt::Plot{PlotlyBackend}) = plotly_html_body(plt)
|
||||
|
||||
function plotly_html_head(plt::Plot)
|
||||
plotly =
|
||||
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) : "https://cdn.plot.ly/$(_plotly_min_js_filename)"
|
||||
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) :
|
||||
"https://cdn.plot.ly/$(_plotly_min_js_filename)"
|
||||
|
||||
include_mathjax = get(plt[:extra_plot_kwargs], :include_mathjax, "")
|
||||
mathjax_file = include_mathjax != "cdn" ? ("file://" * include_mathjax) : "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"
|
||||
mathjax_head = include_mathjax == "" ? "" : "<script src=\"$mathjax_file\"></script>\n\t\t"
|
||||
mathjax_file =
|
||||
include_mathjax != "cdn" ? ("file://" * include_mathjax) :
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"
|
||||
mathjax_head =
|
||||
include_mathjax == "" ? "" : "<script src=\"$mathjax_file\"></script>\n\t\t"
|
||||
|
||||
if isijulia()
|
||||
mathjax_head
|
||||
@ -934,8 +1040,9 @@ function plotly_html_body(plt, style = nothing)
|
||||
if isijulia()
|
||||
# require.js adds .js automatically
|
||||
plotly_no_ext =
|
||||
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) : "https://cdn.plot.ly/$(_plotly_min_js_filename)"
|
||||
plotly_no_ext = plotly_no_ext[1:end-3]
|
||||
use_local_dependencies[] ? ("file:///" * plotly_local_file_path[]) :
|
||||
"https://cdn.plot.ly/$(_plotly_min_js_filename)"
|
||||
plotly_no_ext = plotly_no_ext[1:(end - 3)]
|
||||
|
||||
requirejs_prefix = """
|
||||
requirejs.config({
|
||||
@ -984,12 +1091,10 @@ function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{Plotly
|
||||
plotly_show_js(io, plot)
|
||||
end
|
||||
|
||||
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
|
||||
write(io, embeddable_html(plt))
|
||||
end
|
||||
|
||||
|
||||
function _display(plt::Plot{PlotlyBackend})
|
||||
standalone_html_window(plt)
|
||||
end
|
||||
|
||||
@ -25,16 +25,19 @@ for (mime, fmt) in (
|
||||
"image/svg+xml" => "svg",
|
||||
"image/eps" => "eps",
|
||||
)
|
||||
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plotlyjs_syncplot(plt), format = $fmt)
|
||||
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyJSBackend}) =
|
||||
PlotlyJS.savefig(io, plotlyjs_syncplot(plt), format = $fmt)
|
||||
end
|
||||
|
||||
# Use the Plotly implementation for json and html:
|
||||
_show(io::IO, mime::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend}) = plotly_show_js(io, plt)
|
||||
_show(io::IO, mime::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend}) =
|
||||
plotly_show_js(io, plt)
|
||||
|
||||
html_head(plt::Plot{PlotlyJSBackend}) = plotly_html_head(plt)
|
||||
html_body(plt::Plot{PlotlyJSBackend}) = plotly_html_body(plt)
|
||||
|
||||
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = write(io, embeddable_html(plt))
|
||||
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) =
|
||||
write(io, embeddable_html(plt))
|
||||
|
||||
_display(plt::Plot{PlotlyJSBackend}) = display(plotlyjs_syncplot(plt))
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
is_marker_supported(::PyPlotBackend, shape::Shape) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
# problem: https://github.com/tbreloff/Plots.jl/issues/308
|
||||
@ -25,10 +24,11 @@ pycollections = PyPlot.pyimport("matplotlib.collections")
|
||||
pyart3d = PyPlot.art3D
|
||||
pyrcparams = PyPlot.PyDict(PyPlot.matplotlib."rcParams")
|
||||
|
||||
|
||||
# "support" matplotlib v1.5
|
||||
set_facecolor_sym = if PyPlot.version < v"2"
|
||||
@warn("You are using Matplotlib $(PyPlot.version), which is no longer officialy supported by the Plots community. To ensure smooth Plots.jl integration update your Matplotlib library to a version >= 2.0.0")
|
||||
@warn(
|
||||
"You are using Matplotlib $(PyPlot.version), which is no longer officialy supported by the Plots community. To ensure smooth Plots.jl integration update your Matplotlib library to a version >= 2.0.0"
|
||||
)
|
||||
:set_axis_bgcolor
|
||||
else
|
||||
:set_facecolor
|
||||
@ -39,7 +39,6 @@ if !isdefined(PyPlot.PyCall, :_setproperty!)
|
||||
@warn "Plots no longer supports PyCall < 1.90.0 and PyPlot < 2.8.0. Either update PyCall and PyPlot or pin Plots to a version <= 0.23.2."
|
||||
end
|
||||
|
||||
|
||||
# # convert colorant to 4-tuple RGBA
|
||||
# py_color(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
|
||||
# py_color(cvec::ColorVector, α=nothing) = map(py_color, convertColor(cvec, α).v)
|
||||
@ -85,7 +84,6 @@ function py_colormap(cg::PlotUtils.CategoricalColorGradient)
|
||||
end
|
||||
py_colormap(c) = py_colormap(_as_gradient(c))
|
||||
|
||||
|
||||
function py_shading(c, z)
|
||||
cmap = py_colormap(c)
|
||||
ls = pycolors."LightSource"(270, 45)
|
||||
@ -107,7 +105,7 @@ function py_marker(marker::Shape)
|
||||
x, y = coords(marker)
|
||||
n = length(x)
|
||||
mat = zeros(n + 1, 2)
|
||||
for i=1:n
|
||||
for i in 1:n
|
||||
mat[i, 1] = x[i]
|
||||
mat[i, 2] = y[i]
|
||||
end
|
||||
@ -199,9 +197,9 @@ function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
|
||||
nx, ny = length(x), length(y)
|
||||
if !isa(get(series.plotattributes, :z, nothing), Surface) && nx != ny
|
||||
if nx < ny
|
||||
series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
|
||||
series[:x] = Float64[x[mod1(i, nx)] for i in 1:ny]
|
||||
else
|
||||
series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
|
||||
series[:y] = Float64[y[mod1(i, ny)] for i in 1:nx]
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -246,7 +244,6 @@ py_drawfig(fig) = fig."draw"(py_renderer(fig))
|
||||
# get a vector [left, right, bottom, top] in PyPlot coords (origin is bottom-left!)
|
||||
py_extents(obj) = obj."get_window_extent"()."get_points"()
|
||||
|
||||
|
||||
# compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left
|
||||
function py_bbox(obj)
|
||||
fl, fr, fb, ft = py_extents(obj."get_figure"())
|
||||
@ -329,18 +326,12 @@ function py_init_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
|
||||
proj = (proj in (nothing, :none) ? nothing : string(proj))
|
||||
|
||||
# add a new axis, and force it to create a new one by setting a distinct label
|
||||
ax = fig."add_axes"(
|
||||
[0,0,1,1],
|
||||
label = string(gensym()),
|
||||
projection = proj
|
||||
)
|
||||
ax = fig."add_axes"([0, 0, 1, 1], label = string(gensym()), projection = proj)
|
||||
sp.o = ax
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# function _series_added(pkg::PyPlotBackend, plt::Plot, plotattributes::KW)
|
||||
# TODO: change this to accept Subplot??
|
||||
# function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
@ -392,7 +383,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
handles = []
|
||||
discrete_colorbar_values = nothing
|
||||
|
||||
|
||||
# pass in an integer value as an arg, but a levels list as a keyword arg
|
||||
levels = series[:levels]
|
||||
levelargs = if isscalar(levels)
|
||||
@ -444,15 +434,19 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# else
|
||||
for (k, segment) in enumerate(series_segments(series, st; check = true))
|
||||
i, rng = segment.attr_index, segment.range
|
||||
handle = ax."plot"((arg[rng] for arg in xyargs)...;
|
||||
handle = ax."plot"(
|
||||
(arg[rng] for arg in xyargs)...;
|
||||
label = k == 1 ? series[:label] : "",
|
||||
zorder = series[:series_plotindex],
|
||||
color = py_color(single_color(get_linecolor(series, clims, i)), get_linealpha(series, i)),
|
||||
color = py_color(
|
||||
single_color(get_linecolor(series, clims, i)),
|
||||
get_linealpha(series, i),
|
||||
),
|
||||
linewidth = py_thickness_scale(plt, get_linewidth(series, i)),
|
||||
linestyle = py_linestyle(st, get_linestyle(series, i)),
|
||||
solid_capstyle = "butt",
|
||||
dash_capstyle = "butt",
|
||||
drawstyle = py_stepstyle(st)
|
||||
drawstyle = py_stepstyle(st),
|
||||
)[1]
|
||||
push!(handles, handle)
|
||||
end
|
||||
@ -473,11 +467,15 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
:linestyle => py_linestyle(st, get_linestyle(series)),
|
||||
)
|
||||
add_arrows(x, y) do xyprev, xy
|
||||
ax."annotate"("",
|
||||
xytext = (0.001xyprev[1] + 0.999xy[1], 0.001xyprev[2] + 0.999xy[2]),
|
||||
ax."annotate"(
|
||||
"",
|
||||
xytext = (
|
||||
0.001xyprev[1] + 0.999xy[1],
|
||||
0.001xyprev[2] + 0.999xy[2],
|
||||
),
|
||||
xy = xy,
|
||||
arrowprops = arrowprops,
|
||||
zorder = 999
|
||||
zorder = 999,
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -486,9 +484,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
|
||||
# add markers?
|
||||
if series[:markershape] != :none && st in (
|
||||
:path, :scatter, :path3d, :scatter3d, :steppre, :stepmid, :steppost, :bar
|
||||
)
|
||||
if series[:markershape] != :none &&
|
||||
st in (:path, :scatter, :path3d, :scatter3d, :steppre, :stepmid, :steppost, :bar)
|
||||
for segment in series_segments(series, :scatter)
|
||||
i, rng = segment.attr_index, segment.range
|
||||
xyargs = if st == :bar && !isvertical(series)
|
||||
@ -505,15 +502,22 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
end
|
||||
|
||||
handle = ax."scatter"(xyargs...;
|
||||
handle = ax."scatter"(
|
||||
xyargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(_cycle(series[:markershape], i)),
|
||||
s = py_thickness_scale(plt, _cycle(series[:markersize], i)) .^ 2,
|
||||
facecolors = py_color(get_markercolor(series, i), get_markeralpha(series, i)),
|
||||
edgecolors = py_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i)),
|
||||
facecolors = py_color(
|
||||
get_markercolor(series, i),
|
||||
get_markeralpha(series, i),
|
||||
),
|
||||
edgecolors = py_color(
|
||||
get_markerstrokecolor(series, i),
|
||||
get_markerstrokealpha(series, i),
|
||||
),
|
||||
linewidths = py_thickness_scale(plt, get_markerstrokewidth(series, i)),
|
||||
extrakw...
|
||||
extrakw...,
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
@ -521,8 +525,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
if st == :hexbin
|
||||
extrakw[:mincnt] = get(series[:extra_kwargs], :mincnt, nothing)
|
||||
extrakw[:edgecolors] = get(series[:extra_kwargs], :edgecolors, py_color(get_linecolor(series)))
|
||||
handle = ax."hexbin"(x, y;
|
||||
extrakw[:edgecolors] =
|
||||
get(series[:extra_kwargs], :edgecolors, py_color(get_linecolor(series)))
|
||||
handle = ax."hexbin"(
|
||||
x,
|
||||
y;
|
||||
label = series[:label],
|
||||
C = series[:weights],
|
||||
gridsize = series[:bins] == :auto ? 100 : series[:bins], # 100 is the default value
|
||||
@ -530,7 +537,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
alpha = series[:fillalpha],
|
||||
cmap = py_fillcolormap(series), # applies to the pcolorfast object
|
||||
zorder = series[:series_plotindex],
|
||||
extrakw...
|
||||
extrakw...,
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
@ -550,12 +557,16 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
|
||||
# contour lines
|
||||
handle = ax."contour"(x, y, z, levelargs...;
|
||||
handle = ax."contour"(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
levelargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
linewidths = py_thickness_scale(plt, series[:linewidth]),
|
||||
linestyles = py_linestyle(st, series[:linestyle]),
|
||||
extrakw...
|
||||
extrakw...,
|
||||
)
|
||||
if series[:contour_labels] == true
|
||||
ax."clabel"(handle, handle.levels)
|
||||
@ -564,11 +575,15 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
# contour fills
|
||||
if series[:fillrange] !== nothing
|
||||
handle = ax."contourf"(x, y, z, levelargs...;
|
||||
handle = ax."contourf"(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
levelargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
alpha = series[:fillalpha],
|
||||
extrakw...
|
||||
extrakw...,
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
@ -582,23 +597,24 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
if st == :surface
|
||||
if series[:fill_z] !== nothing
|
||||
# the surface colors are different than z-value
|
||||
extrakw[:facecolors] = py_shading(
|
||||
series[:fillcolor],
|
||||
py_handle_surface(series[:fill_z]),
|
||||
)
|
||||
extrakw[:facecolors] =
|
||||
py_shading(series[:fillcolor], py_handle_surface(series[:fill_z]))
|
||||
extrakw[:shade] = false
|
||||
else
|
||||
extrakw[:cmap] = py_fillcolormap(series)
|
||||
end
|
||||
end
|
||||
handle = getproperty(ax, st == :surface ? :plot_surface : :plot_wireframe)(x, y, z;
|
||||
handle = getproperty(ax, st == :surface ? :plot_surface : :plot_wireframe)(
|
||||
x,
|
||||
y,
|
||||
z;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
rstride = series[:stride][1],
|
||||
cstride = series[:stride][2],
|
||||
linewidth = py_thickness_scale(plt, series[:linewidth]),
|
||||
edgecolor = py_color(get_linecolor(series)),
|
||||
extrakw...
|
||||
extrakw...,
|
||||
)
|
||||
push!(handles, handle)
|
||||
|
||||
@ -606,25 +622,31 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
if series[:contours]
|
||||
for (zdir, mat) in (("x", x), ("y", y), ("z", z))
|
||||
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat)
|
||||
handle = ax."contourf"(x, y, z, levelargs...;
|
||||
handle = ax."contourf"(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
levelargs...;
|
||||
zdir = zdir,
|
||||
cmap = py_fillcolormap(series),
|
||||
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat) # where to draw the contour plane
|
||||
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat), # where to draw the contour plane
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
elseif typeof(z) <: AbstractVector
|
||||
# tri-surface plot (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots)
|
||||
handle = ax."plot_trisurf"(x, y, z;
|
||||
handle = ax."plot_trisurf"(
|
||||
x,
|
||||
y,
|
||||
z;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_fillcolormap(series),
|
||||
linewidth = py_thickness_scale(plt, series[:linewidth]),
|
||||
edgecolor = py_color(get_linecolor(series)),
|
||||
extrakw...
|
||||
extrakw...,
|
||||
)
|
||||
push!(handles, handle)
|
||||
else
|
||||
@ -650,7 +672,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
cmap = py_colormap(cgrad(plot_color([:black, :white]))),
|
||||
vmin = 0.0,
|
||||
vmax = 1.0,
|
||||
extent = (xmin - dx, xmax + dx, ymax + dy, ymin - dy)
|
||||
extent = (xmin - dx, xmax + dx, ymax + dy, ymin - dy),
|
||||
)
|
||||
push!(handles, handle)
|
||||
|
||||
@ -670,13 +692,16 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
discrete_colorbar_values = dvals
|
||||
end
|
||||
|
||||
handle = ax."pcolormesh"(x, y, py_mask_nans(z);
|
||||
handle = ax."pcolormesh"(
|
||||
x,
|
||||
y,
|
||||
py_mask_nans(z);
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_fillcolormap(series),
|
||||
alpha = series[:fillalpha],
|
||||
# edgecolors = (series[:linewidth] > 0 ? py_linecolor(series) : "face"),
|
||||
extrakw...
|
||||
extrakw...,
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
@ -691,11 +716,17 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
path;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
edgecolor = py_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
|
||||
facecolor = py_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
|
||||
edgecolor = py_color(
|
||||
get_linecolor(series, clims, i),
|
||||
get_linealpha(series, i),
|
||||
),
|
||||
facecolor = py_color(
|
||||
get_fillcolor(series, clims, i),
|
||||
get_fillalpha(series, i),
|
||||
),
|
||||
linewidth = py_thickness_scale(plt, get_linewidth(series, i)),
|
||||
linestyle = py_linestyle(st, get_linestyle(series, i)),
|
||||
fill = true
|
||||
fill = true,
|
||||
)
|
||||
push!(handle, ax."add_patch"(patches))
|
||||
end
|
||||
@ -725,10 +756,17 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng)
|
||||
end
|
||||
|
||||
handle = getproperty(ax, f)(args..., trues(n), false, py_fillstepstyle(st);
|
||||
handle = getproperty(ax, f)(
|
||||
args...,
|
||||
trues(n),
|
||||
false,
|
||||
py_fillstepstyle(st);
|
||||
zorder = series[:series_plotindex],
|
||||
facecolor = py_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
|
||||
linewidths = 0
|
||||
facecolor = py_color(
|
||||
get_fillcolor(series, clims, i),
|
||||
get_fillalpha(series, i),
|
||||
),
|
||||
linewidths = 0,
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
@ -751,14 +789,13 @@ end
|
||||
|
||||
function py_surround_latextext(latexstring, env)
|
||||
if !isempty(latexstring) && latexstring[1] == '$' && latexstring[end] == '$'
|
||||
unenclosed = latexstring[2:end-1]
|
||||
unenclosed = latexstring[2:(end - 1)]
|
||||
else
|
||||
unenclosed = latexstring
|
||||
end
|
||||
PyPlot.LaTeXStrings.latexstring(env, "{", unenclosed, "}")
|
||||
end
|
||||
|
||||
|
||||
function py_set_ticks(sp, ax, ticks, letter, env)
|
||||
ticks == :auto && return
|
||||
axis = getproperty(ax, Symbol(letter, "axis"))
|
||||
@ -829,7 +866,8 @@ function py_set_scale(ax, sp::Subplot, scale::Symbol, letter::Symbol)
|
||||
10
|
||||
end
|
||||
axis = sp[Symbol(letter, :axis)]
|
||||
kw[Symbol(:linthresh, pyletter)] = NaNMath.max(1e-16, py_compute_axis_minval(sp, axis))
|
||||
kw[Symbol(:linthresh, pyletter)] =
|
||||
NaNMath.max(1e-16, py_compute_axis_minval(sp, axis))
|
||||
"symlog"
|
||||
end
|
||||
func(arg; kw...)
|
||||
@ -857,18 +895,22 @@ function py_set_axis_colors(sp, ax, a::Axis)
|
||||
py_set_spine_color(ax.spines, py_color(a[:foreground_color_border]))
|
||||
axissym = Symbol(a[:letter], :axis)
|
||||
if PyPlot.PyCall.hasproperty(ax, axissym)
|
||||
tickcolor = sp[:framestyle] in (:zerolines, :grid) ? py_color(plot_color(a[:foreground_color_grid], a[:gridalpha])) : py_color(a[:foreground_color_axis])
|
||||
ax."tick_params"(axis=string(a[:letter]), which="both",
|
||||
tickcolor =
|
||||
sp[:framestyle] in (:zerolines, :grid) ?
|
||||
py_color(plot_color(a[:foreground_color_grid], a[:gridalpha])) :
|
||||
py_color(a[:foreground_color_axis])
|
||||
ax."tick_params"(
|
||||
axis = string(a[:letter]),
|
||||
which = "both",
|
||||
colors = tickcolor,
|
||||
labelcolor=py_color(a[:tickfontcolor]))
|
||||
labelcolor = py_color(a[:tickfontcolor]),
|
||||
)
|
||||
getproperty(ax, axissym).label.set_color(py_color(a[:guidefontcolor]))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
# update the fig
|
||||
w, h = plt[:size]
|
||||
@ -915,7 +957,9 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
:title
|
||||
end
|
||||
getproperty(ax, func)."set_text"(sp[:title])
|
||||
getproperty(ax, func)."set_fontsize"(py_thickness_scale(plt, sp[:titlefontsize]))
|
||||
getproperty(ax, func)."set_fontsize"(
|
||||
py_thickness_scale(plt, sp[:titlefontsize]),
|
||||
)
|
||||
getproperty(ax, func)."set_family"(sp[:titlefontfamily])
|
||||
getproperty(ax, func)."set_color"(py_color(sp[:titlefontcolor]))
|
||||
# ax[:set_title](sp[:title], loc = loc)
|
||||
@ -928,14 +972,17 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
colorbar_series = slist[findfirst(hascolorbar.(slist))]
|
||||
handle = colorbar_series[:serieshandle][end]
|
||||
kw = KW()
|
||||
if !isempty(sp[:zaxis][:discrete_values]) && colorbar_series[:seriestype] == :heatmap
|
||||
if !isempty(sp[:zaxis][:discrete_values]) &&
|
||||
colorbar_series[:seriestype] == :heatmap
|
||||
locator, formatter = get_locator_and_formatter(sp[:zaxis][:discrete_values])
|
||||
# kw[:values] = eachindex(sp[:zaxis][:discrete_values])
|
||||
kw[:values] = sp[:zaxis][:continuous_values]
|
||||
kw[:ticks] = locator
|
||||
kw[:format] = formatter
|
||||
kw[:boundaries] = vcat(0, kw[:values] + 0.5)
|
||||
elseif any(colorbar_series[attr] !== nothing for attr in (:line_z, :fill_z, :marker_z))
|
||||
elseif any(
|
||||
colorbar_series[attr] !== nothing for attr in (:line_z, :fill_z, :marker_z)
|
||||
)
|
||||
cmin, cmax = get_clims(sp)
|
||||
norm = pycolors."Normalize"(vmin = cmin, vmax = cmax)
|
||||
f = if colorbar_series[:line_z] !== nothing
|
||||
@ -952,7 +999,10 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
kw[:spacing] = "proportional"
|
||||
|
||||
if RecipesPipeline.is3d(sp) || ispolar(sp)
|
||||
cbax = fig."add_axes"([0.9, 0.1, 0.03, 0.8], label=string("cbar", sp[:subplot_index]))
|
||||
cbax = fig."add_axes"(
|
||||
[0.9, 0.1, 0.03, 0.8],
|
||||
label = string("cbar", sp[:subplot_index]),
|
||||
)
|
||||
cb = fig."colorbar"(handle; cax = cbax, kw...)
|
||||
else
|
||||
# divider approach works only with 2d plots
|
||||
@ -977,8 +1027,18 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
colorbar_orientation = "horizontal"
|
||||
end
|
||||
|
||||
cbax = divider.append_axes(colorbar_position, size="5%", pad=colorbar_pad, label=string("cbar", sp[:subplot_index])) # Reasonable value works most of the usecases
|
||||
cb = fig."colorbar"(handle; cax=cbax, orientation = colorbar_orientation, kw...)
|
||||
cbax = divider.append_axes(
|
||||
colorbar_position,
|
||||
size = "5%",
|
||||
pad = colorbar_pad,
|
||||
label = string("cbar", sp[:subplot_index]),
|
||||
) # Reasonable value works most of the usecases
|
||||
cb = fig."colorbar"(
|
||||
handle;
|
||||
cax = cbax,
|
||||
orientation = colorbar_orientation,
|
||||
kw...,
|
||||
)
|
||||
|
||||
if sp[:colorbar] == :left
|
||||
cbax.yaxis.set_ticks_position("left")
|
||||
@ -987,10 +1047,14 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
elseif sp[:colorbar] == :bottom
|
||||
cbax.xaxis.set_ticks_position("bottom")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
cb."set_label"(sp[:colorbar_title],size=py_thickness_scale(plt, sp[:colorbar_titlefontsize]),family=sp[:colorbar_titlefontfamily], color = py_color(sp[:colorbar_titlefontcolor]))
|
||||
cb."set_label"(
|
||||
sp[:colorbar_title],
|
||||
size = py_thickness_scale(plt, sp[:colorbar_titlefontsize]),
|
||||
family = sp[:colorbar_titlefontfamily],
|
||||
color = py_color(sp[:colorbar_titlefontcolor]),
|
||||
)
|
||||
|
||||
# cb."formatter".set_useOffset(false) # This for some reason does not work, must be a pyplot bug, instead this is a workaround:
|
||||
cb."formatter".set_powerlimits((-Inf, Inf))
|
||||
@ -1009,7 +1073,8 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
ticks_letter = :y
|
||||
end
|
||||
py_set_scale(cb.ax, sp, sp[:colorbar_scale], ticks_letter)
|
||||
sp[:colorbar_ticks] == :native ? nothing : py_set_ticks(sp, cb.ax, ticks, ticks_letter, env)
|
||||
sp[:colorbar_ticks] == :native ? nothing :
|
||||
py_set_ticks(sp, cb.ax, ticks, ticks_letter, env)
|
||||
|
||||
for lab in cbar_axis."get_ticklabels"()
|
||||
lab."set_fontsize"(py_thickness_scale(plt, sp[:colorbar_tickfontsize]))
|
||||
@ -1022,18 +1087,16 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
cbar_axis."set_tick_params"(
|
||||
direction = axis[:tick_direction] == :out ? "out" : "in",
|
||||
width = py_thickness_scale(plt, intensity),
|
||||
length = axis[:tick_direction] == :none ? 0 : 5 * py_thickness_scale(plt, intensity)
|
||||
length = axis[:tick_direction] == :none ? 0 :
|
||||
5 * py_thickness_scale(plt, intensity),
|
||||
)
|
||||
|
||||
|
||||
|
||||
cb.outline."set_linewidth"(py_thickness_scale(plt, 1))
|
||||
|
||||
sp.attr[:cbar_handle] = cb
|
||||
sp.attr[:cbar_ax] = cbax
|
||||
end
|
||||
|
||||
|
||||
# framestyle
|
||||
if !ispolar(sp) && !RecipesPipeline.is3d(sp)
|
||||
for pos in ("left", "right", "top", "bottom")
|
||||
@ -1056,8 +1119,10 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
ax.tick_params(top = true) # Add ticks too
|
||||
ax.tick_params(right = true) # Add ticks too
|
||||
elseif sp[:framestyle] in (:axes, :origin)
|
||||
sp[:xaxis][:mirror] ? ax.spines["bottom"]."set_visible"(false) : ax.spines["top"]."set_visible"(false)
|
||||
sp[:yaxis][:mirror] ? ax.spines["left"]."set_visible"(false) : ax.spines["right"]."set_visible"(false)
|
||||
sp[:xaxis][:mirror] ? ax.spines["bottom"]."set_visible"(false) :
|
||||
ax.spines["top"]."set_visible"(false)
|
||||
sp[:yaxis][:mirror] ? ax.spines["left"]."set_visible"(false) :
|
||||
ax.spines["right"]."set_visible"(false)
|
||||
if sp[:framestyle] == :origin
|
||||
ax.spines["bottom"]."set_position"("zero")
|
||||
ax.spines["left"]."set_position"("zero")
|
||||
@ -1073,8 +1138,16 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
end
|
||||
end
|
||||
if sp[:framestyle] == :zerolines
|
||||
ax."axhline"(y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
|
||||
ax."axvline"(x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
|
||||
ax."axhline"(
|
||||
y = 0,
|
||||
color = py_color(sp[:xaxis][:foreground_color_axis]),
|
||||
lw = py_thickness_scale(plt, 0.75),
|
||||
)
|
||||
ax."axvline"(
|
||||
x = 0,
|
||||
color = py_color(sp[:yaxis][:foreground_color_axis]),
|
||||
lw = py_thickness_scale(plt, 0.75),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1117,7 +1190,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
"family" => axis[:tickfontfamily],
|
||||
"size" => py_thickness_scale(plt, axis[:tickfontsize]),
|
||||
"rotation" => axis[:tickfontrotation],
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
positions = getproperty(ax, Symbol("get_", letter, "ticks"))()
|
||||
@ -1125,7 +1198,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
if RecipesPipeline.is3d(sp)
|
||||
getproperty(ax, Symbol("set_", letter, "ticklabels"))(
|
||||
positions;
|
||||
(Symbol(k) => v for (k, v) in fontProperties)...
|
||||
(Symbol(k) => v for (k, v) in fontProperties)...,
|
||||
)
|
||||
else
|
||||
getproperty(ax, Symbol("set_", letter, "ticklabels"))(
|
||||
@ -1143,7 +1216,8 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
pyaxis."set_tick_params"(
|
||||
direction = axis[:tick_direction] == :out ? "out" : "in",
|
||||
width = py_thickness_scale(plt, intensity),
|
||||
length = axis[:tick_direction] == :none ? 0 : 5 * py_thickness_scale(plt, intensity)
|
||||
length = axis[:tick_direction] == :none ? 0 :
|
||||
5 * py_thickness_scale(plt, intensity),
|
||||
)
|
||||
|
||||
getproperty(ax, Symbol("set_", letter, "label"))(axis[:guide])
|
||||
@ -1163,14 +1237,15 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
pyaxis."label"."set_rotation"(axis[:guidefontrotation])
|
||||
end
|
||||
|
||||
|
||||
if axis[:grid] && !(ticks in (:none, nothing, false))
|
||||
fgcolor = py_color(axis[:foreground_color_grid])
|
||||
pyaxis."grid"(true,
|
||||
pyaxis."grid"(
|
||||
true,
|
||||
color = fgcolor,
|
||||
linestyle = py_linestyle(:line, axis[:gridstyle]),
|
||||
linewidth = py_thickness_scale(plt, axis[:gridlinewidth]),
|
||||
alpha = axis[:gridalpha])
|
||||
alpha = axis[:gridalpha],
|
||||
)
|
||||
ax."set_axisbelow"(true)
|
||||
else
|
||||
pyaxis."grid"(false)
|
||||
@ -1178,11 +1253,14 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
#
|
||||
|
||||
if axis[:minorticks] > 1
|
||||
pyaxis."set_minor_locator"(PyPlot.matplotlib.ticker.AutoMinorLocator(axis[:minorticks]))
|
||||
pyaxis."set_minor_locator"(
|
||||
PyPlot.matplotlib.ticker.AutoMinorLocator(axis[:minorticks]),
|
||||
)
|
||||
pyaxis."set_tick_params"(
|
||||
which = "minor",
|
||||
direction = axis[:tick_direction] == :out ? "out" : "in",
|
||||
length = axis[:tick_direction] == :none ? 0 : py_thickness_scale(plt, intensity),
|
||||
length = axis[:tick_direction] == :none ? 0 :
|
||||
py_thickness_scale(plt, intensity),
|
||||
)
|
||||
end
|
||||
|
||||
@ -1193,19 +1271,20 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
pyaxis."set_tick_params"(
|
||||
which = "minor",
|
||||
direction = axis[:tick_direction] == :out ? "out" : "in",
|
||||
length = axis[:tick_direction] == :none ? 0 : py_thickness_scale(plt, intensity)
|
||||
length = axis[:tick_direction] == :none ? 0 :
|
||||
py_thickness_scale(plt, intensity),
|
||||
)
|
||||
|
||||
pyaxis."grid"(true,
|
||||
pyaxis."grid"(
|
||||
true,
|
||||
which = "minor",
|
||||
color = fgcolor,
|
||||
linestyle = py_linestyle(:line, axis[:minorgridstyle]),
|
||||
linewidth = py_thickness_scale(plt, axis[:minorgridlinewidth]),
|
||||
alpha = axis[:minorgridalpha])
|
||||
alpha = axis[:minorgridalpha],
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
|
||||
py_set_axis_colors(sp, ax, axis)
|
||||
end
|
||||
|
||||
@ -1261,7 +1340,6 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
py_drawfig(fig)
|
||||
end
|
||||
|
||||
|
||||
# Set the (left, top, right, bottom) minimum padding around the plot area
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
@ -1277,7 +1355,12 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
rightpad = 0mm
|
||||
bottompad = 0mm
|
||||
|
||||
for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax), py_bbox_legend(ax))
|
||||
for bb in (
|
||||
py_bbox_axis(ax, "x"),
|
||||
py_bbox_axis(ax, "y"),
|
||||
py_bbox_title(ax),
|
||||
py_bbox_legend(ax),
|
||||
)
|
||||
if ispositive(width(bb)) && ispositive(height(bb))
|
||||
leftpad = max(leftpad, left(plotbb) - left(bb))
|
||||
toppad = max(toppad, top(plotbb) - top(bb))
|
||||
@ -1288,7 +1371,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
|
||||
if haskey(sp.attr, :cbar_ax) # Treat colorbar the same way
|
||||
ax = sp.attr[:cbar_handle]."ax"
|
||||
for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax), )
|
||||
for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax))
|
||||
if ispositive(width(bb)) && ispositive(height(bb))
|
||||
leftpad = max(leftpad, left(plotbb) - left(bb))
|
||||
toppad = max(toppad, top(plotbb) - top(bb))
|
||||
@ -1298,7 +1381,6 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# optionally add the width of colorbar labels and colorbar to rightpad
|
||||
if RecipesPipeline.is3d(sp) && haskey(sp.attr, :cbar_ax)
|
||||
bb = py_bbox(sp.attr[:cbar_handle]."ax"."get_yticklabels"())
|
||||
@ -1316,7 +1398,6 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
sp.minpad = Tuple(dpi_factor .* [leftpad, toppad, rightpad, bottompad])
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val)
|
||||
@ -1324,19 +1405,21 @@ function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val)
|
||||
ax."annotate"(val, xy = (x, y), zorder = 999, annotation_clip = false)
|
||||
end
|
||||
|
||||
|
||||
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
|
||||
ax = sp.o
|
||||
ax."annotate"(val.str,
|
||||
ax."annotate"(
|
||||
val.str,
|
||||
xy = (x, y),
|
||||
family = val.font.family,
|
||||
color = py_color(val.font.color),
|
||||
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
|
||||
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
|
||||
horizontalalignment = val.font.halign == :hcenter ? "center" :
|
||||
string(val.font.halign),
|
||||
verticalalignment = val.font.valign == :vcenter ? "center" :
|
||||
string(val.font.valign),
|
||||
rotation = val.font.rotation,
|
||||
size = py_thickness_scale(sp.plt, val.font.pointsize),
|
||||
zorder = 999,
|
||||
annotation_clip = false
|
||||
annotation_clip = false,
|
||||
)
|
||||
end
|
||||
|
||||
@ -1355,7 +1438,7 @@ function py_legend_pos(pos::Tuple{<:Real,Symbol})
|
||||
return join([yanchors[legend_anchor_index(s)], xanchors[legend_anchor_index(c)]], ' ')
|
||||
end
|
||||
|
||||
function py_legend_bbox(pos::Tuple{T,Symbol}) where T<:Real
|
||||
function py_legend_bbox(pos::Tuple{T,Symbol}) where {T<:Real}
|
||||
if pos[2] === :outer
|
||||
return legend_pos_from_angle(pos[1], -0.15, 0.5, 1.0, -0.15, 0.5, 1.0)
|
||||
end
|
||||
@ -1374,32 +1457,66 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
if should_add_to_legend(series)
|
||||
clims = get_clims(sp, series)
|
||||
# add a line/marker and a label
|
||||
push!(handles,
|
||||
push!(
|
||||
handles,
|
||||
if series[:seriestype] == :shape || series[:fillrange] !== nothing
|
||||
pypatches."Patch"(
|
||||
edgecolor = py_color(single_color(get_linecolor(series, clims)), get_linealpha(series)),
|
||||
facecolor = py_color(single_color(get_fillcolor(series, clims)), get_fillalpha(series)),
|
||||
linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)),
|
||||
linestyle = py_linestyle(series[:seriestype], get_linestyle(series)),
|
||||
capstyle = "butt"
|
||||
edgecolor = py_color(
|
||||
single_color(get_linecolor(series, clims)),
|
||||
get_linealpha(series),
|
||||
),
|
||||
facecolor = py_color(
|
||||
single_color(get_fillcolor(series, clims)),
|
||||
get_fillalpha(series),
|
||||
),
|
||||
linewidth = py_thickness_scale(
|
||||
plt,
|
||||
clamp(get_linewidth(series), 0, 5),
|
||||
),
|
||||
linestyle = py_linestyle(
|
||||
series[:seriestype],
|
||||
get_linestyle(series),
|
||||
),
|
||||
capstyle = "butt",
|
||||
)
|
||||
elseif series[:seriestype] in (:path, :straightline, :scatter, :steppre, :stepmid, :steppost)
|
||||
elseif series[:seriestype] in
|
||||
(:path, :straightline, :scatter, :steppre, :stepmid, :steppost)
|
||||
hasline = get_linewidth(series) > 0
|
||||
PyPlot.plt."Line2D"((0, 1),(0,0),
|
||||
color = py_color(single_color(get_linecolor(series, clims)), get_linealpha(series)),
|
||||
linewidth = py_thickness_scale(plt, hasline * sp[:legendfontsize] / 8),
|
||||
PyPlot.plt."Line2D"(
|
||||
(0, 1),
|
||||
(0, 0),
|
||||
color = py_color(
|
||||
single_color(get_linecolor(series, clims)),
|
||||
get_linealpha(series),
|
||||
),
|
||||
linewidth = py_thickness_scale(
|
||||
plt,
|
||||
hasline * sp[:legendfontsize] / 8,
|
||||
),
|
||||
linestyle = py_linestyle(:path, get_linestyle(series)),
|
||||
solid_capstyle = "butt", solid_joinstyle = "miter",
|
||||
dash_capstyle = "butt", dash_joinstyle = "miter",
|
||||
solid_capstyle = "butt",
|
||||
solid_joinstyle = "miter",
|
||||
dash_capstyle = "butt",
|
||||
dash_joinstyle = "miter",
|
||||
marker = py_marker(_cycle(series[:markershape], 1)),
|
||||
markersize = py_thickness_scale(plt, 0.8 * sp[:legendfontsize]),
|
||||
markeredgecolor = py_color(single_color(get_markerstrokecolor(series)), get_markerstrokealpha(series)),
|
||||
markerfacecolor = py_color(single_color(get_markercolor(series, clims)), get_markeralpha(series)),
|
||||
markeredgewidth = py_thickness_scale(plt, 0.8 * get_markerstrokewidth(series) * sp[:legendfontsize] / first(series[:markersize])) # retain the markersize/markerstroke ratio from the markers on the plot
|
||||
markeredgecolor = py_color(
|
||||
single_color(get_markerstrokecolor(series)),
|
||||
get_markerstrokealpha(series),
|
||||
),
|
||||
markerfacecolor = py_color(
|
||||
single_color(get_markercolor(series, clims)),
|
||||
get_markeralpha(series),
|
||||
),
|
||||
markeredgewidth = py_thickness_scale(
|
||||
plt,
|
||||
0.8 * get_markerstrokewidth(series) * sp[:legendfontsize] /
|
||||
first(series[:markersize]),
|
||||
), # retain the markersize/markerstroke ratio from the markers on the plot
|
||||
)
|
||||
else
|
||||
series[:serieshandle][1]
|
||||
end
|
||||
end,
|
||||
)
|
||||
push!(labels, series[:label])
|
||||
end
|
||||
@ -1408,7 +1525,8 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
# if anything was added, call ax.legend and set the colors
|
||||
if !isempty(handles)
|
||||
leg = legend_angle(leg)
|
||||
leg = ax."legend"(handles,
|
||||
leg = ax."legend"(
|
||||
handles,
|
||||
labels,
|
||||
loc = py_legend_pos(leg),
|
||||
bbox_to_anchor = py_legend_bbox(leg),
|
||||
@ -1418,18 +1536,28 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
edgecolor = py_color(sp[:foreground_color_legend]),
|
||||
framealpha = alpha(plot_color(sp[:background_color_legend])),
|
||||
fancybox = false, # makes the legend box square
|
||||
borderpad = 0.8 # to match GR legendbox
|
||||
borderpad = 0.8, # to match GR legendbox
|
||||
)
|
||||
frame = leg."get_frame"()
|
||||
frame."set_linewidth"(py_thickness_scale(plt, 1))
|
||||
leg."set_zorder"(1000)
|
||||
if sp[:legendtitle] !== nothing
|
||||
leg."set_title"(sp[:legendtitle])
|
||||
PyPlot.plt."setp"(leg."get_title"(), color = py_color(sp[:legendtitlefontcolor]), family = sp[:legendtitlefontfamily], fontsize = py_thickness_scale(plt, sp[:legendtitlefontsize]))
|
||||
PyPlot.plt."setp"(
|
||||
leg."get_title"(),
|
||||
color = py_color(sp[:legendtitlefontcolor]),
|
||||
family = sp[:legendtitlefontfamily],
|
||||
fontsize = py_thickness_scale(plt, sp[:legendtitlefontsize]),
|
||||
)
|
||||
end
|
||||
|
||||
for txt in leg."get_texts"()
|
||||
PyPlot.plt."setp"(txt, color = py_color(sp[:legendfontcolor]), family = sp[:legendfontfamily], fontsize = py_thickness_scale(plt, sp[:legendfontsize]))
|
||||
PyPlot.plt."setp"(
|
||||
txt,
|
||||
color = py_color(sp[:legendfontcolor]),
|
||||
family = sp[:legendfontfamily],
|
||||
fontsize = py_thickness_scale(plt, sp[:legendfontsize]),
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1437,7 +1565,6 @@ end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
|
||||
# Use the bounding boxes (and methods left/top/right/bottom/width/height) `sp.bbox` and `sp.plotarea` to
|
||||
# position the subplot in the backend.
|
||||
function _update_plot_object(plt::Plot{PyPlotBackend})
|
||||
@ -1452,7 +1579,12 @@ function _update_plot_object(plt::Plot{PyPlotBackend})
|
||||
if haskey(sp.attr, :cbar_ax) && RecipesPipeline.is3d(sp) # 2D plots are completely handled by axis dividers
|
||||
cbw = sp.attr[:cbar_width]
|
||||
# this is the bounding box of just the colors of the colorbar (not labels)
|
||||
cb_bbox = BoundingBox(right(sp.bbox)-cbw - 2mm, top(sp.bbox) + 2mm, _cbar_width-1mm, height(sp.bbox) - 4mm)
|
||||
cb_bbox = BoundingBox(
|
||||
right(sp.bbox) - cbw - 2mm,
|
||||
top(sp.bbox) + 2mm,
|
||||
_cbar_width - 1mm,
|
||||
height(sp.bbox) - 4mm,
|
||||
)
|
||||
pcts = bbox_to_pcts(cb_bbox, figw, figh)
|
||||
sp.attr[:cbar_ax]."set_position"(pcts)
|
||||
end
|
||||
@ -1465,7 +1597,6 @@ end
|
||||
|
||||
_display(plt::Plot{PyPlotBackend}) = plt.o."show"()
|
||||
|
||||
|
||||
for (mime, fmt) in (
|
||||
"application/eps" => "eps",
|
||||
"image/eps" => "eps",
|
||||
@ -1473,7 +1604,7 @@ for (mime, fmt) in (
|
||||
"image/png" => "png",
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg",
|
||||
"application/x-tex" => "pgf"
|
||||
"application/x-tex" => "pgf",
|
||||
)
|
||||
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PyPlotBackend})
|
||||
fig = plt.o
|
||||
@ -1484,7 +1615,7 @@ for (mime, fmt) in (
|
||||
# figsize = map(px2inch, plt[:size]),
|
||||
facecolor = fig."get_facecolor"(),
|
||||
edgecolor = "none",
|
||||
dpi = plt[:dpi]
|
||||
dpi = plt[:dpi],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
|
||||
# https://github.com/Evizero/UnicodePlots.jl
|
||||
|
||||
|
||||
# don't warn on unsupported... there's just too many warnings!!
|
||||
warn_on_unsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing
|
||||
|
||||
@ -17,7 +16,6 @@ function _canvas_map()
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
||||
function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
plt.o = []
|
||||
@ -49,13 +47,16 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
if length(sp.series_list) == 1
|
||||
series = sp.series_list[1]
|
||||
if series[:seriestype] == :spy
|
||||
push!(plt.o, UnicodePlots.spy(
|
||||
push!(
|
||||
plt.o,
|
||||
UnicodePlots.spy(
|
||||
series[:z].surf,
|
||||
width = width,
|
||||
height = height,
|
||||
title = sp[:title],
|
||||
canvas = canvas_type
|
||||
))
|
||||
canvas = canvas_type,
|
||||
),
|
||||
)
|
||||
continue
|
||||
end
|
||||
end
|
||||
@ -65,13 +66,16 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
# canvas_type = UnicodePlots.BarplotGraphics
|
||||
# end
|
||||
|
||||
o = UnicodePlots.Plot(x, y, canvas_type;
|
||||
o = UnicodePlots.Plot(
|
||||
x,
|
||||
y,
|
||||
canvas_type;
|
||||
width = width,
|
||||
height = height,
|
||||
title = sp[:title],
|
||||
xlim = xlim,
|
||||
ylim = ylim,
|
||||
border = isijulia() ? :ascii : :solid
|
||||
border = isijulia() ? :ascii : :solid,
|
||||
)
|
||||
|
||||
# set the axis labels
|
||||
@ -88,7 +92,6 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, plotattributes, addlegend::Bool, xlim, ylim)
|
||||
# get the function, or special handling for step/bar/hist
|
||||
@ -121,10 +124,14 @@ function addUnicodeSeries!(o, plotattributes, addlegend::Bool, xlim, ylim)
|
||||
label = addlegend ? plotattributes[:label] : ""
|
||||
|
||||
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
|
||||
color = plotattributes[:linecolor] in UnicodePlots.color_cycle ? plotattributes[:linecolor] : :auto
|
||||
color =
|
||||
plotattributes[:linecolor] in UnicodePlots.color_cycle ?
|
||||
plotattributes[:linecolor] : :auto
|
||||
|
||||
# add the series
|
||||
x, y = RecipesPipeline.unzip(collect(Base.Iterators.filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
|
||||
x, y = RecipesPipeline.unzip(
|
||||
collect(Base.Iterators.filter(xy -> isfinite(xy[1]) && isfinite(xy[2]), zip(x, y))),
|
||||
)
|
||||
func(o, x, y; color = color, name = label)
|
||||
end
|
||||
|
||||
@ -172,7 +179,6 @@ function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
function _display(plt::Plot{UnicodePlotsBackend})
|
||||
unicodeplots_rebuild(plt)
|
||||
map(show, plt.o)
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
|
||||
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
|
||||
|
||||
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr, :window_title, "Plots.jl"))
|
||||
function standalone_html(
|
||||
plt::AbstractPlot;
|
||||
title::AbstractString = get(plt.attr, :window_title, "Plots.jl"),
|
||||
)
|
||||
"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -49,7 +52,8 @@ function standalone_html_window(plt::AbstractPlot)
|
||||
old = use_local_dependencies[] # save state to restore afterwards
|
||||
# if we open a browser ourself, we can host local files, so
|
||||
# when we have a local plotly downloaded this is the way to go!
|
||||
use_local_dependencies[] = plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
|
||||
use_local_dependencies[] =
|
||||
plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
|
||||
filename = write_temp_html(plt)
|
||||
open_browser_window(filename)
|
||||
# restore for other backends
|
||||
@ -58,7 +62,9 @@ end
|
||||
|
||||
# uses wkhtmltopdf/wkhtmltoimage: http://wkhtmltopdf.org/downloads.html
|
||||
function html_to_png(html_fn, png_fn, w, h)
|
||||
run(`wkhtmltoimage -f png -q --width $w --height $h --disable-smart-width $html_fn $png_fn`)
|
||||
run(
|
||||
`wkhtmltoimage -f png -q --width $w --height $h --disable-smart-width $html_fn $png_fn`,
|
||||
)
|
||||
end
|
||||
|
||||
function show_png_from_html(io::IO, plt::AbstractPlot)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# These functions return an operator for use in `get_clims(::Seres, op)`
|
||||
process_clims(lims::Tuple{<:Number,<:Number}) = (zlims -> ifelse.(isfinite.(lims), lims, zlims)) ∘ ignorenan_extrema
|
||||
process_clims(lims::Tuple{<:Number,<:Number}) =
|
||||
(zlims -> ifelse.(isfinite.(lims), lims, zlims)) ∘ ignorenan_extrema
|
||||
process_clims(s::Union{Symbol,Nothing,Missing}) = ignorenan_extrema
|
||||
# don't specialize on ::Function otherwise python functions won't work
|
||||
process_clims(f) = f
|
||||
@ -33,7 +34,12 @@ values of the input.
|
||||
function get_clims(series::Series, op = ignorenan_extrema)
|
||||
zmin, zmax = Inf, -Inf
|
||||
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin)
|
||||
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
||||
for vals in (
|
||||
series[:seriestype] in z_colored_series ? series[:z] : nothing,
|
||||
series[:line_z],
|
||||
series[:marker_z],
|
||||
series[:fill_z],
|
||||
)
|
||||
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing,Real})
|
||||
zmin, zmax = _update_clims(zmin, zmax, op(vals.surf)...)
|
||||
elseif (vals !== nothing) && (eltype(vals) <: Union{Missing,Real})
|
||||
@ -61,7 +67,7 @@ function colorbar_style(series::Series)
|
||||
elseif iscontour(series)
|
||||
cbar_lines
|
||||
elseif series[:seriestype] ∈ (:heatmap, :surface) ||
|
||||
any(series[z] !== nothing for z ∈ [:marker_z,:line_z,:fill_z])
|
||||
any(series[z] !== nothing for z in [:marker_z, :line_z, :fill_z])
|
||||
cbar_gradient
|
||||
else
|
||||
nothing
|
||||
@ -69,7 +75,8 @@ function colorbar_style(series::Series)
|
||||
end
|
||||
|
||||
hascolorbar(series::Series) = colorbar_style(series) !== nothing
|
||||
hascolorbar(sp::Subplot) = sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp))
|
||||
hascolorbar(sp::Subplot) =
|
||||
sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp))
|
||||
|
||||
function get_colorbar_ticks(sp::Subplot; update = true)
|
||||
if update || !haskey(sp.attr, :colorbar_optimized_ticks)
|
||||
|
||||
@ -52,9 +52,8 @@ function coords(shapes::AVec{<:Shape})
|
||||
end
|
||||
|
||||
"get an array of tuples of points on a circle with radius `r`"
|
||||
partialcircle(start_θ, end_θ, n=20, r=1) = [
|
||||
(r*cos(u), r*sin(u)) for u in range(start_θ, stop=end_θ, length=n)
|
||||
]
|
||||
partialcircle(start_θ, end_θ, n = 20, r = 1) =
|
||||
[(r * cos(u), r * sin(u)) for u in range(start_θ, stop = end_θ, length = n)]
|
||||
|
||||
"interleave 2 vectors into each other (like a zipper's teeth)"
|
||||
function weave(x, y; ordering = Vector[x, y])
|
||||
@ -82,23 +81,32 @@ function makestar(n; offset=-0.5, radius=1.0)
|
||||
end
|
||||
|
||||
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
|
||||
makeshape(n; offset=-0.5, radius=1.0) = Shape(
|
||||
partialcircle(offset * π, offset * π + 2π, n+1, radius)
|
||||
)
|
||||
makeshape(n; offset = -0.5, radius = 1.0) =
|
||||
Shape(partialcircle(offset * π, offset * π + 2π, n + 1, radius))
|
||||
|
||||
function makecross(; offset = -0.5, radius = 1.0)
|
||||
z2 = offset * π
|
||||
z1 = z2 - π / 8
|
||||
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
|
||||
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
|
||||
Shape(weave(outercircle, innercircle,
|
||||
ordering=Vector[outercircle, innercircle, outercircle]))
|
||||
Shape(
|
||||
weave(
|
||||
outercircle,
|
||||
innercircle,
|
||||
ordering = Vector[outercircle, innercircle, outercircle],
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
from_polar(angle, dist) = P2(dist * cos(angle), dist * sin(angle))
|
||||
|
||||
makearrowhead(angle; h = 2.0, w = 0.4, tip = from_polar(angle, h)) = Shape(
|
||||
P2[(0, 0), from_polar(angle - 0.5π, w) - tip, from_polar(angle + 0.5π, w) - tip, (0, 0)]
|
||||
P2[
|
||||
(0, 0),
|
||||
from_polar(angle - 0.5π, w) - tip,
|
||||
from_polar(angle + 0.5π, w) - tip,
|
||||
(0, 0),
|
||||
],
|
||||
)
|
||||
|
||||
const _shapes = KW(
|
||||
@ -133,12 +141,12 @@ function center(shape::Shape)
|
||||
x, y = coords(shape)
|
||||
n = length(x)
|
||||
A, Cx, Cy = 0, 0, 0
|
||||
for i ∈ 1:n
|
||||
for i in 1:n
|
||||
ip1 = i == n ? 1 : i + 1
|
||||
A += x[i] * y[ip1] - x[ip1] * y[i]
|
||||
end
|
||||
A *= 0.5
|
||||
for i ∈ 1:n
|
||||
for i in 1:n
|
||||
ip1 = i == n ? 1 : i + 1
|
||||
m = (x[i] * y[ip1] - x[ip1] * y[i])
|
||||
Cx += (x[i] + x[ip1]) * m
|
||||
@ -150,20 +158,20 @@ end
|
||||
function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = coords(shape)
|
||||
cx, cy = c
|
||||
for i ∈ eachindex(sx)
|
||||
for i in eachindex(sx)
|
||||
sx[i] = (sx[i] - cx) * x + cx
|
||||
sy[i] = (sy[i] - cy) * y + cy
|
||||
end
|
||||
shape
|
||||
end
|
||||
|
||||
scale(shape::Shape, x::Real, y::Real=x, c=center(shape)) = scale!(deepcopy(shape), x, y, c)
|
||||
|
||||
scale(shape::Shape, x::Real, y::Real = x, c = center(shape)) =
|
||||
scale!(deepcopy(shape), x, y, c)
|
||||
|
||||
"translate a Shape in space"
|
||||
function translate!(shape::Shape, x::Real, y::Real = x)
|
||||
sx, sy = coords(shape)
|
||||
for i ∈ eachindex(sx)
|
||||
for i in eachindex(sx)
|
||||
sx[i] += x
|
||||
sy[i] += y
|
||||
end
|
||||
@ -172,22 +180,18 @@ end
|
||||
|
||||
translate(shape::Shape, x::Real, y::Real = x) = translate!(deepcopy(shape), x, y)
|
||||
|
||||
rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real) = (
|
||||
(x - centerx) * cos(Θ) - (y - centery) * sin(Θ) + centerx
|
||||
)
|
||||
rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real) =
|
||||
((x - centerx) * cos(Θ) - (y - centery) * sin(Θ) + centerx)
|
||||
|
||||
rotate_y(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real) = (
|
||||
(y - centery) * cos(Θ) + (x - centerx) * sin(Θ) + centery
|
||||
)
|
||||
rotate_y(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real) =
|
||||
((y - centery) * cos(Θ) + (x - centerx) * sin(Θ) + centery)
|
||||
|
||||
rotate(x::Real, y::Real, θ::Real, c=center(shape)) = (
|
||||
rotate_x(x, y, Θ, c...),
|
||||
rotate_y(x, y, Θ, c...),
|
||||
)
|
||||
rotate(x::Real, y::Real, θ::Real, c = center(shape)) =
|
||||
(rotate_x(x, y, Θ, c...), rotate_y(x, y, Θ, c...))
|
||||
|
||||
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
||||
x, y = coords(shape)
|
||||
for i ∈ eachindex(x)
|
||||
for i in eachindex(x)
|
||||
xi = rotate_x(x[i], y[i], Θ, c...)
|
||||
yi = rotate_y(x[i], y[i], Θ, c...)
|
||||
x[i], y[i] = xi, yi
|
||||
@ -413,7 +417,6 @@ function stroke(args...; alpha=nothing)
|
||||
Stroke(width, color, alpha, style)
|
||||
end
|
||||
|
||||
|
||||
struct Brush
|
||||
size # fillrange, markersize, or any other sizey attribute
|
||||
color
|
||||
@ -490,7 +493,7 @@ function series_annotations(strs::AVec, args...)
|
||||
# scale!(s, scalefactor, scalefactor, (0, 0))
|
||||
# end
|
||||
# end
|
||||
SeriesAnnotations([_text_label(s, fnt) for s ∈ strs], fnt, shp, scalefactor)
|
||||
SeriesAnnotations([_text_label(s, fnt) for s in strs], fnt, shp, scalefactor)
|
||||
end
|
||||
|
||||
function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
@ -512,7 +515,7 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol=:pixels)
|
||||
msw, msh = anns.scalefactor
|
||||
msize = Float64[]
|
||||
shapes = Vector{Shape}(undef, length(anns.strs))
|
||||
for i ∈ eachindex(anns.strs)
|
||||
for i in eachindex(anns.strs)
|
||||
str = _cycle(anns.strs, i)
|
||||
|
||||
# get the width and height of the string (in mm)
|
||||
@ -530,7 +533,8 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol=:pixels)
|
||||
maxscale = max(xscale, yscale)
|
||||
push!(msize, maxscale)
|
||||
baseshape = _cycle(anns.baseshape, i)
|
||||
shapes[i] = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0, 0))
|
||||
shapes[i] =
|
||||
scale(baseshape, msw * xscale / maxscale, msh * yscale / maxscale, (0, 0))
|
||||
end
|
||||
series[:markershape] = shapes
|
||||
series[:markersize] = msize
|
||||
@ -576,7 +580,7 @@ _annotationfont(sp::Subplot) = Plots.font(;
|
||||
|
||||
_annotation(sp::Subplot, font, lab, pos...; alphabet = "abcdefghijklmnopqrstuvwxyz") = (
|
||||
pos...,
|
||||
lab == :auto ? text("($(alphabet[sp[:subplot_index]]))", font) : _text_label(lab, font)
|
||||
lab == :auto ? text("($(alphabet[sp[:subplot_index]]))", font) : _text_label(lab, font),
|
||||
)
|
||||
|
||||
# Expand arrays of coordinates, positions and labels into individual annotations
|
||||
@ -595,7 +599,12 @@ function process_annotation(sp::Subplot, xs, ys, labs, font=_annotationfont(sp))
|
||||
anns
|
||||
end
|
||||
|
||||
function process_annotation(sp::Subplot, positions::Union{AVec{Symbol}, Symbol, Tuple}, labs, font=_annotationfont(sp))
|
||||
function process_annotation(
|
||||
sp::Subplot,
|
||||
positions::Union{AVec{Symbol},Symbol,Tuple},
|
||||
labs,
|
||||
font = _annotationfont(sp),
|
||||
)
|
||||
anns = []
|
||||
positions, labs = makevec(positions), makevec(labs)
|
||||
for i in 1:max(length(positions), length(labs))
|
||||
@ -609,7 +618,9 @@ _relative_position(xmin, xmax, pos::Length{:pct}) = xmin + pos.value * (xmax - x
|
||||
|
||||
# Give each annotation coordinates based on specified position
|
||||
function locate_annotation(
|
||||
sp::Subplot, pos::Symbol, label::PlotText;
|
||||
sp::Subplot,
|
||||
pos::Symbol,
|
||||
label::PlotText;
|
||||
position_multiplier = Dict{Symbol,Tuple{Float64,Float64}}(
|
||||
:topleft => (0.1pct, 0.9pct),
|
||||
:topcenter => (0.5pct, 0.9pct),
|
||||
@ -617,13 +628,13 @@ function locate_annotation(
|
||||
:bottomleft => (0.1pct, 0.1pct),
|
||||
:bottomcenter => (0.5pct, 0.1pct),
|
||||
:bottomright => (0.9pct, 0.1pct),
|
||||
)
|
||||
),
|
||||
)
|
||||
x, y = position_multiplier[pos]
|
||||
(
|
||||
_relative_position(axis_limits(sp, :x)..., x),
|
||||
_relative_position(axis_limits(sp, :y)..., y),
|
||||
label
|
||||
label,
|
||||
)
|
||||
end
|
||||
locate_annotation(sp::Subplot, x, y, label::PlotText) = (x, y, label)
|
||||
@ -632,13 +643,13 @@ locate_annotation(sp::Subplot, x, y, z, label::PlotText) = (x, y, z, label)
|
||||
locate_annotation(sp::Subplot, rel::NTuple{2,<:Number}, label::PlotText) = (
|
||||
_relative_position(axis_limits(sp, :x)..., rel[1] * Plots.pct),
|
||||
_relative_position(axis_limits(sp, :y)..., rel[2] * Plots.pct),
|
||||
label
|
||||
label,
|
||||
)
|
||||
locate_annotation(sp::Subplot, rel::NTuple{3,<:Number}, label::PlotText) = (
|
||||
_relative_position(axis_limits(sp, :x)..., rel[1] * Plots.pct),
|
||||
_relative_position(axis_limits(sp, :y)..., rel[2] * Plots.pct),
|
||||
_relative_position(axis_limits(sp, :z)..., rel[3] * Plots.pct),
|
||||
label
|
||||
label,
|
||||
)
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@ -648,7 +659,10 @@ struct ZValues
|
||||
zrange::Tuple{Float64,Float64}
|
||||
end
|
||||
|
||||
function zvalues(values::AVec{T}, zrange::Tuple{T, T}=(ignorenan_minimum(values), ignorenan_maximum(values))) where T<:Real
|
||||
function zvalues(
|
||||
values::AVec{T},
|
||||
zrange::Tuple{T,T} = (ignorenan_minimum(values), ignorenan_maximum(values)),
|
||||
) where {T<:Real}
|
||||
ZValues(collect(float(values)), map(Float64, zrange))
|
||||
end
|
||||
|
||||
@ -667,7 +681,6 @@ struct SurfaceFunction <: AbstractSurface
|
||||
f::Function
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# # I don't want to clash with ValidatedNumerics, but this would be nice:
|
||||
@ -718,11 +731,10 @@ function arrow(args...)
|
||||
Arrow(style, side, headlength, headwidth)
|
||||
end
|
||||
|
||||
|
||||
# allow for do-block notation which gets called on every valid start/end pair which
|
||||
# we need to draw an arrow
|
||||
function add_arrows(func::Function, x::AVec, y::AVec)
|
||||
for i ∈ 2:length(x)
|
||||
for i in 2:length(x)
|
||||
xyprev = (x[i - 1], y[i - 1])
|
||||
xy = (x[i], y[i])
|
||||
if ok(xyprev) && ok(xy)
|
||||
@ -751,9 +763,8 @@ end
|
||||
|
||||
@deprecate curve_points coords
|
||||
|
||||
coords(curve::BezierCurve, n::Integer=30; range=[0, 1]) = map(
|
||||
curve, Base.range(first(range), stop=last(range), length=n)
|
||||
)
|
||||
coords(curve::BezierCurve, n::Integer = 30; range = [0, 1]) =
|
||||
map(curve, Base.range(first(range), stop = last(range), length = n))
|
||||
|
||||
function extrema_plus_buffer(v, buffmult = 0.2)
|
||||
vmin, vmax = ignorenan_extrema(v)
|
||||
|
||||
309
src/examples.jl
309
src/examples.jl
@ -28,35 +28,33 @@ const _examples = PlotExample[
|
||||
to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the
|
||||
animation.
|
||||
""",
|
||||
[:(
|
||||
[
|
||||
:(
|
||||
begin
|
||||
p = plot([sin, cos], zeros(0), leg = false, xlims = (0, 2π), ylims = (-1, 1))
|
||||
p = plot(
|
||||
[sin, cos],
|
||||
zeros(0),
|
||||
leg = false,
|
||||
xlims = (0, 2π),
|
||||
ylims = (-1, 1),
|
||||
)
|
||||
anim = Animation()
|
||||
for x in range(0, stop = 2π, length = 20)
|
||||
push!(p, x, Float64[sin(x), cos(x)])
|
||||
frame(anim)
|
||||
end
|
||||
end
|
||||
)],
|
||||
),
|
||||
],
|
||||
),
|
||||
PlotExample( # 3
|
||||
"Parametric plots",
|
||||
"Plot function pair (x(u), y(u)).",
|
||||
[
|
||||
:(
|
||||
[:(
|
||||
begin
|
||||
plot(
|
||||
sin,
|
||||
x -> sin(2x),
|
||||
0,
|
||||
2π,
|
||||
line = 4,
|
||||
leg = false,
|
||||
fill = (0, :orange),
|
||||
)
|
||||
plot(sin, x -> sin(2x), 0, 2π, line = 4, leg = false, fill = (0, :orange))
|
||||
end
|
||||
),
|
||||
],
|
||||
)],
|
||||
),
|
||||
PlotExample( # 4
|
||||
"Colors",
|
||||
@ -120,7 +118,6 @@ const _examples = PlotExample[
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
PlotExample( # 6
|
||||
"Images",
|
||||
"Plot an image. y-axis is set to flipped",
|
||||
@ -128,7 +125,9 @@ const _examples = PlotExample[
|
||||
:(
|
||||
begin
|
||||
import FileIO
|
||||
path = download("http://juliaplots.org/PlotReferenceImages.jl/Plots/pyplot/0.7.0/ref1.png")
|
||||
path = download(
|
||||
"http://juliaplots.org/PlotReferenceImages.jl/Plots/pyplot/0.7.0/ref1.png",
|
||||
)
|
||||
img = FileIO.load(path)
|
||||
plot(img)
|
||||
end
|
||||
@ -189,12 +188,11 @@ const _examples = PlotExample[
|
||||
PlotExample( # 11
|
||||
"Line types",
|
||||
"",
|
||||
[
|
||||
:(
|
||||
[:(
|
||||
begin
|
||||
linetypes = [:path :steppre :steppost :sticks :scatter]
|
||||
n = length(linetypes)
|
||||
x = Vector[sort(rand(20)) for i = 1:n]
|
||||
x = Vector[sort(rand(20)) for i in 1:n]
|
||||
y = rand(20, n)
|
||||
plot(
|
||||
x,
|
||||
@ -204,8 +202,7 @@ const _examples = PlotExample[
|
||||
ms = 15,
|
||||
)
|
||||
end
|
||||
),
|
||||
],
|
||||
)],
|
||||
),
|
||||
PlotExample( # 12
|
||||
"Line styles",
|
||||
@ -236,10 +233,8 @@ const _examples = PlotExample[
|
||||
[
|
||||
:(
|
||||
begin
|
||||
markers = filter(
|
||||
m -> m in Plots.supported_markers(),
|
||||
Plots._shape_keys,
|
||||
)
|
||||
markers =
|
||||
filter(m -> m in Plots.supported_markers(), Plots._shape_keys)
|
||||
markers = permutedims(markers)
|
||||
n = length(markers)
|
||||
x = range(0, stop = 10, length = n + 2)[2:(end - 1)]
|
||||
@ -270,17 +265,11 @@ const _examples = PlotExample[
|
||||
PlotExample( # 15
|
||||
"Histogram",
|
||||
"",
|
||||
[
|
||||
:(
|
||||
[:(
|
||||
begin
|
||||
histogram(
|
||||
randn(1000),
|
||||
bins = :scott,
|
||||
weights = repeat(1:5, outer = 200),
|
||||
)
|
||||
histogram(randn(1000), bins = :scott, weights = repeat(1:5, outer = 200))
|
||||
end
|
||||
),
|
||||
],
|
||||
)],
|
||||
),
|
||||
PlotExample( # 16
|
||||
"Subplots",
|
||||
@ -326,15 +315,13 @@ const _examples = PlotExample[
|
||||
PlotExample( # 18
|
||||
"",
|
||||
"",
|
||||
[
|
||||
:(
|
||||
[:(
|
||||
begin
|
||||
using Random
|
||||
Random.seed!(111)
|
||||
plot!(Plots.fakedata(100, 10))
|
||||
end
|
||||
)
|
||||
]
|
||||
)],
|
||||
),
|
||||
PlotExample( # 19
|
||||
"Open/High/Low/Close",
|
||||
@ -357,8 +344,7 @@ const _examples = PlotExample[
|
||||
bot[i] + hgt[i],
|
||||
bot[i],
|
||||
closepct[i] * hgt[i] + bot[i],
|
||||
)
|
||||
for i = 1:n
|
||||
) for i in 1:n
|
||||
]
|
||||
ohlc(y)
|
||||
end
|
||||
@ -396,11 +382,7 @@ const _examples = PlotExample[
|
||||
)
|
||||
annotate!([
|
||||
(5, y[5], ("this is #5", 16, :red, :center)),
|
||||
(
|
||||
10,
|
||||
y[10],
|
||||
("this is #10", :right, 20, "courier"),
|
||||
),
|
||||
(10, y[10], ("this is #10", :right, 20, "courier")),
|
||||
])
|
||||
scatter!(
|
||||
range(2, stop = 8, length = 6),
|
||||
@ -579,8 +561,8 @@ const _examples = PlotExample[
|
||||
"",
|
||||
[:(
|
||||
begin
|
||||
xs = [string("x", i) for i = 1:10]
|
||||
ys = [string("y", i) for i = 1:4]
|
||||
xs = [string("x", i) for i in 1:10]
|
||||
ys = [string("y", i) for i in 1:4]
|
||||
z = float((1:4) * reshape(1:10, 1, :))
|
||||
heatmap(xs, ys, z, aspect_ratio = 1)
|
||||
end
|
||||
@ -615,12 +597,7 @@ const _examples = PlotExample[
|
||||
begin
|
||||
import RDatasets
|
||||
singers = RDatasets.dataset("lattice", "singer")
|
||||
@df singers violin(
|
||||
:VoicePart,
|
||||
:Height,
|
||||
line = 0,
|
||||
fill = (0.2, :blue),
|
||||
)
|
||||
@df singers violin(:VoicePart, :Height, line = 0, fill = (0.2, :blue))
|
||||
@df singers boxplot!(
|
||||
:VoicePart,
|
||||
:Height,
|
||||
@ -648,11 +625,7 @@ const _examples = PlotExample[
|
||||
|
||||
anim = Animation()
|
||||
for x in range(1, stop = 2π, length = 20)
|
||||
plot(push!(
|
||||
p,
|
||||
x,
|
||||
Float64[sin(x), cos(x), atan(x), cos(x), log(x)],
|
||||
))
|
||||
plot(push!(p, x, Float64[sin(x), cos(x), atan(x), cos(x), log(x)]))
|
||||
frame(anim)
|
||||
end
|
||||
end
|
||||
@ -677,18 +650,8 @@ const _examples = PlotExample[
|
||||
10 => ones(40),
|
||||
-10 => ones(40),
|
||||
)
|
||||
b = spdiagm(
|
||||
0 => 1:50,
|
||||
1 => 1:49,
|
||||
-1 => 1:49,
|
||||
10 => 1:40,
|
||||
-10 => 1:40,
|
||||
)
|
||||
plot(
|
||||
spy(a),
|
||||
spy(b),
|
||||
title = ["Unique nonzeros" "Different nonzeros"],
|
||||
)
|
||||
b = spdiagm(0 => 1:50, 1 => 1:49, -1 => 1:49, 10 => 1:40, -10 => 1:40)
|
||||
plot(spy(a), spy(b), title = ["Unique nonzeros" "Different nonzeros"])
|
||||
end
|
||||
),
|
||||
],
|
||||
@ -753,33 +716,24 @@ const _examples = PlotExample[
|
||||
You can use the `line_z` and `marker_z` properties to associate a color with
|
||||
each line segment or marker in the plot.
|
||||
""",
|
||||
[
|
||||
:(
|
||||
[:(
|
||||
begin
|
||||
t = range(0, stop = 1, length = 100)
|
||||
θ = 6π .* t
|
||||
x = t .* cos.(θ)
|
||||
y = t .* sin.(θ)
|
||||
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
|
||||
p2 = scatter(
|
||||
x,
|
||||
y,
|
||||
marker_z = +,
|
||||
color = :bluesreds,
|
||||
legend = false,
|
||||
)
|
||||
p2 = scatter(x, y, marker_z = +, color = :bluesreds, legend = false)
|
||||
plot(p1, p2)
|
||||
end
|
||||
),
|
||||
],
|
||||
)],
|
||||
),
|
||||
PlotExample( # 36
|
||||
"Portfolio Composition maps",
|
||||
"""
|
||||
see: http://stackoverflow.com/a/37732384/5075246
|
||||
""",
|
||||
[
|
||||
:(
|
||||
[:(
|
||||
begin
|
||||
using Random
|
||||
Random.seed!(111)
|
||||
@ -790,14 +744,9 @@ const _examples = PlotExample[
|
||||
weights ./= sum(weights, dims = 2)
|
||||
returns = sort!((1:N) + D * randn(N))
|
||||
|
||||
portfoliocomposition(
|
||||
weights,
|
||||
returns,
|
||||
labels = permutedims(tickers),
|
||||
)
|
||||
portfoliocomposition(weights, returns, labels = permutedims(tickers))
|
||||
end
|
||||
),
|
||||
],
|
||||
)],
|
||||
),
|
||||
PlotExample( # 37
|
||||
"Ribbons",
|
||||
@ -810,10 +759,7 @@ const _examples = PlotExample[
|
||||
:(
|
||||
begin
|
||||
plot(
|
||||
plot(
|
||||
0:10;
|
||||
ribbon = (LinRange(0, 2, 11), LinRange(0, 1, 11)),
|
||||
),
|
||||
plot(0:10; ribbon = (LinRange(0, 2, 11), LinRange(0, 1, 11))),
|
||||
plot(0:10; ribbon = 0:0.5:5),
|
||||
plot(0:10; ribbon = sqrt),
|
||||
plot(0:10; ribbon = 1),
|
||||
@ -878,11 +824,7 @@ const _examples = PlotExample[
|
||||
plot!([(0, 0), (0, 0.9), (2, 0.9), (3, 1), (4, 0.9), (80, 0)])
|
||||
plot!([(0, 0), (0, 0.9), (3, 0.9), (4, 1), (5, 0.9), (80, 0)])
|
||||
plot!([(0, 0), (0, 0.9), (4, 0.9), (5, 1), (6, 0.9), (80, 0)])
|
||||
lens!(
|
||||
[1, 6],
|
||||
[0.9, 1.1],
|
||||
inset = (1, bbox(0.5, 0.0, 0.4, 0.4)),
|
||||
)
|
||||
lens!([1, 6], [0.9, 1.1], inset = (1, bbox(0.5, 0.0, 0.4, 0.4)))
|
||||
end
|
||||
end,
|
||||
],
|
||||
@ -890,8 +832,7 @@ const _examples = PlotExample[
|
||||
PlotExample( # 41
|
||||
"Array Types",
|
||||
"Plots supports different `Array` types that follow the `AbstractArray` interface, like `StaticArrays` and `OffsetArrays.`",
|
||||
[
|
||||
quote
|
||||
[quote
|
||||
begin
|
||||
using StaticArrays, OffsetArrays
|
||||
sv = SVector{10}(rand(10))
|
||||
@ -899,8 +840,7 @@ const _examples = PlotExample[
|
||||
plot([sv, ov], label = ["StaticArray" "OffsetArray"])
|
||||
plot!(3ov, ribbon = ov, label = "OffsetArray ribbon")
|
||||
end
|
||||
end,
|
||||
],
|
||||
end],
|
||||
),
|
||||
PlotExample( # 42
|
||||
"Setting defaults and font arguments",
|
||||
@ -916,7 +856,7 @@ const _examples = PlotExample[
|
||||
tickfont = (12, :orange),
|
||||
guide = "x",
|
||||
framestyle = :zerolines,
|
||||
yminorgrid = true
|
||||
yminorgrid = true,
|
||||
)
|
||||
plot(
|
||||
[sin, cos],
|
||||
@ -935,8 +875,7 @@ const _examples = PlotExample[
|
||||
PlotExample( # 43
|
||||
"Heatmap with DateTime axis",
|
||||
"",
|
||||
[
|
||||
quote
|
||||
[quote
|
||||
begin
|
||||
using Dates
|
||||
z = rand(5, 5)
|
||||
@ -944,20 +883,17 @@ const _examples = PlotExample[
|
||||
y = 1:5
|
||||
heatmap(x, y, z)
|
||||
end
|
||||
end,
|
||||
],
|
||||
end],
|
||||
),
|
||||
PlotExample( # 44
|
||||
"Linked axes",
|
||||
"",
|
||||
[
|
||||
quote
|
||||
[quote
|
||||
begin
|
||||
x = -5:0.1:5
|
||||
plot(plot(x, x -> x^2), plot(x, x -> sin(x)), layout = 2, link = :y)
|
||||
end
|
||||
end,
|
||||
],
|
||||
end],
|
||||
),
|
||||
PlotExample( # 45
|
||||
"Error bars and array type recipes",
|
||||
@ -972,8 +908,21 @@ const _examples = PlotExample[
|
||||
value(m::Measurement) = m.val
|
||||
uncertainty(m::Measurement) = m.err
|
||||
|
||||
@recipe function f(::Type{T}, m::T) where T <: AbstractArray{<:Measurement}
|
||||
if !(get(plotattributes, :seriestype, :path) in [:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image])
|
||||
@recipe function f(
|
||||
::Type{T},
|
||||
m::T,
|
||||
) where {T<:AbstractArray{<:Measurement}}
|
||||
if !(
|
||||
get(plotattributes, :seriestype, :path) in [
|
||||
:contour,
|
||||
:contourf,
|
||||
:contour3d,
|
||||
:heatmap,
|
||||
:surface,
|
||||
:wireframe,
|
||||
:image,
|
||||
]
|
||||
)
|
||||
error_sym = Symbol(plotattributes[:letter], :error)
|
||||
plotattributes[error_sym] = uncertainty.(m)
|
||||
end
|
||||
@ -990,7 +939,7 @@ const _examples = PlotExample[
|
||||
scatter(x, y, z),
|
||||
heatmap(x, y, surf),
|
||||
wireframe(x, y, surf),
|
||||
legend = :topleft
|
||||
legend = :topleft,
|
||||
)
|
||||
end
|
||||
end,
|
||||
@ -1005,7 +954,7 @@ const _examples = PlotExample[
|
||||
d = MvNormal([1.0 0.75; 0.75 2.0])
|
||||
plot([(1, 2), (3, 2), (2, 1), (2, 3)])
|
||||
scatter!(Point2.(eachcol(rand(d, 1000))), alpha = 0.25)
|
||||
end]
|
||||
end],
|
||||
),
|
||||
PlotExample( # 47
|
||||
"Mesh3d",
|
||||
@ -1032,9 +981,16 @@ const _examples = PlotExample[
|
||||
|
||||
# the four triangles gives above give a tetrahedron
|
||||
mesh3d(
|
||||
x, y, z; connections=(i, j, k),
|
||||
title="triangles", xlabel="x", ylabel="y", zlabel="z",
|
||||
legend=:none, margin=2Plots.mm
|
||||
x,
|
||||
y,
|
||||
z;
|
||||
connections = (i, j, k),
|
||||
title = "triangles",
|
||||
xlabel = "x",
|
||||
ylabel = "y",
|
||||
zlabel = "z",
|
||||
legend = :none,
|
||||
margin = 2Plots.mm,
|
||||
)
|
||||
end
|
||||
),
|
||||
@ -1043,7 +999,8 @@ const _examples = PlotExample[
|
||||
PlotExample( # 48
|
||||
"Vectors of markershapes and segments",
|
||||
"",
|
||||
[quote
|
||||
[
|
||||
quote
|
||||
using Base.Iterators: cycle, take
|
||||
|
||||
yv = ones(9)
|
||||
@ -1055,7 +1012,7 @@ const _examples = PlotExample[
|
||||
seriestype = [:path :path :scatter :scatter],
|
||||
markershape = collect(take(cycle((:utriangle, :rect)), 9)),
|
||||
markersize = 8,
|
||||
color = collect(take(cycle((:red, :black)), 9))
|
||||
color = collect(take(cycle((:red, :black)), 9)),
|
||||
)
|
||||
|
||||
plt_z_cols = plot(
|
||||
@ -1064,11 +1021,12 @@ const _examples = PlotExample[
|
||||
markersize = [5 10 10 5],
|
||||
marker_z = [5 4 3 2],
|
||||
line_z = [1 3 3 1],
|
||||
linewidth = [1 10 5 1]
|
||||
linewidth = [1 10 5 1],
|
||||
)
|
||||
|
||||
plot(plt_color_rows, plt_z_cols)
|
||||
end]
|
||||
end,
|
||||
],
|
||||
),
|
||||
PlotExample( # 49
|
||||
"Polar heatmaps",
|
||||
@ -1078,40 +1036,53 @@ const _examples = PlotExample[
|
||||
y = 0:4
|
||||
z = (1:4) .+ (1:8)'
|
||||
heatmap(x, y, z, projection = :polar)
|
||||
end]
|
||||
end],
|
||||
),
|
||||
PlotExample( # 50
|
||||
"3D surface with axis guides",
|
||||
"",
|
||||
[quote
|
||||
[
|
||||
quote
|
||||
f(x, a) = 1 / x + a * x^2
|
||||
xs = collect(0.1:0.05:2.0);
|
||||
as = collect(0.2:0.1:2.0);
|
||||
xs = collect(0.1:0.05:2.0)
|
||||
as = collect(0.2:0.1:2.0)
|
||||
|
||||
x_grid = [x for x in xs for y in as];
|
||||
a_grid = [y for x in xs for y in as];
|
||||
x_grid = [x for x in xs for y in as]
|
||||
a_grid = [y for x in xs for y in as]
|
||||
|
||||
plot(x_grid, a_grid, f.(x_grid,a_grid),
|
||||
plot(
|
||||
x_grid,
|
||||
a_grid,
|
||||
f.(x_grid, a_grid),
|
||||
st = :surface,
|
||||
xlabel = "longer xlabel",
|
||||
ylabel = "longer ylabel",
|
||||
zlabel = "longer zlabel",
|
||||
)
|
||||
end]
|
||||
end,
|
||||
],
|
||||
),
|
||||
PlotExample( # 51
|
||||
"Images with custom axes",
|
||||
"",
|
||||
[quote
|
||||
[
|
||||
quote
|
||||
using Plots
|
||||
using TestImages
|
||||
img = testimage("lighthouse")
|
||||
|
||||
# plot the image reversing the first dimension and setting yflip = false
|
||||
plot([-π, π], [-1, 1], reverse(img, dims=1), yflip=false, aspect_ratio=:none)
|
||||
plot(
|
||||
[-π, π],
|
||||
[-1, 1],
|
||||
reverse(img, dims = 1),
|
||||
yflip = false,
|
||||
aspect_ratio = :none,
|
||||
)
|
||||
# plot other data
|
||||
plot!(sin, -π, π, lw = 3, color = :red)
|
||||
end]
|
||||
end,
|
||||
],
|
||||
),
|
||||
PlotExample( # 52
|
||||
"3d quiver",
|
||||
@ -1132,7 +1103,7 @@ const _examples = PlotExample[
|
||||
w = 0.1 * vec([cos(θ) for (ϕ, θ) in Iterators.product(ϕs, θqs)])
|
||||
|
||||
quiver(x, y, z, quiver = (u, v, w))
|
||||
end]
|
||||
end],
|
||||
),
|
||||
PlotExample( # 53
|
||||
"Step Types",
|
||||
@ -1144,10 +1115,28 @@ const _examples = PlotExample[
|
||||
y = [1, 2, 3, 2, 1]
|
||||
default(shape = :circle)
|
||||
plot(
|
||||
plot(x, y, markershape=:circle, seriestype=:steppre, label="steppre"),
|
||||
plot(x, y, markershape=:circle, seriestype=:stepmid, label="stepmid"),
|
||||
plot(x, y, markershape=:circle, seriestype=:steppost, label="steppost"),
|
||||
layout=(3,1)
|
||||
plot(
|
||||
x,
|
||||
y,
|
||||
markershape = :circle,
|
||||
seriestype = :steppre,
|
||||
label = "steppre",
|
||||
),
|
||||
plot(
|
||||
x,
|
||||
y,
|
||||
markershape = :circle,
|
||||
seriestype = :stepmid,
|
||||
label = "stepmid",
|
||||
),
|
||||
plot(
|
||||
x,
|
||||
y,
|
||||
markershape = :circle,
|
||||
seriestype = :steppost,
|
||||
label = "steppost",
|
||||
),
|
||||
layout = (3, 1),
|
||||
)
|
||||
end
|
||||
),
|
||||
@ -1171,7 +1160,7 @@ const _examples = PlotExample[
|
||||
ymirror = [false true true false],
|
||||
xmirror = [false false true true],
|
||||
legend = false,
|
||||
seriestype=[:bar :scatter :path :stepmid]
|
||||
seriestype = [:bar :scatter :path :stepmid],
|
||||
)
|
||||
end
|
||||
),
|
||||
@ -1184,38 +1173,54 @@ const _examples = PlotExample[
|
||||
:(
|
||||
begin
|
||||
using LinearAlgebra
|
||||
scalefontsizes(.4)
|
||||
scalefontsizes(0.4)
|
||||
|
||||
x, y = collect(-6:0.5:10), collect(-8:0.5:8)
|
||||
|
||||
args = x, y, (x, y) -> sinc(norm([x, y]) / π)
|
||||
kwargs = Dict(:xlabel=>"x", :ylabel=>"y", :zlabel=>"z", :grid=>true, :minorgrid=>true)
|
||||
kwargs = Dict(
|
||||
:xlabel => "x",
|
||||
:ylabel => "y",
|
||||
:zlabel => "z",
|
||||
:grid => true,
|
||||
:minorgrid => true,
|
||||
)
|
||||
|
||||
plots = [wireframe(args..., title = "wire"; kwargs...)]
|
||||
|
||||
for ax ∈ (:x, :y, :z)
|
||||
push!(plots, wireframe(
|
||||
for ax in (:x, :y, :z)
|
||||
push!(
|
||||
plots,
|
||||
wireframe(
|
||||
args...,
|
||||
title = "wire-flip-$ax",
|
||||
xflip = ax == :x,
|
||||
yflip = ax == :y,
|
||||
zflip = ax == :z;
|
||||
kwargs...,
|
||||
))
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
for ax ∈ (:x, :y, :z)
|
||||
push!(plots, wireframe(
|
||||
for ax in (:x, :y, :z)
|
||||
push!(
|
||||
plots,
|
||||
wireframe(
|
||||
args...,
|
||||
title = "wire-mirror-$ax",
|
||||
xmirror = ax == :x,
|
||||
ymirror = ax == :y,
|
||||
zmirror = ax == :z;
|
||||
kwargs...,
|
||||
))
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
plt = plot(plots..., layout=(@layout [_ ° _; ° ° °; ° ° °]), margin=0Plots.px)
|
||||
plt = plot(
|
||||
plots...,
|
||||
layout = (@layout [_ ° _; ° ° °; ° ° °]),
|
||||
margin = 0Plots.px,
|
||||
)
|
||||
|
||||
resetfontsizes()
|
||||
plt
|
||||
@ -1252,8 +1257,6 @@ _backend_skips = Dict(
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# make and display one plot
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
# ---------------------------------------------------------
|
||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||
|
||||
_fileio_load(@nospecialize(filename::AbstractString)) = FileIO.load(filename::AbstractString)
|
||||
_fileio_save(@nospecialize(filename::AbstractString), @nospecialize(x)) = FileIO.save(filename::AbstractString, x)
|
||||
_fileio_load(@nospecialize(filename::AbstractString)) =
|
||||
FileIO.load(filename::AbstractString)
|
||||
_fileio_save(@nospecialize(filename::AbstractString), @nospecialize(x)) =
|
||||
FileIO.save(filename::AbstractString, x)
|
||||
|
||||
function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
|
||||
fn = tempname()
|
||||
@ -21,4 +23,5 @@ function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
|
||||
write(io, read(open(pngfn), String))
|
||||
end
|
||||
|
||||
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
||||
const PDFBackends =
|
||||
Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
const use_local_dependencies = Ref(false)
|
||||
const use_local_plotlyjs = Ref(false)
|
||||
|
||||
|
||||
function _init_ijulia_plotting()
|
||||
# IJulia is more stable with local file
|
||||
use_local_plotlyjs[] = plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
|
||||
use_local_plotlyjs[] =
|
||||
plotly_local_file_path[] === nothing ? false : isfile(plotly_local_file_path[])
|
||||
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
||||
|
||||
@ -20,22 +19,17 @@ frontends like jupyterlab and nteract.
|
||||
_ijulia__extra_mime_info!(plt::Plot, out::Dict) = out
|
||||
|
||||
function _ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out["application/vnd.plotly.v1+json"] =
|
||||
Dict(:data => plotly_series(plt), :layout => plotly_layout(plt))
|
||||
out
|
||||
end
|
||||
|
||||
function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out["application/vnd.plotly.v1+json"] =
|
||||
Dict(:data => plotly_series(plt), :layout => plotly_layout(plt))
|
||||
out
|
||||
end
|
||||
|
||||
|
||||
function _ijulia_display_dict(plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
|
||||
35
src/init.jl
35
src/init.jl
@ -3,7 +3,6 @@ using Scratch
|
||||
|
||||
const plotly_local_file_path = Ref{Union{Nothing,String}}(nothing)
|
||||
|
||||
|
||||
function _plots_defaults()
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
copy(Dict{Symbol,Any}(Main.PLOTS_DEFAULTS))
|
||||
@ -12,7 +11,6 @@ function _plots_defaults()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function __init__()
|
||||
user_defaults = _plots_defaults()
|
||||
if haskey(user_defaults, :theme)
|
||||
@ -21,14 +19,27 @@ function __init__()
|
||||
default(; user_defaults...)
|
||||
end
|
||||
|
||||
insert!(Base.Multimedia.displays, findlast(x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
|
||||
insert!(
|
||||
Base.Multimedia.displays,
|
||||
findlast(
|
||||
x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay,
|
||||
Base.Multimedia.displays,
|
||||
) + 1,
|
||||
PlotsDisplay(),
|
||||
)
|
||||
|
||||
atreplinit(i -> begin
|
||||
atreplinit(
|
||||
i -> begin
|
||||
while PlotsDisplay() in Base.Multimedia.displays
|
||||
popdisplay(PlotsDisplay())
|
||||
end
|
||||
insert!(Base.Multimedia.displays, findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
|
||||
end)
|
||||
insert!(
|
||||
Base.Multimedia.displays,
|
||||
findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1,
|
||||
PlotsDisplay(),
|
||||
)
|
||||
end,
|
||||
)
|
||||
|
||||
@require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" begin
|
||||
fn = joinpath(@__DIR__, "backends", "hdf5.jl")
|
||||
@ -84,9 +95,13 @@ function __init__()
|
||||
end
|
||||
|
||||
if get(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL", "false") == "true"
|
||||
global plotly_local_file_path[] = joinpath(@get_scratch!("plotly"), _plotly_min_js_filename)
|
||||
global plotly_local_file_path[] =
|
||||
joinpath(@get_scratch!("plotly"), _plotly_min_js_filename)
|
||||
if !isfile(plotly_local_file_path[])
|
||||
download("https://cdn.plot.ly/$(_plotly_min_js_filename)", plotly_local_file_path[])
|
||||
download(
|
||||
"https://cdn.plot.ly/$(_plotly_min_js_filename)",
|
||||
plotly_local_file_path[],
|
||||
)
|
||||
end
|
||||
|
||||
use_local_plotlyjs[] = true
|
||||
@ -94,8 +109,8 @@ function __init__()
|
||||
|
||||
use_local_dependencies[] = use_local_plotlyjs[]
|
||||
|
||||
|
||||
@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin
|
||||
_show(io::IO, mime::MIME"image/png", plt::Plot{<:PDFBackends}) = _show_pdfbackends(io, mime, plt)
|
||||
_show(io::IO, mime::MIME"image/png", plt::Plot{<:PDFBackends}) =
|
||||
_show_pdfbackends(io, mime, plt)
|
||||
end
|
||||
end
|
||||
|
||||
116
src/layouts.jl
116
src/layouts.jl
@ -61,7 +61,10 @@ function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true)
|
||||
end
|
||||
|
||||
function Base.show(io::IO, bbox::BoundingBox)
|
||||
print(io, "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}")
|
||||
print(
|
||||
io,
|
||||
"BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}",
|
||||
)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------
|
||||
@ -88,7 +91,6 @@ function resolve_mixed(mix::MixedMeasures, sp::Subplot, letter::Symbol)
|
||||
xy
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# AbstractLayout
|
||||
|
||||
@ -160,7 +162,8 @@ parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
|
||||
# padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
|
||||
|
||||
update_position!(layout::AbstractLayout) = nothing
|
||||
update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm,0mm,0mm,0mm]) = nothing
|
||||
update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm, 0mm, 0mm, 0mm]) =
|
||||
nothing
|
||||
|
||||
left(layout::AbstractLayout) = left(bbox(layout))
|
||||
top(layout::AbstractLayout) = top(bbox(layout))
|
||||
@ -235,11 +238,13 @@ columns of different width.
|
||||
"""
|
||||
grid(args...; kw...) = GridLayout(args...; kw...)
|
||||
|
||||
function GridLayout(dims...;
|
||||
function GridLayout(
|
||||
dims...;
|
||||
parent = RootLayout(),
|
||||
widths = zeros(dims[2]),
|
||||
heights = zeros(dims[1]),
|
||||
kw...)
|
||||
kw...,
|
||||
)
|
||||
grid = Matrix{AbstractLayout}(undef, dims...)
|
||||
layout = GridLayout(
|
||||
parent,
|
||||
@ -250,7 +255,8 @@ function GridLayout(dims...;
|
||||
Measure[h * pct for h in heights],
|
||||
# convert(Vector{Float64}, widths),
|
||||
# convert(Vector{Float64}, heights),
|
||||
KW(kw))
|
||||
KW(kw),
|
||||
)
|
||||
for i in eachindex(grid)
|
||||
grid[i] = EmptyLayout(layout)
|
||||
end
|
||||
@ -269,7 +275,6 @@ toppad(layout::GridLayout) = layout.minpad[2]
|
||||
rightpad(layout::GridLayout) = layout.minpad[3]
|
||||
bottompad(layout::GridLayout) = layout.minpad[4]
|
||||
|
||||
|
||||
# here's how this works... first we recursively "update the minimum padding" (which
|
||||
# means to calculate the minimum size needed from the edge of the subplot to plot area)
|
||||
# for the whole layout tree. then we can compute the "padding borders" of this
|
||||
@ -284,11 +289,10 @@ function _update_min_padding!(layout::GridLayout)
|
||||
maximum(map(leftpad, layout.grid[:, 1])),
|
||||
maximum(map(toppad, layout.grid[1, :])),
|
||||
maximum(map(rightpad, layout.grid[:, end])),
|
||||
maximum(map(bottompad, layout.grid[end,:]))
|
||||
maximum(map(bottompad, layout.grid[end, :])),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
function update_position!(layout::GridLayout)
|
||||
map(update_position!, layout.grid)
|
||||
end
|
||||
@ -307,7 +311,9 @@ function recompute_lengths(v)
|
||||
end
|
||||
leftover = 1.0pct - tot
|
||||
if cnt > 1 && leftover.value <= 0
|
||||
error("Not enough length left over in layout! v = $v, cnt = $cnt, leftover = $leftover")
|
||||
error(
|
||||
"Not enough length left over in layout! v = $v, cnt = $cnt, leftover = $leftover",
|
||||
)
|
||||
end
|
||||
|
||||
# now fill in the blanks
|
||||
@ -355,7 +361,7 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
|
||||
# denom_h = sum(layout.heights)
|
||||
|
||||
# we have all the data we need... lets compute the plot areas and set the bounding boxes
|
||||
for r=1:nr, c=1:nc
|
||||
for r in 1:nr, c in 1:nc
|
||||
child = layout[r, c]
|
||||
|
||||
# get the top-left corner of this child... the first one is top-left of the parent (i.e. layout)
|
||||
@ -367,7 +373,10 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
|
||||
plotarea_top = child_top + pad_top[r]
|
||||
plotarea_width = total_plotarea_horizontal * layout.widths[c]
|
||||
plotarea_height = total_plotarea_vertical * layout.heights[r]
|
||||
plotarea!(child, BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height))
|
||||
plotarea!(
|
||||
child,
|
||||
BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height),
|
||||
)
|
||||
|
||||
# compute child bbox
|
||||
child_width = pad_left[c] + plotarea_width + pad_right[c]
|
||||
@ -380,7 +389,7 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
|
||||
c == 1 ? layout.minpad[1] : pad_left[c],
|
||||
r == 1 ? layout.minpad[2] : pad_top[r],
|
||||
c == nc ? layout.minpad[3] : pad_right[c],
|
||||
r == nr ? layout.minpad[4] : pad_bottom[r]
|
||||
r == nr ? layout.minpad[4] : pad_bottom[r],
|
||||
]
|
||||
|
||||
# recursively update the child's children
|
||||
@ -395,12 +404,15 @@ function update_inset_bboxes!(plt::Plot)
|
||||
p_area = Measures.resolve(plotarea(sp.parent), sp[:relative_bbox])
|
||||
plotarea!(sp, p_area)
|
||||
|
||||
bbox!(sp, bbox(
|
||||
bbox!(
|
||||
sp,
|
||||
bbox(
|
||||
left(p_area) - leftpad(sp),
|
||||
top(p_area) - toppad(sp),
|
||||
width(p_area) + leftpad(sp) + rightpad(sp),
|
||||
height(p_area) + toppad(sp) + bottompad(sp)
|
||||
))
|
||||
height(p_area) + toppad(sp) + bottompad(sp),
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -441,7 +453,9 @@ end
|
||||
function layout_args(plotattributes::AKW, n_override::Integer)
|
||||
layout, n = layout_args(n_override, get(plotattributes, :layout, n_override))
|
||||
if n != n_override
|
||||
error("When doing layout, n ($n) != n_override ($(n_override)). You're probably trying to force existing plots into a layout that doesn't fit them.")
|
||||
error(
|
||||
"When doing layout, n ($n) != n_override ($(n_override)). You're probably trying to force existing plots into a layout that doesn't fit them.",
|
||||
)
|
||||
end
|
||||
layout, n
|
||||
end
|
||||
@ -488,16 +502,13 @@ layout_args(n_override::Integer, layout::GridLayout) = layout_args(layout)
|
||||
|
||||
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
function build_layout(args...)
|
||||
layout, n = layout_args(args...)
|
||||
build_layout(layout, n, Array{Plot}(undef, 0))
|
||||
end
|
||||
|
||||
|
||||
# n is the number of subplots...
|
||||
function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot})
|
||||
nr, nc = size(layout)
|
||||
@ -505,7 +516,7 @@ function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot})
|
||||
spmap = SubplotMap()
|
||||
empty = isempty(plts)
|
||||
i = 0
|
||||
for r = 1:nr, c = 1:nc
|
||||
for r in 1:nr, c in 1:nc
|
||||
l = layout[r, c]
|
||||
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
|
||||
if empty
|
||||
@ -551,7 +562,6 @@ function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot})
|
||||
layout, subplots, spmap
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# @layout macro
|
||||
|
||||
@ -586,15 +596,21 @@ isrow(v) = isa(v, Expr) && v.head in (:hcat,:row)
|
||||
iscol(v) = isa(v, Expr) && v.head == :vcat
|
||||
rowsize(v) = isrow(v) ? length(v.args) : 1
|
||||
|
||||
|
||||
function create_grid(expr::Expr)
|
||||
if iscol(expr)
|
||||
create_grid_vcat(expr)
|
||||
elseif isrow(expr)
|
||||
:(let cell = GridLayout(1, $(length(expr.args)))
|
||||
$([:(cell[1,$i] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
|
||||
:(
|
||||
let cell = GridLayout(1, $(length(expr.args)))
|
||||
$(
|
||||
[
|
||||
:(cell[1, $i] = $(create_grid(v))) for
|
||||
(i, v) in enumerate(expr.args)
|
||||
]...
|
||||
)
|
||||
cell
|
||||
end)
|
||||
end
|
||||
)
|
||||
|
||||
elseif expr.head == :curly
|
||||
create_grid_curly(expr)
|
||||
@ -613,7 +629,7 @@ function create_grid_vcat(expr::Expr)
|
||||
nr = length(expr.args)
|
||||
nc = rmin
|
||||
body = Expr(:block)
|
||||
for r=1:nr
|
||||
for r in 1:nr
|
||||
arg = expr.args[r]
|
||||
if isrow(arg)
|
||||
for (c, item) in enumerate(arg.args)
|
||||
@ -623,16 +639,25 @@ function create_grid_vcat(expr::Expr)
|
||||
push!(body.args, :(cell[$r, 1] = $(create_grid(arg))))
|
||||
end
|
||||
end
|
||||
:(let cell = GridLayout($nr, $nc)
|
||||
:(
|
||||
let cell = GridLayout($nr, $nc)
|
||||
$body
|
||||
cell
|
||||
end)
|
||||
end
|
||||
)
|
||||
else
|
||||
# otherwise just build one row at a time
|
||||
:(let cell = GridLayout($(length(expr.args)), 1)
|
||||
$([:(cell[$i,1] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
|
||||
:(
|
||||
let cell = GridLayout($(length(expr.args)), 1)
|
||||
$(
|
||||
[
|
||||
:(cell[$i, 1] = $(create_grid(v))) for
|
||||
(i, v) in enumerate(expr.args)
|
||||
]...
|
||||
)
|
||||
cell
|
||||
end)
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -643,9 +668,19 @@ function create_grid_curly(expr::Expr)
|
||||
end
|
||||
s = expr.args[1]
|
||||
if isa(s, Expr) && s.head == :call && s.args[1] == :grid
|
||||
create_grid(:(grid($(s.args[2:end]...), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto))))))
|
||||
create_grid(
|
||||
:(grid(
|
||||
$(s.args[2:end]...),
|
||||
width = $(get(kw, :w, QuoteNode(:auto))),
|
||||
height = $(get(kw, :h, QuoteNode(:auto))),
|
||||
)),
|
||||
)
|
||||
elseif isa(s, Symbol)
|
||||
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
|
||||
:(EmptyLayout(
|
||||
label = $(QuoteNode(s)),
|
||||
width = $(get(kw, :w, QuoteNode(:auto))),
|
||||
height = $(get(kw, :h, QuoteNode(:auto))),
|
||||
))
|
||||
else
|
||||
error("Unknown use of curly brackets: $expr")
|
||||
end
|
||||
@ -659,14 +694,13 @@ macro layout(mat::Expr)
|
||||
create_grid(mat)
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# make all reference the same axis extrema/values.
|
||||
# merge subplot lists.
|
||||
function link_axes!(axes::Axis...)
|
||||
a1 = axes[1]
|
||||
for i=2:length(axes)
|
||||
for i in 2:length(axes)
|
||||
a2 = axes[i]
|
||||
expand_extrema!(a1, ignorenan_extrema(a2))
|
||||
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
|
||||
@ -705,19 +739,18 @@ function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
|
||||
end
|
||||
|
||||
# don't do anything for most layout types
|
||||
function link_axes!(l::AbstractLayout, link::Symbol)
|
||||
end
|
||||
function link_axes!(l::AbstractLayout, link::Symbol) end
|
||||
|
||||
# process a GridLayout, recursively linking axes according to the link symbol
|
||||
function link_axes!(layout::GridLayout, link::Symbol)
|
||||
nr, nc = size(layout)
|
||||
if link in (:x, :both)
|
||||
for c=1:nc
|
||||
for c in 1:nc
|
||||
link_axes!(layout.grid[:, c], :xaxis)
|
||||
end
|
||||
end
|
||||
if link in (:y, :both)
|
||||
for r=1:nr
|
||||
for r in 1:nr
|
||||
link_axes!(layout.grid[r, :], :yaxis)
|
||||
end
|
||||
end
|
||||
@ -744,7 +777,8 @@ end
|
||||
|
||||
"Adds a new, empty subplot overlayed on top of `sp`, with a mirrored y-axis and linked x-axis."
|
||||
function twinx(sp::Subplot)
|
||||
plot!(sp.plt,
|
||||
plot!(
|
||||
sp.plt,
|
||||
inset = (sp[:subplot_index], bbox(0, 0, 1, 1)),
|
||||
right_margin = sp[:right_margin],
|
||||
left_margin = sp[:left_margin],
|
||||
|
||||
@ -15,7 +15,6 @@ function legend_pos_from_angle(theta, xmin, xcenter, xmax, ymin, ycenter, ymax)
|
||||
return (xcenter + A * c, ycenter + A * s)
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Split continuous range `[-1,1]` evenly into an integer `[1,2,3]`
|
||||
"""
|
||||
@ -34,7 +33,7 @@ If `leg` is a (::Real,::Real) tuple, keep it as is.
|
||||
"""
|
||||
legend_angle(leg::Real) = (leg, :inner)
|
||||
legend_angle(leg::Tuple{S,T}) where {S<:Real,T<:Real} = leg
|
||||
legend_angle(leg::Tuple{S,Symbol}) where S<:Real = leg
|
||||
legend_angle(leg::Tuple{S,Symbol}) where {S<:Real} = leg
|
||||
legend_angle(leg::Symbol) = get(
|
||||
(
|
||||
topleft = (135, :inner),
|
||||
@ -55,5 +54,5 @@ legend_angle(leg::Symbol) = get(
|
||||
outerbottomright = (315, :outer),
|
||||
),
|
||||
leg,
|
||||
(45, :inner)
|
||||
(45, :inner),
|
||||
)
|
||||
|
||||
@ -73,7 +73,6 @@ function txt(plt::Plot, fn::AbstractString)
|
||||
end
|
||||
txt(fn::AbstractString) = txt(current(), fn)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const _savemap = Dict(
|
||||
@ -128,7 +127,6 @@ function savefig(plt::Plot, fn::AbstractString)
|
||||
end
|
||||
savefig(fn::AbstractString) = savefig(current(), fn)
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
"""
|
||||
@ -224,7 +222,6 @@ Base.show(io::IO, m::MIME"application/prs.juno.plotpane+html", plt::Plot) =
|
||||
"Close all open gui windows of the current backend"
|
||||
closeall() = closeall(backend())
|
||||
|
||||
|
||||
# function html_output_format(fmt)
|
||||
# if fmt == "png"
|
||||
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
@ -241,7 +238,6 @@ closeall() = closeall(backend())
|
||||
#
|
||||
# html_output_format("svg")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Atom PlotPane
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@ -30,7 +30,6 @@ function RecipesPipeline.warn_on_recipe_aliases!(
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
## Grouping
|
||||
|
||||
RecipesPipeline.splittable_attribute(plt::Plot, key, val::SeriesAnnotations, len) =
|
||||
@ -41,7 +40,6 @@ function RecipesPipeline.split_attribute(plt::Plot, key, val::SeriesAnnotations,
|
||||
return SeriesAnnotations(split_strs, val.font, val.baseshape, val.scalefactor)
|
||||
end
|
||||
|
||||
|
||||
## Preprocessing attributes
|
||||
function RecipesPipeline.preprocess_axis_args!(plt::Plot, plotattributes, letter)
|
||||
# Fix letter for seriestypes that are x only but data gets passed as y
|
||||
@ -62,7 +60,6 @@ RecipesPipeline.is_axis_attribute(plt::Plot, attr) = is_axis_attr_noletter(attr)
|
||||
|
||||
RecipesPipeline.is_subplot_attribute(plt::Plot, attr) = is_subplot_attr(attr) # in src/args.jl
|
||||
|
||||
|
||||
## User recipes
|
||||
|
||||
function RecipesPipeline.process_userrecipe!(plt::Plot, kw_list, kw)
|
||||
@ -84,13 +81,15 @@ function _preprocess_userrecipe(kw::AKW)
|
||||
# map marker_z if it's a Function
|
||||
if isa(get(kw, :marker_z, nothing), Function)
|
||||
# TODO: should this take y and/or z as arguments?
|
||||
kw[:marker_z] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) :
|
||||
kw[:marker_z] =
|
||||
isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) :
|
||||
map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# map line_z if it's a Function
|
||||
if isa(get(kw, :line_z, nothing), Function)
|
||||
kw[:line_z] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) :
|
||||
kw[:line_z] =
|
||||
isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) :
|
||||
map(kw[:line_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
@ -140,15 +139,12 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
RecipesPipeline.get_axis_limits(plt::Plot, letter) = axis_limits(plt[1], letter, false)
|
||||
|
||||
|
||||
## Plot recipes
|
||||
|
||||
RecipesPipeline.type_alias(plt::Plot) = get(_typeAliases, st, st)
|
||||
|
||||
|
||||
## Plot setup
|
||||
|
||||
function RecipesPipeline.plot_setup!(plt::Plot, plotattributes, kw_list)
|
||||
@ -159,7 +155,8 @@ end
|
||||
|
||||
function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_list)
|
||||
# swap errors
|
||||
err_inds = findall(kw -> get(kw, :seriestype, :path) in (:xerror, :yerror, :zerror), kw_list)
|
||||
err_inds =
|
||||
findall(kw -> get(kw, :seriestype, :path) in (:xerror, :yerror, :zerror), kw_list)
|
||||
for ind in err_inds
|
||||
if get(kw_list[ind - 1], :seriestype, :path) == :scatter
|
||||
tmp = copy(kw_list[ind])
|
||||
@ -170,7 +167,6 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::Plots.Plot, kw_l
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
# TODO: Should some of this logic be moved to RecipesPipeline?
|
||||
function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# merge in anything meant for the Plot
|
||||
@ -193,7 +189,6 @@ function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
plt.init = true
|
||||
end
|
||||
|
||||
|
||||
# handle inset subplots
|
||||
insets = plt[:inset_subplots]
|
||||
if insets !== nothing
|
||||
@ -299,8 +294,8 @@ function _add_plot_title!(plt)
|
||||
plt.layout.grid[2, 1] = the_layout
|
||||
subplot.plt = plt
|
||||
# propagate arguments plt[:plot_titleXXX] --> subplot[:titleXXX]
|
||||
for sym ∈ filter(x -> startswith(string(x), "plot_title"), keys(_plot_defaults))
|
||||
subplot[Symbol(string(sym)[length("plot_") + 1:end])] = plt[sym]
|
||||
for sym in filter(x -> startswith(string(x), "plot_title"), keys(_plot_defaults))
|
||||
subplot[Symbol(string(sym)[(length("plot_") + 1):end])] = plt[sym]
|
||||
end
|
||||
top = plt.backend isa PyPlotBackend ? nothing : 0mm
|
||||
bot = 0mm
|
||||
@ -346,7 +341,10 @@ function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T}
|
||||
st = _override_seriestype_check(plotattributes, st)
|
||||
|
||||
# change to a 3d projection for this subplot?
|
||||
if RecipesPipeline.needs_3d_axes(st) || (st == :quiver && plotattributes[:z] !== nothing)
|
||||
if (
|
||||
RecipesPipeline.needs_3d_axes(st) ||
|
||||
(st == :quiver && plotattributes[:z] !== nothing)
|
||||
)
|
||||
sp.attr[:projection] = "3d"
|
||||
end
|
||||
|
||||
@ -362,8 +360,10 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !RecipesPipeline.is3d(st) && !(st in (:contour, :contour3d, :quiver))
|
||||
z = plotattributes[:z]
|
||||
if z !== nothing &&
|
||||
if (
|
||||
z !== nothing &&
|
||||
(size(plotattributes[:x]) == size(plotattributes[:y]) == size(z))
|
||||
)
|
||||
st = (st == :scatter ? :scatter3d : :path3d)
|
||||
plotattributes[:seriestype] = st
|
||||
end
|
||||
@ -374,7 +374,7 @@ end
|
||||
function needs_any_3d_axes(sp::Subplot)
|
||||
any(
|
||||
RecipesPipeline.needs_3d_axes(
|
||||
_override_seriestype_check(s.plotattributes, s.plotattributes[:seriestype])
|
||||
_override_seriestype_check(s.plotattributes, s.plotattributes[:seriestype]),
|
||||
) for s in series_list(sp)
|
||||
)
|
||||
end
|
||||
|
||||
19
src/plot.jl
19
src/plot.jl
@ -20,14 +20,17 @@ current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = plot)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
Base.string(plt::Plot) = "Plot{$(plt.backend) n=$(plt.n)}"
|
||||
Base.print(io::IO, plt::Plot) = print(io, string(plt))
|
||||
function Base.show(io::IO, plt::Plot)
|
||||
print(io, string(plt))
|
||||
sp_ekwargs = getindex.(plt.subplots, :extra_kwargs)
|
||||
s_ekwargs = getindex.(plt.series_list, :extra_kwargs)
|
||||
if isempty(plt[:extra_plot_kwargs]) && all(isempty, sp_ekwargs) && all(isempty, s_ekwargs)
|
||||
if (
|
||||
isempty(plt[:extra_plot_kwargs]) &&
|
||||
all(isempty, sp_ekwargs) &&
|
||||
all(isempty, s_ekwargs)
|
||||
)
|
||||
return
|
||||
end
|
||||
print(io, "\nCaptured extra kwargs:\n")
|
||||
@ -62,7 +65,6 @@ convertSeriesIndex(plt::Plot, n::Int) = n
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
"""
|
||||
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
|
||||
|
||||
@ -91,7 +93,8 @@ end
|
||||
|
||||
# build a new plot from existing plots
|
||||
# note: we split into plt1, plt2 and plts_tail so we can dispatch correctly
|
||||
plot(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...) = plot!(deepcopy(plt1), deepcopy(plt2), deepcopy.(plts_tail)...; kw...)
|
||||
plot(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...) =
|
||||
plot!(deepcopy(plt1), deepcopy(plt2), deepcopy.(plts_tail)...; kw...)
|
||||
function plot!(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...)
|
||||
@nospecialize
|
||||
plotattributes = KW(kw)
|
||||
@ -173,8 +176,6 @@ function plot!(plt1::Plot, plt2::Plot, plts_tail::Plot...; kw...)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
|
||||
# this adds to the current plot, or creates a new plot if none are current
|
||||
function plot!(args...; kw...)
|
||||
@nospecialize
|
||||
@ -210,7 +211,6 @@ function _plot!(plt::Plot, plotattributes, args)
|
||||
return plt
|
||||
end
|
||||
|
||||
|
||||
# we're getting ready to display/output. prep for layout calcs, then update
|
||||
# the plot object after
|
||||
function prepare_output(plt::Plot)
|
||||
@ -229,9 +229,10 @@ function prepare_output(plt::Plot)
|
||||
# spedific to :plot_title see _add_plot_title!
|
||||
force_minpad = get(plt, :force_minpad, ())
|
||||
if !isempty(force_minpad)
|
||||
for i ∈ eachindex(plt.layout.grid)
|
||||
for i in eachindex(plt.layout.grid)
|
||||
plt.layout.grid[i].minpad = Tuple(
|
||||
i === nothing ? j : i for (i, j) ∈ zip(force_minpad, plt.layout.grid[i].minpad)
|
||||
i === nothing ? j : i for
|
||||
(i, j) in zip(force_minpad, plt.layout.grid[i].minpad)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
|
||||
const _attribute_defaults = Dict(:Series => _series_defaults,
|
||||
const _attribute_defaults = Dict(
|
||||
:Series => _series_defaults,
|
||||
:Subplot => _subplot_defaults,
|
||||
:Plot => _plot_defaults,
|
||||
:Axis => _axis_defaults)
|
||||
:Axis => _axis_defaults,
|
||||
)
|
||||
|
||||
attrtypes() = join(keys(_attribute_defaults), ", ")
|
||||
attributes(attrtype::Symbol) = sort(collect(keys(_attribute_defaults[attrtype])))
|
||||
@ -21,7 +23,9 @@ Look up the properties of a Plots attribute, or specify an attribute type. Call
|
||||
The information is the same as that given on https://docs.juliaplots.org/latest/attributes/.
|
||||
"""
|
||||
function plotattr()
|
||||
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
|
||||
println(
|
||||
"Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())",
|
||||
)
|
||||
end
|
||||
|
||||
function plotattr(attrtype::Symbol)
|
||||
@ -44,7 +48,8 @@ printnothing(x) = x
|
||||
printnothing(x::Nothing) = "nothing"
|
||||
|
||||
function plotattr(attrtype::Symbol, attribute::AbstractString)
|
||||
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
|
||||
in(attrtype, keys(_attribute_defaults)) ||
|
||||
ArgumentError("`attrtype` must match one of $(attrtypes())")
|
||||
|
||||
attribute = Symbol(lookup_aliases(attrtype, attribute))
|
||||
|
||||
@ -54,17 +59,21 @@ function plotattr(attrtype::Symbol, attribute::AbstractString)
|
||||
typedesc = ""
|
||||
desc = strip(desc)
|
||||
else
|
||||
typedesc = desc[1:first_period_idx-1]
|
||||
desc = strip(desc[first_period_idx+1:end])
|
||||
typedesc = desc[1:(first_period_idx - 1)]
|
||||
desc = strip(desc[(first_period_idx + 1):end])
|
||||
end
|
||||
als = keys(filter(x -> x[2] == attribute, _keyAliases)) |> collect |> sort
|
||||
als = join(map(string, als), ", ")
|
||||
def = _attribute_defaults[attrtype][attribute]
|
||||
|
||||
|
||||
# Looks up the different elements and plots them
|
||||
println("$(printnothing(attribute)) ", typedesc == "" ? "" : "{$(printnothing(typedesc))}", "\n",
|
||||
println(
|
||||
"$(printnothing(attribute)) ",
|
||||
typedesc == "" ? "" : "{$(printnothing(typedesc))}",
|
||||
"\n",
|
||||
als == "" ? "" : "$(printnothing(als))\n",
|
||||
"\n$(printnothing(desc))\n",
|
||||
"$(printnothing(attrtype)) attribute, ", def == "" ? "" : " default: $(printnothing(def))")
|
||||
"$(printnothing(attrtype)) attribute, ",
|
||||
def == "" ? "" : " default: $(printnothing(def))",
|
||||
)
|
||||
end
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
should_precompile = true
|
||||
|
||||
|
||||
# Don't edit the following! Instead change the script for `snoop_bot`.
|
||||
ismultios = false
|
||||
ismultiversion = false
|
||||
@ -8,13 +7,11 @@ ismultiversion = false
|
||||
@static if !should_precompile
|
||||
# nothing
|
||||
elseif !ismultios && !ismultiversion
|
||||
@static if isfile(joinpath(
|
||||
@__DIR__,
|
||||
"../deps/SnoopCompile/precompile/precompile_Plots.jl",
|
||||
))
|
||||
@static if isfile(
|
||||
joinpath(@__DIR__, "../deps/SnoopCompile/precompile/precompile_Plots.jl"),
|
||||
)
|
||||
include("../deps/SnoopCompile/precompile/precompile_Plots.jl")
|
||||
_precompile_()
|
||||
end
|
||||
else
|
||||
|
||||
end # precompile_enclosure
|
||||
|
||||
125
src/recipes.jl
125
src/recipes.jl
@ -41,17 +41,16 @@ function all_seriestypes()
|
||||
sort(collect(sts))
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
num_series(x::AMat) = size(x, 2)
|
||||
num_series(x) = 1
|
||||
|
||||
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} =
|
||||
throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# for seriestype `line`, need to sort by x values
|
||||
|
||||
const POTENTIAL_VECTOR_ARGUMENTS = [
|
||||
@ -115,7 +114,7 @@ end
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = repeat(Float64[1, 2, NaN], n)
|
||||
newy = vec(Float64[yi for i = 1:3, yi in y])
|
||||
newy = vec(Float64[yi for i in 1:3, yi in y])
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :straightline
|
||||
@ -125,7 +124,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i = 1:3, yi in y])
|
||||
newx = vec(Float64[yi for i in 1:3, yi in y])
|
||||
x := newx
|
||||
y := repeat(Float64[1, 2, NaN], n)
|
||||
seriestype := :straightline
|
||||
@ -136,7 +135,7 @@ end
|
||||
@recipe function f(::Type{Val{:hspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
@ -147,7 +146,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:vspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
@ -179,7 +178,6 @@ end
|
||||
end
|
||||
@deps scatterpath path scatter
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# regression line and scatter
|
||||
|
||||
@ -202,10 +200,8 @@ end
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
@specialize
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# steps
|
||||
|
||||
@ -215,7 +211,7 @@ function make_steps(x::AbstractArray, st, even)
|
||||
n == 0 && return zeros(0)
|
||||
newx = zeros(2n - (even ? 0 : 1))
|
||||
newx[1] = x[1]
|
||||
for i = 2:n
|
||||
for i in 2:n
|
||||
idx = 2i - 1
|
||||
if st == :mid
|
||||
newx[idx] = newx[idx - 1] = (x[i] + x[i - 1]) / 2
|
||||
@ -229,7 +225,6 @@ function make_steps(x::AbstractArray, st, even)
|
||||
end
|
||||
make_steps(t::Tuple, st, even) = Tuple(make_steps(ti, st, even) for ti in t)
|
||||
|
||||
|
||||
@nospecialize
|
||||
|
||||
# create a path from steps
|
||||
@ -307,7 +302,6 @@ end
|
||||
end
|
||||
@deps steppost path scatter
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# sticks
|
||||
|
||||
@ -326,7 +320,7 @@ end
|
||||
end
|
||||
newx, newy = zeros(3n), zeros(3n)
|
||||
newz = z !== nothing ? zeros(3n) : nothing
|
||||
for (i, (xi, yi, zi)) = enumerate(zip(x, y, z !== nothing ? z : 1:n))
|
||||
for (i, (xi, yi, zi)) in enumerate(zip(x, y, z !== nothing ? z : 1:n))
|
||||
rng = (3i - 2):(3i)
|
||||
newx[rng] = [xi, xi, NaN]
|
||||
if z !== nothing
|
||||
@ -343,7 +337,11 @@ end
|
||||
end
|
||||
fillrange := nothing
|
||||
seriestype := :path
|
||||
if plotattributes[:linecolor] == :auto && plotattributes[:marker_z] !== nothing && plotattributes[:line_z] === nothing
|
||||
if (
|
||||
plotattributes[:linecolor] == :auto &&
|
||||
plotattributes[:marker_z] !== nothing &&
|
||||
plotattributes[:line_z] === nothing
|
||||
)
|
||||
line_z := plotattributes[:marker_z]
|
||||
end
|
||||
|
||||
@ -368,7 +366,6 @@ end
|
||||
|
||||
@specialize
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# bezier curves
|
||||
|
||||
@ -438,8 +435,7 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
procx, procy, xscale, yscale, baseline =
|
||||
_preprocess_barlike(plotattributes, x, y)
|
||||
procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y)
|
||||
nx, ny = length(procx), length(procy)
|
||||
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi in procx]
|
||||
@ -448,7 +444,9 @@ end
|
||||
elseif nx == ny + 1
|
||||
0.5 * diff(cv) + cv[1:(end - 1)]
|
||||
else
|
||||
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
|
||||
error(
|
||||
"bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))",
|
||||
)
|
||||
end
|
||||
|
||||
# compute half-width of bars
|
||||
@ -473,7 +471,7 @@ end
|
||||
end
|
||||
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i = 1:ny
|
||||
for i in 1:ny
|
||||
yi = procy[i]
|
||||
if !isnan(yi)
|
||||
center = procx[i]
|
||||
@ -532,8 +530,8 @@ end
|
||||
m, n = size(z.surf)
|
||||
x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n)
|
||||
fz = zeros(m * n)
|
||||
for i = 1:m # y
|
||||
for j = 1:n # x
|
||||
for i in 1:m # y
|
||||
for j in 1:n # x
|
||||
k = (j - 1) * m + i
|
||||
inds = (6 * (k - 1) + 1):(6 * k - 1)
|
||||
x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]]
|
||||
@ -580,7 +578,6 @@ function _scale_adjusted_values(
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
|
||||
if (scale in _logScales)
|
||||
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
|
||||
@ -589,7 +586,6 @@ function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _preprocess_binbarlike_weights(
|
||||
::Type{T},
|
||||
w,
|
||||
@ -618,12 +614,10 @@ function _preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline
|
||||
end
|
||||
|
||||
|
||||
@nospecialize
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
if (plotattributes[:bar_width] === nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
@ -634,10 +628,8 @@ end
|
||||
end
|
||||
@deps barbins bar
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
@series begin
|
||||
x := _bin_centers(edge)
|
||||
xerror := diff(edge) / 2
|
||||
@ -654,13 +646,7 @@ end
|
||||
|
||||
@specialize
|
||||
|
||||
function _stepbins_path(
|
||||
edge,
|
||||
weights,
|
||||
baseline::Real,
|
||||
xscale::Symbol,
|
||||
yscale::Symbol,
|
||||
)
|
||||
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
|
||||
log_scale_x = xscale in _logScales
|
||||
log_scale_y = yscale in _logScales
|
||||
|
||||
@ -722,11 +708,9 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||
@nospecialize
|
||||
axis =
|
||||
plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
axis = plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
|
||||
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||
if !isvertical(plotattributes)
|
||||
@ -778,12 +762,9 @@ function _auto_binning_nbins(
|
||||
|
||||
# The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation
|
||||
nd = n_samples^(1 / (2 + N))
|
||||
nd = N == 2 ?
|
||||
min(
|
||||
n_samples^(1 / (2 + N)),
|
||||
nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8),
|
||||
) :
|
||||
nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
nd =
|
||||
N == 2 ?
|
||||
min(n_samples^(1 / (2 + N)), nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8)) : nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
|
||||
v = vs[dim]
|
||||
|
||||
@ -812,11 +793,8 @@ _hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {
|
||||
StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} =
|
||||
_hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
dim::Integer,
|
||||
binning::AbstractVector,
|
||||
) where {N} = binning
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) where {N} =
|
||||
binning
|
||||
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N,Any}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...,))
|
||||
@ -908,7 +886,6 @@ end
|
||||
end
|
||||
@deps scatterhist scatterbins
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T,1,E}) where {T,E}
|
||||
seriestype --> :barbins
|
||||
|
||||
@ -918,8 +895,7 @@ end
|
||||
:step => :stepbins,
|
||||
:steppost => :stepbins, # :step can be mapped to :steppost in pre-processing
|
||||
)
|
||||
seriestype :=
|
||||
get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
|
||||
if plotattributes[:seriestype] == :scatterbins
|
||||
# Workaround, error bars currently not set correctly by scatterbins
|
||||
@ -933,7 +909,6 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(hv::AbstractVector{H}) where {H<:StatsBase.Histogram}
|
||||
for h in hv
|
||||
@series begin
|
||||
@ -942,7 +917,6 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histogram 2D
|
||||
|
||||
@ -969,7 +943,6 @@ end
|
||||
end
|
||||
Plots.@deps bins2d heatmap
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
h = _make_hist(
|
||||
(x, y),
|
||||
@ -985,13 +958,11 @@ Plots.@deps bins2d heatmap
|
||||
end
|
||||
@deps histogram2d bins2d
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T,2,E}) where {T,E}
|
||||
seriestype --> :bins2d
|
||||
(h.edges[1], h.edges[2], Surface(h.weights))
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# pie
|
||||
@recipe function f(::Type{Val{:pie}}, x, y, z)
|
||||
@ -1013,7 +984,6 @@ end
|
||||
end
|
||||
@deps pie shape
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# mesh 3d replacement for non-plotly backends
|
||||
|
||||
@ -1021,12 +991,15 @@ end
|
||||
# As long as no i,j,k are supplied this should work with PyPlot and GR
|
||||
seriestype := :surface
|
||||
if plotattributes[:connections] !== nothing
|
||||
throw(ArgumentError("Giving triangles using the connections argument is only supported on Plotly backend."))
|
||||
throw(
|
||||
ArgumentError(
|
||||
"Giving triangles using the connections argument is only supported on Plotly backend.",
|
||||
),
|
||||
)
|
||||
end
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# scatter 3d
|
||||
|
||||
@ -1072,11 +1045,12 @@ export lens!
|
||||
linecolor := :lightgray
|
||||
bbx_mag = (x1 + x2) / 2
|
||||
bby_mag = (y1 + y2) / 2
|
||||
xi_lens, yi_lens = intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
|
||||
xi_mag, yi_mag = intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
|
||||
xi_lens, yi_lens =
|
||||
intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
|
||||
xi_mag, yi_mag =
|
||||
intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
|
||||
# add lines
|
||||
if xl1 < xi_lens < xl2 &&
|
||||
yl1 < yi_lens < yl2
|
||||
if xl1 < xi_lens < xl2 && yl1 < yi_lens < yl2
|
||||
@series begin
|
||||
primary := false
|
||||
subplot := sp_index
|
||||
@ -1276,7 +1250,7 @@ function quiver_using_arrows(plotattributes::AKW)
|
||||
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
x, y = zeros(0), zeros(0)
|
||||
is_3d && (z = zeros(0))
|
||||
for i = 1:max(length(xorig), length(yorig), is_3d ? 0 : length(zorig))
|
||||
for i in 1:max(length(xorig), length(yorig), is_3d ? 0 : length(zorig))
|
||||
# get the starting position
|
||||
xi = _cycle(xorig, i)
|
||||
yi = _cycle(yorig, i)
|
||||
@ -1326,7 +1300,7 @@ function quiver_using_hack(plotattributes::AKW)
|
||||
|
||||
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
pts = P2[]
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
for i in 1:max(length(xorig), length(yorig))
|
||||
|
||||
# get the starting position
|
||||
xi = _cycle(xorig, i)
|
||||
@ -1355,10 +1329,7 @@ function quiver_using_hack(plotattributes::AKW)
|
||||
U2 *= arrow_w
|
||||
|
||||
ppv = p + v
|
||||
nanappend!(
|
||||
pts,
|
||||
P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1],
|
||||
)
|
||||
nanappend!(pts, P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1])
|
||||
end
|
||||
|
||||
plotattributes[:x], plotattributes[:y] = RecipesPipeline.unzip(pts[2:end])
|
||||
@ -1377,7 +1348,6 @@ end
|
||||
end
|
||||
@deps quiver shape path
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 1 argument
|
||||
# --------------------------------------------------------------------
|
||||
@ -1455,7 +1425,6 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 3 arguments
|
||||
# --------------------------------------------------------------------
|
||||
@ -1554,8 +1523,7 @@ end
|
||||
@recipe f(
|
||||
x::AVec,
|
||||
ohlc::AVec{Tuple{R1,R2,R3,R4}},
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} =
|
||||
x, OHLC[OHLC(t...) for t in ohlc]
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = x, OHLC[OHLC(t...) for t in ohlc]
|
||||
|
||||
@recipe function f(x::AVec, v::AVec{OHLC})
|
||||
seriestype := :path
|
||||
@ -1574,7 +1542,6 @@ end
|
||||
# TODO: everything below here should be either changed to a
|
||||
# series recipe or moved to PlotRecipes
|
||||
|
||||
|
||||
# "Sparsity plot... heatmap of non-zero values of a matrix"
|
||||
# function spy{T<:Real}(z::AMat{T}; kw...)
|
||||
# mat = reshape(map(zi->float(zi!=0), z),1,:)
|
||||
@ -1622,7 +1589,6 @@ end
|
||||
|
||||
@specialize
|
||||
|
||||
|
||||
Plots.findnz(A::AbstractSparseMatrix) = SparseArrays.findnz(A)
|
||||
|
||||
# fallback function for finding non-zero elements of non-sparse matrices
|
||||
@ -1662,7 +1628,6 @@ end
|
||||
x, real.(y), imag.(y)
|
||||
end
|
||||
|
||||
|
||||
# Moved in from PlotRecipes - see: http://stackoverflow.com/a/37732384/5075246
|
||||
@userplot PortfolioComposition
|
||||
|
||||
|
||||
@ -241,7 +241,6 @@ julia> ohlc(y)
|
||||
"""
|
||||
@shorthands ohlc
|
||||
|
||||
|
||||
"""
|
||||
contour(x,y,z)
|
||||
contour!(x,y,z)
|
||||
@ -263,7 +262,6 @@ julia> contour(x, y, (x, y) -> x^2 + y^2)
|
||||
"An alias for `contour` with fill = true."
|
||||
@shorthands contourf
|
||||
|
||||
|
||||
@shorthands contour3d
|
||||
|
||||
"""
|
||||
@ -433,17 +431,16 @@ xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmi
|
||||
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin, ymax), kw...)
|
||||
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin, zmax), kw...)
|
||||
|
||||
|
||||
"Set xticks for an existing plot"
|
||||
xticks!(v::TicksArgs; kw...) = plot!(; xticks = v, kw...)
|
||||
|
||||
"Set yticks for an existing plot"
|
||||
yticks!(v::TicksArgs; kw...) = plot!(; yticks = v, kw...)
|
||||
|
||||
xticks!(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; xticks = (ticks,labels), kw...)
|
||||
yticks!(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; yticks = (ticks,labels), kw...)
|
||||
xticks!(ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} =
|
||||
plot!(; xticks = (ticks, labels), kw...)
|
||||
yticks!(ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} =
|
||||
plot!(; yticks = (ticks, labels), kw...)
|
||||
|
||||
"""
|
||||
annotate!(anns...)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
|
||||
|
||||
function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
|
||||
function Subplot(::T; parent = RootLayout()) where {T<:AbstractBackend}
|
||||
Subplot{T}(
|
||||
parent,
|
||||
Series[],
|
||||
@ -9,7 +8,7 @@ function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
|
||||
defaultbox,
|
||||
DefaultsDict(KW(), _subplot_defaults),
|
||||
nothing,
|
||||
nothing
|
||||
nothing,
|
||||
)
|
||||
end
|
||||
|
||||
@ -21,7 +20,6 @@ Return the bounding box of a subplot
|
||||
plotarea(sp::Subplot) = sp.plotarea
|
||||
plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox)
|
||||
|
||||
|
||||
Base.size(sp::Subplot) = (1, 1)
|
||||
Base.length(sp::Subplot) = 1
|
||||
Base.getindex(sp::Subplot, r::Int, c::Int) = sp
|
||||
|
||||
@ -34,11 +34,8 @@ end
|
||||
|
||||
@userplot ShowTheme
|
||||
|
||||
_color_functions = KW(
|
||||
:protanopic => protanopic,
|
||||
:deuteranopic => deuteranopic,
|
||||
:tritanopic => tritanopic,
|
||||
)
|
||||
_color_functions =
|
||||
KW(:protanopic => protanopic, :deuteranopic => deuteranopic, :tritanopic => tritanopic)
|
||||
_get_showtheme_args(thm::Symbol) = thm, identity
|
||||
_get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func, identity)
|
||||
|
||||
@ -110,8 +107,8 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
subplot := 4
|
||||
seriestype := :heatmap
|
||||
seriescolor := colorgradient
|
||||
xticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
yticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
xticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
|
||||
yticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
|
||||
x, y, z
|
||||
end
|
||||
|
||||
@ -119,8 +116,8 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
subplot := 5
|
||||
seriestype := :surface
|
||||
seriescolor := colorgradient
|
||||
xticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
yticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
xticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
|
||||
yticks := ((-2π):(2π):(2π), string.(-2:2:2, "π"))
|
||||
x, y, z
|
||||
end
|
||||
|
||||
@ -137,5 +134,4 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
line_z := z
|
||||
x, y, z
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
19
src/types.jl
19
src/types.jl
@ -6,7 +6,8 @@ const AVec = AbstractVector
|
||||
const AMat = AbstractMatrix
|
||||
const KW = Dict{Symbol,Any}
|
||||
const AKW = AbstractDict{Symbol,Any}
|
||||
const TicksArgs = Union{AVec{T}, Tuple{AVec{T}, AVec{S}}, Symbol} where {T<:Real, S<:AbstractString}
|
||||
const TicksArgs =
|
||||
Union{AVec{T},Tuple{AVec{T},AVec{S}},Symbol} where {T<:Real,S<:AbstractString}
|
||||
|
||||
struct PlotsDisplay <: AbstractDisplay end
|
||||
|
||||
@ -63,7 +64,6 @@ const SubplotMap = Dict{Any, Subplot}
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
|
||||
mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
backend::T # the backend type
|
||||
n::Int # number of series
|
||||
@ -78,9 +78,18 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
end
|
||||
|
||||
function Plot()
|
||||
Plot(backend(), 0, DefaultsDict(KW(), _plot_defaults), Series[], nothing,
|
||||
Subplot[], SubplotMap(), EmptyLayout(),
|
||||
Subplot[], false)
|
||||
Plot(
|
||||
backend(),
|
||||
0,
|
||||
DefaultsDict(KW(), _plot_defaults),
|
||||
Series[],
|
||||
nothing,
|
||||
Subplot[],
|
||||
SubplotMap(),
|
||||
EmptyLayout(),
|
||||
Subplot[],
|
||||
false,
|
||||
)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
192
src/utils.jl
192
src/utils.jl
@ -1,11 +1,12 @@
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
treats_y_as_x(seriestype) = seriestype in (:vline, :vspan, :histogram, :barhist, :stephist, :scatterhist)
|
||||
treats_y_as_x(seriestype) =
|
||||
seriestype in (:vline, :vspan, :histogram, :barhist, :stephist, :scatterhist)
|
||||
|
||||
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)
|
||||
colors = palette(vec(z))
|
||||
newz = reshape(1:n*m, n, m)
|
||||
newz = reshape(1:(n * m), n, m)
|
||||
newz, colors
|
||||
end
|
||||
|
||||
@ -22,7 +23,6 @@ Segments() = Segments(Float64)
|
||||
Segments(::Type{T}) where {T} = Segments(T[])
|
||||
Segments(p::Int) = Segments(NTuple{p,Float64}[])
|
||||
|
||||
|
||||
# Segments() = Segments(zeros(0))
|
||||
|
||||
to_nan(::Type{Float64}) = NaN
|
||||
@ -30,10 +30,13 @@ to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN)
|
||||
to_nan(::Type{NTuple{3,Float64}}) = (NaN, NaN, NaN)
|
||||
|
||||
coords(segs::Segments{Float64}) = segs.pts
|
||||
coords(segs::Segments{NTuple{2,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
|
||||
coords(segs::Segments{NTuple{3,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts], Float64[p[3] for p in segs.pts]
|
||||
coords(segs::Segments{NTuple{2,Float64}}) =
|
||||
Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
|
||||
coords(segs::Segments{NTuple{3,Float64}}) = Float64[p[1] for p in segs.pts],
|
||||
Float64[p[2] for p in segs.pts],
|
||||
Float64[p[3] for p in segs.pts]
|
||||
|
||||
function Base.push!(segments::Segments{T}, vs...) where T
|
||||
function Base.push!(segments::Segments{T}, vs...) where {T}
|
||||
if !isempty(segments.pts)
|
||||
push!(segments.pts, to_nan(T))
|
||||
end
|
||||
@ -43,7 +46,7 @@ function Base.push!(segments::Segments{T}, vs...) where T
|
||||
segments
|
||||
end
|
||||
|
||||
function Base.push!(segments::Segments{T}, vs::AVec) where T
|
||||
function Base.push!(segments::Segments{T}, vs::AVec) where {T}
|
||||
if !isempty(segments.pts)
|
||||
push!(segments.pts, to_nan(T))
|
||||
end
|
||||
@ -53,7 +56,6 @@ function Base.push!(segments::Segments{T}, vs::AVec) where T
|
||||
segments
|
||||
end
|
||||
|
||||
|
||||
struct SeriesSegment
|
||||
# indexes of this segement in series data vectors
|
||||
range::UnitRange
|
||||
@ -85,10 +87,10 @@ function series_segments(series::Series, seriestype::Symbol=:path; check=false)
|
||||
|
||||
if check
|
||||
scales = :xscale, :yscale, :zscale
|
||||
for (n, s) ∈ enumerate(args)
|
||||
for (n, s) in enumerate(args)
|
||||
scale = get(series, scales[n], :identity)
|
||||
if scale ∈ _logScales
|
||||
for (i, v) ∈ enumerate(s)
|
||||
for (i, v) in enumerate(s)
|
||||
if v <= 0
|
||||
@warn "Invalid negative or zero value $v found at series index $i for $(scale) based $(scales[n])"
|
||||
@debug "" exception = (DomainError(v), stacktrace())
|
||||
@ -104,7 +106,7 @@ function series_segments(series::Series, seriestype::Symbol=:path; check=false)
|
||||
if seriestype in (:scatter, :scatter3d)
|
||||
(SeriesSegment(i:i, i) for i in r)
|
||||
else
|
||||
(SeriesSegment(i:i+1, i) for i in first(r):last(r)-1)
|
||||
(SeriesSegment(i:(i + 1), i) for i in first(r):(last(r) - 1))
|
||||
end
|
||||
end)
|
||||
else
|
||||
@ -117,8 +119,10 @@ end
|
||||
|
||||
function warn_on_attr_dim_mismatch(series, x, y, z, segments)
|
||||
isempty(segments) && return
|
||||
seg_range = UnitRange(minimum(first(seg.range) for seg in segments),
|
||||
maximum(last(seg.range) for seg in segments))
|
||||
seg_range = UnitRange(
|
||||
minimum(first(seg.range) for seg in segments),
|
||||
maximum(last(seg.range) for seg in segments),
|
||||
)
|
||||
for attr in _segmenting_vector_attributes
|
||||
v = get(series, attr, nothing)
|
||||
if v isa AVec && eachindex(v) != seg_range
|
||||
@ -137,20 +141,24 @@ function warn_on_attr_dim_mismatch(series, x, y, z, segments)
|
||||
end
|
||||
|
||||
# helpers to figure out if there are NaN values in a list of array types
|
||||
anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, args)
|
||||
anynan(i::Int, args::Tuple) = any(a -> try
|
||||
isnan(_cycle(a, i))
|
||||
catch MethodError
|
||||
false
|
||||
end, args)
|
||||
anynan(args::Tuple) = i -> anynan(i, args)
|
||||
anynan(istart::Int, iend::Int, args::Tuple) = any(anynan(args), istart:iend)
|
||||
allnan(istart::Int, iend::Int, args::Tuple) = all(anynan(args), istart:iend)
|
||||
|
||||
function Base.iterate(itr::NaNSegmentsIterator, nextidx::Int = itr.n1)
|
||||
i = findfirst(!anynan(itr.args), nextidx:itr.n2)
|
||||
i = findfirst(!anynan(itr.args), nextidx:(itr.n2))
|
||||
i === nothing && return
|
||||
nextval = nextidx + i - 1
|
||||
|
||||
j = findfirst(anynan(itr.args), nextval:itr.n2)
|
||||
j = findfirst(anynan(itr.args), nextval:(itr.n2))
|
||||
nextnan = j === nothing ? itr.n2 + 1 : nextval + j - 1
|
||||
|
||||
nextval:nextnan-1, nextnan
|
||||
nextval:(nextnan - 1), nextnan
|
||||
end
|
||||
Base.IteratorSize(::NaNSegmentsIterator) = Base.SizeUnknown()
|
||||
|
||||
@ -162,7 +170,6 @@ float_extended_type(x::AbstractArray{T}) where {T<:Real} = Float64
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
nop() = nothing
|
||||
notimpl() = error("This has not been implemented yet")
|
||||
|
||||
@ -199,16 +206,15 @@ for i in 2:4
|
||||
@eval begin
|
||||
RecipesPipeline.unzip(
|
||||
v::Union{AVec{<:Tuple{Vararg{T,$i} where T}},AVec{<:GeometryBasics.Point{$i}}},
|
||||
) = $(Expr(:tuple, (:([t[$j] for t in v]) for j=1:i)...))
|
||||
) = $(Expr(:tuple, (:([t[$j] for t in v]) for j in 1:i)...))
|
||||
end
|
||||
end
|
||||
|
||||
RecipesPipeline.unzip(
|
||||
::Union{AVec{<:GeometryBasics.Point{N}}, AVec{<:Tuple{Vararg{T,N} where T}}}
|
||||
) where N = error("$N-dimensional unzip not implemented.")
|
||||
RecipesPipeline.unzip(::Union{AVec{<:GeometryBasics.Point}, AVec{<:Tuple}}) = error(
|
||||
"Can't unzip points of different dimensions."
|
||||
)
|
||||
::Union{AVec{<:GeometryBasics.Point{N}},AVec{<:Tuple{Vararg{T,N} where T}}},
|
||||
) where {N} = error("$N-dimensional unzip not implemented.")
|
||||
RecipesPipeline.unzip(::Union{AVec{<:GeometryBasics.Point},AVec{<:Tuple}}) =
|
||||
error("Can't unzip points of different dimensions.")
|
||||
|
||||
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
||||
function _expand_limits(lims, x)
|
||||
@ -221,7 +227,7 @@ function _expand_limits(lims, x)
|
||||
nothing
|
||||
end
|
||||
|
||||
expand_data(v, n::Integer) = [_cycle(v, i) for i=1:n]
|
||||
expand_data(v, n::Integer) = [_cycle(v, i) for i in 1:n]
|
||||
|
||||
# if the type exists in a list, replace the first occurence. otherwise add it to the end
|
||||
function addOrReplace(v::AbstractVector, t::DataType, args...; kw...)
|
||||
@ -259,22 +265,36 @@ sortedkeys(plotattributes::Dict) = sort(collect(keys(plotattributes)))
|
||||
|
||||
function _heatmap_edges(v::AVec, isedges::Bool = false, ispolar::Bool = false)
|
||||
length(v) == 1 && return v[1] .+ [ispolar ? max(-v[1], -0.5) : -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
|
||||
# and does not need to be extended.
|
||||
vmin, vmax = ignorenan_extrema(v)
|
||||
extra_min = ispolar ? min(v[1], (v[2] - v[1]) / 2) : (v[2] - v[1]) / 2
|
||||
extra_max = (v[end] - v[end - 1]) / 2
|
||||
vcat(vmin-extra_min, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra_max)
|
||||
vcat(vmin - extra_min, 0.5 * (v[1:(end - 1)] + v[2:end]), vmax + extra_max)
|
||||
end
|
||||
|
||||
"create an (n+1) list of the outsides of heatmap rectangles"
|
||||
function heatmap_edges(v::AVec, scale::Symbol = :identity, isedges::Bool = false, ispolar::Bool = false)
|
||||
function heatmap_edges(
|
||||
v::AVec,
|
||||
scale::Symbol = :identity,
|
||||
isedges::Bool = false,
|
||||
ispolar::Bool = false,
|
||||
)
|
||||
f, invf = RecipesPipeline.scale_func(scale), RecipesPipeline.inverse_scale_func(scale)
|
||||
map(invf, _heatmap_edges(map(f, v), isedges, ispolar))
|
||||
end
|
||||
|
||||
function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size::Tuple{Int, Int}, ispolar::Bool = false)
|
||||
function heatmap_edges(
|
||||
x::AVec,
|
||||
xscale::Symbol,
|
||||
y::AVec,
|
||||
yscale::Symbol,
|
||||
z_size::Tuple{Int,Int},
|
||||
ispolar::Bool = false,
|
||||
)
|
||||
nx, ny = length(x), length(y)
|
||||
# ismidpoints = z_size == (ny, nx) # This fails some tests, but would actually be
|
||||
# the correct check, since (4, 3) != (3, 4) and a missleading plot is produced.
|
||||
@ -285,8 +305,7 @@ function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size:
|
||||
Must be either `size(z) == (length(y), length(x))` (x & y define midpoints)
|
||||
or `size(z) == (length(y)+1, length(x)+1))` (x & y define edges).""")
|
||||
end
|
||||
x, y = heatmap_edges(x, xscale, isedges),
|
||||
heatmap_edges(y, yscale, isedges, ispolar) # special handle for `r` in polar plots
|
||||
x, y = heatmap_edges(x, xscale, isedges), heatmap_edges(y, yscale, isedges, ispolar) # special handle for `r` in polar plots
|
||||
return x, y
|
||||
end
|
||||
|
||||
@ -327,13 +346,14 @@ isscalar(::Any) = false
|
||||
|
||||
is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2
|
||||
|
||||
isvertical(plotattributes::AKW) = get(plotattributes, :orientation, :vertical) in (:vertical, :v, :vert)
|
||||
isvertical(plotattributes::AKW) =
|
||||
get(plotattributes, :orientation, :vertical) in (:vertical, :v, :vert)
|
||||
isvertical(series::Series) = isvertical(series.plotattributes)
|
||||
|
||||
|
||||
ticksType(ticks::AVec{T}) where {T<:Real} = :ticks
|
||||
ticksType(ticks::AVec{T}) where {T<:AbstractString} = :labels
|
||||
ticksType(ticks::Tuple{T,S}) where {T<:Union{AVec,Tuple},S<:Union{AVec,Tuple}} = :ticks_and_labels
|
||||
ticksType(ticks::Tuple{T,S}) where {T<:Union{AVec,Tuple},S<:Union{AVec,Tuple}} =
|
||||
:ticks_and_labels
|
||||
ticksType(ticks) = :invalid
|
||||
|
||||
limsType(lims::Tuple{T,S}) where {T<:Real,S<:Real} = :limits
|
||||
@ -357,9 +377,9 @@ function nansplit(v::AVec)
|
||||
push!(vs, v)
|
||||
break
|
||||
elseif idx > 1
|
||||
push!(vs, v[1:idx-1])
|
||||
push!(vs, v[1:(idx - 1)])
|
||||
end
|
||||
v = v[idx+1:end]
|
||||
v = v[(idx + 1):end]
|
||||
end
|
||||
vs
|
||||
end
|
||||
@ -454,7 +474,7 @@ function contour_levels(series::Series, clims)
|
||||
if levels isa Integer
|
||||
levels = range(zmin, stop = zmax, length = levels + 2)
|
||||
if !isfilledcontour(series)
|
||||
levels = levels[2:end-1]
|
||||
levels = levels[2:(end - 1)]
|
||||
end
|
||||
end
|
||||
levels
|
||||
@ -479,7 +499,8 @@ for comp in (:line, :fill, :marker)
|
||||
end
|
||||
end
|
||||
|
||||
$get_compcolor(series, clims, i::Int=1) = $get_compcolor(series, clims[1], clims[2], i)
|
||||
$get_compcolor(series, clims, i::Int = 1) =
|
||||
$get_compcolor(series, clims[1], clims[2], i)
|
||||
|
||||
function $get_compcolor(series, i::Int = 1)
|
||||
if series[$Symbol($comp_z)] === nothing
|
||||
@ -551,8 +572,9 @@ function has_attribute_segments(series::Series)
|
||||
# of its attributes
|
||||
series[:seriestype] == :shape && return false
|
||||
# check relevant attributes if they have multiple inputs
|
||||
return any(series[attr] isa AbstractVector && length(series[attr]) > 1
|
||||
for attr in _segmenting_vector_attributes
|
||||
return any(
|
||||
series[attr] isa AbstractVector && length(series[attr]) > 1 for
|
||||
attr in _segmenting_vector_attributes
|
||||
) || any(series[attr] isa AbstractArray for attr in _segmenting_array_attributes)
|
||||
end
|
||||
|
||||
@ -577,7 +599,8 @@ get_size(sp::Subplot) = get_size(sp.plt)
|
||||
get_thickness_scaling(kw) = get(kw, :thickness_scaling, default(:thickness_scaling))
|
||||
get_thickness_scaling(plt::Plot) = get_thickness_scaling(plt.attr)
|
||||
get_thickness_scaling(sp::Subplot) = get_thickness_scaling(sp.plt)
|
||||
get_thickness_scaling(series::Series) = get_thickness_scaling(series.plotattributes[:subplot])
|
||||
get_thickness_scaling(series::Series) =
|
||||
get_thickness_scaling(series.plotattributes[:subplot])
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
makekw(; kw...) = KW(kw)
|
||||
@ -595,8 +618,11 @@ allShapes(arg) = (
|
||||
trueOrAllTrue(a -> isa(a, Shape), arg)
|
||||
)
|
||||
allAlphas(arg) = trueOrAllTrue(
|
||||
a -> (typeof(a) <: Real && a > 0 && a < 1) ||
|
||||
(typeof(a) <: AbstractFloat && (a == zero(typeof(a)) || a == one(typeof(a)))), arg
|
||||
a ->
|
||||
(typeof(a) <: Real && a > 0 && a < 1) || (
|
||||
typeof(a) <: AbstractFloat && (a == zero(typeof(a)) || a == one(typeof(a)))
|
||||
),
|
||||
arg,
|
||||
)
|
||||
allReals(arg) = trueOrAllTrue(a -> typeof(a) <: Real, arg)
|
||||
allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
|
||||
@ -747,16 +773,17 @@ function setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z}
|
||||
_series_updated(plt, series)
|
||||
end
|
||||
|
||||
setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z<:AbstractMatrix} = (
|
||||
setxyz!(plt, (xyz[1], xyz[2], Surface(xyz[3])), i)
|
||||
)
|
||||
setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z<:AbstractMatrix} =
|
||||
(setxyz!(plt, (xyz[1], xyz[2], Surface(xyz[3])), i))
|
||||
|
||||
# -------------------------------------------------------
|
||||
# indexing notation
|
||||
|
||||
# Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
|
||||
Base.setindex!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y} = (setxy!(plt, xy, i); 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, xy::Tuple{X,Y}, i::Integer) where {X,Y} =
|
||||
(setxy!(plt, xy, 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
|
||||
@ -810,7 +837,8 @@ function extend_to_length!(v::AbstractVector, n)
|
||||
extend_by_data!(v, vmax .+ (1:(n - length(v))))
|
||||
end
|
||||
extend_by_data!(v::AbstractVector, x) = isimmutable(v) ? vcat(v, x) : push!(v, x)
|
||||
extend_by_data!(v::AbstractVector, x::AbstractVector) = isimmutable(v) ? vcat(v, x) : append!(v, x)
|
||||
extend_by_data!(v::AbstractVector, x::AbstractVector) =
|
||||
isimmutable(v) ? vcat(v, x) : append!(v, x)
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
@ -861,7 +889,7 @@ Base.append!(plt::Plot, i::Integer, t::Tuple) = append!(plt, i, t...)
|
||||
# push y[i] to the ith series
|
||||
function Base.push!(plt::Plot, y::AVec)
|
||||
ny = length(y)
|
||||
for i in 1:plt.n
|
||||
for i in 1:(plt.n)
|
||||
push!(plt, i, y[mod1(i, ny)])
|
||||
end
|
||||
plt
|
||||
@ -875,7 +903,7 @@ Base.push!(plt::Plot, x::Real, y::AVec) = push!(plt, [x], y)
|
||||
function Base.push!(plt::Plot, x::AVec, y::AVec)
|
||||
nx = length(x)
|
||||
ny = length(y)
|
||||
for i in 1:plt.n
|
||||
for i in 1:(plt.n)
|
||||
push!(plt, i, x[mod1(i, nx)], y[mod1(i, ny)])
|
||||
end
|
||||
plt
|
||||
@ -886,7 +914,7 @@ function Base.push!(plt::Plot, x::AVec, y::AVec, z::AVec)
|
||||
nx = length(x)
|
||||
ny = length(y)
|
||||
nz = length(z)
|
||||
for i in 1:plt.n
|
||||
for i in 1:(plt.n)
|
||||
push!(plt, i, x[mod1(i, nx)], y[mod1(i, ny)], z[mod1(i, nz)])
|
||||
end
|
||||
plt
|
||||
@ -909,15 +937,14 @@ mm2inch(mm::Real) = float(mm / MM_PER_INCH)
|
||||
px2mm(px::Real) = float(px * MM_PER_PX)
|
||||
mm2px(mm::Real) = float(mm / MM_PER_PX)
|
||||
|
||||
|
||||
"Smallest x in plot"
|
||||
xmin(plt::Plot) = ignorenan_minimum(
|
||||
[ignorenan_minimum(series.plotattributes[:x]) for series in plt.series_list]
|
||||
)
|
||||
xmin(plt::Plot) = ignorenan_minimum([
|
||||
ignorenan_minimum(series.plotattributes[:x]) for series in plt.series_list
|
||||
])
|
||||
"Largest x in plot"
|
||||
xmax(plt::Plot) = ignorenan_maximum(
|
||||
[ignorenan_maximum(series.plotattributes[:x]) for series in plt.series_list]
|
||||
)
|
||||
xmax(plt::Plot) = ignorenan_maximum([
|
||||
ignorenan_maximum(series.plotattributes[:x]) for series in plt.series_list
|
||||
])
|
||||
|
||||
"Extrema of x-values in plot"
|
||||
ignorenan_extrema(plt::Plot) = (xmin(plt), xmax(plt))
|
||||
@ -1040,11 +1067,14 @@ function straightline_data(series, expansion_factor = 1)
|
||||
xdata, ydata = fill(NaN, n), fill(NaN, n)
|
||||
for i in 1:k
|
||||
inds = (3 * i - 2):(3 * i - 1)
|
||||
xdata[inds], ydata[inds] = straightline_data(xl, yl, x[inds], y[inds], expansion_factor)
|
||||
xdata[inds], ydata[inds] =
|
||||
straightline_data(xl, yl, x[inds], y[inds], expansion_factor)
|
||||
end
|
||||
xdata, ydata
|
||||
else
|
||||
error("Misformed data. `straightline_data` either accepts vectors of length 2 or 3k. The provided series has length $n")
|
||||
error(
|
||||
"Misformed data. `straightline_data` either accepts vectors of length 2 or 3k. The provided series has length $n",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1066,7 +1096,10 @@ function straightline_data(xl, yl, x, y, expansion_factor = 1)
|
||||
b = y[1] - (y[1] - y[2]) * x[1] / (x[1] - x[2])
|
||||
a = (y[1] - y[2]) / (x[1] - x[2])
|
||||
# get the data values
|
||||
xdata = [clamp(x[1] + (x[1] - x[2]) * (ylim - y[1]) / (y[1] - y[2]), xl...) for ylim in yl]
|
||||
xdata = [
|
||||
clamp(x[1] + (x[1] - x[2]) * (ylim - y[1]) / (y[1] - y[2]), xl...) for
|
||||
ylim in yl
|
||||
]
|
||||
|
||||
xdata, a .* xdata .+ b
|
||||
end
|
||||
@ -1107,17 +1140,18 @@ function shape_data(series, expansion_factor = 1)
|
||||
return x, y
|
||||
end
|
||||
|
||||
construct_categorical_data(x::AbstractArray, axis::Axis) = (
|
||||
map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x)
|
||||
)
|
||||
construct_categorical_data(x::AbstractArray, axis::Axis) =
|
||||
(map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x))
|
||||
|
||||
_fmt_paragraph(paragraph::AbstractString;kwargs...) = _fmt_paragraph(
|
||||
IOBuffer(), paragraph, 0; kwargs...
|
||||
)
|
||||
_fmt_paragraph(paragraph::AbstractString; kwargs...) =
|
||||
_fmt_paragraph(IOBuffer(), paragraph, 0; kwargs...)
|
||||
|
||||
function _fmt_paragraph(
|
||||
io::IOBuffer, remaining_text::AbstractString, column_count::Integer;
|
||||
fillwidth=60, leadingspaces=0
|
||||
io::IOBuffer,
|
||||
remaining_text::AbstractString,
|
||||
column_count::Integer;
|
||||
fillwidth = 60,
|
||||
leadingspaces = 0,
|
||||
)
|
||||
kwargs = (fillwidth = fillwidth, leadingspaces = leadingspaces)
|
||||
|
||||
@ -1141,15 +1175,16 @@ function _fmt_paragraph(
|
||||
end
|
||||
end
|
||||
|
||||
_document_argument(S::AbstractString) = _fmt_paragraph(
|
||||
"`$S`: "*_arg_desc[Symbol(S)], leadingspaces=6+length(S)
|
||||
)
|
||||
_document_argument(S::AbstractString) =
|
||||
_fmt_paragraph("`$S`: " * _arg_desc[Symbol(S)], leadingspaces = 6 + length(S))
|
||||
|
||||
function mesh3d_triangles(x, y, z, cns)
|
||||
if typeof(cns) <: Tuple{Array,Array,Array}
|
||||
ci, cj, ck = cns
|
||||
if !(length(ci) == length(cj) == length(ck))
|
||||
throw(ArgumentError("Argument connections must consist of equally sized arrays."))
|
||||
throw(
|
||||
ArgumentError("Argument connections must consist of equally sized arrays."),
|
||||
)
|
||||
end
|
||||
else
|
||||
throw(ArgumentError("Argument connections has to be a tuple of three arrays."))
|
||||
@ -1157,11 +1192,14 @@ function mesh3d_triangles(x, y, z, cns)
|
||||
X = zeros(eltype(x), 4length(ci))
|
||||
Y = zeros(eltype(y), 4length(cj))
|
||||
Z = zeros(eltype(z), 4length(ck))
|
||||
@inbounds for I ∈ 1:length(ci)
|
||||
@inbounds for I in 1:length(ci)
|
||||
i = ci[I] + 1 # connections are 0-based
|
||||
j = cj[I] + 1
|
||||
k = ck[I] + 1
|
||||
m = 4(I - 1) + 1; n = m + 1; o = m + 2; p = m + 3
|
||||
m = 4(I - 1) + 1
|
||||
n = m + 1
|
||||
o = m + 2
|
||||
p = m + 3
|
||||
X[m] = X[p] = x[i]
|
||||
Y[m] = Y[p] = y[i]
|
||||
Z[m] = Z[p] = z[i]
|
||||
|
||||
@ -59,7 +59,7 @@ function image_comparison_facts(
|
||||
sigma = [1, 1], # number of pixels to "blur"
|
||||
tol = 1e-2,
|
||||
) # acceptable error (percent)
|
||||
for i = 1:length(Plots._examples)
|
||||
for i in 1:length(Plots._examples)
|
||||
i in skip && continue
|
||||
if only === nothing || i in only
|
||||
@test image_comparison_tests(pkg, i, debug = debug, sigma = sigma, tol = tol) |>
|
||||
|
||||
@ -39,7 +39,8 @@ include("test_recipes.jl")
|
||||
include("test_hdf5plots.jl")
|
||||
include("test_pgfplotsx.jl")
|
||||
|
||||
reference_dir(args...) = joinpath(homedir(), ".julia", "dev", "PlotReferenceImages", args...)
|
||||
reference_dir(args...) =
|
||||
joinpath(homedir(), ".julia", "dev", "PlotReferenceImages", args...)
|
||||
|
||||
function reference_file(backend, i, version)
|
||||
refdir = reference_dir("Plots", string(backend))
|
||||
@ -62,7 +63,10 @@ reference_path(backend, version) = reference_dir("Plots", string(backend), strin
|
||||
|
||||
if !isdir(reference_dir())
|
||||
mkpath(reference_dir())
|
||||
LibGit2.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git", reference_dir())
|
||||
LibGit2.clone(
|
||||
"https://github.com/JuliaPlots/PlotReferenceImages.jl.git",
|
||||
reference_dir(),
|
||||
)
|
||||
end
|
||||
|
||||
include("imgcomp.jl")
|
||||
@ -98,7 +102,6 @@ const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? "1e-4"
|
||||
##
|
||||
|
||||
@testset "Backends" begin
|
||||
|
||||
@testset "GR" begin
|
||||
ENV["PLOTS_TEST"] = "true"
|
||||
ENV["GKSwstype"] = "100"
|
||||
@ -108,7 +111,11 @@ const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? "1e-4"
|
||||
@static if haskey(ENV, "APPVEYOR")
|
||||
@info "Skipping GR image comparison tests on AppVeyor"
|
||||
else
|
||||
image_comparison_facts(:gr, tol=PLOTS_IMG_TOL, skip=Plots._backend_skips[:gr])
|
||||
image_comparison_facts(
|
||||
:gr,
|
||||
tol = PLOTS_IMG_TOL,
|
||||
skip = Plots._backend_skips[:gr],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -148,7 +155,6 @@ const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? "1e-4"
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test_broken isa(display(p), Nothing) == true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@testset "Axes" begin
|
||||
@ -160,8 +166,8 @@ end
|
||||
@test Plots.ignorenan_extrema(axis) == (0.5, 1.5)
|
||||
@test axis[:discrete_map] == Dict{Any,Any}(:yo => 2, "HI" => 1)
|
||||
|
||||
Plots.discrete_value!(axis, ["x$i" for i = 1:5])
|
||||
Plots.discrete_value!(axis, ["x$i" for i = 0:2])
|
||||
Plots.discrete_value!(axis, ["x$i" for i in 1:5])
|
||||
Plots.discrete_value!(axis, ["x$i" for i in 0:2])
|
||||
@test Plots.ignorenan_extrema(axis) == (0.5, 7.5)
|
||||
end
|
||||
|
||||
@ -171,14 +177,16 @@ end
|
||||
@test backend() == Plots.UnicodePlotsBackend()
|
||||
|
||||
@testset "Plot" begin
|
||||
plots = [histogram([1, 0, 0, 0, 0, 0]),
|
||||
plots = [
|
||||
histogram([1, 0, 0, 0, 0, 0]),
|
||||
plot([missing]),
|
||||
plot([missing, missing]),
|
||||
plot(fill(missing, 10)),
|
||||
plot([missing; 1:4]),
|
||||
plot([fill(missing, 10); 1:4]),
|
||||
plot([1 1; 1 missing]),
|
||||
plot(["a" "b"; missing "d"], [1 2; 3 4])]
|
||||
plot(["a" "b"; missing "d"], [1 2; 3 4]),
|
||||
]
|
||||
for plt in plots
|
||||
display(plt)
|
||||
end
|
||||
@ -186,13 +194,12 @@ end
|
||||
end
|
||||
|
||||
@testset "Bar" begin
|
||||
p = bar([3,2,1], [1,2,3]);
|
||||
p = bar([3, 2, 1], [1, 2, 3])
|
||||
@test isa(p, Plots.Plot)
|
||||
@test isa(display(p), Nothing) == true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@testset "EmptyAnim" begin
|
||||
anim = @animate for i in []
|
||||
end
|
||||
@ -215,10 +222,18 @@ end
|
||||
end
|
||||
|
||||
@testset "Utils" begin
|
||||
zipped = ([(1, 2)], [("a", "b")], [(1, "a"),(2, "b")],
|
||||
[(1, 2),(3, 4)], [(1, 2, 3),(3, 4, 5)], [(1, 2, 3, 4),(3, 4, 5, 6)],
|
||||
[(1, 2.0),(missing, missing)], [(1, missing),(missing, "a")],
|
||||
[(missing, missing)], [(missing, missing, missing),("a", "b", "c")])
|
||||
zipped = (
|
||||
[(1, 2)],
|
||||
[("a", "b")],
|
||||
[(1, "a"), (2, "b")],
|
||||
[(1, 2), (3, 4)],
|
||||
[(1, 2, 3), (3, 4, 5)],
|
||||
[(1, 2, 3, 4), (3, 4, 5, 6)],
|
||||
[(1, 2.0), (missing, missing)],
|
||||
[(1, missing), (missing, "a")],
|
||||
[(missing, missing)],
|
||||
[(missing, missing, missing), ("a", "b", "c")],
|
||||
)
|
||||
for z in zipped
|
||||
@test isequal(collect(zip(Plots.unzip(z)...)), z)
|
||||
@test isequal(collect(zip(Plots.unzip(GeometryBasics.Point.(z))...)), z)
|
||||
@ -227,5 +242,7 @@ end
|
||||
op2 = Plots.process_clims((1, 2.0))
|
||||
data = randn(100, 100)
|
||||
@test op1(data) == op2(data)
|
||||
@test Plots.process_clims(nothing) == Plots.process_clims(missing) == Plots.process_clims(:auto)
|
||||
@test Plots.process_clims(nothing) ==
|
||||
Plots.process_clims(missing) ==
|
||||
Plots.process_clims(:auto)
|
||||
end
|
||||
|
||||
@ -9,12 +9,15 @@ using Plots, Test
|
||||
value(m::MyType) = m.val
|
||||
data = MyType.(sort(randn(20)))
|
||||
# A recipe that puts the axis letter in the title
|
||||
@recipe function f(::Type{T}, m::T) where T <: AbstractArray{<:MyType}
|
||||
@recipe function f(::Type{T}, m::T) where {T<:AbstractArray{<:MyType}}
|
||||
title --> string(plotattributes[:letter])
|
||||
value.(m)
|
||||
end
|
||||
@testset "$f (orientation = $o)" for f in [histogram, barhist, stephist, scatterhist], o in [:vertical, :horizontal]
|
||||
@test f(data, orientation=o).subplots[1].attr[:title] == (o == :vertical ? "x" : "y")
|
||||
@testset "$f (orientation = $o)" for f in [histogram, barhist, stephist, scatterhist],
|
||||
o in [:vertical, :horizontal]
|
||||
|
||||
@test f(data, orientation = o).subplots[1].attr[:title] ==
|
||||
(o == :vertical ? "x" : "y")
|
||||
end
|
||||
@testset "$f" for f in [hline, hspan]
|
||||
@test f(data).subplots[1].attr[:title] == "y"
|
||||
|
||||
@ -52,7 +52,7 @@ using Plots, Test
|
||||
@testset "Plot" begin
|
||||
ang = range(0, 2π, length = 60)
|
||||
ellipse(x, y, w, h) = Shape(w * sin.(ang) .+ x, h * cos.(ang) .+ y)
|
||||
myshapes = [ellipse(x, rand(), rand(), rand()) for x = 1:4]
|
||||
myshapes = [ellipse(x, rand(), rand(), rand()) for x in 1:4]
|
||||
@test coords(myshapes) isa Tuple{Vector{Vector{S}},Vector{Vector{T}}} where {T,S}
|
||||
local p
|
||||
@test_nowarn p = plot(myshapes)
|
||||
@ -116,7 +116,7 @@ end
|
||||
end
|
||||
|
||||
@testset "Series Annotations" begin
|
||||
square = Shape([(0., 0.), (1., 0.), (1., 1.), (0., 1.)])
|
||||
square = Shape([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)])
|
||||
@test_logs (:warn, "Unused SeriesAnnotations arg: triangle (Symbol)") begin
|
||||
p = plot(
|
||||
[1, 2, 3],
|
||||
@ -143,8 +143,9 @@ end
|
||||
xlims = (0, 5),
|
||||
series_annotations = permutedims([["1/1"], ["1/2"], ["1/3"], ["1/4"], ["1/5"]]),
|
||||
)
|
||||
for i ∈ 1:5
|
||||
@test only(spl.series_list[i].plotattributes[:series_annotations].strs).str == "1/$i"
|
||||
for i in 1:5
|
||||
@test only(spl.series_list[i].plotattributes[:series_annotations].strs).str ==
|
||||
"1/$i"
|
||||
end
|
||||
|
||||
p = plot([1, 2], annotations = (1.5, 2, text("foo", :left)))
|
||||
@ -152,8 +153,8 @@ end
|
||||
@test (x, y) == (1.5, 2)
|
||||
@test txt.str == "foo"
|
||||
|
||||
p = plot([1, 2], annotations=((.1, .5), :auto))
|
||||
p = plot([1, 2], annotations = ((0.1, 0.5), :auto))
|
||||
pos, txt = only(p.subplots[end][:annotations])
|
||||
@test pos == (.1, .5)
|
||||
@test pos == (0.1, 0.5)
|
||||
@test txt.str == "(a)"
|
||||
end
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
using Plots, HDF5
|
||||
|
||||
|
||||
@testset "HDF5_Plots" begin
|
||||
fname = "tmpplotsave.hdf5"
|
||||
hdf5()
|
||||
|
||||
x = 1:10
|
||||
psrc=plot(x, x.*x); #Create some plot
|
||||
psrc = plot(x, x .* x) #Create some plot
|
||||
Plots.hdf5plot_write(psrc, fname)
|
||||
|
||||
#Read back file:
|
||||
|
||||
@ -82,8 +82,7 @@ end
|
||||
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"]["color"] == RGBA{Float64}(colorant"green", 0.8)
|
||||
@test marker.options["mark options"]["line width"] == 0.75 # 1px is 0.75pt
|
||||
end # testset
|
||||
@testset "Plot in pieces" begin
|
||||
@ -170,8 +169,8 @@ end
|
||||
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]
|
||||
xs = [string("x", i) for i in 1:10]
|
||||
ys = [string("y", i) for i in 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)
|
||||
@ -205,15 +204,9 @@ end
|
||||
x = t .* cos.(θ)
|
||||
y = t .* sin.(θ)
|
||||
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
|
||||
p2 = scatter(
|
||||
x,
|
||||
y,
|
||||
marker_z = ((x, y) -> begin
|
||||
p2 = scatter(x, y, marker_z = ((x, y) -> begin
|
||||
x + y
|
||||
end),
|
||||
color = :bwr,
|
||||
legend = false,
|
||||
)
|
||||
end), color = :bwr, legend = false)
|
||||
plot(p1, p2)
|
||||
end # testset
|
||||
@testset "Framestyles" begin
|
||||
@ -245,11 +238,8 @@ end
|
||||
end # testset
|
||||
@testset "Annotations" begin
|
||||
y = rand(10)
|
||||
pgfx_plot = plot(
|
||||
y,
|
||||
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
|
||||
leg = false,
|
||||
)
|
||||
pgfx_plot =
|
||||
plot(y, annotations = (3, y[3], Plots.text("this is \\#3", :left)), leg = false)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
|
||||
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
|
||||
@ -314,8 +304,7 @@ end
|
||||
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]))
|
||||
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)
|
||||
@ -342,7 +331,13 @@ end
|
||||
group = rand(map((i -> begin
|
||||
"group $(i)"
|
||||
end), 1:4), 100)
|
||||
pl = plot(rand(100), layout = @layout([a b; c]), group = group, linetype = [:bar :scatter :steppre], linecolor = :match)
|
||||
pl = plot(
|
||||
rand(100),
|
||||
layout = @layout([a b; c]),
|
||||
group = group,
|
||||
linetype = [:bar :scatter :steppre],
|
||||
linecolor = :match,
|
||||
)
|
||||
Plots._update_plot_object(pl)
|
||||
axis = Plots.pgfx_axes(pl.o)[1]
|
||||
legend_entries = filter(x -> x isa PGFPlotsX.LegendEntry, axis.contents)
|
||||
@ -378,12 +373,8 @@ end # testset
|
||||
axes = Plots.pgfx_axes(pl.o)
|
||||
@test !haskey(axes[1].options.dict, "axis line shift")
|
||||
@test haskey(axes[2].options.dict, "axis line shift")
|
||||
pl = plot(
|
||||
x -> x,
|
||||
-1:1;
|
||||
add = raw"\node at (0,0.5) {\huge hi};",
|
||||
extra_kwargs = :subplot,
|
||||
)
|
||||
pl =
|
||||
plot(x -> x, -1:1; add = raw"\node at (0,0.5) {\huge hi};", extra_kwargs = :subplot)
|
||||
@test pl[1][:extra_kwargs] == Dict(:add => raw"\node at (0,0.5) {\huge hi};")
|
||||
Plots._update_plot_object(pl)
|
||||
axes = Plots.pgfx_axes(pl.o)
|
||||
@ -410,11 +401,7 @@ end # testset
|
||||
@test pl[:plot_title] == "Test me"
|
||||
@test pl[:plot_titlefontsize] == 2
|
||||
@test pl[:plot_titlefonthalign] == :left
|
||||
pl = heatmap(
|
||||
rand(3, 3),
|
||||
colorbar_title = "Test me",
|
||||
colorbar_titlefont = (12, :right),
|
||||
)
|
||||
pl = heatmap(rand(3, 3), colorbar_title = "Test me", colorbar_titlefont = (12, :right))
|
||||
@test pl[1][:colorbar_title] == "Test me"
|
||||
@test pl[1][:colorbar_titlefontsize] == 12
|
||||
@test pl[1][:colorbar_titlefonthalign] == :right
|
||||
|
||||
@ -11,8 +11,8 @@ using RecipesPipeline
|
||||
end
|
||||
|
||||
@testset "get_axis_limits" begin
|
||||
x = [.1, 5]
|
||||
p1 = plot(x, [5, .1], yscale=:log10)
|
||||
x = [0.1, 5]
|
||||
p1 = plot(x, [5, 0.1], yscale = :log10)
|
||||
p2 = plot!(identity)
|
||||
@test all(RecipesPipeline.get_axis_limits(p1, :x) .== x)
|
||||
@test all(RecipesPipeline.get_axis_limits(p2, :x) .== x)
|
||||
|
||||
@ -40,7 +40,7 @@ using Plots, Test
|
||||
xticks!(xticks, xlabels)
|
||||
@test Plots.get_subplot(current(), 1).attr[:xaxis][:ticks] == (xticks, xlabels)
|
||||
|
||||
yticks = [.5,.6,.75]
|
||||
yticks = [0.5, 0.6, 0.75]
|
||||
ylabels = ["z", "y", "x"]
|
||||
yticks!(yticks, ylabels)
|
||||
@test Plots.get_subplot(current(), 1).attr[:yaxis][:ticks] == (yticks, ylabels)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user