Compare commits
203 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8eac99a389 | |||
| a65d41ca39 | |||
| dd25893a70 | |||
| 52c081758d | |||
| 7ed9fbd7fc | |||
| 6bb53b5181 | |||
| 9d57817bdf | |||
| 4b94a8c2f6 | |||
| aafc1e1989 | |||
| 4e6c64ebcc | |||
| cb103598d7 | |||
| 5728e9fb46 | |||
| 74a6520960 | |||
| f557ca3b74 | |||
| bb08d61709 | |||
| 857d37c380 | |||
| 261f47fb6c | |||
| 476769fab6 | |||
| f7282dd49a | |||
| 5f09d97ca0 | |||
| e4754f3aeb | |||
| 43fd0e76be | |||
| 3ed781ac2d | |||
| 6821148e8e | |||
| 3255c5a122 | |||
| bb9d36eb7a | |||
| 50984b3bd2 | |||
| 7520c1dced | |||
| bfd468fc3a | |||
| 3ab0078818 | |||
| 1fc587559c | |||
| 4821e04145 | |||
| bb8e62769f | |||
| 15dc61feb5 | |||
| d0d72242cb | |||
| bece5a2620 | |||
| d972702d3e | |||
| d08b33e696 | |||
| da60b47af5 | |||
| 0ac63857c4 | |||
| d08422811e | |||
| d5788fc1e7 | |||
| 6da5bb8822 | |||
| a0a3b42872 | |||
| caf0aa098a | |||
| 55ea8b4a70 | |||
| 25c53f0e52 | |||
| 7c3938f793 | |||
| 20a10fcf7d | |||
| 6ee49f8430 | |||
| 0768c8a658 | |||
| 89b2be4ecb | |||
| 369d510b3e | |||
| d1dbb1bf2a | |||
| ccf726ddcb | |||
| 5b2cd1b8ee | |||
| 16b8d01b88 | |||
| 8d7b07eab3 | |||
| 599d6a2ec8 | |||
| 345f5654a3 | |||
| 77e04c45db | |||
| ad4d7214e4 | |||
| 4051d016b9 | |||
| 8610a82a88 | |||
| 4832d870dd | |||
| ace915fbfb | |||
| c6593423f5 | |||
| f69daa8cfe | |||
| 71246620aa | |||
| 6e3181375c | |||
| e7b1dc0cac | |||
| d78bb56316 | |||
| 648c5ac587 | |||
| df7d28dc99 | |||
| 4d28a49c70 | |||
| ef3e7af329 | |||
| 3221b80aeb | |||
| 13fc936e1f | |||
| 4e5b1aa6e5 | |||
| cb577f0ef3 | |||
| b23dd39e06 | |||
| 7498b3c769 | |||
| 0c0a2c215c | |||
| b682bc8e96 | |||
| 205345a8d3 | |||
| faff9cebe4 | |||
| bcd1bc1009 | |||
| f2ca493a14 | |||
| 54301fa97a | |||
| 20a290792f | |||
| 31f5490517 | |||
| 8554e632bd | |||
| 70d0d786e2 | |||
| 5af5ec7313 | |||
| 7fab2f1c6b | |||
| 461ef20225 | |||
| 4357fde5ea | |||
| f93977b1a4 | |||
| 2e77d0df98 | |||
| fc40b0e0ac | |||
| 85aca2aa67 | |||
| 961d6be9f9 | |||
| 33553eb055 | |||
| fbe8dcaf78 | |||
| b5f54afed2 | |||
| c99fcc3dcd | |||
| 4a7ea82862 | |||
| 3bad6e9b4c | |||
| 96ab1537d3 | |||
| c62714d188 | |||
| 2b6a90766a | |||
| 366d598ff0 | |||
| 84a8039022 | |||
| 8011c9f675 | |||
| d709b53ec7 | |||
| 39c97ecdc5 | |||
| 45c434b5ed | |||
| 312f98e380 | |||
| d093d1ad7e | |||
| 22e8718e85 | |||
| e489787535 | |||
| 73ff286162 | |||
| c75f6f5a9d | |||
| 33d04c3e6b | |||
| 2ede388c15 | |||
| 745189981b | |||
| dd70192de4 | |||
| 5fc2f6cf3b | |||
| 2c6f54579d | |||
| 9bcab45cb8 | |||
| 3b7464e739 | |||
| 11c86b2d30 | |||
| 7a521ef21f | |||
| 7fb1a6a517 | |||
| f20582e528 | |||
| 92e609876d | |||
| 5a251e5702 | |||
| 33cf0e66ec | |||
| 9d75916244 | |||
| 6ed18617d5 | |||
| 2022aebb07 | |||
| b73c38fe69 | |||
| 11322d0d8b | |||
| 82479d48b9 | |||
| a07fd5d3db | |||
| 10ab3ae974 | |||
| b908202c47 | |||
| 61d4c84ef4 | |||
| 68a0baf299 | |||
| aef75cb073 | |||
| c5fe90d135 | |||
| 85e5f2e971 | |||
| 58bf14fc85 | |||
| 0d09975fba | |||
| 06afcedf56 | |||
| c7cc76b2b6 | |||
| 84b108f6d0 | |||
| cb6f0772b7 | |||
| 4263ced69b | |||
| f5c6b06eb6 | |||
| 05ef841d63 | |||
| 140afeb53d | |||
| 639a09bccb | |||
| f105d41e54 | |||
| 140a429e09 | |||
| 28d5e0bb7d | |||
| 3dec7fe394 | |||
| e54eb53665 | |||
| 9b3cf3a818 | |||
| 3d306578f1 | |||
| e615718f1b | |||
| fbbbd40353 | |||
| 0e694a9767 | |||
| 203e351cef | |||
| 74ec895aca | |||
| 3459bcd817 | |||
| d2c0840c43 | |||
| 9534bae4ef | |||
| 2a720a7491 | |||
| 7b31e5f9b8 | |||
| d0be1e7d1d | |||
| 8434741599 | |||
| cfe1ede25a | |||
| ecc891279e | |||
| 750f96cca7 | |||
| 5051e06be0 | |||
| cde041e977 | |||
| 0fdf46fd2e | |||
| c902c3ff35 | |||
| f0b006d3c9 | |||
| 044daf08d4 | |||
| 0b5b8b2cc2 | |||
| fae5c45cc4 | |||
| 3bc8ffda58 | |||
| 589713cb75 | |||
| ca9ce5427d | |||
| 374809d3cf | |||
| e1d45dec5d | |||
| bf60d6482f | |||
| e04858c975 | |||
| e0148ba1a8 | |||
| ee521107d3 | |||
| acbce64be9 |
@@ -0,0 +1,11 @@
|
||||
name: TagBot
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 * * * *
|
||||
jobs:
|
||||
TagBot:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: JuliaRegistries/TagBot@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
+6
-2
@@ -5,7 +5,7 @@ os:
|
||||
# - osx
|
||||
julia:
|
||||
- 1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
@@ -20,6 +20,10 @@ addons:
|
||||
- xauth
|
||||
- xvfb
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.julia/artifacts
|
||||
|
||||
sudo: required
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
|
||||
@@ -31,4 +35,4 @@ notifications:
|
||||
script:
|
||||
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
||||
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
|
||||
- $TESTCMD -e 'using Pkg; Pkg.build(); Pkg.test(coverage=true)'
|
||||
- $TESTCMD -e 'using Pkg; pkg"add https://github.com/mkborregaard/RecipeUtils.jl"; Pkg.build(); Pkg.test(coverage=true)'
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- Minor version 0.17 is the last one to support Julia 0.6!!
|
||||
- Minor version 0.11 is the last one to support Julia 0.5!!
|
||||
- Critical bugfixes only
|
||||
- `backports` branch is for Julia 0.5
|
||||
|
||||
---
|
||||
## (current master)
|
||||
|
||||
## 0.28.3
|
||||
- support generalized array interface
|
||||
- save to pdf, svg and eps in plotlyjs
|
||||
- fix for clims in line_z
|
||||
- optimize heatmap logic in gr
|
||||
|
||||
## 0.26.3
|
||||
- fix `vline` with dates
|
||||
- fix PyPlot logscale bug
|
||||
|
||||
+9
-7
@@ -1,7 +1,7 @@
|
||||
name = "Plots"
|
||||
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
author = ["Tom Breloff (@tbreloff)"]
|
||||
version = "0.28.2"
|
||||
version = "0.29.7"
|
||||
|
||||
[deps]
|
||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
@@ -21,6 +21,7 @@ PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
|
||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
RecipePipeline = "01d81517-befc-4cb6-b9ec-a95719d0359c"
|
||||
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
|
||||
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
|
||||
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
|
||||
@@ -32,18 +33,19 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
|
||||
[compat]
|
||||
Contour = "0.5"
|
||||
FFMPEG = "0.2"
|
||||
FixedPointNumbers = "0.6"
|
||||
GR = "0.44"
|
||||
GeometryTypes = "0.7"
|
||||
FFMPEG = "0.2, 0.3"
|
||||
FixedPointNumbers = "0.6, 0.7"
|
||||
GR = "0.46, 0.47, 0.48"
|
||||
GeometryTypes = "0.7, 0.8"
|
||||
JSON = "0.21"
|
||||
Measures = "0.3"
|
||||
NaNMath = "0.3"
|
||||
PGFPlotsX = "1.2.0"
|
||||
PlotThemes = "1"
|
||||
PlotUtils = "0.6.1"
|
||||
RecipesBase = "0.6, 0.7"
|
||||
RecipesBase = "0.8"
|
||||
Reexport = "0.2"
|
||||
Requires = "0.5"
|
||||
Requires = "0.5, 1.0"
|
||||
Showoff = "0.3.1"
|
||||
StatsBase = "0.32"
|
||||
julia = "1"
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/JuliaPlots/Plots.jl)
|
||||
[](https://ci.appveyor.com/project/mkborregaard/plots-jl)
|
||||
[](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<a href="http://docs.juliaplots.org/latest/" target="_blank"><img src="https://img.shields.io/badge/docs-latest-blue.svg" alt="Latest documentation"></a>
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
|
||||
<!-- [](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
|
||||
<!-- [](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
|
||||
[travis-img]: https://img.shields.io/travis/JuliaPlots/Plots.jl?logo=travis
|
||||
[travis-url]: https://travis-ci.org/JuliaPlots/Plots.jl
|
||||
|
||||
[appveyor-img]: https://ci.appveyor.com/api/projects/status/github/juliaplots/plots.jl?branch=master&svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/mkborregaard/plots-jl
|
||||
|
||||
[pkgeval-img]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/P/Plots.svg
|
||||
[pkgeval-url]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html
|
||||
|
||||
[gitter-img]: https://badges.gitter.im/tbreloff/Plots.jl.svg
|
||||
[gitter-url]: https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
[docs-img]: https://img.shields.io/badge/docs-stable-blue.svg
|
||||
[docs-url]: http://docs.juliaplots.org/latest/
|
||||
|
||||
[![][travis-img]][travis-url]
|
||||
[![][appveyor-img]][appveyor-url]
|
||||
[![][pkgeval-img]][pkgeval-url]
|
||||
[![][gitter-img]][gitter-url]
|
||||
[![][docs-img]][docs-url]
|
||||
|
||||
#### Created by Tom Breloff (@tbreloff)
|
||||
|
||||
|
||||
+4
-1
@@ -2,7 +2,7 @@ environment:
|
||||
matrix:
|
||||
# - julia_version: 0.7
|
||||
- julia_version: 1
|
||||
- julia_version: 1.2
|
||||
- julia_version: 1.3
|
||||
- julia_version: nightly
|
||||
|
||||
platform:
|
||||
@@ -20,6 +20,9 @@ branches:
|
||||
- master
|
||||
- /release-.*/
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.julia\artifacts'
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
on_build_success: false
|
||||
|
||||
+28
-3
@@ -11,13 +11,38 @@ import SparseArrays: findnz
|
||||
using FFMPEG
|
||||
|
||||
@reexport using RecipesBase
|
||||
import RecipesBase: plot, plot!, animate
|
||||
import RecipesBase: plot, plot!, animate, is_explicit
|
||||
using Base.Meta
|
||||
@reexport using PlotUtils
|
||||
@reexport using PlotThemes
|
||||
import Showoff
|
||||
import StatsBase
|
||||
import JSON
|
||||
import RecipePipeline:
|
||||
_process_userrecipe,
|
||||
_process_plotrecipe,
|
||||
_process_seriesrecipe,
|
||||
_preprocess_args,
|
||||
preprocessArgs!,
|
||||
is_st_supported,
|
||||
finalize_subplot!,
|
||||
recipe_pipeline!,
|
||||
_recipe_init!,
|
||||
_recipe_after_user!,
|
||||
_recipe_after_plot!,
|
||||
_recipe_before_series!,
|
||||
_recipe_finish!,
|
||||
is_st_supported,
|
||||
Formatted,
|
||||
SliceIt,
|
||||
FuncOrFuncs,
|
||||
MaybeNumber,
|
||||
MaybeString,
|
||||
DataPoint,
|
||||
trueOrAllTrue,
|
||||
Surface,
|
||||
AbstractSurface,
|
||||
Volume
|
||||
|
||||
using Requires
|
||||
|
||||
@@ -222,7 +247,7 @@ end
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
include("precompile.jl")
|
||||
_precompile_()
|
||||
# include("precompile.jl")
|
||||
# _precompile_()
|
||||
|
||||
end # module
|
||||
|
||||
+3
-3
@@ -163,16 +163,16 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
error("Unsupported animate filter: $args")
|
||||
end
|
||||
|
||||
push!(block.args, :(if $filterexpr; frame($animsym); end))
|
||||
push!(block.args, :(if $filterexpr; Plots.frame($animsym); end))
|
||||
push!(block.args, :(global $countersym += 1))
|
||||
|
||||
# add a final call to `gif(anim)`?
|
||||
retval = callgif ? :(gif($animsym)) : animsym
|
||||
retval = callgif ? :(Plots.gif($animsym)) : animsym
|
||||
|
||||
# full expression:
|
||||
esc(quote
|
||||
$freqassert # if filtering, check frequency is an Integer > 0
|
||||
$animsym = Animation() # init animation object
|
||||
$animsym = Plots.Animation() # init animation object
|
||||
global $countersym = 1 # init iteration counter
|
||||
$forloop # for loop, saving a frame after each iteration
|
||||
$retval # return the animation object, or the gif
|
||||
|
||||
+109
-101
@@ -90,7 +90,7 @@ like_surface(seriestype::Symbol) = seriestype in _surface_like
|
||||
|
||||
is3d(seriestype::Symbol) = seriestype in _3dTypes
|
||||
is3d(series::Series) = is3d(series.plotattributes)
|
||||
is3d(plotattributes::KW) = trueOrAllTrue(is3d, Symbol(plotattributes[:seriestype]))
|
||||
is3d(plotattributes::AKW) = trueOrAllTrue(is3d, Symbol(plotattributes[:seriestype]))
|
||||
|
||||
is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d"
|
||||
ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar"
|
||||
@@ -345,7 +345,7 @@ const _subplot_defaults = KW(
|
||||
:legendtitlefontcolor => :match,
|
||||
:annotations => [], # annotation tuples... list of (x,y,annotation)
|
||||
:projection => :none, # can also be :polar or :3d
|
||||
:aspect_ratio => :none, # choose from :none or :equal
|
||||
:aspect_ratio => :auto, # choose from :none or :equal
|
||||
:margin => 1mm,
|
||||
:left_margin => :match,
|
||||
:top_margin => :match,
|
||||
@@ -418,37 +418,26 @@ const _suppress_warnings = Set{Symbol}([
|
||||
|
||||
# add defaults for the letter versions
|
||||
const _axis_defaults_byletter = KW()
|
||||
for letter in (:x,:y,:z)
|
||||
for k in keys(_axis_defaults)
|
||||
# for k in (
|
||||
# :guide,
|
||||
# :lims,
|
||||
# :ticks,
|
||||
# :scale,
|
||||
# :rotation,
|
||||
# :flip,
|
||||
# :link,
|
||||
# :tickfont,
|
||||
# :guidefont,
|
||||
# :foreground_color_axis,
|
||||
# :foreground_color_border,
|
||||
# :foreground_color_text,
|
||||
# :foreground_color_guide,
|
||||
# :discrete_values,
|
||||
# :formatter,
|
||||
# )
|
||||
_axis_defaults_byletter[Symbol(letter,k)] = :match
|
||||
|
||||
# allow the underscore version too: xguide or x_guide
|
||||
add_aliases(Symbol(letter, k), Symbol(letter, "_", k))
|
||||
function reset_axis_defaults_byletter!()
|
||||
for letter in (:x,:y,:z)
|
||||
_axis_defaults_byletter[letter] = KW()
|
||||
for (k,v) in _axis_defaults
|
||||
_axis_defaults_byletter[letter][k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
reset_axis_defaults_byletter!()
|
||||
|
||||
for letter in (:x,:y,:z), k in keys(_axis_defaults)
|
||||
# allow the underscore version too: xguide or x_guide
|
||||
add_aliases(Symbol(letter, k), Symbol(letter, "_", k))
|
||||
end
|
||||
|
||||
const _all_defaults = KW[
|
||||
_series_defaults,
|
||||
_plot_defaults,
|
||||
_subplot_defaults,
|
||||
_axis_defaults_byletter
|
||||
_subplot_defaults
|
||||
]
|
||||
|
||||
const _initial_defaults = deepcopy(_all_defaults)
|
||||
@@ -463,6 +452,12 @@ const _initial_fontsizes = Dict(:titlefontsize => _subplot_defaults[:titlefonts
|
||||
|
||||
const _all_args = sort(collect(union(map(keys, _all_defaults)...)))
|
||||
|
||||
is_subplot_attr(k) = haskey(_subplot_defaults, k)
|
||||
is_series_attr(k) = haskey(_series_defaults, k)
|
||||
#is_axis_attr(k) = haskey(_axis_defaults_byletter, k)
|
||||
is_axis_attr(k) = haskey(_axis_defaults, Symbol(chop(string(k); head=1, tail=0)))
|
||||
is_axis_attr_noletter(k) = haskey(_axis_defaults, k)
|
||||
|
||||
RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -475,7 +470,7 @@ autopick(notarr, idx::Integer) = notarr
|
||||
autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
|
||||
autopick_ignore_none_auto(notarr, idx::Integer) = notarr
|
||||
|
||||
function aliasesAndAutopick(plotattributes::KW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
|
||||
function aliasesAndAutopick(plotattributes::AKW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
|
||||
if plotattributes[sym] == :auto
|
||||
plotattributes[sym] = autopick_ignore_none_auto(options, plotIndex)
|
||||
elseif haskey(aliases, plotattributes[sym])
|
||||
@@ -611,6 +606,16 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function parse_axis_kw(s::Symbol)
|
||||
s = string(s)
|
||||
for letter in ('x', 'y', 'z')
|
||||
if startswith(s, letter)
|
||||
return (Symbol(letter), Symbol(chop(s, head=1, tail=0)))
|
||||
end
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
# update the defaults globally
|
||||
|
||||
"""
|
||||
@@ -629,6 +634,10 @@ function default(k::Symbol)
|
||||
if haskey(_axis_defaults, k)
|
||||
return _axis_defaults[k]
|
||||
end
|
||||
if (axis_k = parse_axis_kw(k)) !== nothing
|
||||
letter, key = axis_k
|
||||
return _axis_defaults_byletter[letter][key]
|
||||
end
|
||||
k in _suppress_warnings || error("Unknown key: ", k)
|
||||
end
|
||||
|
||||
@@ -644,6 +653,11 @@ function default(k::Symbol, v)
|
||||
_axis_defaults[k] = v
|
||||
return v
|
||||
end
|
||||
if (axis_k = parse_axis_kw(k)) !== nothing
|
||||
letter, key = axis_k
|
||||
_axis_defaults_byletter[letter][key] = v
|
||||
return v
|
||||
end
|
||||
k in _suppress_warnings || error("Unknown key: ", k)
|
||||
end
|
||||
|
||||
@@ -655,19 +669,20 @@ function default(; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
function default(plotattributes::KW, k::Symbol)
|
||||
function default(plotattributes::AKW, k::Symbol)
|
||||
get(plotattributes, k, default(k))
|
||||
end
|
||||
|
||||
function reset_defaults()
|
||||
foreach(merge!, _all_defaults, _initial_defaults)
|
||||
merge!(_axis_defaults, _initial_axis_defaults)
|
||||
reset_axis_defaults_byletter!()
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# if arg is a valid color value, then set plotattributes[csym] and return true
|
||||
function handleColors!(plotattributes::KW, arg, csym::Symbol)
|
||||
function handleColors!(plotattributes::AKW, arg, csym::Symbol)
|
||||
try
|
||||
if arg == :auto
|
||||
plotattributes[csym] = :auto
|
||||
@@ -684,7 +699,7 @@ end
|
||||
|
||||
|
||||
|
||||
function processLineArg(plotattributes::KW, arg)
|
||||
function processLineArg(plotattributes::AKW, arg)
|
||||
# seriestype
|
||||
if allLineTypes(arg)
|
||||
plotattributes[:seriestype] = arg
|
||||
@@ -723,7 +738,7 @@ function processLineArg(plotattributes::KW, arg)
|
||||
end
|
||||
|
||||
|
||||
function processMarkerArg(plotattributes::KW, arg)
|
||||
function processMarkerArg(plotattributes::AKW, arg)
|
||||
# markershape
|
||||
if allShapes(arg)
|
||||
plotattributes[:markershape] = arg
|
||||
@@ -763,7 +778,7 @@ function processMarkerArg(plotattributes::KW, arg)
|
||||
end
|
||||
|
||||
|
||||
function processFillArg(plotattributes::KW, arg)
|
||||
function processFillArg(plotattributes::AKW, arg)
|
||||
# fr = get(plotattributes, :fillrange, 0)
|
||||
if typeof(arg) <: Brush
|
||||
arg.size === nothing || (plotattributes[:fillrange] = arg.size)
|
||||
@@ -786,7 +801,6 @@ function processFillArg(plotattributes::KW, arg)
|
||||
plotattributes[:fillrange] = arg
|
||||
|
||||
elseif !handleColors!(plotattributes, arg, :fillcolor)
|
||||
|
||||
plotattributes[:fillrange] = arg
|
||||
end
|
||||
# plotattributes[:fillrange] = fr
|
||||
@@ -794,7 +808,7 @@ function processFillArg(plotattributes::KW, arg)
|
||||
end
|
||||
|
||||
|
||||
function processGridArg!(plotattributes::KW, arg, letter)
|
||||
function processGridArg!(plotattributes::AKW, arg, letter)
|
||||
if arg in _allGridArgs || isa(arg, Bool)
|
||||
plotattributes[Symbol(letter, :grid)] = hasgrid(arg, letter)
|
||||
|
||||
@@ -822,7 +836,7 @@ function processGridArg!(plotattributes::KW, arg, letter)
|
||||
end
|
||||
end
|
||||
|
||||
function processMinorGridArg!(plotattributes::KW, arg, letter)
|
||||
function processMinorGridArg!(plotattributes::AKW, arg, letter)
|
||||
if arg in _allGridArgs || isa(arg, Bool)
|
||||
plotattributes[Symbol(letter, :minorgrid)] = hasgrid(arg, letter)
|
||||
|
||||
@@ -855,7 +869,7 @@ function processMinorGridArg!(plotattributes::KW, arg, letter)
|
||||
end
|
||||
end
|
||||
|
||||
function processFontArg!(plotattributes::KW, fontname::Symbol, arg)
|
||||
function processFontArg!(plotattributes::AKW, fontname::Symbol, arg)
|
||||
T = typeof(arg)
|
||||
if T <: Font
|
||||
plotattributes[Symbol(fontname, :family)] = arg.family
|
||||
@@ -892,7 +906,7 @@ _replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
|
||||
_replace_markershape(shapes::AVec) = map(_replace_markershape, shapes)
|
||||
_replace_markershape(shape) = shape
|
||||
|
||||
function _add_markershape(plotattributes::KW)
|
||||
function _add_markershape(plotattributes::AKW)
|
||||
# add the markershape if it needs to be added... hack to allow "m=10" to add a shape,
|
||||
# and still allow overriding in _apply_recipe
|
||||
ms = pop!(plotattributes, :markershape_to_add, :none)
|
||||
@@ -902,7 +916,7 @@ function _add_markershape(plotattributes::KW)
|
||||
end
|
||||
|
||||
"Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
|
||||
function preprocessArgs!(plotattributes::KW)
|
||||
function preprocessArgs!(plotattributes::AKW)
|
||||
replaceAliases!(plotattributes, _keyAliases)
|
||||
|
||||
# clear all axis stuff
|
||||
@@ -922,7 +936,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# end
|
||||
|
||||
# handle axis args common to all axis
|
||||
args = pop!(plotattributes, :axis, ())
|
||||
args = pop_kw!(plotattributes, :axis, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
process_axis_arg!(plotattributes, arg, letter)
|
||||
@@ -931,7 +945,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle axis args
|
||||
for letter in (:x, :y, :z)
|
||||
asym = Symbol(letter, :axis)
|
||||
args = pop!(plotattributes, asym, ())
|
||||
args = pop_kw!(plotattributes, asym, ())
|
||||
if !(typeof(args) <: Axis)
|
||||
for arg in wraptuple(args)
|
||||
process_axis_arg!(plotattributes, arg, letter)
|
||||
@@ -949,7 +963,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
end
|
||||
|
||||
# handle grid args common to all axes
|
||||
args = pop!(plotattributes, :grid, ())
|
||||
args = pop_kw!(plotattributes, :grid, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
processGridArg!(plotattributes, arg, letter)
|
||||
@@ -958,13 +972,13 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle individual axes grid args
|
||||
for letter in (:x, :y, :z)
|
||||
gridsym = Symbol(letter, :grid)
|
||||
args = pop!(plotattributes, gridsym, ())
|
||||
args = pop_kw!(plotattributes, gridsym, ())
|
||||
for arg in wraptuple(args)
|
||||
processGridArg!(plotattributes, arg, letter)
|
||||
end
|
||||
end
|
||||
# handle minor grid args common to all axes
|
||||
args = pop!(plotattributes, :minorgrid, ())
|
||||
args = pop_kw!(plotattributes, :minorgrid, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
processMinorGridArg!(plotattributes, arg, letter)
|
||||
@@ -973,21 +987,21 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle individual axes grid args
|
||||
for letter in (:x, :y, :z)
|
||||
gridsym = Symbol(letter, :minorgrid)
|
||||
args = pop!(plotattributes, gridsym, ())
|
||||
args = pop_kw!(plotattributes, gridsym, ())
|
||||
for arg in wraptuple(args)
|
||||
processMinorGridArg!(plotattributes, arg, letter)
|
||||
end
|
||||
end
|
||||
# fonts
|
||||
for fontname in (:titlefont, :legendfont, :legendtitlefont)
|
||||
args = pop!(plotattributes, fontname, ())
|
||||
args = pop_kw!(plotattributes, fontname, ())
|
||||
for arg in wraptuple(args)
|
||||
processFontArg!(plotattributes, fontname, arg)
|
||||
end
|
||||
end
|
||||
# handle font args common to all axes
|
||||
for fontname in (:tickfont, :guidefont)
|
||||
args = pop!(plotattributes, fontname, ())
|
||||
args = pop_kw!(plotattributes, fontname, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
processFontArg!(plotattributes, Symbol(letter, fontname), arg)
|
||||
@@ -997,7 +1011,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle individual axes font args
|
||||
for letter in (:x, :y, :z)
|
||||
for fontname in (:tickfont, :guidefont)
|
||||
args = pop!(plotattributes, Symbol(letter, fontname), ())
|
||||
args = pop_kw!(plotattributes, Symbol(letter, fontname), ())
|
||||
for arg in wraptuple(args)
|
||||
processFontArg!(plotattributes, Symbol(letter, fontname), arg)
|
||||
end
|
||||
@@ -1005,7 +1019,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
end
|
||||
|
||||
# handle line args
|
||||
for arg in wraptuple(pop!(plotattributes, :line, ()))
|
||||
for arg in wraptuple(pop_kw!(plotattributes, :line, ()))
|
||||
processLineArg(plotattributes, arg)
|
||||
end
|
||||
|
||||
@@ -1019,7 +1033,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
processMarkerArg(plotattributes, arg)
|
||||
anymarker = true
|
||||
end
|
||||
delete!(plotattributes, :marker)
|
||||
reset_kw!(plotattributes, :marker)
|
||||
if haskey(plotattributes, :markershape)
|
||||
plotattributes[:markershape] = _replace_markershape(plotattributes[:markershape])
|
||||
if plotattributes[:markershape] == :none && plotattributes[:seriestype] in (:scatter, :scatterbins, :scatterhist, :scatter3d) #the default should be :auto, not :none, so that :none can be set explicitly and would be respected
|
||||
@@ -1033,7 +1047,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
for arg in wraptuple(get(plotattributes, :fill, ()))
|
||||
processFillArg(plotattributes, arg)
|
||||
end
|
||||
delete!(plotattributes, :fill)
|
||||
reset_kw!(plotattributes, :fill)
|
||||
|
||||
# handle series annotations
|
||||
if haskey(plotattributes, :series_annotations)
|
||||
@@ -1098,7 +1112,7 @@ function extractGroupArgs(v::AVec, args...; legendEntry = string)
|
||||
if n > 100
|
||||
@warn("You created n=$n groups... Is that intended?")
|
||||
end
|
||||
groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels]
|
||||
groupIds = Vector{Int}[filter(i -> v[i] == glab, eachindex(v)) for glab in groupLabels]
|
||||
GroupBy(map(legendEntry, groupLabels), groupIds)
|
||||
end
|
||||
|
||||
@@ -1106,7 +1120,7 @@ legendEntryFromTuple(ns::Tuple) = join(ns, ' ')
|
||||
|
||||
# this is when given a tuple of vectors of values to group by
|
||||
function extractGroupArgs(vs::Tuple, args...)
|
||||
isempty(vs) && return GroupBy([""], [1:size(args[1],1)])
|
||||
isempty(vs) && return GroupBy([""], [axes(args[1],1)])
|
||||
v = map(tuple, vs...)
|
||||
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple)
|
||||
end
|
||||
@@ -1116,7 +1130,7 @@ legendEntryFromTuple(ns::NamedTuple) =
|
||||
join(["$k = $v" for (k, v) in pairs(ns)], ", ")
|
||||
|
||||
function extractGroupArgs(vs::NamedTuple, args...)
|
||||
isempty(vs) && return GroupBy([""], [1:size(args[1],1)])
|
||||
isempty(vs) && return GroupBy([""], [axes(args[1],1)])
|
||||
v = map(NamedTuple{keys(vs)}∘tuple, values(vs)...)
|
||||
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple)
|
||||
end
|
||||
@@ -1131,13 +1145,13 @@ end
|
||||
filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
|
||||
filter_data(v, idxfilter) = v
|
||||
|
||||
function filter_data!(plotattributes::KW, idxfilter)
|
||||
function filter_data!(plotattributes::AKW, idxfilter)
|
||||
for s in (:x, :y, :z)
|
||||
plotattributes[s] = filter_data(get(plotattributes, s, nothing), idxfilter)
|
||||
end
|
||||
end
|
||||
|
||||
function _filter_input_data!(plotattributes::KW)
|
||||
function _filter_input_data!(plotattributes::AKW)
|
||||
idxfilter = pop!(plotattributes, :idxfilter, nothing)
|
||||
if idxfilter !== nothing
|
||||
filter_data!(plotattributes, idxfilter)
|
||||
@@ -1150,7 +1164,7 @@ end
|
||||
const _already_warned = Dict{Symbol,Set{Symbol}}()
|
||||
const _to_warn = Set{Symbol}()
|
||||
|
||||
function warnOnUnsupported_args(pkg::AbstractBackend, plotattributes::KW)
|
||||
function warnOnUnsupported_args(pkg::AbstractBackend, plotattributes)
|
||||
empty!(_to_warn)
|
||||
bend = backend_name(pkg)
|
||||
already_warned = get!(_already_warned, bend, Set{Symbol}())
|
||||
@@ -1174,7 +1188,7 @@ end
|
||||
# _markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg)
|
||||
# _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
|
||||
|
||||
function warnOnUnsupported(pkg::AbstractBackend, plotattributes::KW)
|
||||
function warnOnUnsupported(pkg::AbstractBackend, plotattributes)
|
||||
if !is_seriestype_supported(pkg, plotattributes[:seriestype])
|
||||
@warn("seriestype $(plotattributes[:seriestype]) is unsupported with $pkg. Choose from: $(supported_seriestypes(pkg))")
|
||||
end
|
||||
@@ -1186,7 +1200,7 @@ function warnOnUnsupported(pkg::AbstractBackend, plotattributes::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::KW)
|
||||
function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::AKW)
|
||||
for k in (:xscale, :yscale, :zscale, :scale)
|
||||
if haskey(plotattributes, k)
|
||||
v = plotattributes[k]
|
||||
@@ -1225,24 +1239,26 @@ convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||
# anything else is returned as-is
|
||||
function slice_arg(v::AMat, idx::Int)
|
||||
c = mod1(idx, size(v,2))
|
||||
size(v,1) == 1 ? v[1,c] : v[:,c]
|
||||
m,n = axes(v)
|
||||
size(v,1) == 1 ? v[first(m),n[c]] : v[:,n[c]]
|
||||
end
|
||||
slice_arg(wrapper::InputWrapper, idx) = wrapper.obj
|
||||
slice_arg(v, idx) = v
|
||||
|
||||
|
||||
# given an argument key (k), we want to extract the argument value for this index.
|
||||
# matrices are sliced by column, otherwise we
|
||||
# if nothing is set (or container is empty), return the default or the existing value.
|
||||
function slice_arg!(plotattributes_in::KW,plotattributes_out::KW, k::Symbol, default_value, idx::Int, remove_pair::Bool)
|
||||
v = get(plotattributes_in, k, get(plotattributes_out, k, default_value))
|
||||
plotattributes_out[k] = if haskey(plotattributes_in, k) && typeof(v) <: AMat && !isempty(v)
|
||||
# given an argument key (k), extract the argument value for this index,
|
||||
# and set into plotattributes[k]. Matrices are sliced by column.
|
||||
# if nothing is set (or container is empty), return the existing value.
|
||||
function slice_arg!(plotattributes_in, plotattributes_out,
|
||||
k::Symbol, idx::Int, remove_pair::Bool)
|
||||
v = get(plotattributes_in, k, plotattributes_out[k])
|
||||
plotattributes_out[k] = if haskey(plotattributes_in, k) && typeof(v) <: AMat && !isempty(v)
|
||||
slice_arg(v, idx)
|
||||
else
|
||||
v
|
||||
end
|
||||
if remove_pair
|
||||
delete!(plotattributes_in, k)
|
||||
reset_kw!(plotattributes_in, k)
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -1251,7 +1267,7 @@ end
|
||||
|
||||
# # if the value is `:match` then we take whatever match_color is.
|
||||
# # this is mainly used for cascading defaults for foreground and background colors
|
||||
# function color_or_match!(plotattributes::KW, k::Symbol, match_color)
|
||||
# function color_or_match!(plotattributes::AKW, k::Symbol, match_color)
|
||||
# v = plotattributes[k]
|
||||
# plotattributes[k] = if v == :match
|
||||
# match_color
|
||||
@@ -1262,15 +1278,9 @@ end
|
||||
# end
|
||||
# end
|
||||
|
||||
function color_or_nothing!(plotattributes::KW, k::Symbol)
|
||||
function color_or_nothing!(plotattributes, k::Symbol)
|
||||
v = plotattributes[k]
|
||||
plotattributes[k] = if v === nothing || v == false
|
||||
RGBA{Float64}(0,0,0,0)
|
||||
elseif v != :match
|
||||
plot_color(v)
|
||||
else
|
||||
v
|
||||
end
|
||||
plotattributes[k] = v == :match ? v : plot_color(v)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1369,18 +1379,18 @@ Base.get(series::Series, k::Symbol, v) = get(series.plotattributes, k, v)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function fg_color(plotattributes::KW)
|
||||
fg = default(plotattributes, :foreground_color)
|
||||
function fg_color(plotattributes::AKW)
|
||||
fg = get(plotattributes, :foreground_color, :auto)
|
||||
if fg == :auto
|
||||
bg = plot_color(default(plotattributes, :background_color))
|
||||
bg = plot_color(get(plotattributes, :background_color, :white))
|
||||
fg = isdark(bg) ? colorant"white" : colorant"black"
|
||||
else
|
||||
plot_color(fg)
|
||||
end
|
||||
end
|
||||
|
||||
function fg_color_sp(plotattributes::KW)
|
||||
fgsp = default(plotattributes, :foreground_color_subplot)
|
||||
function fg_color_sp(plotattributes::AKW)
|
||||
fgsp = get(plotattributes, :foreground_color_subplot, :match)
|
||||
if fg == :match
|
||||
fg_color(plotattributes)
|
||||
else
|
||||
@@ -1391,13 +1401,13 @@ end
|
||||
|
||||
|
||||
# update attr from an input dictionary
|
||||
function _update_plot_args(plt::Plot, plotattributes_in::KW)
|
||||
function _update_plot_args(plt::Plot, plotattributes_in::AKW)
|
||||
for (k,v) in _plot_defaults
|
||||
slice_arg!(plotattributes_in, plt.attr, k, v, 1, true)
|
||||
slice_arg!(plotattributes_in, plt.attr, k, 1, true)
|
||||
end
|
||||
|
||||
# handle colors
|
||||
plotattributes= plt.attr
|
||||
plotattributes= plt.attr
|
||||
plt[:background_color] = plot_color(plotattributes[:background_color])
|
||||
plt[:foreground_color] = fg_color(plotattributes)
|
||||
# bg = plot_color(plt.attr[:background_color])
|
||||
@@ -1444,7 +1454,7 @@ function _update_subplot_colors(sp::Subplot)
|
||||
return
|
||||
end
|
||||
|
||||
function _update_axis(plt::Plot, sp::Subplot, plotattributes_in::KW, letter::Symbol, subplot_index::Int)
|
||||
function _update_axis(plt::Plot, sp::Subplot, plotattributes_in::AKW, letter::Symbol, subplot_index::Int)
|
||||
# get (maybe initialize) the axis
|
||||
axis = get_axis(sp, letter)
|
||||
|
||||
@@ -1460,13 +1470,13 @@ function _update_axis(plt::Plot, sp::Subplot, plotattributes_in::KW, letter::Sym
|
||||
return
|
||||
end
|
||||
|
||||
function _update_axis(axis::Axis, plotattributes_in::KW, letter::Symbol, subplot_index::Int)
|
||||
function _update_axis(axis::Axis, plotattributes_in::AKW, letter::Symbol, subplot_index::Int)
|
||||
# grab magic args (for example `xaxis = (:flip, :log)`)
|
||||
args = wraptuple(get(plotattributes_in, Symbol(letter, :axis), ()))
|
||||
|
||||
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
|
||||
kw = KW()
|
||||
for (k,v) in _axis_defaults
|
||||
for k in keys(_axis_defaults)
|
||||
# first get the args without the letter: `tickfont = font(10)`
|
||||
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
|
||||
if haskey(plotattributes_in, k)
|
||||
@@ -1511,12 +1521,12 @@ function _update_axis_links(plt::Plot, axis::Axis, letter::Symbol)
|
||||
end
|
||||
|
||||
# update a subplots args and axes
|
||||
function _update_subplot_args(plt::Plot, sp::Subplot, plotattributes_in::KW, subplot_index::Int, remove_pair::Bool)
|
||||
anns = pop!(sp.attr, :annotations, [])
|
||||
function _update_subplot_args(plt::Plot, sp::Subplot, plotattributes_in, subplot_index::Int, remove_pair::Bool)
|
||||
anns = pop_kw!(sp.attr, :annotations)
|
||||
|
||||
# grab those args which apply to this subplot
|
||||
for (k,v) in _subplot_defaults
|
||||
slice_arg!(plotattributes_in, sp.attr, k, v, subplot_index, remove_pair)
|
||||
# # grab those args which apply to this subplot
|
||||
for k in keys(_subplot_defaults)
|
||||
slice_arg!(plotattributes_in, sp.attr, k, subplot_index, remove_pair)
|
||||
end
|
||||
|
||||
_update_subplot_periphery(sp, anns)
|
||||
@@ -1550,30 +1560,27 @@ function get_series_color(c::AbstractArray, sp::Subplot, n::Int, seriestype)
|
||||
map(x->get_series_color(x, sp, n, seriestype), c)
|
||||
end
|
||||
|
||||
function ensure_gradient!(plotattributes::KW, csym::Symbol, asym::Symbol)
|
||||
function ensure_gradient!(plotattributes::AKW, csym::Symbol, asym::Symbol)
|
||||
if !isa(plotattributes[csym], ColorGradient)
|
||||
plotattributes[csym] = typeof(plotattributes[asym]) <: AbstractVector ? cgrad() : cgrad(alpha = plotattributes[asym])
|
||||
end
|
||||
end
|
||||
|
||||
function _replace_linewidth(plotattributes::KW)
|
||||
function _replace_linewidth(plotattributes::AKW)
|
||||
# get a good default linewidth... 0 for surface and heatmaps
|
||||
if get(plotattributes, :linewidth, :auto) == :auto
|
||||
if plotattributes[:linewidth] == :auto
|
||||
plotattributes[:linewidth] = (get(plotattributes, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_defaults!(plotattributes::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
# add default values to our dictionary, being careful not to delete what we just added!
|
||||
for (k,v) in _series_defaults
|
||||
slice_arg!(plotattributes, plotattributes, k, v, commandIndex, false)
|
||||
function _slice_series_args!(plotattributes::AKW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
for k in keys(_series_defaults)
|
||||
haskey(plotattributes, k) && slice_arg!(plotattributes, plotattributes, k, commandIndex, false)
|
||||
end
|
||||
|
||||
return plotattributes
|
||||
end
|
||||
|
||||
|
||||
function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot)
|
||||
pkg = plt.backend
|
||||
globalIndex = plotattributes[:series_plotindex]
|
||||
plotIndex = _series_index(plotattributes, sp)
|
||||
@@ -1643,6 +1650,7 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
# set label
|
||||
label = plotattributes[:label]
|
||||
label = (label == "AUTO" ? "y$globalIndex" : label)
|
||||
label = label in (:none, nothing, false) ? "" : label
|
||||
plotattributes[:label] = label
|
||||
|
||||
_replace_linewidth(plotattributes)
|
||||
|
||||
+327
-90
@@ -7,29 +7,20 @@
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
# init with values from _plot_defaults
|
||||
plotattributes = KW(
|
||||
explicit = KW(
|
||||
:letter => letter,
|
||||
# :extrema => (Inf, -Inf),
|
||||
:extrema => Extrema(),
|
||||
:discrete_map => Dict(), # map discrete values to discrete indices
|
||||
:continuous_values => zeros(0),
|
||||
:discrete_values => [],
|
||||
:use_minor => false,
|
||||
:show => true, # show or hide the axis? (useful for linked subplots)
|
||||
)
|
||||
|
||||
# get defaults from letter version, unless match
|
||||
for (k,v) in _axis_defaults
|
||||
lk = Symbol(letter, k)
|
||||
lv = _axis_defaults_byletter[lk]
|
||||
plotattributes[k] = (lv == :match ? v : lv)
|
||||
end
|
||||
|
||||
# merge!(plotattributes, _axis_defaults)
|
||||
plotattributes[:discrete_values] = []
|
||||
attr = Attr(explicit, _axis_defaults_byletter[letter])
|
||||
|
||||
# update the defaults
|
||||
attr!(Axis([sp], plotattributes), args...; kw...)
|
||||
attr!(Axis([sp], attr), args...; kw...)
|
||||
end
|
||||
|
||||
function get_axis(sp::Subplot, letter::Symbol)
|
||||
@@ -41,7 +32,7 @@ function get_axis(sp::Subplot, letter::Symbol)
|
||||
end::Axis
|
||||
end
|
||||
|
||||
function process_axis_arg!(plotattributes::KW, arg, letter = "")
|
||||
function process_axis_arg!(plotattributes::AKW, arg, letter = "")
|
||||
T = typeof(arg)
|
||||
arg = get(_scaleAliases, arg, arg)
|
||||
|
||||
@@ -311,7 +302,9 @@ function get_minor_ticks(sp, axis, ticks)
|
||||
minorticks = typeof(ticks[1])[]
|
||||
for (i,hi) in enumerate(ticks[2:end])
|
||||
lo = ticks[i]
|
||||
append!(minorticks,collect(lo + (hi-lo)/n :(hi-lo)/n: hi - (hi-lo)/2n))
|
||||
if isfinite(lo) && hi > lo
|
||||
append!(minorticks,collect(lo + (hi-lo)/n :(hi-lo)/n: hi - (hi-lo)/2n))
|
||||
end
|
||||
end
|
||||
minorticks[amin .<= minorticks .<= amax]
|
||||
end
|
||||
@@ -359,7 +352,7 @@ function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
vert = isvertical(plotattributes)
|
||||
|
||||
# first expand for the data
|
||||
@@ -575,10 +568,10 @@ end
|
||||
|
||||
# add the discrete value for each item. return the continuous values and the indices
|
||||
function discrete_value!(axis::Axis, v::AVec)
|
||||
n = length(v)
|
||||
cvec = zeros(n)
|
||||
discrete_indices = zeros(Int, n)
|
||||
for i=1:n
|
||||
n = eachindex(v)
|
||||
cvec = zeros(axes(v))
|
||||
discrete_indices = similar(Array{Int}, axes(v))
|
||||
for i in n
|
||||
cvec[i], discrete_indices[i] = discrete_value!(axis, v[i])
|
||||
end
|
||||
cvec, discrete_indices
|
||||
@@ -586,10 +579,10 @@ end
|
||||
|
||||
# add the discrete value for each item. return the continuous values and the indices
|
||||
function discrete_value!(axis::Axis, v::AMat)
|
||||
n,m = size(v)
|
||||
cmat = zeros(n,m)
|
||||
discrete_indices = zeros(Int, n, m)
|
||||
for i=1:n, j=1:m
|
||||
n,m = axes(v)
|
||||
cmat = zeros(axes(v))
|
||||
discrete_indices = similar(Array{Int}, axes(v))
|
||||
for i in n, j in m
|
||||
cmat[i,j], discrete_indices[i,j] = discrete_value!(axis, v[i,j])
|
||||
end
|
||||
cmat, discrete_indices
|
||||
@@ -635,13 +628,13 @@ function axis_drawing_info(sp::Subplot)
|
||||
|
||||
if sp[:framestyle] != :none
|
||||
# xaxis
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
if xaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
push!(xaxis_segs, (xmin, y1), (xmax, y1))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(xticks in (:none, nothing, false)) && length(xticks) > 1
|
||||
@@ -654,55 +647,48 @@ function axis_drawing_info(sp::Subplot)
|
||||
if !(xaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(ymin) + 0.015 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t2 = invf(f(ymax) - 0.015 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.015 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(y1) + 0.012 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
|
||||
for xtick in xticks[1]
|
||||
if xaxis[:showaxis]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
(0, t3)
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, t2) : (ymin, t1)
|
||||
end
|
||||
push!(xtick_segs, (xtick, tick_start), (xtick, tick_stop)) # bottom tick
|
||||
end
|
||||
# sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick
|
||||
xaxis[:grid] && push!(xgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid
|
||||
end
|
||||
end
|
||||
if !(xaxis[:minorticks] in (:none, nothing, false)) || xaxis[:minorgrid]
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(ymin) + 0.01 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t2 = invf(f(ymax) - 0.01 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.01 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
|
||||
for xminortick in xminorticks
|
||||
if xaxis[:showaxis]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
(0, t3)
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, t2) : (ymin, t1)
|
||||
end
|
||||
push!(xtick_segs, (xminortick, tick_start), (xminortick, tick_stop)) # bottom tick
|
||||
if !(xaxis[:minorticks] in (:none, nothing, false)) || xaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(y1) + 0.006 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
for xtick in xminorticks
|
||||
if xaxis[:showaxis]
|
||||
push!(xtick_segs, (xtick, tick_start), (xtick, tick_stop)) # bottom tick
|
||||
end
|
||||
xaxis[:minorgrid] && push!(xminorgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid
|
||||
end
|
||||
# sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick
|
||||
xaxis[:minorgrid] && push!(xminorgrid_segs, (xminortick, ymin), (xminortick, ymax)) # vertical grid
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# yaxis
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, xmin) : (xmin, xmax)
|
||||
end
|
||||
if yaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, xmin) : (xmin, xmax)
|
||||
end
|
||||
push!(yaxis_segs, (x1, ymin), (x1, ymax))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(yticks in (:none, nothing,false)) && length(yticks) > 1
|
||||
@@ -715,46 +701,297 @@ function axis_drawing_info(sp::Subplot)
|
||||
if !(yaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(xmin) + 0.015 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t2 = invf(f(xmax) - 0.015 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.015 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(x1) + 0.012 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
|
||||
for ytick in yticks[1]
|
||||
if yaxis[:showaxis]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
(0, t3)
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, t2) : (xmin, t1)
|
||||
end
|
||||
push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick
|
||||
end
|
||||
# sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick
|
||||
yaxis[:grid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
||||
end
|
||||
end
|
||||
if !(yaxis[:minorticks] in (:none, nothing, false)) || yaxis[:minorgrid]
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(xmin) + 0.01 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t2 = invf(f(xmax) - 0.01 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.01 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
|
||||
for ytick in yminorticks
|
||||
if yaxis[:showaxis]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
(0, t3)
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, t2) : (xmin, t1)
|
||||
end
|
||||
push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick
|
||||
if !(yaxis[:minorticks] in (:none, nothing, false)) || yaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(x1) + 0.006 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
for ytick in yminorticks
|
||||
if yaxis[:showaxis]
|
||||
push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick
|
||||
end
|
||||
yaxis[:minorgrid] && push!(yminorgrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
||||
end
|
||||
# sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick
|
||||
yaxis[:minorgrid] && push!(yminorgrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs
|
||||
end
|
||||
|
||||
|
||||
function axis_drawing_info_3d(sp::Subplot)
|
||||
xaxis, yaxis, zaxis = sp[:xaxis], sp[:yaxis], sp[:zaxis]
|
||||
xmin, xmax = axis_limits(sp, :x)
|
||||
ymin, ymax = axis_limits(sp, :y)
|
||||
zmin, zmax = axis_limits(sp, :z)
|
||||
xticks = get_ticks(sp, xaxis)
|
||||
yticks = get_ticks(sp, yaxis)
|
||||
zticks = get_ticks(sp, zaxis)
|
||||
xminorticks = get_minor_ticks(sp, xaxis, xticks)
|
||||
yminorticks = get_minor_ticks(sp, yaxis, yticks)
|
||||
zminorticks = get_minor_ticks(sp, zaxis, zticks)
|
||||
xaxis_segs = Segments(3)
|
||||
yaxis_segs = Segments(3)
|
||||
zaxis_segs = Segments(3)
|
||||
xtick_segs = Segments(3)
|
||||
ytick_segs = Segments(3)
|
||||
ztick_segs = Segments(3)
|
||||
xgrid_segs = Segments(3)
|
||||
ygrid_segs = Segments(3)
|
||||
zgrid_segs = Segments(3)
|
||||
xminorgrid_segs = Segments(3)
|
||||
yminorgrid_segs = Segments(3)
|
||||
zminorgrid_segs = Segments(3)
|
||||
xborder_segs = Segments(3)
|
||||
yborder_segs = Segments(3)
|
||||
zborder_segs = Segments(3)
|
||||
|
||||
if sp[:framestyle] != :none
|
||||
|
||||
# xaxis
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
z1, z2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], zaxis[:flip]) ? (zmax, zmin) : (zmin, zmax)
|
||||
end
|
||||
if xaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
push!(xaxis_segs, (xmin, y1, z1), (xmax, y1, z1))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(xticks in (:none, nothing, false)) && length(xticks) > 1
|
||||
showticks = xticks[1] .!= 0
|
||||
xticks = (xticks[1][showticks], xticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin, y2, z2), (xmax, y2, z2)) # top spine
|
||||
end
|
||||
if !(xaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(y1) + 0.012 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
|
||||
for xtick in xticks[1]
|
||||
if xaxis[:showaxis]
|
||||
push!(xtick_segs, (xtick, tick_start, z1), (xtick, tick_stop, z1)) # bottom tick
|
||||
end
|
||||
if xaxis[:grid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(xgrid_segs, (xtick, ymin, 0.0), (xtick, ymax, 0.0))
|
||||
push!(xgrid_segs, (xtick, 0.0, zmin), (xtick, 0.0, zmax))
|
||||
else
|
||||
push!(xgrid_segs, (xtick, y1, z1), (xtick, y2, z1))
|
||||
push!(xgrid_segs, (xtick, y2, z1), (xtick, y2, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !(xaxis[:minorticks] in (:none, nothing, false)) || xaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(y1) + 0.006 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
for xtick in xminorticks
|
||||
if xaxis[:showaxis]
|
||||
push!(xtick_segs, (xtick, tick_start, z1), (xtick, tick_stop, z1)) # bottom tick
|
||||
end
|
||||
if xaxis[:minorgrid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(xminorgrid_segs, (xtick, ymin, 0.0), (xtick, ymax, 0.0))
|
||||
push!(xminorgrid_segs, (xtick, 0.0, zmin), (xtick, 0.0, zmax))
|
||||
else
|
||||
push!(xminorgrid_segs, (xtick, y1, z1), (xtick, y2, z1))
|
||||
push!(xminorgrid_segs, (xtick, y2, z1), (xtick, y2, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# yaxis
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmin, xmax) : (xmax, xmin)
|
||||
end
|
||||
z1, z2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], zaxis[:flip]) ? (zmax, zmin) : (zmin, zmax)
|
||||
end
|
||||
if yaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
push!(yaxis_segs, (x1, ymin, z1), (x1, ymax, z1))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(yticks in (:none, nothing,false)) && length(yticks) > 1
|
||||
showticks = yticks[1] .!= 0
|
||||
yticks = (yticks[1][showticks], yticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (x2, ymin, z2), (x2, ymax, z2)) # right spine
|
||||
end
|
||||
if !(yaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(x1) + 0.012 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
|
||||
for ytick in yticks[1]
|
||||
if yaxis[:showaxis]
|
||||
push!(ytick_segs, (tick_start, ytick, z1), (tick_stop, ytick, z1)) # left tick
|
||||
end
|
||||
if yaxis[:grid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(ygrid_segs, (xmin, ytick, 0.0), (xmax, ytick, 0.0))
|
||||
push!(ygrid_segs, (0.0, ytick, zmin), (0.0, ytick, zmax))
|
||||
else
|
||||
push!(ygrid_segs, (x1, ytick, z1), (x2, ytick, z1))
|
||||
push!(ygrid_segs, (x2, ytick, z1), (x2, ytick, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !(yaxis[:minorticks] in (:none, nothing, false)) || yaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(x1) + 0.006 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
for ytick in yminorticks
|
||||
if yaxis[:showaxis]
|
||||
push!(ytick_segs, (tick_start, ytick, z1), (tick_stop, ytick, z1)) # left tick
|
||||
end
|
||||
if yaxis[:minorgrid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(yminorgrid_segs, (xmin, ytick, 0.0), (xmax, ytick, 0.0))
|
||||
push!(yminorgrid_segs, (0.0, ytick, zmin), (0.0, ytick, zmax))
|
||||
else
|
||||
push!(yminorgrid_segs, (x1, ytick, z1), (x2, ytick, z1))
|
||||
push!(yminorgrid_segs, (x2, ytick, z1), (x2, ytick, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# zaxis
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(zaxis[:mirror], xaxis[:flip]) ? (xmax, xmin) : (xmin, xmax)
|
||||
end
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(zaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
if zaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
push!(zaxis_segs, (x1, y1, zmin), (x1, y1, zmax))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(zticks in (:none, nothing,false)) && length(zticks) > 1
|
||||
showticks = zticks[1] .!= 0
|
||||
zticks = (zticks[1][showticks], zticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(zborder_segs, (x2, y2, zmin), (x2, y2, zmax))
|
||||
end
|
||||
if !(zaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = zaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(y1) + 0.012 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
|
||||
for ztick in zticks[1]
|
||||
if zaxis[:showaxis]
|
||||
push!(ztick_segs, (x1, tick_start, ztick), (x1, tick_stop, ztick)) # left tick
|
||||
end
|
||||
if zaxis[:grid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(zgrid_segs, (xmin, 0.0, ztick), (xmax, 0.0, ztick))
|
||||
push!(ygrid_segs, (0.0, ymin, ztick), (0.0, ymax, ztick))
|
||||
else
|
||||
push!(ygrid_segs, (x1, y1, ztick), (x1, y2, ztick))
|
||||
push!(ygrid_segs, (x1, y2, ztick), (x2, y2, ztick))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !(zaxis[:minorticks] in (:none, nothing, false)) || zaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(y1) + 0.006 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
for ztick in zminorticks
|
||||
if zaxis[:showaxis]
|
||||
push!(ztick_segs, (x1, tick_start, ztick), (x1, tick_stop, ztick)) # left tick
|
||||
end
|
||||
if zaxis[:minorgrid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(zminorgrid_segs, (xmin, 0.0, ztick), (xmax, 0.0, ztick))
|
||||
push!(zminorgrid_segs, (0.0, ymin, ztick), (0.0, ymax, ztick))
|
||||
else
|
||||
push!(zminorgrid_segs, (x1, y1, ztick), (x1, y2, ztick))
|
||||
push!(zminorgrid_segs, (x1, y2, ztick), (x2, y2, ztick))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xticks, yticks, zticks, xaxis_segs, yaxis_segs, zaxis_segs, xtick_segs, ytick_segs, ztick_segs, xgrid_segs, ygrid_segs, zgrid_segs, xminorgrid_segs, yminorgrid_segs, zminorgrid_segs, xborder_segs, yborder_segs, zborder_segs
|
||||
end
|
||||
|
||||
+108
-30
@@ -673,37 +673,86 @@ const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
const _pgfplotsx_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,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:match_dimensions,
|
||||
: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,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
: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,
|
||||
:fill_z,
|
||||
:line_z,
|
||||
:marker_z,
|
||||
:levels,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:aspect_ratio,
|
||||
:normalize, :weights,
|
||||
:normalize,
|
||||
:weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:arrow,
|
||||
@@ -712,13 +761,42 @@ const _pgfplotsx_attr = merge_with_base_supported([
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pgfplotsx_seriestype =
|
||||
[:path, :scatter, :straightline,
|
||||
:path3d, :scatter3d, :surface, :wireframe,
|
||||
:heatmap, :contour, :contour3d,
|
||||
:shape,
|
||||
:steppre, :stepmid, :steppost, :ysticks, :xsticks]
|
||||
const _pgfplotsx_seriestype = [
|
||||
:path,
|
||||
:scatter,
|
||||
:straightline,
|
||||
:path3d,
|
||||
:scatter3d,
|
||||
:surface,
|
||||
:wireframe,
|
||||
:heatmap,
|
||||
:contour,
|
||||
:contour3d,
|
||||
:shape,
|
||||
:steppre,
|
||||
:stepmid,
|
||||
:steppost,
|
||||
:ysticks,
|
||||
:xsticks,
|
||||
]
|
||||
const _pgfplotsx_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplotsx_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :ltriangle, :rtriangle, :cross, :xcross, :star5, :pentagon, :hline, :vline, Shape]
|
||||
const _pgfplotsx_marker = [
|
||||
:none,
|
||||
:auto,
|
||||
:circle,
|
||||
:rect,
|
||||
:diamond,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:ltriangle,
|
||||
:rtriangle,
|
||||
:cross,
|
||||
:xcross,
|
||||
:star5,
|
||||
:pentagon,
|
||||
:hline,
|
||||
:vline,
|
||||
Shape,
|
||||
]
|
||||
const _pgfplotsx_scale = [:identity, :ln, :log2, :log10]
|
||||
is_marker_supported(::PGFPlotsXBackend, shape::Shape) = true
|
||||
|
||||
+522
-182
@@ -69,6 +69,19 @@ 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 +
|
||||
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)) )
|
||||
function gr_color(c, ::Type{<:AbstractGray})
|
||||
g = round(UInt, clamp(gray(c) * 255, 0, 255))
|
||||
α = round(UInt, clamp(alpha(c) * 255, 0, 255))
|
||||
rgba = UInt32( α<<24 + g<<16 + g<<8 + g )
|
||||
end
|
||||
gr_color(c, ::Type) = gr_color(RGBA(c), RGB)
|
||||
|
||||
function gr_getcolorind(c)
|
||||
gr_set_transparency(float(alpha(c)))
|
||||
convert(Int, GR.inqcolorfromrgb(red(c), green(c), blue(c)))
|
||||
@@ -77,6 +90,7 @@ end
|
||||
gr_set_linecolor(c) = GR.setlinecolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_bordercolor(c) = GR.setbordercolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(_cycle(c,1)))
|
||||
gr_set_transparency(α::Real) = GR.settransparency(clamp(α, 0, 1))
|
||||
gr_set_transparency(::Nothing) = GR.settransparency(1)
|
||||
@@ -142,6 +156,48 @@ function gr_polyline(x, y, func = GR.polyline; arrowside = :none, arrowstyle = :
|
||||
end
|
||||
end
|
||||
|
||||
function gr_polyline3d(x, y, z, func = GR.polyline3d; arrowside = :none, arrowstyle = :simple)
|
||||
iend = 0
|
||||
n = length(x)
|
||||
while iend < n-1
|
||||
# set istart to the first index that is finite
|
||||
istart = -1
|
||||
for j = iend+1:n
|
||||
if isfinite(x[j]) && isfinite(y[j]) && isfinite(z[j])
|
||||
istart = j
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if istart > 0
|
||||
# iend is the last finite index
|
||||
iend = -1
|
||||
for j = istart+1:n
|
||||
if isfinite(x[j]) && isfinite(y[j]) && isfinite(z[j])
|
||||
iend = j
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# if we found a start and end, draw the line segment, otherwise we're done
|
||||
if istart > 0 && iend > 0
|
||||
func(x[istart:iend], y[istart:iend], z[istart:iend])
|
||||
if arrowside in (:head,:both)
|
||||
gr_set_arrowstyle(arrowstyle)
|
||||
GR.drawarrow(x[iend-1], y[iend-1], z[iend-1], x[iend], y[iend], z[iend])
|
||||
end
|
||||
if arrowside in (:tail,:both)
|
||||
gr_set_arrowstyle(arrowstyle)
|
||||
GR.drawarrow(x[istart+1], y[istart+1], z[istart+1], x[istart], y[istart], z[istart])
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
gr_inqtext(x, y, s::Symbol) = gr_inqtext(x, y, string(s))
|
||||
|
||||
function gr_inqtext(x, y, s)
|
||||
@@ -176,15 +232,13 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
sinf = sind.(a)
|
||||
cosf = cosd.(a)
|
||||
rtick_values, rtick_labels = get_ticks(sp, yaxis)
|
||||
if yaxis[:formatter] in (:scientific, :auto) && yaxis[:ticks] in (:auto, :native)
|
||||
rtick_labels = convert_sci_unicode.(rtick_labels)
|
||||
end
|
||||
rtick_labels = gr_tick_label.((yaxis,), rtick_labels)
|
||||
|
||||
#draw angular grid
|
||||
if xaxis[:grid]
|
||||
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
|
||||
gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:gridalpha])
|
||||
for i in 1:length(α)
|
||||
for i in eachindex(α)
|
||||
GR.polyline([sinf[i], 0], [cosf[i], 0])
|
||||
end
|
||||
end
|
||||
@@ -193,7 +247,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
if yaxis[:grid]
|
||||
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
|
||||
gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:gridalpha])
|
||||
for i in 1:length(rtick_values)
|
||||
for i in eachindex(rtick_values)
|
||||
r = (rtick_values[i] - rmin) / (rmax - rmin)
|
||||
if r <= 1.0 && r >= 0.0
|
||||
GR.drawarc(-r, r, -r, r, 0, 359)
|
||||
@@ -210,7 +264,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
#draw angular ticks
|
||||
if xaxis[:showaxis]
|
||||
GR.drawarc(-1, 1, -1, 1, 0, 359)
|
||||
for i in 1:length(α)
|
||||
for i in eachindex(α)
|
||||
x, y = GR.wctondc(1.1 * sinf[i], 1.1 * cosf[i])
|
||||
GR.textext(x, y, string((360-α[i])%360, "^o"))
|
||||
end
|
||||
@@ -218,7 +272,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
|
||||
|
||||
#draw radial ticks
|
||||
if yaxis[:showaxis]
|
||||
for i in 1:length(rtick_values)
|
||||
for i in eachindex(rtick_values)
|
||||
r = (rtick_values[i] - rmin) / (rmax - rmin)
|
||||
if r <= 1.0 && r >= 0.0
|
||||
x, y = GR.wctondc(0.05, r)
|
||||
@@ -236,18 +290,6 @@ 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_lims(sp::Subplot, axis::Axis, adjust::Bool, expand = nothing)
|
||||
if expand !== nothing
|
||||
expand_extrema!(axis, expand)
|
||||
end
|
||||
lims = axis_limits(sp, axis[:letter])
|
||||
if adjust
|
||||
GR.adjustrange(lims...)
|
||||
else
|
||||
lims
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function gr_fill_viewport(vp::AVec{Float64}, c)
|
||||
GR.savestate()
|
||||
@@ -263,23 +305,43 @@ end
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# draw ONE Shape
|
||||
function gr_draw_marker(xi, yi, msize, shape::Shape)
|
||||
function gr_draw_marker(series, xi, yi, clims, i, msize, shape::Shape)
|
||||
sx, sy = coords(shape)
|
||||
# convert to ndc coords (percentages of window)
|
||||
GR.selntran(0)
|
||||
w, h = gr_plot_size
|
||||
f = msize / (w + h)
|
||||
xi, yi = GR.wctondc(xi, yi)
|
||||
GR.fillarea(xi .+ sx .* f,
|
||||
yi .+ sy .* f)
|
||||
xs = xi .+ sx .* f
|
||||
ys = yi .+ sy .* f
|
||||
|
||||
# draw the interior
|
||||
mc = get_markercolor(series, clims, i)
|
||||
gr_set_fill(mc)
|
||||
gr_set_transparency(mc, get_markeralpha(series, i))
|
||||
GR.fillarea(xs, ys)
|
||||
|
||||
# draw the shapes
|
||||
msc = get_markerstrokecolor(series, i)
|
||||
gr_set_line(get_markerstrokewidth(series, i), :solid, msc)
|
||||
gr_set_transparency(msc, get_markerstrokealpha(series, i))
|
||||
GR.polyline(xs, ys)
|
||||
GR.selntran(1)
|
||||
end
|
||||
|
||||
# draw ONE symbol marker
|
||||
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
function nominal_size()
|
||||
w, h = gr_plot_size
|
||||
GR.setmarkersize(0.3msize / ((w + h) * 0.001))
|
||||
min(w, h) / 500
|
||||
end
|
||||
|
||||
# draw ONE symbol marker
|
||||
function gr_draw_marker(series, xi, yi, clims, i, msize::Number, shape::Symbol)
|
||||
GR.setborderwidth(series[:markerstrokewidth]);
|
||||
gr_set_bordercolor(get_markerstrokecolor(series, i));
|
||||
gr_set_markercolor(get_markercolor(series, clims, i));
|
||||
gr_set_transparency(get_markeralpha(series, i))
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
GR.setmarkersize(0.3msize / nominal_size())
|
||||
GR.polymarker([xi], [yi])
|
||||
end
|
||||
|
||||
@@ -292,26 +354,10 @@ function gr_draw_markers(series::Series, x, y, clims, msize = series[:markersize
|
||||
|
||||
shapes = series[:markershape]
|
||||
if shapes != :none
|
||||
for i=1:length(x)
|
||||
for i=eachindex(x)
|
||||
msi = _cycle(msize, i)
|
||||
shape = _cycle(shapes, i)
|
||||
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
|
||||
|
||||
# draw a filled in shape, slightly bigger, to estimate a stroke
|
||||
if series[:markerstrokewidth] > 0
|
||||
c = get_markerstrokecolor(series, i)
|
||||
cfunc(c)
|
||||
gr_set_transparency(c, get_markerstrokealpha(series, i))
|
||||
gr_draw_marker(x[i], y[i], msi + series[:markerstrokewidth], shape)
|
||||
end
|
||||
|
||||
# draw the shape - don't draw filled area if marker shape is 1D
|
||||
if !(shape in (:hline, :vline, :+, :x))
|
||||
c = get_markercolor(series, clims, i)
|
||||
cfunc(c)
|
||||
gr_set_transparency(c, get_markeralpha(series, i))
|
||||
gr_draw_marker(x[i], y[i], msi, shape)
|
||||
end
|
||||
gr_draw_marker(series, x[i], y[i], clims, i, msi, shape)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -321,7 +367,7 @@ end
|
||||
function gr_set_line(lw, style, c) #, a)
|
||||
GR.setlinetype(gr_linetype[style])
|
||||
w, h = gr_plot_size
|
||||
GR.setlinewidth(_gr_thickness_scaling[1] * max(0, lw / ((w + h) * 0.001)))
|
||||
GR.setlinewidth(_gr_thickness_scaling[1] * max(0, lw / nominal_size()))
|
||||
gr_set_linecolor(c) #, a)
|
||||
end
|
||||
|
||||
@@ -357,6 +403,12 @@ function gr_nans_to_infs!(z)
|
||||
end
|
||||
end
|
||||
|
||||
function gr_w3tondc(x, y, z)
|
||||
xw, yw, zw = GR.wc3towc(x, y, z)
|
||||
x, y = GR.wctondc(xw, yw)
|
||||
return x, y
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# viewport plot area
|
||||
|
||||
@@ -375,16 +427,8 @@ function gr_viewport_from_bbox(sp::Subplot{GRBackend}, bb::BoundingBox, w, h, vi
|
||||
viewport[2] = viewport_canvas[2] * (right(bb) / w)
|
||||
viewport[3] = viewport_canvas[4] * (1.0 - bottom(bb) / h)
|
||||
viewport[4] = viewport_canvas[4] * (1.0 - top(bb) / h)
|
||||
if is3d(sp)
|
||||
vp = viewport[:]
|
||||
extent = min(vp[2] - vp[1], vp[4] - vp[3])
|
||||
viewport[1] = 0.5 * (vp[1] + vp[2] - extent)
|
||||
viewport[2] = 0.5 * (vp[1] + vp[2] + extent)
|
||||
viewport[3] = 0.5 * (vp[3] + vp[4] - extent)
|
||||
viewport[4] = 0.5 * (vp[3] + vp[4] + extent)
|
||||
end
|
||||
if hascolorbar(sp)
|
||||
viewport[2] -= gr_colorbar_ratio
|
||||
viewport[2] -= gr_colorbar_ratio * (1 + is3d(sp) / 2)
|
||||
end
|
||||
viewport
|
||||
end
|
||||
@@ -554,13 +598,13 @@ function gr_legend_pos(sp::Subplot, w, h)
|
||||
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] + 0.11 + ymirror * gr_yaxis_width(sp)
|
||||
xpos = viewport_plotarea[2] + 0.11 + ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
else
|
||||
xpos = viewport_plotarea[2] - 0.05 - w
|
||||
end
|
||||
elseif occursin("left", str)
|
||||
if occursin("outer", str)
|
||||
xpos = viewport_plotarea[1] - 0.05 - w - !ymirror * gr_yaxis_width(sp)
|
||||
xpos = viewport_plotarea[1] - 0.05 - w - !ymirror * gr_axis_width(sp, sp[:yaxis])
|
||||
else
|
||||
xpos = viewport_plotarea[1] + 0.11
|
||||
end
|
||||
@@ -569,13 +613,13 @@ function gr_legend_pos(sp::Subplot, w, h)
|
||||
end
|
||||
if occursin("top", str)
|
||||
if s == :outertop
|
||||
ypos = viewport_plotarea[4] + 0.02 + h + xmirror * gr_xaxis_height(sp)
|
||||
ypos = viewport_plotarea[4] + 0.02 + h + xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
else
|
||||
ypos = viewport_plotarea[4] - 0.06
|
||||
end
|
||||
elseif occursin("bottom", str)
|
||||
if s == :outerbottom
|
||||
ypos = viewport_plotarea[3] - 0.05 - !xmirror * gr_xaxis_height(sp)
|
||||
ypos = viewport_plotarea[3] - 0.05 - !xmirror * gr_axis_height(sp, sp[:xaxis])
|
||||
else
|
||||
ypos = viewport_plotarea[3] + h + 0.06
|
||||
end
|
||||
@@ -734,26 +778,37 @@ function gr_get_ticks_size(ticks, rot)
|
||||
return w, h
|
||||
end
|
||||
|
||||
function gr_xaxis_height(sp)
|
||||
xaxis = sp[:xaxis]
|
||||
xticks, yticks = axis_drawing_info(sp)[1:2]
|
||||
gr_set_font(tickfont(xaxis))
|
||||
h = (xticks in (nothing, false, :none) ? 0 : last(gr_get_ticks_size(xticks, xaxis[:rotation])))
|
||||
if xaxis[:guide] != ""
|
||||
gr_set_font(guidefont(xaxis))
|
||||
h += last(gr_text_size(xaxis[:guide]))
|
||||
gr_tick_label(axis,label) = (axis[:formatter] in (:scientific, :auto)) ?
|
||||
gr_convert_sci_tick_label(label) :
|
||||
label
|
||||
|
||||
function gr_convert_sci_tick_label(label)
|
||||
caret_split = split(label,'^')
|
||||
if length(caret_split) == 2
|
||||
base, exponent = caret_split
|
||||
label = "$base^{$exponent}"
|
||||
end
|
||||
convert_sci_unicode(label)
|
||||
end
|
||||
|
||||
function gr_axis_height(sp, axis)
|
||||
ticks = get_ticks(sp, axis)
|
||||
gr_set_font(tickfont(axis))
|
||||
h = (ticks in (nothing, false, :none) ? 0 : last(gr_get_ticks_size(ticks, axis[:rotation])))
|
||||
if axis[:guide] != ""
|
||||
gr_set_font(guidefont(axis))
|
||||
h += last(gr_text_size(axis[:guide]))
|
||||
end
|
||||
return h
|
||||
end
|
||||
|
||||
function gr_yaxis_width(sp)
|
||||
yaxis = sp[:yaxis]
|
||||
xticks, yticks = axis_drawing_info(sp)[1:2]
|
||||
gr_set_font(tickfont(yaxis))
|
||||
w = (xticks in (nothing, false, :none) ? 0 : first(gr_get_ticks_size(yticks, yaxis[:rotation])))
|
||||
if yaxis[:guide] != ""
|
||||
gr_set_font(guidefont(yaxis))
|
||||
w += last(gr_text_size(yaxis[:guide]))
|
||||
function gr_axis_width(sp, axis)
|
||||
ticks = get_ticks(sp, axis)
|
||||
gr_set_font(tickfont(axis))
|
||||
w = (ticks in (nothing, false, :none) ? 0 : first(gr_get_ticks_size(ticks, axis[:rotation])))
|
||||
if axis[:guide] != ""
|
||||
gr_set_font(guidefont(axis))
|
||||
w += last(gr_text_size(axis[:guide]))
|
||||
end
|
||||
return w
|
||||
end
|
||||
@@ -777,48 +832,132 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
h = 1mm + gr_plot_size[2] * l * px
|
||||
toppad += h
|
||||
end
|
||||
# Add margin for x and y ticks
|
||||
xticks, yticks = axis_drawing_info(sp)[1:2]
|
||||
if !(xticks in (nothing, false, :none))
|
||||
flip, mirror = gr_set_xticks_font(sp)
|
||||
l = 0.01 + last(gr_get_ticks_size(xticks, sp[:xaxis][:rotation]))
|
||||
h = 1mm + gr_plot_size[2] * l * px
|
||||
if mirror
|
||||
toppad += h
|
||||
else
|
||||
bottompad += h
|
||||
|
||||
if 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)
|
||||
# Add margin for x and y ticks
|
||||
h = 0mm
|
||||
if !(xticks in (nothing, false, :none))
|
||||
gr_set_font(
|
||||
tickfont(xaxis),
|
||||
halign = (:left, :hcenter, :right)[sign(xaxis[:rotation]) + 2],
|
||||
valign = (xaxis[:mirror] ? :bottom : :top),
|
||||
rotation = xaxis[:rotation]
|
||||
)
|
||||
l = 0.01 + last(gr_get_ticks_size(xticks, xaxis[:rotation]))
|
||||
h = max(h, 1mm + gr_plot_size[2] * l * px)
|
||||
end
|
||||
end
|
||||
if !(yticks in (nothing, false, :none))
|
||||
flip, mirror = gr_set_yticks_font(sp)
|
||||
l = 0.01 + first(gr_get_ticks_size(yticks, sp[:yaxis][:rotation]))
|
||||
w = 1mm + gr_plot_size[1] * l * px
|
||||
if mirror
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
if !(yticks in (nothing, false, :none))
|
||||
gr_set_font(
|
||||
tickfont(yaxis),
|
||||
halign = (:left, :hcenter, :right)[sign(yaxis[:rotation]) + 2],
|
||||
valign = (yaxis[:mirror] ? :bottom : :top),
|
||||
rotation = yaxis[:rotation]
|
||||
)
|
||||
l = 0.01 + last(gr_get_ticks_size(yticks, yaxis[:rotation]))
|
||||
h = max(h, 1mm + gr_plot_size[2] * l * px)
|
||||
end
|
||||
end
|
||||
# Add margin for x label
|
||||
if sp[:xaxis][:guide] != ""
|
||||
gr_set_font(guidefont(sp[:xaxis]))
|
||||
l = last(gr_text_size(sp[:xaxis][:guide]))
|
||||
h = 1mm + gr_plot_size[2] * l * px
|
||||
if sp[:xaxis][:guide_position] == :top || (sp[:xaxis][:guide_position] == :auto && sp[:xaxis][:mirror] == true)
|
||||
toppad += h
|
||||
else
|
||||
bottompad += h
|
||||
if h > 0mm
|
||||
if xaxis[:mirror] || yaxis[:mirror]
|
||||
toppad += h
|
||||
end
|
||||
if !xaxis[:mirror] || !yaxis[:mirror]
|
||||
bottompad += h
|
||||
end
|
||||
end
|
||||
end
|
||||
# Add margin for y label
|
||||
if sp[:yaxis][:guide] != ""
|
||||
gr_set_font(guidefont(sp[:yaxis]))
|
||||
l = last(gr_text_size(sp[:yaxis][:guide]))
|
||||
w = 1mm + gr_plot_size[2] * l * px
|
||||
if sp[:yaxis][:guide_position] == :right || (sp[:yaxis][:guide_position] == :auto && sp[:yaxis][:mirror] == true)
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
|
||||
if !(zticks in (nothing, false, :none))
|
||||
gr_set_font(
|
||||
tickfont(zaxis),
|
||||
halign = (zaxis[:mirror] ? :left : :right),
|
||||
valign = (:top, :vcenter, :bottom)[sign(zaxis[:rotation]) + 2],
|
||||
rotation = zaxis[:rotation]
|
||||
)
|
||||
l = 0.01 + first(gr_get_ticks_size(zticks, zaxis[:rotation]))
|
||||
w = 1mm + gr_plot_size[1] * l * px
|
||||
if zaxis[:mirror]
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
end
|
||||
end
|
||||
|
||||
# Add margin for x or y label
|
||||
h = 0mm
|
||||
if xaxis[:guide] != ""
|
||||
gr_set_font(guidefont(sp[:xaxis]))
|
||||
l = last(gr_text_size(sp[:xaxis][:guide]))
|
||||
h = max(h, 1mm + gr_plot_size[2] * l * px)
|
||||
end
|
||||
if yaxis[:guide] != ""
|
||||
gr_set_font(guidefont(sp[:yaxis]))
|
||||
l = last(gr_text_size(sp[:yaxis][:guide]))
|
||||
h = max(h, 1mm + gr_plot_size[2] * l * px)
|
||||
end
|
||||
if h > 0mm
|
||||
if xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
toppad += h
|
||||
else
|
||||
bottompad += h
|
||||
end
|
||||
end
|
||||
# Add margin for z label
|
||||
if zaxis[:guide] != ""
|
||||
gr_set_font(guidefont(sp[:zaxis]))
|
||||
l = last(gr_text_size(sp[:zaxis][:guide]))
|
||||
w = 1mm + gr_plot_size[2] * l * px
|
||||
if zaxis[:guide_position] == :right || (zaxis[:guide_position] == :auto && zaxis[:mirror] == true)
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
end
|
||||
end
|
||||
else
|
||||
# Add margin for x and y ticks
|
||||
xticks, yticks = get_ticks(sp, sp[:xaxis]), get_ticks(sp, sp[:yaxis])
|
||||
if !(xticks in (nothing, false, :none))
|
||||
flip, mirror = gr_set_xticks_font(sp)
|
||||
l = 0.01 + last(gr_get_ticks_size(xticks, sp[:xaxis][:rotation]))
|
||||
h = 1mm + gr_plot_size[2] * l * px
|
||||
if mirror
|
||||
toppad += h
|
||||
else
|
||||
bottompad += h
|
||||
end
|
||||
end
|
||||
if !(yticks in (nothing, false, :none))
|
||||
flip, mirror = gr_set_yticks_font(sp)
|
||||
l = 0.01 + first(gr_get_ticks_size(yticks, sp[:yaxis][:rotation]))
|
||||
w = 1mm + gr_plot_size[1] * l * px
|
||||
if mirror
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
end
|
||||
end
|
||||
|
||||
# Add margin for x label
|
||||
if sp[:xaxis][:guide] != ""
|
||||
gr_set_font(guidefont(sp[:xaxis]))
|
||||
l = last(gr_text_size(sp[:xaxis][:guide]))
|
||||
h = 1mm + gr_plot_size[2] * l * px
|
||||
if sp[:xaxis][:guide_position] == :top || (sp[:xaxis][:guide_position] == :auto && sp[:xaxis][:mirror] == true)
|
||||
toppad += h
|
||||
else
|
||||
bottompad += h
|
||||
end
|
||||
end
|
||||
# Add margin for y label
|
||||
if sp[:yaxis][:guide] != ""
|
||||
gr_set_font(guidefont(sp[:yaxis]))
|
||||
l = last(gr_text_size(sp[:yaxis][:guide]))
|
||||
w = 1mm + gr_plot_size[2] * l * px
|
||||
if sp[:yaxis][:guide_position] == :right || (sp[:yaxis][:guide_position] == :auto && sp[:yaxis][:mirror] == true)
|
||||
rightpad += w
|
||||
else
|
||||
leftpad += w
|
||||
end
|
||||
end
|
||||
end
|
||||
if sp[:colorbar_title] != ""
|
||||
@@ -827,6 +966,11 @@ function _update_min_padding!(sp::Subplot{GRBackend})
|
||||
sp.minpad = Tuple(dpi * [leftpad, toppad, rightpad, bottompad])
|
||||
end
|
||||
|
||||
function is_equally_spaced(v)
|
||||
d = collect(v[2:end] .- v[1:end-1])
|
||||
all(d .≈ d[1])
|
||||
end
|
||||
|
||||
function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
_update_min_padding!(sp)
|
||||
|
||||
@@ -837,7 +981,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
xy_lims = data_lims
|
||||
|
||||
ratio = sp[:aspect_ratio]
|
||||
ratio = get_aspect_ratio(sp)
|
||||
if ratio != :none
|
||||
if ratio == :equal
|
||||
ratio = 1
|
||||
@@ -890,7 +1034,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
leg_str = string(sp[:legend])
|
||||
if occursin("outer", leg_str)
|
||||
if occursin("right", leg_str)
|
||||
viewport_plotarea[2] -= legendw + 0.11
|
||||
viewport_plotarea[2] -= legendw + 0.12
|
||||
elseif occursin("left", leg_str)
|
||||
viewport_plotarea[1] += legendw + 0.11
|
||||
elseif occursin("top", leg_str)
|
||||
@@ -902,7 +1046,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# fill in the plot area background
|
||||
bg = plot_color(sp[:background_color_inside])
|
||||
gr_fill_viewport(viewport_plotarea, bg)
|
||||
is3d(sp) || gr_fill_viewport(viewport_plotarea, bg)
|
||||
|
||||
# reduced from before... set some flags based on the series in this subplot
|
||||
# TODO: can these be generic flags?
|
||||
@@ -916,11 +1060,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
if st == :pie
|
||||
draw_axes = false
|
||||
end
|
||||
if st == :heatmap
|
||||
if st in (:heatmap, :image)
|
||||
outside_ticks = true
|
||||
for ax in (sp[:xaxis], sp[:yaxis])
|
||||
v = series[ax[:letter]]
|
||||
end
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(series[:z]))
|
||||
xy_lims = x[1], x[end], y[1], y[end]
|
||||
expand_extrema!(sp[:xaxis], x)
|
||||
@@ -977,39 +1118,204 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.setlinewidth(sp.plt[:thickness_scaling])
|
||||
|
||||
if is3d(sp)
|
||||
# TODO do we really need a different clims computation here from the one
|
||||
# computed above using get_clims(sp)?
|
||||
zmin, zmax = gr_lims(sp, zaxis, true)
|
||||
clims3d = sp[:clims]
|
||||
if is_2tuple(clims3d)
|
||||
isfinite(clims3d[1]) && (zmin = clims3d[1])
|
||||
isfinite(clims3d[2]) && (zmax = clims3d[2])
|
||||
end
|
||||
zmin, zmax = axis_limits(sp, :z)
|
||||
GR.setspace(zmin, zmax, round.(Int, sp[:camera])...)
|
||||
xtick = GR.tick(xmin, xmax) / 2
|
||||
ytick = GR.tick(ymin, ymax) / 2
|
||||
ztick = GR.tick(zmin, zmax) / 2
|
||||
ticksize = 0.01 * (viewport_plotarea[2] - viewport_plotarea[1])
|
||||
|
||||
xticks, yticks, zticks, xaxis_segs, yaxis_segs, zaxis_segs, xtick_segs, ytick_segs, ztick_segs, xgrid_segs, ygrid_segs, zgrid_segs, xminorgrid_segs, yminorgrid_segs, zminorgrid_segs, xborder_segs, yborder_segs, zborder_segs = axis_drawing_info_3d(sp)
|
||||
|
||||
# fill the plot area
|
||||
gr_set_fill(sp[:background_color_inside])
|
||||
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 = unzip(GR.wc3towc.(plot_area_x, plot_area_y, plot_area_z))
|
||||
GR.fillarea(x_bg, y_bg)
|
||||
|
||||
# draw the grid lines
|
||||
if xaxis[:grid]
|
||||
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
|
||||
gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:gridalpha])
|
||||
GR.grid3d(xtick, 0, 0, xmin, ymax, zmin, 2, 0, 0)
|
||||
gr_polyline3d(coords(xgrid_segs)...)
|
||||
end
|
||||
if yaxis[:grid]
|
||||
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
|
||||
gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:gridalpha])
|
||||
GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0)
|
||||
gr_polyline3d(coords(ygrid_segs)...)
|
||||
end
|
||||
if zaxis[:grid]
|
||||
gr_set_line(zaxis[:gridlinewidth], zaxis[:gridstyle], zaxis[:foreground_color_grid])
|
||||
gr_set_transparency(zaxis[:foreground_color_grid], zaxis[:gridalpha])
|
||||
GR.grid3d(0, 0, ztick, xmin, ymax, zmin, 0, 0, 2)
|
||||
gr_polyline3d(coords(zgrid_segs)...)
|
||||
end
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_axis])
|
||||
gr_set_transparency(xaxis[:foreground_color_axis])
|
||||
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
|
||||
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
|
||||
|
||||
if xaxis[:minorgrid]
|
||||
gr_set_line(xaxis[:minorgridlinewidth], xaxis[:minorgridstyle], xaxis[:foreground_color_minor_grid])
|
||||
gr_set_transparency(xaxis[:foreground_color_minor_grid], xaxis[:minorgridalpha])
|
||||
gr_polyline3d(coords(xminorgrid_segs)...)
|
||||
end
|
||||
if yaxis[:minorgrid]
|
||||
gr_set_line(yaxis[:minorgridlinewidth], yaxis[:minorgridstyle], yaxis[:foreground_color_minor_grid])
|
||||
gr_set_transparency(yaxis[:foreground_color_minor_grid], yaxis[:minorgridalpha])
|
||||
gr_polyline3d(coords(yminorgrid_segs)...)
|
||||
end
|
||||
if zaxis[:minorgrid]
|
||||
gr_set_line(zaxis[:minorgridlinewidth], zaxis[:minorgridstyle], zaxis[:foreground_color_minor_grid])
|
||||
gr_set_transparency(zaxis[:foreground_color_minor_grid], zaxis[:minorgridalpha])
|
||||
gr_polyline3d(coords(zminorgrid_segs)...)
|
||||
end
|
||||
gr_set_transparency(1.0)
|
||||
|
||||
# axis lines
|
||||
if xaxis[:showaxis]
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_border])
|
||||
GR.setclip(0)
|
||||
gr_polyline3d(coords(xaxis_segs)...)
|
||||
end
|
||||
if yaxis[:showaxis]
|
||||
gr_set_line(1, :solid, yaxis[:foreground_color_border])
|
||||
GR.setclip(0)
|
||||
gr_polyline3d(coords(yaxis_segs)...)
|
||||
end
|
||||
if zaxis[:showaxis]
|
||||
gr_set_line(1, :solid, zaxis[:foreground_color_border])
|
||||
GR.setclip(0)
|
||||
gr_polyline3d(coords(zaxis_segs)...)
|
||||
end
|
||||
GR.setclip(1)
|
||||
|
||||
# axis ticks
|
||||
if xaxis[:showaxis]
|
||||
if sp[:framestyle] in (:zerolines, :grid)
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_grid])
|
||||
gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:tick_direction] == :out ? xaxis[:gridalpha] : 0)
|
||||
else
|
||||
gr_set_line(1, :solid, xaxis[:foreground_color_axis])
|
||||
end
|
||||
GR.setclip(0)
|
||||
gr_polyline3d(coords(xtick_segs)...)
|
||||
end
|
||||
if yaxis[:showaxis]
|
||||
if sp[:framestyle] in (:zerolines, :grid)
|
||||
gr_set_line(1, :solid, yaxis[:foreground_color_grid])
|
||||
gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:tick_direction] == :out ? yaxis[:gridalpha] : 0)
|
||||
else
|
||||
gr_set_line(1, :solid, yaxis[:foreground_color_axis])
|
||||
end
|
||||
GR.setclip(0)
|
||||
gr_polyline3d(coords(ytick_segs)...)
|
||||
end
|
||||
if zaxis[:showaxis]
|
||||
if sp[:framestyle] in (:zerolines, :grid)
|
||||
gr_set_line(1, :solid, zaxis[:foreground_color_grid])
|
||||
gr_set_transparency(zaxis[:foreground_color_grid], zaxis[:tick_direction] == :out ? zaxis[:gridalpha] : 0)
|
||||
else
|
||||
gr_set_line(1, :solid, zaxis[:foreground_color_axis])
|
||||
end
|
||||
GR.setclip(0)
|
||||
gr_polyline3d(coords(ztick_segs)...)
|
||||
end
|
||||
GR.setclip(1)
|
||||
|
||||
# tick marks
|
||||
if !(xticks in (:none, nothing, false)) && xaxis[:showaxis]
|
||||
# x labels
|
||||
gr_set_font(
|
||||
tickfont(xaxis),
|
||||
halign = (:left, :hcenter, :right)[sign(xaxis[:rotation]) + 2],
|
||||
valign = (xaxis[:mirror] ? :bottom : :top),
|
||||
rotation = xaxis[:rotation]
|
||||
)
|
||||
yt = if sp[:framestyle] == :origin
|
||||
0
|
||||
elseif xor(xaxis[:mirror], yaxis[:flip])
|
||||
ymax
|
||||
else
|
||||
ymin
|
||||
end
|
||||
zt = if sp[:framestyle] == :origin
|
||||
0
|
||||
elseif xor(xaxis[:mirror], zaxis[:flip])
|
||||
zmax
|
||||
else
|
||||
zmin
|
||||
end
|
||||
for (cv, dv) in zip(xticks...)
|
||||
xi, yi = gr_w3tondc(cv, yt, zt)
|
||||
xi += (yaxis[:mirror] ? 1 : -1) * 1e-2 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0)
|
||||
yi += (xaxis[:mirror] ? 1 : -1) * 5e-3 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0)
|
||||
gr_text(xi, yi, gr_tick_label(xaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
if !(yticks in (:none, nothing, false)) && yaxis[:showaxis]
|
||||
# y labels
|
||||
gr_set_font(
|
||||
tickfont(yaxis),
|
||||
halign = (:left, :hcenter, :right)[sign(yaxis[:rotation]) + 2],
|
||||
valign = (yaxis[:mirror] ? :bottom : :top),
|
||||
rotation = yaxis[:rotation]
|
||||
)
|
||||
xt = if sp[:framestyle] == :origin
|
||||
0
|
||||
elseif xor(yaxis[:mirror], xaxis[:flip])
|
||||
xmin
|
||||
else
|
||||
xmax
|
||||
end
|
||||
zt = if sp[:framestyle] == :origin
|
||||
0
|
||||
elseif xor(yaxis[:mirror], zaxis[:flip])
|
||||
zmax
|
||||
else
|
||||
zmin
|
||||
end
|
||||
for (cv, dv) in zip(yticks...)
|
||||
xi, yi = gr_w3tondc(xt, cv, zt)
|
||||
gr_text(xi + (yaxis[:mirror] ? -1 : 1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
yi + (yaxis[:mirror] ? 1 : -1) * 5e-3 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
gr_tick_label(yaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
if !(zticks in (:none, nothing, false)) && zaxis[:showaxis]
|
||||
# z labels
|
||||
gr_set_font(
|
||||
tickfont(zaxis),
|
||||
halign = (zaxis[:mirror] ? :left : :right),
|
||||
valign = (:top, :vcenter, :bottom)[sign(zaxis[:rotation]) + 2],
|
||||
rotation = zaxis[:rotation]
|
||||
)
|
||||
xt = if sp[:framestyle] == :origin
|
||||
0
|
||||
elseif xor(zaxis[:mirror], xaxis[:flip])
|
||||
xmax
|
||||
else
|
||||
xmin
|
||||
end
|
||||
yt = if sp[:framestyle] == :origin
|
||||
0
|
||||
elseif xor(zaxis[:mirror], yaxis[:flip])
|
||||
ymax
|
||||
else
|
||||
ymin
|
||||
end
|
||||
for (cv, dv) in zip(zticks...)
|
||||
xi, yi = gr_w3tondc(xt, yt, cv)
|
||||
gr_text(xi + (zaxis[:mirror] ? 1 : -1) * 1e-2 * (zaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
yi, gr_tick_label(zaxis, dv))
|
||||
end
|
||||
end
|
||||
#
|
||||
# # border
|
||||
# intensity = sp[:framestyle] == :semi ? 0.5 : 1.0
|
||||
# if sp[:framestyle] in (:box, :semi)
|
||||
# gr_set_line(intensity, :solid, xaxis[:foreground_color_border])
|
||||
# gr_set_transparency(xaxis[:foreground_color_border], intensity)
|
||||
# gr_polyline3d(coords(xborder_segs)...)
|
||||
# gr_set_line(intensity, :solid, yaxis[:foreground_color_border])
|
||||
# gr_set_transparency(yaxis[:foreground_color_border], intensity)
|
||||
# gr_polyline3d(coords(yborder_segs)...)
|
||||
# end
|
||||
|
||||
elseif ispolar(sp)
|
||||
r = gr_set_viewport_polar()
|
||||
@@ -1093,18 +1399,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# x labels
|
||||
flip, mirror = gr_set_xticks_font(sp)
|
||||
for (cv, dv) in zip(xticks...)
|
||||
# use xor ($) to get the right y coords
|
||||
xi, yi = GR.wctondc(cv, sp[:framestyle] == :origin ? 0 : xor(flip, mirror) ? ymax : ymin)
|
||||
# @show cv dv ymin xi yi flip mirror (flip $ mirror)
|
||||
if xaxis[:ticks] in (:auto, :native)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if xaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
elseif xaxis[:formatter] in (:scientific, :auto)
|
||||
dv = convert_sci_unicode(dv)
|
||||
end
|
||||
end
|
||||
gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0), string(dv))
|
||||
gr_text(xi, yi + (mirror ? 1 : -1) * 5e-3 * (xaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
gr_tick_label(xaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1112,18 +1409,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# y labels
|
||||
flip, mirror = gr_set_yticks_font(sp)
|
||||
for (cv, dv) in zip(yticks...)
|
||||
# use xor ($) to get the right y coords
|
||||
xi, yi = GR.wctondc(sp[:framestyle] == :origin ? 0 : xor(flip, mirror) ? xmax : xmin, cv)
|
||||
# @show cv dv xmin xi yi
|
||||
if yaxis[:ticks] in (:auto, :native)
|
||||
# ensure correct dispatch in gr_text for automatic log ticks
|
||||
if yaxis[:scale] in _logScales
|
||||
dv = string(dv, "\\ ")
|
||||
elseif yaxis[:formatter] in (:scientific, :auto)
|
||||
dv = convert_sci_unicode(dv)
|
||||
end
|
||||
end
|
||||
gr_text(xi + (mirror ? 1 : -1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0), yi, string(dv))
|
||||
gr_text(xi + (mirror ? 1 : -1) * 1e-2 * (yaxis[:tick_direction] == :out ? 1.5 : 1.0),
|
||||
yi,
|
||||
gr_tick_label(yaxis, dv))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1159,12 +1448,54 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
gr_text(xpos, viewport_subplot[4], sp[:title])
|
||||
end
|
||||
if is3d(sp)
|
||||
gr_set_font(guidefont(xaxis))
|
||||
GR.titles3d(xaxis[:guide], yaxis[:guide], zaxis[:guide])
|
||||
else
|
||||
xticks, yticks = axis_drawing_info(sp)[1:2]
|
||||
if xaxis[:guide] != ""
|
||||
h = 0.01 + gr_xaxis_height(sp)
|
||||
gr_set_font(
|
||||
guidefont(xaxis),
|
||||
halign = (:left, :hcenter, :right)[sign(xaxis[:rotation]) + 2],
|
||||
valign = (xaxis[:mirror] ? :bottom : :top),
|
||||
rotation = xaxis[:rotation]
|
||||
)
|
||||
yg = xor(xaxis[:mirror], yaxis[:flip]) ? ymax : ymin
|
||||
zg = xor(xaxis[:mirror], zaxis[:flip]) ? zmax : zmin
|
||||
xg = (xmin + xmax) / 2
|
||||
xndc, yndc = gr_w3tondc(xg, yg, zg)
|
||||
h = gr_axis_height(sp, xaxis)
|
||||
gr_text(xndc - h, yndc - h, xaxis[:guide])
|
||||
end
|
||||
|
||||
if yaxis[:guide] != ""
|
||||
gr_set_font(
|
||||
guidefont(yaxis),
|
||||
halign = (:left, :hcenter, :right)[sign(yaxis[:rotation]) + 2],
|
||||
valign = (yaxis[:mirror] ? :bottom : :top),
|
||||
rotation = yaxis[:rotation]
|
||||
)
|
||||
xg = xor(yaxis[:mirror], xaxis[:flip]) ? xmin : xmax
|
||||
yg = (ymin + ymax) / 2
|
||||
zg = xor(yaxis[:mirror], zaxis[:flip]) ? zmax : zmin
|
||||
xndc, yndc = gr_w3tondc(xg, yg, zg)
|
||||
h = gr_axis_height(sp, yaxis)
|
||||
gr_text(xndc + h, yndc - h, yaxis[:guide])
|
||||
end
|
||||
|
||||
if zaxis[:guide] != ""
|
||||
gr_set_font(
|
||||
guidefont(zaxis),
|
||||
halign = (:left, :hcenter, :right)[sign(zaxis[:rotation]) + 2],
|
||||
valign = (zaxis[:mirror] ? :bottom : :top),
|
||||
rotation = zaxis[:rotation]
|
||||
)
|
||||
xg = xor(zaxis[:mirror], xaxis[:flip]) ? xmax : xmin
|
||||
yg = xor(zaxis[:mirror], yaxis[:flip]) ? ymax : ymin
|
||||
zg = (zmin + zmax) / 2
|
||||
xndc, yndc = gr_w3tondc(xg, yg, zg)
|
||||
w = gr_axis_width(sp, zaxis)
|
||||
GR.setcharup(-1, 0)
|
||||
gr_text(xndc - w, yndc, zaxis[:guide])
|
||||
end
|
||||
else
|
||||
if xaxis[:guide] != ""
|
||||
h = 0.01 + gr_axis_height(sp, xaxis)
|
||||
gr_set_font(guidefont(xaxis))
|
||||
if xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
|
||||
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
|
||||
@@ -1176,7 +1507,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
|
||||
if yaxis[:guide] != ""
|
||||
w = 0.02 + gr_yaxis_width(sp)
|
||||
w = 0.02 + gr_axis_width(sp, yaxis)
|
||||
gr_set_font(guidefont(yaxis))
|
||||
GR.setcharup(-1, 0)
|
||||
if yaxis[:guide_position] == :right || (yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
|
||||
@@ -1275,7 +1606,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
elseif st == :contour
|
||||
GR.setspace(clims[1], clims[2], 0, 90)
|
||||
GR.setlinetype(gr_linetype[get_linestyle(series)])
|
||||
GR.setlinewidth(max(0, get_linewidth(series) / (sum(gr_plot_size) * 0.001)))
|
||||
GR.setlinewidth(max(0, get_linewidth(series)) / nominal_size())
|
||||
is_lc_black = let black=plot_color(:black)
|
||||
plot_color(series[:linecolor]) in (black,[black])
|
||||
end
|
||||
@@ -1318,9 +1649,24 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(series[:z]))
|
||||
w, h = length(x) - 1, length(y) - 1
|
||||
z_normalized = map(x -> GR.jlgr.normalize_color(x, zmin, zmax), z)
|
||||
z_normalized = map(x -> isnan(x) ? 256/255 : x, z_normalized) # results in color index = 1256 -> transparent
|
||||
colors = Int32[round(Int32, 1000 + _i * 255) for _i in z_normalized]
|
||||
GR.nonuniformcellarray(x, y, w, h, colors)
|
||||
if is_uniformly_spaced(x) && is_uniformly_spaced(y)
|
||||
# For uniformly spaced data use GR.drawimage, which can be
|
||||
# much faster than GR.nonuniformcellarray, especially for
|
||||
# pdf output, and also supports alpha values.
|
||||
# Note that drawimage draws uniformly spaced data correctly
|
||||
# even on log scales, where it is visually non-uniform.
|
||||
fillgrad = _as_gradient(series[:fillcolor])
|
||||
colors = plot_color.(fillgrad[z_normalized], series[:fillalpha])
|
||||
colors[isnan.(z_normalized)] .= RGBA(0,0,0,0)
|
||||
rgba = gr_color.(colors)
|
||||
GR.drawimage(first(x), last(x), last(y), first(y), w, h, rgba)
|
||||
else
|
||||
(something(series[:fillalpha],1) < 1 || any(_gr_gradient_alpha .< 1)) && @warn(
|
||||
"GR: transparency not supported in non-uniform heatmaps. Alpha values ignored.")
|
||||
z_normalized = map(x -> isnan(x) ? 256/255 : x, z_normalized) # results in color index = 1256 -> transparent
|
||||
colors = Int32[round(Int32, 1000 + _i * 255) for _i in z_normalized]
|
||||
GR.nonuniformcellarray(x, y, w, h, colors)
|
||||
end
|
||||
else
|
||||
phimin, phimax = 0.0, 360.0 # nonuniform polar array is not yet supported in GR.jl
|
||||
nx, ny = length(series[:x]), length(series[:y])
|
||||
@@ -1437,17 +1783,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st == :image
|
||||
z = transpose_z(series, series[:z].surf, true)'
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(z))
|
||||
w, h = size(z)
|
||||
xmin, xmax = ignorenan_extrema(series[:x]); ymin, ymax = ignorenan_extrema(series[:y])
|
||||
if eltype(z) <: Colors.AbstractGray
|
||||
grey = round.(UInt8, clamp.(float(z) * 255, 0, 255))
|
||||
rgba = map(c -> UInt32( 0xff000000 + UInt(c)<<16 + UInt(c)<<8 + UInt(c) ), grey)
|
||||
else
|
||||
rgba = map(c -> 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)) ), z)
|
||||
end
|
||||
xmin, xmax = ignorenan_extrema(x)
|
||||
ymin, ymax = ignorenan_extrema(y)
|
||||
rgba = gr_color.(z)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
||||
end
|
||||
|
||||
@@ -1549,7 +1889,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
for ann in sp[:annotations]
|
||||
x, y, val = locate_annotation(sp, ann...)
|
||||
x, y = if is3d(sp)
|
||||
# GR.wc3towc(x, y, z)
|
||||
gr_w3tondc(x, y, z)
|
||||
else
|
||||
GR.wctondc(x, y)
|
||||
end
|
||||
|
||||
@@ -109,7 +109,7 @@ _hdf5_datapath(subpath::String) = "$_hdf5_dataroot/$subpath"
|
||||
_hdf5_map_str2telem(k::String) = HDF5PLOT_MAP_STR2TELEM[k]
|
||||
_hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
|
||||
|
||||
function _hdf5_merge!(dest::Dict, src::Dict)
|
||||
function _hdf5_merge!(dest, src)
|
||||
for (k, v) in src
|
||||
if isa(v, Axis)
|
||||
_hdf5_merge!(dest[k].plotattributes, v.plotattributes)
|
||||
@@ -295,7 +295,7 @@ function _hdf5plot_gwrite(grp, k::String, v::Tuple)
|
||||
end
|
||||
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::Dict)
|
||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::AKW)
|
||||
# @warn("Cannot write dict: $k=$plotattributes")
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::AbstractRange)
|
||||
@@ -376,7 +376,7 @@ function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
||||
_hdf5plot_writetype(grp, Subplot)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_write(grp, plotattributes::Dict)
|
||||
function _hdf5plot_write(grp, plotattributes::AKW)
|
||||
for (k, v) in plotattributes
|
||||
kstr = string(k)
|
||||
_hdf5plot_gwrite(grp, kstr, v)
|
||||
@@ -567,7 +567,7 @@ function _hdf5plot_read(grp, k::String)
|
||||
end
|
||||
|
||||
#Read in values in group to populate plotattributes:
|
||||
function _hdf5plot_read(grp, plotattributes::Dict)
|
||||
function _hdf5plot_read(grp, plotattributes::AKW)
|
||||
gnames = names(grp)
|
||||
for k in gnames
|
||||
try
|
||||
|
||||
+10
-1
@@ -12,4 +12,13 @@ function plotlybase_syncplot(plt::Plot)
|
||||
return plt.o
|
||||
end
|
||||
|
||||
_show(io::IO, ::MIME{Symbol("image/png")}, plt::Plot{PlotlyBackend}) = ORCA.PlotlyBase.savefig(io, plotlybase_syncplot(plt), format = "png")
|
||||
const _orca_mimeformats = Dict(
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"image/svg+xml" => "svg",
|
||||
"image/eps" => "eps",
|
||||
)
|
||||
|
||||
for (mime, fmt) in _orca_mimeformats
|
||||
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyBackend}) = ORCA.PlotlyBase.savefig(io, plotlybase_syncplot(plt), format = $fmt)
|
||||
end
|
||||
|
||||
+12
-12
@@ -180,11 +180,11 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
end
|
||||
|
||||
# PGFPlots can't handle non-Vector?
|
||||
args = map(a -> if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
collect(a)
|
||||
else
|
||||
a
|
||||
end, args)
|
||||
# args = map(a -> if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
# collect(a)
|
||||
# else
|
||||
# a
|
||||
# end, args)
|
||||
|
||||
if st in (:contour, :histogram2d)
|
||||
style = []
|
||||
@@ -470,7 +470,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
|
||||
end
|
||||
|
||||
if sp[:aspect_ratio] in (1, :equal)
|
||||
if get_aspect_ratio(sp) in (1, :equal)
|
||||
kw[:axisEqual] = "true"
|
||||
end
|
||||
|
||||
@@ -482,12 +482,12 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
||||
|
||||
push!(style, string(
|
||||
"legend style = {",
|
||||
pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], fg_alpha, "solid", ), ",",
|
||||
"fill = $cstr,",
|
||||
"fill opacity = $bg_alpha,",
|
||||
"text opacity = $(alpha(plot_color(sp[:legendfontcolor]))),",
|
||||
"font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
|
||||
"legend style = {",
|
||||
pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], fg_alpha, "solid", ), ",",
|
||||
"fill = $cstr,",
|
||||
"fill opacity = $bg_alpha,",
|
||||
"text opacity = $(alpha(plot_color(sp[:legendfontcolor]))),",
|
||||
"font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
|
||||
"}",
|
||||
))
|
||||
|
||||
|
||||
+624
-376
File diff suppressed because it is too large
Load Diff
@@ -108,7 +108,7 @@ function shrink_by(lo, sz, ratio)
|
||||
end
|
||||
|
||||
function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
|
||||
aspect_ratio = sp[:aspect_ratio]
|
||||
aspect_ratio = get_aspect_ratio(sp)
|
||||
if aspect_ratio != :none
|
||||
if aspect_ratio == :equal
|
||||
aspect_ratio = 1.0
|
||||
@@ -351,7 +351,7 @@ end
|
||||
|
||||
|
||||
function plotly_colorscale(grad::ColorGradient, α)
|
||||
[[grad.values[i], rgba_string(plot_color(grad.colors[i], α))] for i in 1:length(grad.colors)]
|
||||
[[grad.values[i], rgba_string(plot_color(grad.colors[i], α))] for i in eachindex(grad.colors)]
|
||||
end
|
||||
plotly_colorscale(c::Colorant,α) = plotly_colorscale(_as_gradient(c),α)
|
||||
function plotly_colorscale(c::AbstractVector{<:RGBA}, α)
|
||||
@@ -392,7 +392,7 @@ end
|
||||
# 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=1:length(xs)
|
||||
for i=eachindex(xs)
|
||||
shape = Shape(xs[i], ys[i])
|
||||
xs[i], ys[i] = coords(shape)
|
||||
end
|
||||
@@ -415,6 +415,7 @@ function plotly_data(series::Series, letter::Symbol, data)
|
||||
end
|
||||
end
|
||||
plotly_data(v) = v !== nothing ? collect(v) : v
|
||||
plotly_data(v::AbstractArray) = v
|
||||
plotly_data(surf::Surface) = surf.surf
|
||||
plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v)
|
||||
|
||||
@@ -779,9 +780,9 @@ end
|
||||
|
||||
function plotly_hover!(plotattributes_out::KW, hover)
|
||||
# hover text
|
||||
if hover in (:none, false)
|
||||
if hover === nothing || all(in([:none, false]), hover)
|
||||
plotattributes_out[:hoverinfo] = "none"
|
||||
elseif hover !== nothing
|
||||
elseif any(!isnothing, hover)
|
||||
plotattributes_out[:hoverinfo] = "text"
|
||||
plotattributes_out[:text] = hover
|
||||
end
|
||||
@@ -859,6 +860,8 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot{PlotlyBackend}) = true
|
||||
|
||||
function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{PlotlyBackend})
|
||||
plotly_show_js(io, plot)
|
||||
end
|
||||
|
||||
@@ -52,3 +52,5 @@ function closeall(::PlotlyJSBackend)
|
||||
close(current().o)
|
||||
end
|
||||
end
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot{PlotlyJSBackend}) = true
|
||||
|
||||
@@ -187,7 +187,7 @@ end
|
||||
# end
|
||||
|
||||
function get_locator_and_formatter(vals::AVec)
|
||||
pyticker."FixedLocator"(1:length(vals)), pyticker."FixedFormatter"(vals)
|
||||
pyticker."FixedLocator"(eachindex(vals)), pyticker."FixedFormatter"(vals)
|
||||
end
|
||||
|
||||
function add_pyfixedformatter(cbar, vals::AVec)
|
||||
@@ -535,7 +535,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
shapes = series[:markershape]
|
||||
msc = py_markerstrokecolor(series)
|
||||
lw = py_thickness_scale(plt, series[:markerstrokewidth])
|
||||
for i=1:length(y)
|
||||
for i=eachindex(y)
|
||||
extrakw[:c] = _cycle(markercolor, i)
|
||||
|
||||
push!(handle, ax."scatter"(_cycle(x,i), _cycle(y,i);
|
||||
@@ -564,7 +564,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
delete!(extrakw, :c)
|
||||
|
||||
for i=1:length(y)
|
||||
for i=eachindex(y)
|
||||
cur_marker = py_marker(_cycle(shapes,i))
|
||||
|
||||
if ( cur_marker == prev_marker )
|
||||
@@ -1013,7 +1013,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
kw = KW()
|
||||
if !isempty(sp[:zaxis][:discrete_values]) && colorbar_series[:seriestype] == :heatmap
|
||||
locator, formatter = get_locator_and_formatter(sp[:zaxis][:discrete_values])
|
||||
# kw[:values] = 1:length(sp[:zaxis][:discrete_values])
|
||||
# kw[:values] = eachindex(sp[:zaxis][:discrete_values])
|
||||
kw[:values] = sp[:zaxis][:continuous_values]
|
||||
kw[:ticks] = locator
|
||||
kw[:format] = formatter
|
||||
@@ -1155,7 +1155,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
end
|
||||
|
||||
# aspect ratio
|
||||
aratio = sp[:aspect_ratio]
|
||||
aratio = get_aspect_ratio(sp)
|
||||
if aratio != :none
|
||||
ax."set_aspect"(isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
|
||||
end
|
||||
|
||||
@@ -92,7 +92,7 @@ end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, plotattributes::KW, addlegend::Bool, xlim, ylim)
|
||||
function addUnicodeSeries!(o, plotattributes, addlegend::Bool, xlim, ylim)
|
||||
# get the function, or special handling for step/bar/hist
|
||||
st = plotattributes[:seriestype]
|
||||
if st == :histogram2d
|
||||
|
||||
+6
-52
@@ -187,7 +187,7 @@ end
|
||||
function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(sx)
|
||||
for i=eachindex(sx)
|
||||
sx[i] = (sx[i] - cx) * x + cx
|
||||
sy[i] = (sy[i] - cy) * y + cy
|
||||
end
|
||||
@@ -202,7 +202,7 @@ end
|
||||
"translate a Shape in space"
|
||||
function translate!(shape::Shape, x::Real, y::Real = x)
|
||||
sx, sy = coords(shape)
|
||||
for i=1:length(sx)
|
||||
for i=eachindex(sx)
|
||||
sx[i] += x
|
||||
sy[i] += y
|
||||
end
|
||||
@@ -230,7 +230,7 @@ end
|
||||
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
||||
x, y = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(x)
|
||||
for i=eachindex(x)
|
||||
xi = rotate_x(x[i], y[i], Θ, cx, cy)
|
||||
yi = rotate_y(x[i], y[i], Θ, cx, cy)
|
||||
x[i], y[i] = xi, yi
|
||||
@@ -591,7 +591,9 @@ annotations(sa::SeriesAnnotations) = sa
|
||||
function process_annotation(sp::Subplot, xs, ys, labs, font = font())
|
||||
anns = []
|
||||
labs = makevec(labs)
|
||||
for i in 1:max(length(xs), length(ys), length(labs))
|
||||
xlength = length(methods(length, (typeof(xs),))) == 0 ? 1 : length(xs)
|
||||
ylength = length(methods(length, (typeof(ys),))) == 0 ? 1 : length(ys)
|
||||
for i in 1:max(xlength, ylength, length(labs))
|
||||
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
|
||||
if lab == :auto
|
||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
@@ -648,23 +650,6 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
abstract type AbstractSurface end
|
||||
|
||||
"represents a contour or surface mesh"
|
||||
struct Surface{M<:AMat} <: AbstractSurface
|
||||
surf::M
|
||||
end
|
||||
|
||||
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for yi in y, xi in x])
|
||||
|
||||
Base.Array(surf::Surface) = surf.surf
|
||||
|
||||
for f in (:length, :size)
|
||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||
end
|
||||
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
||||
Base.eltype(surf::Surface{T}) where {T} = eltype(T)
|
||||
|
||||
function expand_extrema!(a::Axis, surf::Surface)
|
||||
ex = a[:extrema]
|
||||
for vi in surf.surf
|
||||
@@ -684,30 +669,6 @@ end
|
||||
# # I don't want to clash with ValidatedNumerics, but this would be nice:
|
||||
# ..(a::T, b::T) = (a,b)
|
||||
|
||||
struct Volume{T}
|
||||
v::Array{T,3}
|
||||
x_extents::Tuple{T,T}
|
||||
y_extents::Tuple{T,T}
|
||||
z_extents::Tuple{T,T}
|
||||
end
|
||||
|
||||
default_extents(::Type{T}) where {T} = (zero(T), one(T))
|
||||
|
||||
function Volume(v::Array{T,3},
|
||||
x_extents = default_extents(T),
|
||||
y_extents = default_extents(T),
|
||||
z_extents = default_extents(T)) where T
|
||||
Volume(v, x_extents, y_extents, z_extents)
|
||||
end
|
||||
|
||||
Base.Array(vol::Volume) = vol.v
|
||||
for f in (:length, :size)
|
||||
@eval Base.$f(vol::Volume, args...) = $f(vol.v, args...)
|
||||
end
|
||||
Base.copy(vol::Volume{T}) where {T} = Volume{T}(copy(vol.v), vol.x_extents, vol.y_extents, vol.z_extents)
|
||||
Base.eltype(vol::Volume{T}) where {T} = T
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# style is :open or :closed (for now)
|
||||
struct Arrow
|
||||
@@ -769,13 +730,6 @@ function add_arrows(func::Function, x::AVec, y::AVec)
|
||||
end
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
"Represents data values with formatting that should apply to the tick labels."
|
||||
struct Formatted{T}
|
||||
data::T
|
||||
formatter::Function
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
"create a BezierCurve for plotting"
|
||||
|
||||
+911
-521
File diff suppressed because it is too large
Load Diff
+25
-8
@@ -464,12 +464,12 @@ end
|
||||
# constructors
|
||||
|
||||
# pass the layout arg through
|
||||
function layout_args(plotattributes::KW)
|
||||
layout_args(get(plotattributes, :layout, default(:layout)))
|
||||
function layout_args(plotattributes::AKW)
|
||||
layout_args(plotattributes[:layout])
|
||||
end
|
||||
|
||||
function layout_args(plotattributes::KW, n_override::Integer)
|
||||
layout, n = layout_args(get(plotattributes, :layout, n_override))
|
||||
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.")
|
||||
end
|
||||
@@ -481,12 +481,27 @@ function layout_args(n::Integer)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args(sztup::NTuple{2,I}) where I<:Integer
|
||||
function layout_args(sztup::NTuple{2, Integer})
|
||||
nr, nc = sztup
|
||||
GridLayout(nr, nc), nr*nc
|
||||
end
|
||||
|
||||
function layout_args(sztup::NTuple{3,I}) where I<:Integer
|
||||
layout_args(n_override::Integer, n::Integer) = layout_args(n)
|
||||
layout_args(n, sztup::NTuple{2, Integer}) = layout_args(sztup)
|
||||
|
||||
function layout_args(n, sztup::Tuple{Colon, Integer})
|
||||
nc = sztup[2]
|
||||
nr = ceil(Int, n / nc)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args(n, sztup::Tuple{Integer, Colon})
|
||||
nr = sztup[1]
|
||||
nc = ceil(Int, n / nr)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args(sztup::NTuple{3, Integer})
|
||||
n, nr, nc = sztup
|
||||
nr, nc = compute_gridsize(n, nr, nc)
|
||||
GridLayout(nr, nc), n
|
||||
@@ -499,6 +514,8 @@ function layout_args(layout::GridLayout)
|
||||
layout, n
|
||||
end
|
||||
|
||||
layout_args(n_override::Integer, layout::GridLayout) = layout_args(layout)
|
||||
|
||||
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
|
||||
|
||||
|
||||
@@ -599,7 +616,7 @@ end
|
||||
# ----------------------------------------------------------------------
|
||||
# @layout macro
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
|
||||
function add_layout_pct!(kw::AKW, v::Expr, idx::Integer, nidx::Integer)
|
||||
# dump(v)
|
||||
# something like {0.2w}?
|
||||
if v.head == :call && v.args[1] == :*
|
||||
@@ -620,7 +637,7 @@ function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
|
||||
error("Couldn't match layout curly (idx=$idx): $v")
|
||||
end
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Number, idx::Integer)
|
||||
function add_layout_pct!(kw::AKW, v::Number, idx::Integer)
|
||||
# kw[idx == 1 ? :w : :h] = v*pct
|
||||
idx == 1 && (kw[:w] = v*pct)
|
||||
(idx == 2 || nidx == 1) && (kw[:h] = v*pct)
|
||||
|
||||
+9
-1
@@ -60,6 +60,13 @@ function html(plt::Plot, fn::AbstractString)
|
||||
end
|
||||
html(fn::AbstractString) = html(current(), fn)
|
||||
|
||||
function txt(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "txt")
|
||||
io = open(fn, "w")
|
||||
show(io, MIME("text/plain"), plt)
|
||||
close(io)
|
||||
end
|
||||
txt(fn::AbstractString) = txt(current(), fn)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -73,6 +80,7 @@ const _savemap = Dict(
|
||||
"tex" => tex,
|
||||
"html" => html,
|
||||
"tikz" => tex,
|
||||
"txt" => txt,
|
||||
)
|
||||
|
||||
function getExtension(fn::AbstractString)
|
||||
@@ -262,6 +270,6 @@ function _showjuno(io::IO, m::MIME"image/svg+xml", plt)
|
||||
end
|
||||
end
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot) = showable(MIME"text/html"(), plt)
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot) = false
|
||||
|
||||
_showjuno(io::IO, m, plt) = _show(io, m, plt)
|
||||
|
||||
+30
-129
@@ -1,18 +1,26 @@
|
||||
function finalize_subplot!(plt::Plot, st, plotattributes::AKW)
|
||||
sp = _prepare_subplot(plt, plotattributes)
|
||||
_prepare_annotations(sp, plotattributes)
|
||||
_expand_subplot_extrema(sp, plotattributes, st)
|
||||
_update_series_attributes!(plotattributes, plt, sp)
|
||||
_add_the_series(plt, sp, plotattributes)
|
||||
end
|
||||
|
||||
|
||||
# Override the RecipesPipeline `is_st_supported` for Plots.
|
||||
is_st_supported(::Plot, st::Symbol) = is_seriestype_supported(st)
|
||||
# ------------------------------------------------------------------
|
||||
# preprocessing
|
||||
|
||||
function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
function series_idx(kw_list::AVec{KW}, kw::AKW)
|
||||
Int(kw[:series_plotindex]) - Int(kw_list[1][:series_plotindex]) + 1
|
||||
end
|
||||
|
||||
function _expand_seriestype_array(plotattributes::KW, args)
|
||||
function _expand_seriestype_array(plotattributes::AKW, args)
|
||||
sts = get(plotattributes, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(plotattributes, :seriestype)
|
||||
reset_kw!(plotattributes, :seriestype)
|
||||
rd = Vector{RecipeData}(undef, size(sts, 1))
|
||||
for r in 1:size(sts, 1)
|
||||
for r in axes(sts, 1)
|
||||
dc = copy(plotattributes)
|
||||
dc[:seriestype] = sts[r:r,:]
|
||||
rd[r] = RecipeData(dc, args)
|
||||
@@ -23,7 +31,7 @@ function _expand_seriestype_array(plotattributes::KW, args)
|
||||
end
|
||||
end
|
||||
|
||||
function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{RecipeData})
|
||||
function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{RecipeData})
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
@@ -42,9 +50,11 @@ function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{Rec
|
||||
for (k,v) in plotattributes
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
_axis_defaults_byletter[:x],
|
||||
_axis_defaults_byletter[:y],
|
||||
_axis_defaults_byletter[:z])
|
||||
if haskey(defdict, k)
|
||||
delete!(plotattributes, k)
|
||||
reset_kw!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -57,39 +67,6 @@ end
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(plotattributes, args, still_to_process)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a stack of series that still need to be processed.
|
||||
# each pass through the loop, we pop one off and apply the recipe.
|
||||
# the recipe will return a list a Series objects... the ones that are
|
||||
# finished (no more args) get added to the kw_list, the ones that are not
|
||||
# are placed on top of the stack and are then processed further.
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
# grab the first in line to be processed and either add it to the kw_list or
|
||||
# pass it through apply_recipe to generate a list of RecipeData objects (data + attributes)
|
||||
# for further processing.
|
||||
next_series = popfirst!(still_to_process)
|
||||
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
|
||||
if !(typeof(next_series) <: RecipeData)
|
||||
error("Inputs couldn't be processed... expected RecipeData but got: $next_series")
|
||||
end
|
||||
if isempty(next_series.args)
|
||||
_process_userrecipe(plt, kw_list, next_series)
|
||||
else
|
||||
rd_list = RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...)
|
||||
prepend!(still_to_process,rd_list)
|
||||
end
|
||||
end
|
||||
|
||||
# don't allow something else to handle it
|
||||
plotattributes[:smooth] = false
|
||||
kw_list
|
||||
end
|
||||
|
||||
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
@@ -108,7 +85,7 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD
|
||||
return
|
||||
end
|
||||
|
||||
function _preprocess_userrecipe(kw::KW)
|
||||
function _preprocess_userrecipe(kw::AKW)
|
||||
_add_markershape(kw)
|
||||
|
||||
# if there was a grouping, filter the data here
|
||||
@@ -132,7 +109,7 @@ function _preprocess_userrecipe(kw::KW)
|
||||
return
|
||||
end
|
||||
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
# handle error bars by creating new recipedata data... these will have
|
||||
# the same recipedata index as the recipedata they are copied from
|
||||
for esym in (:xerror, :yerror)
|
||||
@@ -147,7 +124,7 @@ function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
# handle smoothing by adding a new series
|
||||
if get(kw, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
@@ -166,44 +143,11 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# plot recipes
|
||||
|
||||
# Grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes).
|
||||
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
|
||||
# otherwise we just add the original KW.
|
||||
function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_process::Vector{KW})
|
||||
if !isa(get(kw, :seriestype, nothing), Symbol)
|
||||
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
|
||||
push!(kw_list, kw)
|
||||
return
|
||||
end
|
||||
try
|
||||
st = kw[:seriestype]
|
||||
st = kw[:seriestype] = get(_typeAliases, st, st)
|
||||
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
|
||||
for data in datalist
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.plotattributes)")
|
||||
end
|
||||
push!(still_to_process, data.plotattributes)
|
||||
end
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
push!(kw_list, kw)
|
||||
else
|
||||
rethrow()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
|
||||
function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (plotattributes[k] = pop!(kw, k))
|
||||
@@ -252,7 +196,7 @@ function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
plt[:inset_subplots] = nothing
|
||||
end
|
||||
|
||||
function _subplot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# we'll keep a map of subplot to an attribute override dict.
|
||||
# Subplot/Axis attributes set by a user/series recipe apply only to the
|
||||
# Subplot object which they belong to.
|
||||
@@ -261,16 +205,16 @@ function _subplot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
for kw in kw_list
|
||||
# get the Subplot object to which the series belongs.
|
||||
sps = get(kw, :subplot, :auto)
|
||||
sp = get_subplot(plt, _cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
|
||||
sp = get_subplot(plt, _cycle(sps == :auto ? plt.subplots : plt.subplots[sps], series_idx(kw_list,kw)))
|
||||
kw[:subplot] = sp
|
||||
|
||||
# extract subplot/axis attributes from kw and add to sp_attr
|
||||
attr = KW()
|
||||
for (k,v) in collect(kw)
|
||||
if haskey(_subplot_defaults, k) || haskey(_axis_defaults_byletter, k)
|
||||
if is_subplot_attr(k) || is_axis_attr(k)
|
||||
attr[k] = pop!(kw, k)
|
||||
end
|
||||
if haskey(_axis_defaults, k)
|
||||
if is_axis_attr_noletter(k)
|
||||
v = pop!(kw, k)
|
||||
for letter in (:x,:y,:z)
|
||||
attr[Symbol(letter,k)] = v
|
||||
@@ -303,7 +247,7 @@ end
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
# that might have been added during series recipes
|
||||
function _prepare_subplot(plt::Plot{T}, plotattributes::KW) where T
|
||||
function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where T
|
||||
st::Symbol = plotattributes[:seriestype]
|
||||
sp::Subplot{T} = plotattributes[:subplot]
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
@@ -327,7 +271,7 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# series types
|
||||
|
||||
function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
function _override_seriestype_check(plotattributes::AKW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !is3d(st) && !(st in (:contour,:contour3d))
|
||||
z = plotattributes[:z]
|
||||
@@ -339,7 +283,7 @@ function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
st
|
||||
end
|
||||
|
||||
function _prepare_annotations(sp::Subplot, plotattributes::KW)
|
||||
function _prepare_annotations(sp::Subplot, plotattributes::AKW)
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
@@ -356,7 +300,7 @@ function _prepare_annotations(sp::Subplot, plotattributes::KW)
|
||||
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
|
||||
end
|
||||
|
||||
function _expand_subplot_extrema(sp::Subplot, plotattributes::KW, st::Symbol)
|
||||
function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol)
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
xmin, xmax = ignorenan_extrema(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y])
|
||||
@@ -380,46 +324,3 @@ function _add_the_series(plt, sp, plotattributes)
|
||||
push!(sp.series_list, series)
|
||||
_series_added(plt, series)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _process_seriesrecipe(plt::Plot, plotattributes::KW)
|
||||
# replace seriestype aliases
|
||||
st = Symbol(plotattributes[:seriestype])
|
||||
st = plotattributes[:seriestype] = get(_typeAliases, st, st)
|
||||
|
||||
# shapes shouldn't have fillrange set
|
||||
if plotattributes[:seriestype] == :shape
|
||||
plotattributes[:fillrange] = nothing
|
||||
end
|
||||
|
||||
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
|
||||
if is_seriestype_supported(st)
|
||||
sp = _prepare_subplot(plt, plotattributes)
|
||||
_prepare_annotations(sp, plotattributes)
|
||||
_expand_subplot_extrema(sp, plotattributes, st)
|
||||
_update_series_attributes!(plotattributes, plt, sp)
|
||||
_add_the_series(plt, sp, plotattributes)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z])
|
||||
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
if isa(data, RecipeData)
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
|
||||
end
|
||||
_process_seriesrecipe(plt, data.plotattributes)
|
||||
else
|
||||
@warn("Unhandled recipe: $(data)")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
nothing
|
||||
end
|
||||
|
||||
+22
-61
@@ -54,7 +54,7 @@ function plot(args...; kw...)
|
||||
# create an empty Plot then process
|
||||
plt = Plot()
|
||||
# plt.user_attr = plotattributes
|
||||
_plot!(plt, plotattributes, args)
|
||||
recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
|
||||
end
|
||||
|
||||
# build a new plot from existing plots
|
||||
@@ -96,7 +96,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
|
||||
series_attr = KW()
|
||||
for (k,v) in plotattributes
|
||||
if haskey(_series_defaults, k)
|
||||
if is_series_attr(k)
|
||||
series_attr[k] = pop!(plotattributes,k)
|
||||
end
|
||||
end
|
||||
@@ -119,7 +119,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
sp.attr[:subplot_index] = idx
|
||||
for series in serieslist
|
||||
merge!(series.plotattributes, series_attr)
|
||||
_add_defaults!(series.plotattributes, plt, sp, cmdidx)
|
||||
_slice_series_args!(series.plotattributes, plt, sp, cmdidx)
|
||||
push!(plt.series_list, series)
|
||||
_series_added(plt, series)
|
||||
cmdidx += 1
|
||||
@@ -155,7 +155,7 @@ function plot!(plt::Plot, args...; kw...)
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
# merge!(plt.user_attr, plotattributes)
|
||||
_plot!(plt, plotattributes, args)
|
||||
recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
@@ -163,79 +163,41 @@ end
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
|
||||
## here we implement the recipe interface
|
||||
function _recipe_init!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
plotattributes[:plot_object] = plt
|
||||
|
||||
if !isempty(args) && !isdefined(Main, :StatsPlots) &&
|
||||
first(split(string(typeof(args[1])), ".")) == "DataFrames"
|
||||
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots")
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
# "USER RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
kw_list = _process_userrecipes(plt, plotattributes, args)
|
||||
|
||||
# @info(1)
|
||||
# map(DD, kw_list)
|
||||
|
||||
|
||||
# --------------------------------
|
||||
# "PLOT RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# "plot recipe", which acts like a series type, and is processed before
|
||||
# the plot layout is created, which allows for setting layouts and other plot-wide attributes.
|
||||
# we get inputs which have been fully processed by "user recipes" and "type recipes",
|
||||
# so we can expect standard vectors, surfaces, etc. No defaults have been set yet.
|
||||
still_to_process = kw_list
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
next_kw = popfirst!(still_to_process)
|
||||
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
|
||||
end
|
||||
|
||||
# @info(2)
|
||||
# map(DD, kw_list)
|
||||
|
||||
function _recipe_after_plot!(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# --------------------------------
|
||||
# Plot/Subplot/Layout setup
|
||||
# --------------------------------
|
||||
_plot_setup(plt, plotattributes, kw_list)
|
||||
_subplot_setup(plt, plotattributes, kw_list)
|
||||
end
|
||||
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
|
||||
function _recipe_after_user!(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# don't allow something else to handle it
|
||||
plotattributes[:smooth] = false
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
# "SERIES RECIPES"
|
||||
# --------------------------------
|
||||
function _recipe_before_series!(plt::Plot, kw, kw_list)
|
||||
sp::Subplot = kw[:subplot]
|
||||
|
||||
# @info(3)
|
||||
# map(DD, kw_list)
|
||||
# in series attributes given as vector with one element per series,
|
||||
# select the value for current series
|
||||
_slice_series_args!(kw, plt, sp, series_idx(kw_list,kw))
|
||||
|
||||
for kw in kw_list
|
||||
sp::Subplot = kw[:subplot]
|
||||
# idx = get_subplot_index(plt, sp)
|
||||
|
||||
# # we update subplot args in case something like the color palatte is part of the recipe
|
||||
# _update_subplot_args(plt, sp, kw, idx, true)
|
||||
|
||||
# set default values, select from attribute cycles, and generally set the final attributes
|
||||
_add_defaults!(kw, plt, sp, command_idx(kw_list,kw))
|
||||
|
||||
# now we have a fully specified series, with colors chosen. we must recursively handle
|
||||
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
|
||||
# we check for a recipe that will convert that series type into one made up of lower-level components.
|
||||
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
|
||||
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
|
||||
# be able to support step, bar, and histogram plots (and any recipes that use those components).
|
||||
_process_seriesrecipe(plt, kw)
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
series_attr = Attr(kw, _series_defaults)
|
||||
end
|
||||
|
||||
function _recipe_finish!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
current(plt)
|
||||
|
||||
# do we want to force display?
|
||||
@@ -247,7 +209,6 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# we're getting ready to display/output. prep for layout calcs, then update
|
||||
# the plot object after
|
||||
function prepare_output(plt::Plot)
|
||||
|
||||
+778
-863
File diff suppressed because it is too large
Load Diff
+328
-128
@@ -44,10 +44,10 @@ end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
num_series(x::AMat) = size(x,2)
|
||||
num_series(x::AMat) = size(x, 2)
|
||||
num_series(x) = 1
|
||||
|
||||
RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -55,13 +55,26 @@ RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where
|
||||
# for seriestype `line`, need to sort by x values
|
||||
|
||||
const POTENTIAL_VECTOR_ARGUMENTS = [
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linealpha, :linewidth, :linestyle, :line_z,
|
||||
:fillcolor, :fillalpha, :fill_z,
|
||||
:markercolor, :markeralpha, :markershape, :marker_z,
|
||||
:markerstrokecolor, :markerstrokealpha,
|
||||
:yerror, :yerror,
|
||||
:series_annotations, :fillrange
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linealpha,
|
||||
:linewidth,
|
||||
:linestyle,
|
||||
:line_z,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:fill_z,
|
||||
:markercolor,
|
||||
:markeralpha,
|
||||
:markershape,
|
||||
:marker_z,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:yerror,
|
||||
:yerror,
|
||||
:series_annotations,
|
||||
:fillrange,
|
||||
]
|
||||
|
||||
@recipe function f(::Type{Val{:line}}, x, y, z)
|
||||
@@ -99,7 +112,7 @@ end
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = repeat(Float64[-1, 1, NaN], n)
|
||||
newy = vec(Float64[yi for i=1:3,yi=y])
|
||||
newy = vec(Float64[yi for i = 1:3, yi in y])
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :straightline
|
||||
@@ -109,7 +122,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i=1:3,yi=y])
|
||||
newx = vec(Float64[yi for i = 1:3, yi in y])
|
||||
newy = repeat(Float64[-1, 1, NaN], n)
|
||||
x := newx
|
||||
y := newy
|
||||
@@ -121,7 +134,7 @@ end
|
||||
@recipe function f(::Type{Val{:hspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
newy = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
@@ -132,7 +145,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:vspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
@@ -156,7 +169,7 @@ end
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
()
|
||||
()
|
||||
end
|
||||
@deps scatterpath path scatter
|
||||
|
||||
@@ -169,7 +182,7 @@ function make_steps(x::AbstractArray, st)
|
||||
n = length(x)
|
||||
n == 0 && return zeros(0)
|
||||
newx = zeros(2n - 1)
|
||||
for i in 1:n
|
||||
for i = 1:n
|
||||
idx = 2i - 1
|
||||
newx[idx] = x[i]
|
||||
if i > 1
|
||||
@@ -249,10 +262,10 @@ end
|
||||
end
|
||||
end
|
||||
newx, newy = zeros(3n), zeros(3n)
|
||||
for i=1:n
|
||||
rng = 3i-2:3i
|
||||
for i = 1:n
|
||||
rng = (3i - 2):(3i)
|
||||
newx[rng] = [x[i], x[i], NaN]
|
||||
newy[rng] = [_cycle(fr,i), y[i], NaN]
|
||||
newy[rng] = [_cycle(fr, i), y[i], NaN]
|
||||
end
|
||||
x := newx
|
||||
y := newy
|
||||
@@ -282,16 +295,16 @@ end
|
||||
# get the value of the curve point at position t
|
||||
function bezier_value(pts::AVec, t::Real)
|
||||
val = 0.0
|
||||
n = length(pts)-1
|
||||
for (i,p) in enumerate(pts)
|
||||
val += p * binomial(n, i-1) * (1-t)^(n-i+1) * t^(i-1)
|
||||
n = length(pts) - 1
|
||||
for (i, p) in enumerate(pts)
|
||||
val += p * binomial(n, i - 1) * (1 - t)^(n - i + 1) * t^(i - 1)
|
||||
end
|
||||
val
|
||||
end
|
||||
|
||||
# create segmented bezier curves in place of line segments
|
||||
@recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30)
|
||||
args = z !== nothing ? (x,y,z) : (x,y)
|
||||
args = z !== nothing ? (x, y, z) : (x, y)
|
||||
newx, newy = zeros(0), zeros(0)
|
||||
fr = plotattributes[:fillrange]
|
||||
newfr = fr !== nothing ? zeros(0) : nothing
|
||||
@@ -304,13 +317,13 @@ end
|
||||
for rng in iter_segments(args...)
|
||||
length(rng) < 2 && continue
|
||||
ts = range(0, stop = 1, length = npoints)
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x,rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y,rng), t), ts))
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x, rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y, rng), t), ts))
|
||||
if z !== nothing
|
||||
nanappend!(newz, map(t -> bezier_value(_cycle(z,rng), t), ts))
|
||||
nanappend!(newz, map(t -> bezier_value(_cycle(z, rng), t), ts))
|
||||
end
|
||||
if fr !== nothing
|
||||
nanappend!(newfr, map(t -> bezier_value(_cycle(fr,rng), t), ts))
|
||||
nanappend!(newfr, map(t -> bezier_value(_cycle(fr, rng), t), ts))
|
||||
end
|
||||
# if lz !== nothing
|
||||
# lzrng = _cycle(lz, rng) # the line_z's for this segment
|
||||
@@ -343,14 +356,15 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y)
|
||||
procx, procy, xscale, yscale, baseline =
|
||||
_preprocess_barlike(plotattributes, x, y)
|
||||
nx, ny = length(procx), length(procy)
|
||||
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=procx]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi in procx]
|
||||
procx = if nx == ny
|
||||
cv
|
||||
elseif nx == ny + 1
|
||||
0.5diff(cv) + cv[1:end-1]
|
||||
0.5 * diff(cv) + cv[1:(end - 1)]
|
||||
else
|
||||
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
|
||||
end
|
||||
@@ -359,12 +373,12 @@ end
|
||||
bw = plotattributes[:bar_width]
|
||||
hw = if bw === nothing
|
||||
if nx > 1
|
||||
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
|
||||
0.5 * _bar_width * ignorenan_minimum(filter(x -> x > 0, diff(procx)))
|
||||
else
|
||||
0.5 * _bar_width
|
||||
end
|
||||
else
|
||||
Float64[0.5_cycle(bw,i) for i=1:length(procx)]
|
||||
Float64[0.5 * _cycle(bw, i) for i in eachindex(procx)]
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
@@ -378,13 +392,20 @@ end
|
||||
|
||||
# create the bar shapes by adding x/y segments
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i=1:ny
|
||||
for i = 1:ny
|
||||
yi = procy[i]
|
||||
if !isnan(yi)
|
||||
center = procx[i]
|
||||
hwi = _cycle(hw,i)
|
||||
fi = _cycle(fillto,i)
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
hwi = _cycle(hw, i)
|
||||
fi = _cycle(fillto, i)
|
||||
push!(
|
||||
xseg,
|
||||
center - hwi,
|
||||
center - hwi,
|
||||
center + hwi,
|
||||
center + hwi,
|
||||
center - hwi,
|
||||
)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
end
|
||||
end
|
||||
@@ -415,8 +436,8 @@ end
|
||||
m, n = size(z.surf)
|
||||
x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n)
|
||||
fz = zeros(m * n)
|
||||
for i in 1:m # y
|
||||
for j in 1:n # x
|
||||
for i = 1:m # y
|
||||
for j = 1:n # x
|
||||
k = (j - 1) * m + i
|
||||
inds = (6 * (k - 1) + 1):(6 * k - 1)
|
||||
x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]]
|
||||
@@ -440,13 +461,17 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
|
||||
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
||||
_bin_centers(v::AVec) = (v[1:(end - 1)] + v[2:end]) / 2
|
||||
|
||||
_is_positive(x) = (x > 0) && !(x ≈ 0)
|
||||
|
||||
_positive_else_nan(::Type{T}, x::Real) where {T} = _is_positive(x) ? T(x) : T(NaN)
|
||||
|
||||
function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) where T<:AbstractFloat
|
||||
function _scale_adjusted_values(
|
||||
::Type{T},
|
||||
V::AbstractVector,
|
||||
scale::Symbol,
|
||||
) where {T<:AbstractFloat}
|
||||
if scale in _logScales
|
||||
[_positive_else_nan(T, x) for x in V]
|
||||
else
|
||||
@@ -455,7 +480,7 @@ function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) whe
|
||||
end
|
||||
|
||||
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
|
||||
if (scale in _logScales)
|
||||
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
|
||||
else
|
||||
@@ -464,7 +489,11 @@ function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
|
||||
end
|
||||
|
||||
|
||||
function _preprocess_binbarlike_weights(::Type{T}, w, wscale::Symbol) where T<:AbstractFloat
|
||||
function _preprocess_binbarlike_weights(
|
||||
::Type{T},
|
||||
w,
|
||||
wscale::Symbol,
|
||||
) where {T<:AbstractFloat}
|
||||
w_adj = _scale_adjusted_values(T, w, wscale)
|
||||
w_min = ignorenan_minimum(w_adj)
|
||||
w_max = ignorenan_maximum(w_adj)
|
||||
@@ -490,7 +519,8 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
if (plotattributes[:bar_width] === nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
@@ -503,8 +533,9 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
xerror := diff(edge)/2
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
xerror := diff(edge) / 2
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
seriestype := :scatter
|
||||
@@ -513,7 +544,13 @@ end
|
||||
@deps scatterbins scatter
|
||||
|
||||
|
||||
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
|
||||
function _stepbins_path(
|
||||
edge,
|
||||
weights,
|
||||
baseline::Real,
|
||||
xscale::Symbol,
|
||||
yscale::Symbol,
|
||||
)
|
||||
log_scale_x = xscale in _logScales
|
||||
log_scale_y = yscale in _logScales
|
||||
|
||||
@@ -538,7 +575,7 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
||||
w, it_state_w = it_tuple_w
|
||||
|
||||
if (log_scale_x && a ≈ 0)
|
||||
a = oftype(a, b/_logScaleBases[xscale]^3)
|
||||
a = oftype(a, b / _logScaleBases[xscale]^3)
|
||||
end
|
||||
|
||||
if isnan(w)
|
||||
@@ -575,9 +612,11 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||
axis = plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
axis =
|
||||
plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
|
||||
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||
if !isvertical(plotattributes)
|
||||
@@ -607,9 +646,19 @@ end
|
||||
end
|
||||
Plots.@deps stepbins path
|
||||
|
||||
wand_edges(x...) = (@warn("Load the StatsPlots package in order to use :wand bins. Defaulting to :auto", once = true); :auto)
|
||||
wand_edges(x...) = (
|
||||
@warn(
|
||||
"Load the StatsPlots package in order to use :wand bins. Defaulting to :auto",
|
||||
once = true
|
||||
);
|
||||
:auto
|
||||
)
|
||||
|
||||
function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto) where N
|
||||
function _auto_binning_nbins(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
dim::Integer;
|
||||
mode::Symbol = :auto,
|
||||
) where {N}
|
||||
max_bins = 10_000
|
||||
_cl(x) = min(ceil(Int, max(x, one(x))), max_bins)
|
||||
_iqr(v) = (q = quantile(v, 0.75) - quantile(v, 0.25); q > 0 ? q : oftype(q, 1))
|
||||
@@ -618,8 +667,13 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
||||
n_samples = length(LinearIndices(first(vs)))
|
||||
|
||||
# The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation
|
||||
nd = n_samples^(1/(2+N))
|
||||
nd = N == 2 ? min(n_samples^(1/(2+N)), nd / (1-cor(first(vs), last(vs))^2)^(3//8)) : nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
nd = n_samples^(1 / (2 + N))
|
||||
nd = N == 2 ?
|
||||
min(
|
||||
n_samples^(1 / (2 + N)),
|
||||
nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8),
|
||||
) :
|
||||
nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
|
||||
v = vs[dim]
|
||||
|
||||
@@ -644,32 +698,52 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
||||
end
|
||||
end
|
||||
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) where {N} = binning
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} =
|
||||
StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} =
|
||||
_hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
dim::Integer,
|
||||
binning::AbstractVector,
|
||||
) where {N} = binning
|
||||
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N, Any}) where {N} =
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N,Any}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...,))
|
||||
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
|
||||
_hist_edges(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
binning::Union{Integer,Symbol,AbstractVector},
|
||||
) where {N} = map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
|
||||
|
||||
_hist_norm_mode(mode::Symbol) = mode
|
||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
||||
|
||||
_filternans(vs::NTuple{1,AbstractVector}) = filter!.(isfinite, vs)
|
||||
function _filternans(vs::NTuple{N,AbstractVector}) where N
|
||||
_invertedindex(v, not) = [j for (i,j) in enumerate(v) if !(i ∈ not)]
|
||||
function _filternans(vs::NTuple{N,AbstractVector}) where {N}
|
||||
_invertedindex(v, not) = [j for (i, j) in enumerate(v) if !(i ∈ not)]
|
||||
nots = union(Set.(findall.(!isfinite, vs))...)
|
||||
_invertedindex.(vs, Ref(nots))
|
||||
end
|
||||
|
||||
function _make_hist(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing) where N
|
||||
function _make_hist(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
binning;
|
||||
normed = false,
|
||||
weights = nothing,
|
||||
) where {N}
|
||||
localvs = _filternans(vs)
|
||||
edges = _hist_edges(localvs, binning)
|
||||
h = float( weights === nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, StatsBase.Weights(weights), edges, closed = :left)
|
||||
h = float(
|
||||
weights === nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||
StatsBase.fit(
|
||||
StatsBase.Histogram,
|
||||
localvs,
|
||||
StatsBase.Weights(weights),
|
||||
edges,
|
||||
closed = :left,
|
||||
),
|
||||
)
|
||||
normalize!(h, mode = _hist_norm_mode(normed))
|
||||
end
|
||||
@@ -682,7 +756,12 @@ end
|
||||
@deps histogram barhist
|
||||
|
||||
@recipe function f(::Type{Val{:barhist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :barbins
|
||||
@@ -691,7 +770,12 @@ end
|
||||
@deps barhist barbins
|
||||
|
||||
@recipe function f(::Type{Val{:stephist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :stepbins
|
||||
@@ -700,7 +784,12 @@ end
|
||||
@deps stephist stepbins
|
||||
|
||||
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :scatterbins
|
||||
@@ -709,19 +798,23 @@ end
|
||||
@deps scatterhist scatterbins
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T, 1, E}) where {T, E}
|
||||
@recipe function f(h::StatsBase.Histogram{T,1,E}) where {T,E}
|
||||
seriestype --> :barbins
|
||||
|
||||
st_map = Dict(
|
||||
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
|
||||
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
|
||||
:bar => :barbins,
|
||||
:scatter => :scatterbins,
|
||||
:step => :stepbins,
|
||||
:steppost => :stepbins, # :step can be mapped to :steppost in pre-processing
|
||||
)
|
||||
seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
seriestype :=
|
||||
get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
|
||||
if plotattributes[:seriestype] == :scatterbins
|
||||
# Workaround, error bars currently not set correctly by scatterbins
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1])/2
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1]) / 2
|
||||
seriestype := :scatter
|
||||
(Plots._bin_centers(edge), weights)
|
||||
else
|
||||
@@ -730,7 +823,7 @@ end
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(hv::AbstractVector{H}) where H <: StatsBase.Histogram
|
||||
@recipe function f(hv::AbstractVector{H}) where {H<:StatsBase.Histogram}
|
||||
for h in hv
|
||||
@series begin
|
||||
h
|
||||
@@ -769,7 +862,12 @@ Plots.@deps bins2d heatmap
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
h = _make_hist((x, y), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(x, y),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.edges[2]
|
||||
z := Surface(h.weights)
|
||||
@@ -779,7 +877,7 @@ end
|
||||
@deps histogram2d bins2d
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T, 2, E}) where {T, E}
|
||||
@recipe function f(h::StatsBase.Histogram{T,2,E}) where {T,E}
|
||||
seriestype --> :bins2d
|
||||
(h.edges[1], h.edges[2], Surface(h.weights))
|
||||
end
|
||||
@@ -801,6 +899,91 @@ end
|
||||
# note: don't add dependencies because this really isn't a drop-in replacement
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# lens! - magnify a region of a plot
|
||||
lens!(args...;kwargs...) = plot!(args...; seriestype=:lens, kwargs...)
|
||||
export lens!
|
||||
@recipe function f(::Type{Val{:lens}}, plt::AbstractPlot)
|
||||
sp_index, inset_bbox = plotattributes[:inset_subplots]
|
||||
if !(width(inset_bbox) isa Measures.Length{:w,<:Real})
|
||||
throw(ArgumentError("Inset bounding box needs to in relative coordinates."))
|
||||
end
|
||||
sp = plt.subplots[sp_index]
|
||||
xl1, xl2 = xlims(plt.subplots[sp_index])
|
||||
bbx1 = xl1 + left(inset_bbox).value * (xl2 - xl1)
|
||||
bbx2 = bbx1 + width(inset_bbox).value * (xl2 - xl1)
|
||||
yl1, yl2 = ylims(plt.subplots[sp_index])
|
||||
bby1 = yl1 + (1 - bottom(inset_bbox).value) * (yl2 - yl1)
|
||||
bby2 = bby1 + height(inset_bbox).value * (yl2 - yl1)
|
||||
bbx = bbx1 + width(inset_bbox).value * (xl2 - xl1) / 2
|
||||
bby = bby1 + height(inset_bbox).value * (yl2 - yl1) / 2
|
||||
lens_index = last(plt.subplots)[:subplot_index] + 1
|
||||
x1, x2 = plotattributes[:x]
|
||||
y1, y2 = plotattributes[:y]
|
||||
seriestype := :path
|
||||
label := ""
|
||||
linecolor := :lightgray
|
||||
bbx_mag = (x1 + x2) / 2
|
||||
bby_mag = (y1 + y2) / 2
|
||||
xi_lens, yi_lens = intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
|
||||
xi_mag, yi_mag = intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
|
||||
# add lines
|
||||
if xl1 < xi_lens < xl2 &&
|
||||
yl1 < yi_lens < yl2
|
||||
@series begin
|
||||
subplot := sp_index
|
||||
x := [xi_mag, xi_lens]
|
||||
y := [yi_mag, yi_lens]
|
||||
()
|
||||
end
|
||||
end
|
||||
# add magnification shape
|
||||
@series begin
|
||||
subplot := sp_index
|
||||
x := [x1, x1, x2, x2, x1]
|
||||
y := [y1, y2, y2, y1, y1]
|
||||
()
|
||||
end
|
||||
# add subplot
|
||||
for series in sp.series_list
|
||||
@series begin
|
||||
plotattributes = merge(plotattributes, copy(series.plotattributes))
|
||||
subplot := lens_index
|
||||
label := ""
|
||||
xlims := (x1, x2)
|
||||
ylims := (y1, y2)
|
||||
()
|
||||
end
|
||||
end
|
||||
backup = copy(plotattributes)
|
||||
empty!(plotattributes)
|
||||
seriestype := :path
|
||||
series_plotindex := backup[:series_plotindex]
|
||||
end
|
||||
|
||||
function intersection_point(xA, yA, xB, yB, h, w)
|
||||
s = (yA - yB) / (xA - xB)
|
||||
hh = h / 2
|
||||
hw = w / 2
|
||||
# left or right?
|
||||
if -hh <= s * hw <= hh
|
||||
if xA > xB
|
||||
# right
|
||||
return xB + hw, yB + s * hw
|
||||
else # left
|
||||
return xB - hw, yB - s * hw
|
||||
end
|
||||
# top or bot?
|
||||
elseif -hw <= hh/s <= hw
|
||||
if yA > yB
|
||||
# top
|
||||
return xB + hh/s, yB + hh
|
||||
else
|
||||
# bottom
|
||||
return xB - hh/s, yB - hh
|
||||
end
|
||||
end
|
||||
end
|
||||
# ---------------------------------------------------------------------------
|
||||
# contourf - filled contours
|
||||
|
||||
@@ -813,7 +996,7 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# Error Bars
|
||||
|
||||
function error_style!(plotattributes::KW)
|
||||
function error_style!(plotattributes::AKW)
|
||||
plotattributes[:seriestype] = :path
|
||||
plotattributes[:linecolor] = plotattributes[:markerstrokecolor]
|
||||
plotattributes[:linewidth] = plotattributes[:markerstrokewidth]
|
||||
@@ -855,7 +1038,11 @@ end
|
||||
@recipe function f(::Type{Val{:yerror}}, x, y, z)
|
||||
error_style!(plotattributes)
|
||||
markershape := :hline
|
||||
plotattributes[:x], plotattributes[:y] = error_coords(plotattributes[:x], plotattributes[:y], error_zipit(plotattributes[:yerror]))
|
||||
plotattributes[:x], plotattributes[:y] = error_coords(
|
||||
plotattributes[:x],
|
||||
plotattributes[:y],
|
||||
error_zipit(plotattributes[:yerror]),
|
||||
)
|
||||
()
|
||||
end
|
||||
@deps yerror path
|
||||
@@ -863,7 +1050,11 @@ end
|
||||
@recipe function f(::Type{Val{:xerror}}, x, y, z)
|
||||
error_style!(plotattributes)
|
||||
markershape := :vline
|
||||
plotattributes[:y], plotattributes[:x] = error_coords(plotattributes[:y], plotattributes[:x], error_zipit(plotattributes[:xerror]))
|
||||
plotattributes[:y], plotattributes[:x] = error_coords(
|
||||
plotattributes[:y],
|
||||
plotattributes[:x],
|
||||
error_zipit(plotattributes[:xerror]),
|
||||
)
|
||||
()
|
||||
end
|
||||
@deps xerror path
|
||||
@@ -874,8 +1065,8 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# quiver
|
||||
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(plotattributes::KW)
|
||||
# function apply_series_recipe(plotattributes::AKW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(plotattributes::AKW)
|
||||
plotattributes[:label] = ""
|
||||
plotattributes[:seriestype] = :path
|
||||
if !isa(plotattributes[:arrow], Arrow)
|
||||
@@ -898,23 +1089,23 @@ function quiver_using_arrows(plotattributes::KW)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
elseif isa(vi, Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
end
|
||||
|
||||
# add the points
|
||||
nanappend!(x, [xi, xi+vx, NaN])
|
||||
nanappend!(y, [yi, yi+vy, NaN])
|
||||
nanappend!(x, [xi, xi + vx, NaN])
|
||||
nanappend!(y, [yi, yi + vy, NaN])
|
||||
end
|
||||
|
||||
plotattributes[:x], plotattributes[:y] = x, y
|
||||
# KW[plotattributes]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(plotattributes::KW)
|
||||
# function apply_series_recipe(plotattributes::AKW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(plotattributes::AKW)
|
||||
plotattributes[:label] = ""
|
||||
plotattributes[:seriestype] = :shape
|
||||
|
||||
@@ -936,7 +1127,7 @@ function quiver_using_hack(plotattributes::KW)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
elseif isa(vi, Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
@@ -951,15 +1142,18 @@ function quiver_using_hack(plotattributes::KW)
|
||||
U1 *= arrow_h
|
||||
U2 *= arrow_w
|
||||
|
||||
ppv = p+v
|
||||
nanappend!(pts, P2[p, ppv-U1, ppv-U1+U2, ppv, ppv-U1-U2, ppv-U1])
|
||||
ppv = p + v
|
||||
nanappend!(
|
||||
pts,
|
||||
P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1],
|
||||
)
|
||||
end
|
||||
|
||||
plotattributes[:x], plotattributes[:y] = Plots.unzip(pts[2:end])
|
||||
# KW[plotattributes]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
# function apply_series_recipe(plotattributes::AKW, ::Type{Val{:quiver}})
|
||||
@recipe function f(::Type{Val{:quiver}}, x, y, z)
|
||||
if :arrow in supported_attrs()
|
||||
quiver_using_arrows(plotattributes)
|
||||
@@ -977,32 +1171,28 @@ end
|
||||
|
||||
"Represent Open High Low Close data (used in finance)"
|
||||
mutable struct OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
end
|
||||
Base.convert(::Type{OHLC}, tup::Tuple) = OHLC(tup...)
|
||||
# Base.tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close)
|
||||
|
||||
# get one OHLC path
|
||||
function get_xy(o::OHLC, x, xdiff)
|
||||
xl, xm, xr = x-xdiff, x, x+xdiff
|
||||
ox = [xl, xm, NaN,
|
||||
xm, xm, NaN,
|
||||
xm, xr]
|
||||
oy = [o.open, o.open, NaN,
|
||||
o.low, o.high, NaN,
|
||||
o.close, o.close]
|
||||
xl, xm, xr = x - xdiff, x, x + xdiff
|
||||
ox = [xl, xm, NaN, xm, xm, NaN, xm, xr]
|
||||
oy = [o.open, o.open, NaN, o.low, o.high, NaN, o.close, o.close]
|
||||
ox, oy
|
||||
end
|
||||
|
||||
# get the joined vector
|
||||
function get_xy(v::AVec{OHLC}, x = 1:length(v))
|
||||
xdiff = 0.3ignorenan_mean(abs.(diff(x)))
|
||||
function get_xy(v::AVec{OHLC}, x = eachindex(v))
|
||||
xdiff = 0.3 * ignorenan_mean(abs.(diff(x)))
|
||||
x_out, y_out = zeros(0), zeros(0)
|
||||
for (i,ohlc) in enumerate(v)
|
||||
ox,oy = get_xy(ohlc, x[i], xdiff)
|
||||
for (i, ohlc) in enumerate(v)
|
||||
ox, oy = get_xy(ohlc, x[i], xdiff)
|
||||
nanappend!(x_out, ox)
|
||||
nanappend!(y_out, oy)
|
||||
end
|
||||
@@ -1015,10 +1205,17 @@ end
|
||||
|
||||
# to squash ambiguity warnings...
|
||||
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
|
||||
@recipe f(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
|
||||
@recipe f(
|
||||
x::AVec{Function},
|
||||
v::AVec{Tuple{R1,R2,R3,R4}},
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
|
||||
|
||||
# this must be OHLC?
|
||||
@recipe f(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = x, OHLC[OHLC(t...) for t in ohlc]
|
||||
@recipe f(
|
||||
x::AVec,
|
||||
ohlc::AVec{Tuple{R1,R2,R3,R4}},
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} =
|
||||
x, OHLC[OHLC(t...) for t in ohlc]
|
||||
|
||||
@recipe function f(x::AVec, v::AVec{OHLC})
|
||||
seriestype := :path
|
||||
@@ -1056,11 +1253,11 @@ end
|
||||
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
|
||||
seriestype := :spy
|
||||
mat = g.args[1]
|
||||
n,m = size(mat)
|
||||
Plots.SliceIt, 1:m, 1:n, Surface(mat)
|
||||
n, m = axes(mat)
|
||||
Plots.SliceIt, m, n, Surface(mat)
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:spy}}, x,y,z)
|
||||
@recipe function f(::Type{Val{:spy}}, x, y, z)
|
||||
yflip := true
|
||||
aspect_ratio := 1
|
||||
rs, cs, zs = findnz(z.surf)
|
||||
@@ -1086,7 +1283,8 @@ end
|
||||
# -------------------------------------------------
|
||||
|
||||
"Adds ax+b... straight line over the current plot, without changing the axis limits"
|
||||
abline!(plt::Plot, a, b; kw...) = plot!(plt, [0, 1], [b, b+a]; seriestype = :straightline, kw...)
|
||||
abline!(plt::Plot, a, b; kw...) =
|
||||
plot!(plt, [0, 1], [b, b + a]; seriestype = :straightline, kw...)
|
||||
|
||||
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
|
||||
@@ -1099,9 +1297,11 @@ datetimeformatter(dt) = string(DateTime(Dates.UTM(dt)))
|
||||
timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> Dates.value(dt), dateformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> Dates.value(dt), datetimeformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) =
|
||||
(dt -> Dates.value(dt), datetimeformatter)
|
||||
@recipe f(::Type{Dates.Time}, t::Dates.Time) = (t -> Dates.value(t), timeformatter)
|
||||
@recipe f(::Type{P}, t::P) where P <: Dates.Period = (t -> Dates.value(t), t -> string(P(t)))
|
||||
@recipe f(::Type{P}, t::P) where {P<:Dates.Period} =
|
||||
(t -> Dates.value(t), t -> string(P(t)))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Characters
|
||||
@@ -1111,7 +1311,7 @@ timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
@recipe function f(A::Array{Complex{T}}) where T<:Number
|
||||
@recipe function f(A::Array{Complex{T}}) where {T<:Number}
|
||||
xguide --> "Re(x)"
|
||||
yguide --> "Im(x)"
|
||||
real.(A), imag.(A)
|
||||
@@ -1120,10 +1320,10 @@ end
|
||||
# Splits a complex matrix to its real and complex parts
|
||||
# Reals defaults solid, imaginary defaults dashed
|
||||
# Label defaults are changed to match the real-imaginary reference / indexing
|
||||
@recipe function f(x::AbstractArray{T},y::Array{Complex{T2}}) where {T<:Real,T2}
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x,real.(y),imag.(y)
|
||||
@recipe function f(x::AbstractArray{T}, y::Array{Complex{T2}}) where {T<:Real,T2}
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x, real.(y), imag.(y)
|
||||
end
|
||||
|
||||
|
||||
@@ -1137,7 +1337,7 @@ end
|
||||
end
|
||||
|
||||
library = PlotUtils.color_libraries[cl.args[1]]
|
||||
z = sqrt.((1:15)*reshape(1:20,1,:))
|
||||
z = sqrt.((1:15) * reshape(1:20, 1, :))
|
||||
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
@@ -1161,7 +1361,7 @@ end
|
||||
if !(length(grad.args) == 1 && isa(grad.args[1], Symbol))
|
||||
error("showgradient takes the name of a color gradient as a Symbol")
|
||||
end
|
||||
z = sqrt.((1:15)*reshape(1:20,1,:))
|
||||
z = sqrt.((1:15) * reshape(1:20, 1, :))
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
legend := false
|
||||
@@ -1184,9 +1384,9 @@ end
|
||||
weights = cumsum(weights, dims = 2)
|
||||
seriestype := :shape
|
||||
|
||||
# create a filled polygon for each item
|
||||
for c=1:size(weights,2)
|
||||
sx = vcat(weights[:,c], c==1 ? zeros(n) : reverse(weights[:,c-1]))
|
||||
# create a filled polygon for each item
|
||||
for c in axes(weights, 2)
|
||||
sx = vcat(weights[:, c], c == 1 ? zeros(n) : reverse(weights[:, c - 1]))
|
||||
sy = vcat(returns, reverse(returns))
|
||||
@series Plots.isvertical(plotattributes) ? (sx, sy) : (sy, sx)
|
||||
end
|
||||
@@ -1205,13 +1405,13 @@ julia> areaplot(1:3, [1 2 3; 7 8 9; 4 5 6], seriescolor = [:red :green :blue], f
|
||||
@userplot AreaPlot
|
||||
|
||||
@recipe function f(a::AreaPlot)
|
||||
data = cumsum(a.args[end], dims=2)
|
||||
x = length(a.args) == 1 ? (1:size(data, 1)) : a.args[1]
|
||||
data = cumsum(a.args[end], dims = 2)
|
||||
x = length(a.args) == 1 ? (axes(data, 1)) : a.args[1]
|
||||
seriestype := :line
|
||||
for i in 1:size(data, 2)
|
||||
for i in axes(data, 2)
|
||||
@series begin
|
||||
fillrange := i > 1 ? data[:,i-1] : 0
|
||||
x, data[:,i]
|
||||
fillrange := i > 1 ? data[:, i - 1] : 0
|
||||
x, data[:, i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+20
-150
@@ -1,82 +1,16 @@
|
||||
|
||||
|
||||
# create a new "build_series_args" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
|
||||
# Special handling for: no args, xmin/xmax, parametric, dataframes
|
||||
# Then once inputs have been converted, build the series args, map functions, etc.
|
||||
# This should cut down on boilerplate code and allow more focused dispatch on type
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
|
||||
const MaybeNumber = Union{Number, Missing}
|
||||
const MaybeString = Union{AbstractString, Missing}
|
||||
const DataPoint = Union{MaybeNumber, MaybeString}
|
||||
|
||||
prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plotting")
|
||||
prepareSeriesData(::Nothing) = nothing
|
||||
prepareSeriesData(f::Function) = f
|
||||
prepareSeriesData(a::AbstractArray{<:MaybeNumber}) = replace!(
|
||||
x -> ismissing(x) || isinf(x) ? NaN : x,
|
||||
map(float,a))
|
||||
prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a)
|
||||
prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf))
|
||||
prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image
|
||||
prepareSeriesData(v::Volume) = Volume(prepareSeriesData(v.v), v.x_extents, v.y_extents, v.z_extents)
|
||||
|
||||
# default: assume x represents a single series
|
||||
convertToAnyVector(x, plotattributes) = Any[prepareSeriesData(x)]
|
||||
|
||||
# fixed number of blank series
|
||||
convertToAnyVector(n::Integer, plotattributes) = Any[zeros(0) for i in 1:n]
|
||||
|
||||
# vector of data points is a single series
|
||||
convertToAnyVector(v::AVec{<:DataPoint}, plotattributes) = Any[prepareSeriesData(v)]
|
||||
|
||||
# list of things (maybe other vectors, functions, or something else)
|
||||
function convertToAnyVector(v::AVec, plotattributes)
|
||||
if all(x -> x isa MaybeNumber, v)
|
||||
convertToAnyVector(Vector{MaybeNumber}(v), plotattributes)
|
||||
elseif all(x -> x isa MaybeString, v)
|
||||
convertToAnyVector(Vector{MaybeString}(v), plotattributes)
|
||||
else
|
||||
vcat((convertToAnyVector(vi, plotattributes) for vi in v)...)
|
||||
end
|
||||
end
|
||||
|
||||
# Matrix is split into columns
|
||||
function convertToAnyVector(v::AMat{<:DataPoint}, plotattributes)
|
||||
if all3D(plotattributes)
|
||||
Any[prepareSeriesData(Surface(v))]
|
||||
else
|
||||
Any[prepareSeriesData(v[:, i]) for i in 1:size(v, 2)]
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Fillranges & ribbons
|
||||
|
||||
|
||||
process_fillrange(range::Number, plotattributes) = [range]
|
||||
process_fillrange(range, plotattributes) = convertToAnyVector(range, plotattributes)
|
||||
|
||||
process_ribbon(ribbon::Number, plotattributes) = [ribbon]
|
||||
process_ribbon(ribbon, plotattributes) = convertToAnyVector(ribbon, plotattributes)
|
||||
# ribbon as a tuple: (lower_ribbons, upper_ribbons)
|
||||
process_ribbon(ribbon::Tuple{Any,Any}, plotattributes) = collect(zip(convertToAnyVector(ribbon[1], plotattributes),
|
||||
convertToAnyVector(ribbon[2], plotattributes)))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
||||
# result in that array being shared. push!, etc will add too many items to that array
|
||||
|
||||
compute_x(x::Nothing, y::Nothing, z) = 1:size(z,1)
|
||||
compute_x(x::Nothing, y, z) = 1:size(y,1)
|
||||
compute_x(x::Nothing, y::Nothing, z) = axes(z,1)
|
||||
compute_x(x::Nothing, y, z) = axes(y,1)
|
||||
compute_x(x::Function, y, z) = map(x, y)
|
||||
compute_x(x, y, z) = copy(x)
|
||||
|
||||
# compute_y(x::Void, y::Function, z) = error()
|
||||
compute_y(x::Nothing, y::Nothing, z) = 1:size(z,2)
|
||||
compute_y(x::Nothing, y::Nothing, z) = axes(z,2)
|
||||
compute_y(x, y::Function, z) = map(y, x)
|
||||
compute_y(x, y, z) = copy(y)
|
||||
|
||||
@@ -106,70 +40,6 @@ compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all no
|
||||
|
||||
# we are going to build recipes to do the processing and splitting of the args
|
||||
|
||||
# ensure we dispatch to the slicer
|
||||
struct SliceIt end
|
||||
|
||||
# the catch-all recipes
|
||||
@recipe function f(::Type{SliceIt}, x, y, z)
|
||||
|
||||
# handle data with formatting attached
|
||||
if typeof(x) <: Formatted
|
||||
xformatter := x.formatter
|
||||
x = x.data
|
||||
end
|
||||
if typeof(y) <: Formatted
|
||||
yformatter := y.formatter
|
||||
y = y.data
|
||||
end
|
||||
if typeof(z) <: Formatted
|
||||
zformatter := z.formatter
|
||||
z = z.data
|
||||
end
|
||||
|
||||
xs = convertToAnyVector(x, plotattributes)
|
||||
ys = convertToAnyVector(y, plotattributes)
|
||||
zs = convertToAnyVector(z, plotattributes)
|
||||
|
||||
|
||||
fr = pop!(plotattributes, :fillrange, nothing)
|
||||
fillranges = process_fillrange(fr, plotattributes)
|
||||
mf = length(fillranges)
|
||||
|
||||
rib = pop!(plotattributes, :ribbon, nothing)
|
||||
ribbons = process_ribbon(rib, plotattributes)
|
||||
mr = length(ribbons)
|
||||
|
||||
# @show zs
|
||||
|
||||
mx = length(xs)
|
||||
my = length(ys)
|
||||
mz = length(zs)
|
||||
if mx > 0 && my > 0 && mz > 0
|
||||
for i in 1:max(mx, my, mz)
|
||||
# add a new series
|
||||
di = copy(plotattributes)
|
||||
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
|
||||
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
|
||||
|
||||
# handle fillrange
|
||||
fr = fillranges[mod1(i,mf)]
|
||||
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
|
||||
|
||||
# handle ribbons
|
||||
rib = ribbons[mod1(i,mr)]
|
||||
di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib
|
||||
|
||||
push!(series_list, RecipeData(di, ()))
|
||||
end
|
||||
end
|
||||
nothing # don't add a series for the main block
|
||||
end
|
||||
|
||||
# this is the default "type recipe"... just pass the object through
|
||||
@recipe f(::Type{T}, v::T) where {T<:Any} = v
|
||||
|
||||
# this should catch unhandled "series recipes" and error with a nice message
|
||||
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
|
||||
|
||||
_apply_type_recipe(plotattributes, v) = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
|
||||
|
||||
@@ -266,7 +136,7 @@ end
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# helper function to ensure relevant attributes are wrapped by Surface
|
||||
function wrap_surfaces(plotattributes::KW)
|
||||
function wrap_surfaces(plotattributes::AKW)
|
||||
if haskey(plotattributes, :fill_z)
|
||||
v = plotattributes[:fill_z]
|
||||
if !isa(v, Surface)
|
||||
@@ -277,14 +147,13 @@ end
|
||||
|
||||
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||
|
||||
all3D(plotattributes::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(plotattributes, :seriestype, :none))
|
||||
|
||||
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
||||
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat,Missing}
|
||||
if all3D(plotattributes)
|
||||
n,m = size(mat)
|
||||
n,m = axes(mat)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
SliceIt, m, n, Surface(mat)
|
||||
else
|
||||
SliceIt, nothing, mat, nothing
|
||||
end
|
||||
@@ -294,9 +163,9 @@ end
|
||||
@recipe function f(fmt::Formatted{T}) where T<:AbstractMatrix
|
||||
if all3D(plotattributes)
|
||||
mat = fmt.data
|
||||
n,m = size(mat)
|
||||
n,m = axes(mat)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
|
||||
SliceIt, m, n, Formatted(Surface(mat), fmt.formatter)
|
||||
else
|
||||
SliceIt, nothing, fmt, nothing
|
||||
end
|
||||
@@ -319,35 +188,36 @@ function clamp_greys!(mat::AMat{T}) where T<:Gray
|
||||
end
|
||||
|
||||
@recipe function f(mat::AMat{T}) where T<:Gray
|
||||
n, m = size(mat)
|
||||
n, m = axes(mat)
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(clamp_greys!(mat))
|
||||
SliceIt, m, n, Surface(clamp_greys!(mat))
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
fillcolor --> ColorGradient([:black, :white])
|
||||
SliceIt, 1:m, 1:n, Surface(clamp!(convert(Matrix{Float64}, mat), 0., 1.))
|
||||
SliceIt, m, n, Surface(clamp!(convert(Matrix{Float64}, mat), 0., 1.))
|
||||
end
|
||||
end
|
||||
|
||||
# # images - colors
|
||||
|
||||
@recipe function f(mat::AMat{T}) where T<:Colorant
|
||||
n, m = size(mat)
|
||||
n, m = axes(mat)
|
||||
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
SliceIt, m, n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
aspect_ratio --> :equal
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(z)
|
||||
SliceIt, m, n, Surface(z)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -366,7 +236,7 @@ end
|
||||
|
||||
@recipe function f(shapes::AMat{Shape})
|
||||
seriestype --> :shape
|
||||
for j in 1:size(shapes,2)
|
||||
for j in axes(shapes,2)
|
||||
@series coords(vec(shapes[:,j]))
|
||||
end
|
||||
end
|
||||
@@ -583,7 +453,7 @@ end
|
||||
# end
|
||||
|
||||
splittable_kw(key, val, lengthGroup) = false
|
||||
splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && size(val,1) == lengthGroup
|
||||
splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && length(axes(val,1)) == lengthGroup
|
||||
splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, lengthGroup))
|
||||
splittable_kw(key, val::SeriesAnnotations, lengthGroup) = splittable_kw(key, val.strs, lengthGroup)
|
||||
|
||||
@@ -597,7 +467,7 @@ end
|
||||
function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1])
|
||||
y_mat = Array{promote_type(eltype(y), typeof(def_val))}(undef, length(keys(x_ind)), length(groupby.groupLabels))
|
||||
fill!(y_mat, def_val)
|
||||
for i in 1:length(groupby.groupLabels)
|
||||
for i in eachindex(groupby.groupLabels)
|
||||
xi = x[groupby.groupIds[i]]
|
||||
yi = y[groupby.groupIds[i]]
|
||||
y_mat[getindex.(Ref(x_ind), xi), i] = yi
|
||||
@@ -630,7 +500,7 @@ group_as_matrix(t) = false
|
||||
if length(g.args) == 1
|
||||
x = zeros(Int, lengthGroup)
|
||||
for indexes in groupby.groupIds
|
||||
x[indexes] = 1:length(indexes)
|
||||
x[indexes] = eachindex(indexes)
|
||||
end
|
||||
last_args = g.args
|
||||
else
|
||||
@@ -638,7 +508,7 @@ group_as_matrix(t) = false
|
||||
last_args = g.args[2:end]
|
||||
end
|
||||
x_u = unique(sort(x))
|
||||
x_ind = Dict(zip(x_u, 1:length(x_u)))
|
||||
x_ind = Dict(zip(x_u, eachindex(x_u)))
|
||||
for (key,val) in plotattributes
|
||||
if splittable_kw(key, val, lengthGroup)
|
||||
:($key) := groupedvec2mat(x_ind, x, val, groupby)
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
|
||||
(20mm, 5mm, 2mm, 10mm),
|
||||
defaultbox,
|
||||
defaultbox,
|
||||
KW(),
|
||||
Attr(KW(), _subplot_defaults),
|
||||
nothing,
|
||||
nothing
|
||||
)
|
||||
|
||||
+7
-4
@@ -4,11 +4,11 @@
|
||||
Specify the colour theme for plots.
|
||||
"""
|
||||
function theme(s::Symbol; kw...)
|
||||
defaults = PlotThemes._themes[s].defaults
|
||||
defaults = copy(PlotThemes._themes[s].defaults)
|
||||
_theme(s, defaults; kw...)
|
||||
end
|
||||
|
||||
function _theme(s::Symbol, defaults::KW; kw...)
|
||||
function _theme(s::Symbol, defaults::AKW; kw...)
|
||||
# Reset to defaults to overwrite active theme
|
||||
reset_defaults()
|
||||
|
||||
@@ -120,7 +120,8 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
subplot := 4
|
||||
seriestype := :heatmap
|
||||
seriescolor := colorgradient
|
||||
ticks := -5:5:5
|
||||
xticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
yticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
x, y, z
|
||||
end
|
||||
|
||||
@@ -128,12 +129,14 @@ _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, "π"))
|
||||
x, y, z
|
||||
end
|
||||
|
||||
n = 100
|
||||
ts = range(0, stop = 10π, length = n)
|
||||
x = ts .* cos.(ts)
|
||||
x = (0.1ts) .* cos.(ts)
|
||||
y = (0.1ts) .* sin.(ts)
|
||||
z = 1:n
|
||||
|
||||
|
||||
+59
-6
@@ -5,6 +5,7 @@
|
||||
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}
|
||||
|
||||
struct PlotsDisplay <: AbstractDisplay end
|
||||
@@ -18,10 +19,63 @@ wrap(obj::T) where {T} = InputWrapper{T}(obj)
|
||||
Base.isempty(wrapper::InputWrapper) = false
|
||||
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
struct Attr <: AbstractDict{Symbol,Any}
|
||||
explicit::KW
|
||||
defaults::KW
|
||||
end
|
||||
|
||||
Base.getindex(attr::Attr, k) = haskey(attr.explicit,k) ?
|
||||
attr.explicit[k] : attr.defaults[k]
|
||||
Base.haskey(attr::Attr, k) = haskey(attr.explicit,k) || haskey(attr.defaults,k)
|
||||
Base.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default
|
||||
function Base.get!(attr::Attr, k, default)
|
||||
v = if haskey(attr, k)
|
||||
attr[k]
|
||||
else
|
||||
attr.defaults[k] = default
|
||||
end
|
||||
return v
|
||||
end
|
||||
function Base.delete!(attr::Attr, k)
|
||||
haskey(attr.explicit, k) && delete!(attr.explicit, k)
|
||||
haskey(attr.defaults, k) && delete!(attr.defaults, k)
|
||||
end
|
||||
Base.length(attr::Attr) = length(union(keys(attr.explicit), keys(attr.defaults)))
|
||||
function Base.iterate(attr::Attr)
|
||||
exp_keys = keys(attr.explicit)
|
||||
def_keys = setdiff(keys(attr.defaults), exp_keys)
|
||||
key_list = collect(Iterators.flatten((exp_keys, def_keys)))
|
||||
iterate(attr, (key_list, 1))
|
||||
end
|
||||
function Base.iterate(attr::Attr, (key_list, i))
|
||||
i > length(key_list) && return nothing
|
||||
k = key_list[i]
|
||||
(k=>attr[k], (key_list, i+1))
|
||||
end
|
||||
|
||||
Base.copy(attr::Attr) = Attr(copy(attr.explicit), attr.defaults)
|
||||
|
||||
RecipesBase.is_explicit(attr::Attr, k) = haskey(attr.explicit,k)
|
||||
isdefault(attr::Attr, k) = !is_explicit(attr,k) && haskey(attr.defaults,k)
|
||||
|
||||
Base.setindex!(attr::Attr, v, k) = attr.explicit[k] = v
|
||||
|
||||
# Reset to default value and return dict
|
||||
reset_kw!(attr::Attr, k) = is_explicit(attr, k) ? delete!(attr.explicit, k) : attr
|
||||
# Reset to default value and return old value
|
||||
pop_kw!(attr::Attr, k) = is_explicit(attr, k) ? pop!(attr.explicit, k) : attr.defaults[k]
|
||||
pop_kw!(attr::Attr, k, default) = is_explicit(attr, k) ? pop!(attr.explicit, k) : get(attr.defaults, k, default)
|
||||
# Fallbacks for dicts without defaults
|
||||
reset_kw!(d::AKW, k) = delete!(d, k)
|
||||
pop_kw!(d::AKW, k) = pop!(d, k)
|
||||
pop_kw!(d::AKW, k, default) = pop!(d, k, default)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
mutable struct Series
|
||||
plotattributes::KW
|
||||
plotattributes::Attr
|
||||
end
|
||||
|
||||
attr(series::Series, k::Symbol) = series.plotattributes[k]
|
||||
@@ -36,7 +90,7 @@ mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
bbox::BoundingBox # the canvas area which is available to this subplot
|
||||
plotarea::BoundingBox # the part where the data goes
|
||||
attr::KW # args specific to this subplot
|
||||
attr::Attr # args specific to this subplot
|
||||
o # can store backend-specific data... like a pyplot ax
|
||||
plt # the enclosing Plot object (can't give it a type because of no forward declarations)
|
||||
end
|
||||
@@ -48,7 +102,7 @@ Base.show(io::IO, sp::Subplot) = print(io, "Subplot{$(sp[:subplot_index])}")
|
||||
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
|
||||
mutable struct Axis
|
||||
sps::Vector{Subplot}
|
||||
plotattributes::KW
|
||||
plotattributes::Attr
|
||||
end
|
||||
|
||||
mutable struct Extrema
|
||||
@@ -67,8 +121,7 @@ const SubplotMap = Dict{Any, Subplot}
|
||||
mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
backend::T # the backend type
|
||||
n::Int # number of series
|
||||
attr::KW # arguments for the whole plot
|
||||
user_attr::KW # raw arg inputs (after aliases). these are used as the input dict in `_plot!`
|
||||
attr::Attr # arguments for the whole plot
|
||||
series_list::Vector{Series} # arguments for each series
|
||||
o # the backend's plot object
|
||||
subplots::Vector{Subplot}
|
||||
@@ -79,7 +132,7 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
end
|
||||
|
||||
function Plot()
|
||||
Plot(backend(), 0, KW(), KW(), Series[], nothing,
|
||||
Plot(backend(), 0, Attr(KW(), _plot_defaults), Series[], nothing,
|
||||
Subplot[], SubplotMap(), EmptyLayout(),
|
||||
Subplot[], false)
|
||||
end
|
||||
|
||||
+57
-47
@@ -43,7 +43,7 @@ function barHack(; kw...)
|
||||
# estimate the edges
|
||||
dists = diff(midpoints) * 0.5
|
||||
edges = zeros(length(midpoints)+1)
|
||||
for i in 1:length(edges)
|
||||
for i in eachindex(edges)
|
||||
if i == 1
|
||||
edge = midpoints[1] - dists[1]
|
||||
elseif i == length(edges)
|
||||
@@ -56,7 +56,7 @@ function barHack(; kw...)
|
||||
|
||||
x = Float64[]
|
||||
y = Float64[]
|
||||
for i in 1:length(heights)
|
||||
for i in eachindex(heights)
|
||||
e1, e2 = edges[i:i+1]
|
||||
append!(x, [e1, e1, e2, e2])
|
||||
append!(y, [fillrange, heights[i], heights[i], fillrange])
|
||||
@@ -116,24 +116,11 @@ end
|
||||
|
||||
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
|
||||
n, m = size(z)
|
||||
# idx = 0
|
||||
colors = ColorGradient(vec(z))
|
||||
newz = reshape(range(0, stop=1, length=n*m), n, m)
|
||||
newz, colors
|
||||
# newz = zeros(n, m)
|
||||
# for i=1:n, j=1:m
|
||||
# push!(colors, T(z[i,j]...))
|
||||
# newz[i,j] = idx / (n*m-1)
|
||||
# idx += 1
|
||||
# end
|
||||
# newz, ColorGradient(colors)
|
||||
end
|
||||
|
||||
function imageHack(plotattributes::KW)
|
||||
is_seriestype_supported(:heatmap) || error("Neither :image or :heatmap are supported!")
|
||||
plotattributes[:seriestype] = :heatmap
|
||||
plotattributes[:z], plotattributes[:fillcolor] = replace_image_with_heatmap(plotattributes[:z].surf)
|
||||
end
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
"Build line segments for plotting"
|
||||
@@ -145,16 +132,18 @@ end
|
||||
|
||||
Segments() = Segments(Float64)
|
||||
Segments(::Type{T}) where {T} = Segments(T[])
|
||||
Segments(p::Int) = Segments(NTuple{2,Float64}[])
|
||||
Segments(p::Int) = Segments(NTuple{p, Float64}[])
|
||||
|
||||
|
||||
# Segments() = Segments(zeros(0))
|
||||
|
||||
to_nan(::Type{Float64}) = NaN
|
||||
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]
|
||||
|
||||
function Base.push!(segments::Segments{T}, vs...) where T
|
||||
if !isempty(segments.pts)
|
||||
@@ -182,13 +171,15 @@ end
|
||||
|
||||
mutable struct SegmentsIterator
|
||||
args::Tuple
|
||||
n::Int
|
||||
n1::Int
|
||||
n2::Int
|
||||
end
|
||||
|
||||
function iter_segments(args...)
|
||||
tup = Plots.wraptuple(args)
|
||||
n = maximum(map(length, tup))
|
||||
SegmentsIterator(tup, n)
|
||||
n1 = minimum(map(firstindex, tup))
|
||||
n2 = maximum(map(lastindex, tup))
|
||||
SegmentsIterator(tup, n1, n2)
|
||||
end
|
||||
|
||||
function iter_segments(series::Series)
|
||||
@@ -197,9 +188,9 @@ function iter_segments(series::Series)
|
||||
return UnitRange{Int}[]
|
||||
elseif has_attribute_segments(series)
|
||||
if series[:seriestype] in (:scatter, :scatter3d)
|
||||
return [[i] for i in 1:length(y)]
|
||||
return [[i] for i in eachindex(y)]
|
||||
else
|
||||
return [i:(i + 1) for i in 1:(length(y) - 1)]
|
||||
return [i:(i + 1) for i in firstindex(y):lastindex(y)-1]
|
||||
end
|
||||
else
|
||||
segs = UnitRange{Int}[]
|
||||
@@ -217,13 +208,13 @@ 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::SegmentsIterator, nextidx::Int = 1)
|
||||
i = findfirst(!anynan(itr.args), nextidx:itr.n)
|
||||
function Base.iterate(itr::SegmentsIterator, nextidx::Int = itr.n1)
|
||||
i = findfirst(!anynan(itr.args), nextidx:itr.n2)
|
||||
i === nothing && return nothing
|
||||
nextval = nextidx + i - 1
|
||||
|
||||
j = findfirst(anynan(itr.args), nextval:itr.n)
|
||||
nextnan = j === nothing ? itr.n + 1 : nextval + j - 1
|
||||
j = findfirst(anynan(itr.args), nextval:itr.n2)
|
||||
nextnan = j === nothing ? itr.n2 + 1 : nextval + j - 1
|
||||
|
||||
nextval:nextnan-1, nextnan
|
||||
end
|
||||
@@ -314,13 +305,13 @@ function replaceType(vec, val)
|
||||
push!(vec, val)
|
||||
end
|
||||
|
||||
function replaceAlias!(plotattributes::KW, k::Symbol, aliases::Dict{Symbol,Symbol})
|
||||
function replaceAlias!(plotattributes::AKW, k::Symbol, aliases::Dict{Symbol,Symbol})
|
||||
if haskey(aliases, k)
|
||||
plotattributes[aliases[k]] = pop!(plotattributes, k)
|
||||
plotattributes[aliases[k]] = pop_kw!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
|
||||
function replaceAliases!(plotattributes::KW, aliases::Dict{Symbol,Symbol})
|
||||
function replaceAliases!(plotattributes::AKW, aliases::Dict{Symbol,Symbol})
|
||||
ks = collect(keys(plotattributes))
|
||||
for k in ks
|
||||
replaceAlias!(plotattributes, k, aliases)
|
||||
@@ -344,8 +335,8 @@ const _scale_base = Dict{Symbol, Real}(
|
||||
|
||||
function _heatmap_edges(v::AVec, isedges::Bool = false)
|
||||
length(v) == 1 && return v[1] .+ [-0.5, 0.5]
|
||||
if isedges return v end
|
||||
# `isedges = true` means that v is a vector which already describes edges
|
||||
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 = (v[2] - v[1]) / 2
|
||||
@@ -361,20 +352,25 @@ end
|
||||
|
||||
function heatmap_edges(x::AVec, xscale::Symbol, y::AVec, yscale::Symbol, z_size::Tuple{Int, Int})
|
||||
nx, ny = length(x), length(y)
|
||||
# ismidpoints = z_size == (ny, nx) # This fails some tests, but would actually be
|
||||
# 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.
|
||||
ismidpoints = prod(z_size) == (ny * nx)
|
||||
ismidpoints = prod(z_size) == (ny * nx)
|
||||
isedges = z_size == (ny - 1, nx - 1)
|
||||
if !ismidpoints && !isedges
|
||||
error("""Length of x & y does not match the size of z.
|
||||
error("""Length of x & y does not match the size of z.
|
||||
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),
|
||||
x, y = heatmap_edges(x, xscale, isedges),
|
||||
heatmap_edges(y, yscale, isedges)
|
||||
return x, y
|
||||
end
|
||||
|
||||
function is_uniformly_spaced(v; tol=1e-6)
|
||||
dv = diff(v)
|
||||
maximum(dv) - minimum(dv) < tol * mean(abs.(dv))
|
||||
end
|
||||
|
||||
function convert_to_polar(theta, r, r_extrema = ignorenan_extrema(r))
|
||||
rmin, rmax = r_extrema
|
||||
r = (r .- rmin) ./ (rmax .- rmin)
|
||||
@@ -406,7 +402,7 @@ isscalar(::Any) = false
|
||||
is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2
|
||||
|
||||
|
||||
isvertical(plotattributes::KW) = 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)
|
||||
|
||||
|
||||
@@ -490,7 +486,7 @@ function make_fillrange_side(y, rib)
|
||||
end
|
||||
|
||||
# turn a ribbon into a fillrange
|
||||
function make_fillrange_from_ribbon(kw::KW)
|
||||
function make_fillrange_from_ribbon(kw::AKW)
|
||||
y, rib = kw[:y], kw[:ribbon]
|
||||
rib = wraptuple(rib)
|
||||
rib1, rib2 = -first(rib), last(rib)
|
||||
@@ -552,7 +548,7 @@ function get_clims(sp::Subplot)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
return zmin < zmax ? (zmin, zmax) : (-0.1, 0.1)
|
||||
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
||||
end
|
||||
|
||||
function get_clims(sp::Subplot, series::Series)
|
||||
@@ -566,7 +562,7 @@ function get_clims(sp::Subplot, series::Series)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
return zmin < zmax ? (zmin, zmax) : (-0.1, 0.1)
|
||||
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
||||
end
|
||||
|
||||
function get_clims(series::Series)
|
||||
@@ -579,10 +575,10 @@ function get_clims(series::Series)
|
||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals)...)
|
||||
end
|
||||
end
|
||||
return zmin < zmax ? (zmin, zmax) : (-0.1, 0.1)
|
||||
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
||||
end
|
||||
|
||||
_update_clims(zmin, zmax, emin, emax) = min(zmin, emin), max(zmax, emax)
|
||||
_update_clims(zmin, zmax, emin, emax) = NaNMath.min(zmin, emin), NaNMath.max(zmax, emax)
|
||||
|
||||
@enum ColorbarStyle cbar_gradient cbar_fill cbar_lines
|
||||
|
||||
@@ -684,6 +680,10 @@ function get_markerstrokealpha(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokealpha], i)
|
||||
end
|
||||
|
||||
function get_markerstrokewidth(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokewidth], i)
|
||||
end
|
||||
|
||||
function has_attribute_segments(series::Series)
|
||||
# we want to check if a series needs to be split into segments just because
|
||||
# of its attributes
|
||||
@@ -697,6 +697,19 @@ function has_attribute_segments(series::Series)
|
||||
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :linestyle, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z))
|
||||
end
|
||||
|
||||
function get_aspect_ratio(sp)
|
||||
aspect_ratio = sp[:aspect_ratio]
|
||||
if aspect_ratio == :auto
|
||||
aspect_ratio = :none
|
||||
for series in series_list(sp)
|
||||
if series[:seriestype] == :image
|
||||
aspect_ratio = :equal
|
||||
end
|
||||
end
|
||||
end
|
||||
return aspect_ratio
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
makekw(; kw...) = KW(kw)
|
||||
@@ -704,9 +717,6 @@ makekw(; kw...) = KW(kw)
|
||||
wraptuple(x::Tuple) = x
|
||||
wraptuple(x) = (x,)
|
||||
|
||||
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
|
||||
trueOrAllTrue(f::Function, x) = f(x)
|
||||
|
||||
allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
|
||||
allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
|
||||
allShapes(arg) = trueOrAllTrue(a -> is_marker_supported(get(_markerAliases, a, a)), arg) ||
|
||||
@@ -820,7 +830,7 @@ end
|
||||
debugshow(io, x) = show(io, x)
|
||||
debugshow(io, x::AbstractArray) = print(io, summary(x))
|
||||
|
||||
function dumpdict(io::IO, plotattributes::KW, prefix = "", alwaysshow = false)
|
||||
function dumpdict(io::IO, plotattributes::AKW, prefix = "", alwaysshow = false)
|
||||
_debugMode.on || alwaysshow || return
|
||||
println(io)
|
||||
if prefix != ""
|
||||
@@ -833,8 +843,8 @@ function dumpdict(io::IO, plotattributes::KW, prefix = "", alwaysshow = false)
|
||||
end
|
||||
println(io)
|
||||
end
|
||||
DD(io::IO, plotattributes::KW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
|
||||
DD(plotattributes::KW, prefix = "") = DD(stdout, plotattributes, prefix)
|
||||
DD(io::IO, plotattributes::AKW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
|
||||
DD(plotattributes::AKW, prefix = "") = DD(stdout, plotattributes, prefix)
|
||||
|
||||
function dumpcallstack()
|
||||
error() # well... you wanted the stacktrace, didn't you?!?
|
||||
@@ -1149,7 +1159,7 @@ function convert_sci_unicode(label::AbstractString)
|
||||
for key in keys(unicode_dict)
|
||||
label = replace(label, key => unicode_dict[key])
|
||||
end
|
||||
if occursin("10^{", label)
|
||||
if occursin("×10^{", label)
|
||||
label = string(label, "}")
|
||||
end
|
||||
label
|
||||
|
||||
+44
-2
@@ -7,6 +7,7 @@ using FileIO
|
||||
using Gtk
|
||||
using LibGit2
|
||||
using GeometryTypes
|
||||
using Dates
|
||||
|
||||
include("test_pgfplotsx.jl")
|
||||
|
||||
@@ -41,7 +42,31 @@ include("imgcomp.jl")
|
||||
Random.seed!(1234)
|
||||
default(show=false, reuse=true)
|
||||
is_ci() = get(ENV, "CI", "false") == "true"
|
||||
img_tol = is_ci() ? 10e-2 : 10e-2
|
||||
img_tol = is_ci() ? 1e-2 : Sys.islinux() ? 1e-3 : 0.1
|
||||
|
||||
## Uncomment the following lines to update reference images for different backends
|
||||
|
||||
#=
|
||||
@testset "GR" begin
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
|
||||
end
|
||||
|
||||
plotly()
|
||||
@testset "Plotly" begin
|
||||
image_comparison_facts(:plotly, tol=img_tol, skip = Plots._backend_skips[:plotlyjs])
|
||||
end
|
||||
|
||||
pyplot()
|
||||
@testset "PyPlot" begin
|
||||
image_comparison_facts(:pyplot, tol=img_tol, skip = Plots._backend_skips[:pyplot])
|
||||
end
|
||||
|
||||
pgfplots()
|
||||
@testset "PGFPlots" begin
|
||||
image_comparison_facts(:pgfplots, tol=img_tol, skip = Plots._backend_skips[:pgfplots])
|
||||
end
|
||||
=#
|
||||
##
|
||||
|
||||
@testset "Backends" begin
|
||||
|
||||
@@ -51,7 +76,9 @@ img_tol = is_ci() ? 10e-2 : 10e-2
|
||||
@test gr() == Plots.GRBackend()
|
||||
@test backend() == Plots.GRBackend()
|
||||
|
||||
@static if Sys.islinux()
|
||||
@static if haskey(ENV, "APPVEYOR")
|
||||
@info "Skipping GR image comparison tests on AppVeyor"
|
||||
else
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
|
||||
end
|
||||
end
|
||||
@@ -67,6 +94,21 @@ img_tol = is_ci() ? 10e-2 : 10e-2
|
||||
p = bar(randn(10))
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
p = plot([1, 2], [3, 4])
|
||||
annotate!(p, [(1.5, 3.2, Plots.text("Test", :red, :center))])
|
||||
hline!(p, [3.1])
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4])
|
||||
hline!(p, [3.1])
|
||||
annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, Plots.text("Test", :red, :center))])
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4])
|
||||
annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, Plots.text("Test", :red, :center))])
|
||||
hline!(p, [3.1])
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+260
-124
@@ -1,136 +1,272 @@
|
||||
using Plots, Test
|
||||
pgfplotsx()
|
||||
|
||||
function create_plot( args...; kwargs... )
|
||||
pgfx_plot = plot(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
function create_plot(args...; kwargs...)
|
||||
pgfx_plot = plot(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
end
|
||||
|
||||
function create_plot!( args...; kwargs... )
|
||||
pgfx_plot = plot!(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
function create_plot!(args...; kwargs...)
|
||||
pgfx_plot = plot!(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
end
|
||||
|
||||
@testset "PGFPlotsX" begin
|
||||
pgfx_plot = plot(1:5)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
|
||||
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
|
||||
pgfx_plot = plot(1:5)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
|
||||
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
|
||||
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
|
||||
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1
|
||||
@test !haskey(axis.contents[1].options.dict, "fill")
|
||||
|
||||
@testset "3D docs example" begin
|
||||
n = 100
|
||||
ts = range(0, stop=8π, length=n)
|
||||
x = ts .* map(cos, ts)
|
||||
y = (0.1ts) .* map(sin, ts)
|
||||
z = 1:n
|
||||
pl = plot(x, y, z, zcolor=reverse(z), m=(10, 0.8, :blues, Plots.stroke(0)), leg=false, cbar=true, w=5)
|
||||
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w=10)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
end
|
||||
end # testset
|
||||
@testset "Color docs example" begin
|
||||
y = rand(100)
|
||||
plot(0:10:100, rand(11, 4), lab="lines", w=3, palette=:grays, fill=0, α=0.6)
|
||||
pl = scatter!(y, zcolor=abs.(y .- 0.5), m=(:heat, 0.8, Plots.stroke(1, :green)), ms=10 * abs.(y .- 0.5) .+ 4, lab="grad")
|
||||
Plots._update_plot_object(pl)
|
||||
axis = Plots.pgfx_axes(pl.o)[1]
|
||||
@test count( x->x isa PGFPlotsX.LegendEntry, axis.contents ) == 5
|
||||
@test count( x->x isa PGFPlotsX.Plot, axis.contents ) == 108 # each marker is its own plot, fillranges create 2 plot-objects
|
||||
marker = axis.contents[15]
|
||||
@test marker isa PGFPlotsX.Plot
|
||||
@test marker.options["mark"] == "*"
|
||||
@test marker.options["mark options"]["color"] == RGBA{Float64}( colorant"green", 0.8)
|
||||
@test marker.options["mark options"]["line width"] == 1
|
||||
end # testset
|
||||
@testset "Plot in pieces" begin
|
||||
plot(rand(100) / 3, reg=true, fill=(0, :green))
|
||||
scatter!(rand(100), markersize=6, c=:orange)
|
||||
end # testset
|
||||
@testset "Marker types" begin
|
||||
markers = filter((m->begin
|
||||
m in Plots.supported_markers()
|
||||
end), Plots._shape_keys)
|
||||
markers = reshape(markers, 1, length(markers))
|
||||
n = length(markers)
|
||||
x = (range(0, stop=10, length=n + 2))[2:end - 1]
|
||||
y = repeat(reshape(reverse(x), 1, :), n, 1)
|
||||
scatter(x, y, m=(8, :auto), lab=map(string, markers), bg=:linen, xlim=(0, 10), ylim=(0, 10))
|
||||
end # testset
|
||||
@testset "Layout" begin
|
||||
plot(Plots.fakedata(100, 10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
|
||||
end # testset
|
||||
@testset "Polar plots" begin
|
||||
Θ = range(0, stop=1.5π, length=100)
|
||||
r = abs.(0.1 * randn(100) + sin.(3Θ))
|
||||
plot(Θ, r, proj=:polar, m=2)
|
||||
end # testset
|
||||
@testset "Drawing shapes" begin
|
||||
verts = [(-1.0, 1.0), (-1.28, 0.6), (-0.2, -1.4), (0.2, -1.4), (1.28, 0.6), (1.0, 1.0), (-1.0, 1.0), (-0.2, -0.6), (0.0, -0.2), (-0.4, 0.6), (1.28, 0.6), (0.2, -1.4), (-0.2, -1.4), (0.6, 0.2), (-0.2, 0.2), (0.0, -0.2), (0.2, 0.2), (-0.2, -0.6)]
|
||||
x = 0.1:0.2:0.9
|
||||
y = 0.7 * rand(5) .+ 0.15
|
||||
plot(x, y, line=(3, :dash, :lightblue), marker=(Shape(verts), 30, RGBA(0, 0, 0, 0.2)), bg=:pink, fg=:darkblue, xlim=(0, 1), ylim=(0, 1), leg=false)
|
||||
end # testset
|
||||
@testset "Histogram 2D" begin
|
||||
histogram2d(randn(10000), randn(10000), nbins=20)
|
||||
end # testset
|
||||
@testset "Heatmap-like" begin
|
||||
xs = [string("x", i) for i = 1:10]
|
||||
ys = [string("y", i) for i = 1:4]
|
||||
z = float((1:4) * reshape(1:10, 1, :))
|
||||
pgfx_plot = heatmap(xs, ys, z, aspect_ratio=1)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
|
||||
end
|
||||
@testset "3D docs example" begin
|
||||
n = 100
|
||||
ts = range(0, stop = 8π, length = n)
|
||||
x = ts .* map(cos, ts)
|
||||
y = (0.1ts) .* map(sin, ts)
|
||||
z = 1:n
|
||||
pl = plot(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
zcolor = reverse(z),
|
||||
m = (10, 0.8, :blues, Plots.stroke(0)),
|
||||
leg = false,
|
||||
cbar = true,
|
||||
w = 5,
|
||||
)
|
||||
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w = 10)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(
|
||||
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||
)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
end
|
||||
end # testset
|
||||
@testset "Color docs example" begin
|
||||
y = rand(100)
|
||||
plot(
|
||||
0:10:100,
|
||||
rand(11, 4),
|
||||
lab = "lines",
|
||||
w = 3,
|
||||
palette = :grays,
|
||||
fill = 0,
|
||||
α = 0.6,
|
||||
)
|
||||
pl = scatter!(
|
||||
y,
|
||||
zcolor = abs.(y .- 0.5),
|
||||
m = (:heat, 0.8, Plots.stroke(1, :green)),
|
||||
ms = 10 * abs.(y .- 0.5) .+ 4,
|
||||
lab = ["grad", "", "ient"],
|
||||
)
|
||||
Plots._update_plot_object(pl)
|
||||
axis = Plots.pgfx_axes(pl.o)[1]
|
||||
@test count(x -> x isa PGFPlotsX.LegendEntry, axis.contents) == 6
|
||||
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 108 # each marker is its own plot, fillranges create 2 plot-objects
|
||||
marker = axis.contents[15]
|
||||
@test marker isa PGFPlotsX.Plot
|
||||
@test marker.options["mark"] == "*"
|
||||
@test marker.options["mark options"]["color"] ==
|
||||
RGBA{Float64}(colorant"green", 0.8)
|
||||
@test marker.options["mark options"]["line width"] == 1
|
||||
end # testset
|
||||
@testset "Plot in pieces" begin
|
||||
plot(rand(100) / 3, reg = true, fill = (0, :green))
|
||||
scatter!(rand(100), markersize = 6, c = :orange)
|
||||
end # testset
|
||||
@testset "Marker types" begin
|
||||
markers = filter((m -> begin
|
||||
m in Plots.supported_markers()
|
||||
end), Plots._shape_keys)
|
||||
markers = reshape(markers, 1, length(markers))
|
||||
n = length(markers)
|
||||
x = (range(0, stop = 10, length = n + 2))[2:(end - 1)]
|
||||
y = repeat(reshape(reverse(x), 1, :), n, 1)
|
||||
scatter(
|
||||
x,
|
||||
y,
|
||||
m = (8, :auto),
|
||||
lab = map(string, markers),
|
||||
bg = :linen,
|
||||
xlim = (0, 10),
|
||||
ylim = (0, 10),
|
||||
)
|
||||
end # testset
|
||||
@testset "Layout" begin
|
||||
plot(
|
||||
Plots.fakedata(100, 10),
|
||||
layout = 4,
|
||||
palette = [:grays :blues :heat :lightrainbow],
|
||||
bg_inside = [:orange :pink :darkblue :black],
|
||||
)
|
||||
end # testset
|
||||
@testset "Polar plots" begin
|
||||
Θ = range(0, stop = 1.5π, length = 100)
|
||||
r = abs.(0.1 * randn(100) + sin.(3Θ))
|
||||
plot(Θ, r, proj = :polar, m = 2)
|
||||
end # testset
|
||||
@testset "Drawing shapes" begin
|
||||
verts = [
|
||||
(-1.0, 1.0),
|
||||
(-1.28, 0.6),
|
||||
(-0.2, -1.4),
|
||||
(0.2, -1.4),
|
||||
(1.28, 0.6),
|
||||
(1.0, 1.0),
|
||||
(-1.0, 1.0),
|
||||
(-0.2, -0.6),
|
||||
(0.0, -0.2),
|
||||
(-0.4, 0.6),
|
||||
(1.28, 0.6),
|
||||
(0.2, -1.4),
|
||||
(-0.2, -1.4),
|
||||
(0.6, 0.2),
|
||||
(-0.2, 0.2),
|
||||
(0.0, -0.2),
|
||||
(0.2, 0.2),
|
||||
(-0.2, -0.6),
|
||||
]
|
||||
x = 0.1:0.2:0.9
|
||||
y = 0.7 * rand(5) .+ 0.15
|
||||
plot(
|
||||
x,
|
||||
y,
|
||||
line = (3, :dash, :lightblue),
|
||||
marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
|
||||
bg = :pink,
|
||||
fg = :darkblue,
|
||||
xlim = (0, 1),
|
||||
ylim = (0, 1),
|
||||
leg = false,
|
||||
)
|
||||
end # testset
|
||||
@testset "Histogram 2D" begin
|
||||
histogram2d(randn(10000), randn(10000), nbins = 20)
|
||||
end # testset
|
||||
@testset "Heatmap-like" begin
|
||||
xs = [string("x", i) for i = 1:10]
|
||||
ys = [string("y", i) for i = 1:4]
|
||||
z = float((1:4) * reshape(1:10, 1, :))
|
||||
pgfx_plot = heatmap(xs, ys, z, aspect_ratio = 1)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(
|
||||
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||
)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
|
||||
end
|
||||
|
||||
pgfx_plot = wireframe(xs, ys, z, aspect_ratio=1)
|
||||
# TODO: clims are wrong
|
||||
end # testset
|
||||
@testset "Contours" begin
|
||||
x = 1:0.5:20
|
||||
y = 1:0.5:10
|
||||
f(x, y) = begin
|
||||
(3x + y ^ 2) * abs(sin(x) + cos(y))
|
||||
end
|
||||
X = repeat(reshape(x, 1, :), length(y), 1)
|
||||
Y = repeat(y, 1, length(x))
|
||||
Z = map(f, X, Y)
|
||||
p2 = contour(x, y, Z)
|
||||
p1 = contour(x, y, f, fill=true)
|
||||
plot(p1, p2)
|
||||
# TODO: colorbar for filled contours
|
||||
end # testset
|
||||
@testset "Varying colors" 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=((x, y)->begin
|
||||
x + y
|
||||
end), color=:bluesreds, legend=false)
|
||||
plot(p1, p2)
|
||||
end # testset
|
||||
@testset "Framestyles" begin
|
||||
scatter(fill(randn(10), 6), fill(randn(10), 6), framestyle=[:box :semi :origin :zerolines :grid :none], title=[":box" ":semi" ":origin" ":zerolines" ":grid" ":none"], color=permutedims(1:6), layout=6, label="", markerstrokewidth=0, ticks=-2:2)
|
||||
# TODO: support :semi
|
||||
end # testset
|
||||
@testset "Quiver" begin
|
||||
x = -2pi:0.2:2*pi
|
||||
y = sin.(x)
|
||||
pgfx_plot = wireframe(xs, ys, z, aspect_ratio = 1)
|
||||
# TODO: clims are wrong
|
||||
end # testset
|
||||
@testset "Contours" begin
|
||||
x = 1:0.5:20
|
||||
y = 1:0.5:10
|
||||
f(x, y) = begin
|
||||
(3x + y^2) * abs(sin(x) + cos(y))
|
||||
end
|
||||
X = repeat(reshape(x, 1, :), length(y), 1)
|
||||
Y = repeat(y, 1, length(x))
|
||||
Z = map(f, X, Y)
|
||||
p2 = contour(x, y, Z)
|
||||
p1 = contour(x, y, f, fill = true)
|
||||
plot(p1, p2)
|
||||
# TODO: colorbar for filled contours
|
||||
end # testset
|
||||
@testset "Varying colors" 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 = ((x, y) -> begin
|
||||
x + y
|
||||
end),
|
||||
color = :bluesreds,
|
||||
legend = false,
|
||||
)
|
||||
plot(p1, p2)
|
||||
end # testset
|
||||
@testset "Framestyles" begin
|
||||
scatter(
|
||||
fill(randn(10), 6),
|
||||
fill(randn(10), 6),
|
||||
framestyle = [:box :semi :origin :zerolines :grid :none],
|
||||
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
|
||||
color = permutedims(1:6),
|
||||
layout = 6,
|
||||
label = "",
|
||||
markerstrokewidth = 0,
|
||||
ticks = -2:2,
|
||||
)
|
||||
# TODO: support :semi
|
||||
end # testset
|
||||
@testset "Quiver" begin
|
||||
x = (-2pi):0.2:(2 * pi)
|
||||
y = sin.(x)
|
||||
|
||||
u = ones(length(x))
|
||||
v = cos.(x)
|
||||
plot( x, y, quiver = (u, v), arrow = true )
|
||||
# TODO: could adjust limits to fit arrows if too long, but how?
|
||||
end # testset
|
||||
@testset "Annotations" begin
|
||||
y = rand(10)
|
||||
plot(y, annotations=(3, y[3], Plots.text("this is \\#3", :left)), leg=false)
|
||||
annotate!([(5, y[5], Plots.text("this is \\#5", 16, :red, :center)), (10, y[10], Plots.text("this is \\#10", :right, 20, "courier"))])
|
||||
scatter!(range(2, stop=8, length=6), rand(6), marker=(50, 0.2, :orange), series_annotations=["series", "annotations", "map", "to", "series", Plots.text("data", :green)])
|
||||
end # testset
|
||||
u = ones(length(x))
|
||||
v = cos.(x)
|
||||
arrow_plot = plot(x, y, quiver = (u, v), arrow = true)
|
||||
# TODO: could adjust limits to fit arrows if too long, but how?
|
||||
# TODO: get latex available on CI
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
|
||||
# end
|
||||
end # testset
|
||||
@testset "Annotations" begin
|
||||
y = rand(10)
|
||||
plot(
|
||||
y,
|
||||
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
|
||||
leg = false,
|
||||
)
|
||||
annotate!([
|
||||
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
|
||||
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
|
||||
])
|
||||
annotation_plot = scatter!(
|
||||
range(2, stop = 8, length = 6),
|
||||
rand(6),
|
||||
marker = (50, 0.2, :orange),
|
||||
series_annotations = [
|
||||
"series",
|
||||
"annotations",
|
||||
"map",
|
||||
"to",
|
||||
"series",
|
||||
Plots.text("data", :green),
|
||||
],
|
||||
)
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
|
||||
# end
|
||||
end # testset
|
||||
@testset "Ribbon" begin
|
||||
aa = rand(10)
|
||||
bb = rand(10)
|
||||
cc = rand(10)
|
||||
conf = [aa - cc bb - cc]
|
||||
ribbon_plot =
|
||||
plot(collect(1:10), fill(1, 10), ribbon = (conf[:, 1], conf[:, 2]))
|
||||
Plots._update_plot_object(ribbon_plot)
|
||||
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
|
||||
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
|
||||
@test length(plots) == 4
|
||||
@test !haskey(plots[1].options.dict, "fill")
|
||||
@test !haskey(plots[2].options.dict, "fill")
|
||||
@test !haskey(plots[3].options.dict, "fill")
|
||||
@test haskey(plots[4].options.dict, "fill")
|
||||
@test ribbon_plot.o !== nothing
|
||||
@test ribbon_plot.o.the_plot !== nothing
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
|
||||
# end
|
||||
end # testset
|
||||
end # testset
|
||||
|
||||
Reference in New Issue
Block a user