Merge branch 'master' into plotly_autoresize
This commit is contained in:
commit
86ff99f214
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@ examples/.ipynb_checkpoints/*
|
|||||||
examples/meetup/.ipynb_checkpoints/*
|
examples/meetup/.ipynb_checkpoints/*
|
||||||
deps/plotly-latest.min.js
|
deps/plotly-latest.min.js
|
||||||
deps/build.log
|
deps/build.log
|
||||||
|
deps/deps.jl
|
||||||
|
Manifest.toml
|
||||||
|
|||||||
48
.travis.yml
48
.travis.yml
@ -4,57 +4,17 @@ os:
|
|||||||
- linux
|
- linux
|
||||||
# - osx
|
# - osx
|
||||||
julia:
|
julia:
|
||||||
# - 1.0
|
- 1.1
|
||||||
- nightly
|
- nightly
|
||||||
# matrix:
|
|
||||||
# allow_failures:
|
|
||||||
# - julia: nightly
|
|
||||||
|
|
||||||
# # before install:
|
matrix:
|
||||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
allow_failures:
|
||||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install wkhtmltopdf; fi
|
- julia: nightly
|
||||||
|
|
||||||
# ref: http://askubuntu.com/a/556672 for the wkhtmltopdf apt repository info
|
|
||||||
|
|
||||||
sudo: required
|
sudo: required
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./test/install_wkhtmltoimage.sh ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./test/install_wkhtmltoimage.sh ; fi
|
||||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository -y ppa:pov/wkhtmltopdf ; fi
|
|
||||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update ; fi
|
|
||||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y wkhtmltopdf ; fi
|
|
||||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wkhtmltopdf -V ; fi
|
|
||||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wkhtmltoimage -V ; fi
|
|
||||||
# echo 'exec xvfb-run -a -s "-screen 0 640x480x16" wkhtmltopdf "$@"' | sudo tee /usr/local/bin/wkhtmltopdf.sh >/dev/null
|
|
||||||
# sudo chmod a+x /usr/local/bin/wkhtmltopdf.sh
|
|
||||||
|
|
||||||
# # borrowed from Blink.jl's travis file
|
|
||||||
# matrix:
|
|
||||||
# include:
|
|
||||||
# - os: linux
|
|
||||||
# julia: 0.4
|
|
||||||
# env: TESTCMD="xvfb-run julia"
|
|
||||||
# - os: osx
|
|
||||||
# julia: 0.4
|
|
||||||
# env: TESTCMD="julia"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: true
|
email: true
|
||||||
# uncomment the following lines to override the default test script
|
|
||||||
# script:
|
|
||||||
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
|
||||||
# - julia -e 'import Pkg; Pkg.add(Pkg.PackageSpec(path=pwd())); Pkg.build("Plots")'
|
|
||||||
# - julia test/travis_commands.jl
|
|
||||||
# - julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
|
|
||||||
# - julia -e 'Pkg.clone("GR"); Pkg.build("GR")'
|
|
||||||
# # - julia -e 'Pkg.clone("https://github.com/tbreloff/ImageMagick.jl.git"); Pkg.checkout("ImageMagick","tb_write"); Pkg.build("ImageMagick")'
|
|
||||||
# - julia -e 'Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");'
|
|
||||||
# # - julia -e 'Pkg.clone("https://github.com/JunoLab/Blink.jl.git"); Pkg.build("Blink"); import Blink; Blink.AtomShell.install()'
|
|
||||||
# # - julia -e 'Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")'
|
|
||||||
# - julia -e 'ENV["PYTHON"] = ""; Pkg.add("PyPlot"); Pkg.build("PyPlot")'
|
|
||||||
#
|
|
||||||
# # - $TESTCMD -e 'Pkg.test("Plots"; coverage=false)'
|
|
||||||
# - julia -e 'Pkg.test("Plots"; coverage=false)'
|
|
||||||
# # - julia -e 'cd(Pkg.dir("Plots")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(process_folder())'
|
|
||||||
|
|||||||
116
NEWS.md
116
NEWS.md
@ -10,7 +10,113 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
## (current master)
|
## (current master)
|
||||||
- All new development should target Julia 1.x!
|
|
||||||
|
## 0.26.0
|
||||||
|
- use FFMPEG.jl
|
||||||
|
- add missing method for convertToAnyVector
|
||||||
|
|
||||||
|
## 0.25.3
|
||||||
|
- add areaplot
|
||||||
|
- allow missing in z_color arguments
|
||||||
|
- more general tuple recipe
|
||||||
|
- stephist logscale improvements
|
||||||
|
|
||||||
|
## 0.25.2
|
||||||
|
- improvements to handle missings
|
||||||
|
- pyplot: allow setting the color gradient for z values
|
||||||
|
- document :colorbar_entry
|
||||||
|
- limit number of automatic bins
|
||||||
|
- fix ENV['PLOTS_DEFAULT_BACKEND']
|
||||||
|
- don't let aspect_ratio impact subplot size
|
||||||
|
- implement arrowstyle for GR
|
||||||
|
- fix bug in plotly_convert_to_datetime
|
||||||
|
- improve missing support
|
||||||
|
- gr: polar heatmaps
|
||||||
|
- make sure show returns nothing
|
||||||
|
|
||||||
|
## 0.25.1
|
||||||
|
- fix gr_display
|
||||||
|
|
||||||
|
## 0.25.0
|
||||||
|
- Replace StaticArrays with GeometryTypes
|
||||||
|
- Contour fixes for GR
|
||||||
|
|
||||||
|
## 0.24.0
|
||||||
|
- Update to the new PyCall and PyPlot API
|
||||||
|
- fix drawing of ticks
|
||||||
|
- fix y label position with GR
|
||||||
|
|
||||||
|
## 0.23.2
|
||||||
|
- pyplot fixes
|
||||||
|
- Add option :tex_output_standalone to set the 'include_preamble' argument in the PGFPlots backend.
|
||||||
|
- fix ticks
|
||||||
|
- support plotly json mime
|
||||||
|
- fix image axis limits
|
||||||
|
- default to radius 0 at center for polar plots
|
||||||
|
|
||||||
|
## 0.23.1
|
||||||
|
- slightly faster load time
|
||||||
|
- fixed errant MethodError
|
||||||
|
- fix bar plots with unicodeplots
|
||||||
|
- better colorbars for contour
|
||||||
|
- add volume seriestype for GR
|
||||||
|
- fix passing a tuple to custom ticks
|
||||||
|
- add vline to pgfplots
|
||||||
|
- add tex output for pyplot
|
||||||
|
- better 3d axis labels for GR
|
||||||
|
|
||||||
|
## 0.23.0
|
||||||
|
- compatible with StatPlots -> StatsPlots name shift
|
||||||
|
- fix histograms for vectors with NaN and Inf
|
||||||
|
- change gif behaviour (remove cache-busting)
|
||||||
|
- improved docstrings for shorthands functions
|
||||||
|
- fix font rotation for pyplot
|
||||||
|
- fix greyscale images for pyplot
|
||||||
|
- clamp greyscale images with values outside 0,1
|
||||||
|
- support keyword argument for font options
|
||||||
|
- allow vector of markers for pyplot scatter
|
||||||
|
|
||||||
|
## 0.22.5
|
||||||
|
- improve behaviour of plotlyjs backend
|
||||||
|
|
||||||
|
## 0.22.4
|
||||||
|
- Add support for discrete contourf plots with GR
|
||||||
|
|
||||||
|
## 0.22.3
|
||||||
|
- Fix the `showtheme` function
|
||||||
|
|
||||||
|
## 0.22.2
|
||||||
|
- Allow annotations to accept a Tuple instead of the result of a text call (making it possible to specify font characteristics in recipes). E.g. `annotations = (2, 4, ("test", :right, 8, :red))` is the same as `annotations = (2, 4, text("test", :right, 8, :red))`
|
||||||
|
|
||||||
|
## 0.22.1
|
||||||
|
- push PlotsDisplay just after REPLDisplay
|
||||||
|
|
||||||
|
## 0.22.0
|
||||||
|
- deprecate GLVisualize
|
||||||
|
- allow 1-row and 1-column heatmaps
|
||||||
|
- add portfoliodecomposition recipe from PlotRecipes
|
||||||
|
- solve Shape bug
|
||||||
|
- simplify PyPlot backend installation
|
||||||
|
- fix wireframe bug in PyPlot
|
||||||
|
- fix color bug in PyPlot
|
||||||
|
- minor bug fixes in gr and pyplot
|
||||||
|
|
||||||
|
## 0.21.0
|
||||||
|
- Compatibility with StaticArrays 0.9.0
|
||||||
|
- Up GR min version to 0.35
|
||||||
|
- fix :mirror
|
||||||
|
|
||||||
|
## 0.20.6
|
||||||
|
- fixes for PlotDocs.jl
|
||||||
|
- fix gr axis color argument
|
||||||
|
- Shapes for inspectdr
|
||||||
|
- don't load plotly js file by default
|
||||||
|
|
||||||
|
## 0.20.5
|
||||||
|
- fix precompilation issue when depending on Plots
|
||||||
|
|
||||||
|
## 0.20.4
|
||||||
|
- honour `html_output_format` in Juno
|
||||||
|
|
||||||
## 0.20.3
|
## 0.20.3
|
||||||
- implement guide position in gr, pyplot and pgfplots
|
- implement guide position in gr, pyplot and pgfplots
|
||||||
@ -153,7 +259,7 @@ Many updates, min julia 1.0
|
|||||||
- add `reset_defaults()` function to reset plot defaults
|
- add `reset_defaults()` function to reset plot defaults
|
||||||
- update syntax to 0.6
|
- update syntax to 0.6
|
||||||
- make `fill = true` fill to 0 rather than to 1
|
- make `fill = true` fill to 0 rather than to 1
|
||||||
- use new `@df` syntax in StatPlots examples
|
- use new `@df` syntax in StatsPlots examples
|
||||||
- allow changing the color of legend box
|
- allow changing the color of legend box
|
||||||
- implement `title_location` for gr
|
- implement `title_location` for gr
|
||||||
- add `hline` marker to pgfplots - fixes errorbars
|
- add `hline` marker to pgfplots - fixes errorbars
|
||||||
@ -305,7 +411,7 @@ Many updates, min julia 1.0
|
|||||||
- added dependency on PlotThemes
|
- added dependency on PlotThemes
|
||||||
- set_theme --> theme
|
- set_theme --> theme
|
||||||
- remove Compat from REQUIRE
|
- remove Compat from REQUIRE
|
||||||
- warning for DataFrames without StatPlots
|
- warning for DataFrames without StatsPlots
|
||||||
- closeall exported and implemented for gr/pyplot
|
- closeall exported and implemented for gr/pyplot
|
||||||
- fix DateTime recipe
|
- fix DateTime recipe
|
||||||
- reset theme with theme(:none)
|
- reset theme with theme(:none)
|
||||||
@ -427,8 +533,8 @@ Many updates, min julia 1.0
|
|||||||
#### 0.8.0
|
#### 0.8.0
|
||||||
|
|
||||||
- added dependency on PlotUtils
|
- added dependency on PlotUtils
|
||||||
- BREAKING: removed DataFrames support (now in StatPlots.jl)
|
- BREAKING: removed DataFrames support (now in StatsPlots.jl)
|
||||||
- BREAKING: removed boxplot/violin/density recipes (now in StatPlots.jl)
|
- BREAKING: removed boxplot/violin/density recipes (now in StatsPlots.jl)
|
||||||
- GR:
|
- GR:
|
||||||
- inline iterm2 support
|
- inline iterm2 support
|
||||||
- trisurface support
|
- trisurface support
|
||||||
|
|||||||
57
Project.toml
Normal file
57
Project.toml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
name = "Plots"
|
||||||
|
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||||
|
author = ["Tom Breloff (@tbreloff)"]
|
||||||
|
version = "0.26.0"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||||
|
Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
|
||||||
|
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||||
|
FFMPEG = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
|
||||||
|
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
|
||||||
|
GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
|
||||||
|
GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
|
||||||
|
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
|
||||||
|
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||||
|
Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
|
||||||
|
NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
|
||||||
|
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||||
|
PlotThemes = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
|
||||||
|
PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
|
||||||
|
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||||
|
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
||||||
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
|
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
|
||||||
|
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
|
||||||
|
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
|
||||||
|
Showoff = "992d4aef-0814-514b-bc4d-f2e9a6c4116f"
|
||||||
|
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
||||||
|
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
|
||||||
|
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
|
||||||
|
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||||
|
|
||||||
|
[compat]
|
||||||
|
FixedPointNumbers = "≥ 0.3.0"
|
||||||
|
GR = "≥ 0.31.0"
|
||||||
|
PlotThemes = "≥ 0.1.3"
|
||||||
|
PlotUtils = "≥ 0.4.1"
|
||||||
|
RecipesBase = "≥ 0.6.0"
|
||||||
|
StatsBase = "≥ 0.14.0"
|
||||||
|
julia = "≥ 1.0.0"
|
||||||
|
|
||||||
|
[extras]
|
||||||
|
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
|
||||||
|
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
|
||||||
|
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
|
||||||
|
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
|
||||||
|
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||||
|
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
|
||||||
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
|
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
|
||||||
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
|
||||||
|
VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92"
|
||||||
|
BinaryProvider = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
|
||||||
|
|
||||||
|
[targets]
|
||||||
|
test = ["BinaryProvider", "Pkg", "Test", "Random", "StatsPlots", "VisualRegressionTests", "LaTeXStrings", "Images", "ImageMagick", "RDatasets", "FileIO", "UnicodePlots"]
|
||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#### Created by Tom Breloff (@tbreloff)
|
#### Created by Tom Breloff (@tbreloff)
|
||||||
|
|
||||||
#### Maintained by the [JuliaPlot members](https://github.com/orgs/JuliaPlots/people)
|
#### Maintained by the [JuliaPlots members](https://github.com/orgs/JuliaPlots/people)
|
||||||
|
|
||||||
Plots is a plotting API and toolset. My goals with the package are:
|
Plots is a plotting API and toolset. My goals with the package are:
|
||||||
|
|
||||||
|
|||||||
16
REQUIRE
16
REQUIRE
@ -1,16 +0,0 @@
|
|||||||
julia 1.0
|
|
||||||
|
|
||||||
RecipesBase 0.6.0
|
|
||||||
PlotUtils 0.4.1
|
|
||||||
PlotThemes 0.1.3
|
|
||||||
Reexport
|
|
||||||
StaticArrays 0.5
|
|
||||||
FixedPointNumbers 0.3
|
|
||||||
Measures
|
|
||||||
Showoff
|
|
||||||
StatsBase 0.14.0
|
|
||||||
JSON
|
|
||||||
NaNMath
|
|
||||||
Requires
|
|
||||||
Contour
|
|
||||||
GR 0.34.0
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- julia_version: 0.7
|
# - julia_version: 0.7
|
||||||
- julia_version: 1
|
- julia_version: 1
|
||||||
- julia_version: nightly
|
- julia_version: nightly
|
||||||
|
|
||||||
|
|||||||
12
deps/build.jl
vendored
12
deps/build.jl
vendored
@ -1,8 +1,18 @@
|
|||||||
|
|
||||||
#TODO: download https://cdn.plot.ly/plotly-latest.min.js to deps/ if it doesn't exist
|
#TODO: download https://cdn.plot.ly/plotly-latest.min.js to deps/ if it doesn't exist
|
||||||
|
file_path = ""
|
||||||
|
if get(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL", "false") == "true"
|
||||||
|
global file_path
|
||||||
local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
|
local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
|
||||||
if !isfile(local_fn)
|
if !isfile(local_fn)
|
||||||
@info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
|
@info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
|
||||||
download("https://cdn.plot.ly/plotly-latest.min.js", local_fn)
|
download("https://cdn.plot.ly/plotly-latest.min.js", local_fn)
|
||||||
|
isfile(local_fn) && (file_path = local_fn)
|
||||||
|
else
|
||||||
|
file_path = local_fn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
open("deps.jl", "w") do io
|
||||||
|
println(io, "const plotly_local_file_path = $(repr(file_path))")
|
||||||
end
|
end
|
||||||
|
|||||||
115
src/Plots.jl
115
src/Plots.jl
@ -1,12 +1,15 @@
|
|||||||
module Plots
|
module Plots
|
||||||
|
|
||||||
|
_current_plots_version = v"0.25.0"
|
||||||
|
|
||||||
using Reexport
|
using Reexport
|
||||||
|
|
||||||
import StaticArrays
|
import GeometryTypes
|
||||||
using StaticArrays.FixedSizeArrays
|
using Dates, Printf, Statistics, Base64, LinearAlgebra, Random
|
||||||
using Dates, Printf, Statistics, Base64, LinearAlgebra
|
|
||||||
import SparseArrays: findnz
|
import SparseArrays: findnz
|
||||||
|
|
||||||
|
using FFMPEG
|
||||||
|
|
||||||
@reexport using RecipesBase
|
@reexport using RecipesBase
|
||||||
import RecipesBase: plot, plot!, animate
|
import RecipesBase: plot, plot!, animate
|
||||||
using Base.Meta
|
using Base.Meta
|
||||||
@ -18,6 +21,17 @@ import JSON
|
|||||||
|
|
||||||
using Requires
|
using Requires
|
||||||
|
|
||||||
|
if isfile(joinpath(@__DIR__, "..", "deps", "deps.jl"))
|
||||||
|
include(joinpath(@__DIR__, "..", "deps", "deps.jl"))
|
||||||
|
else
|
||||||
|
# This is a bit dirty, but I don't really see why anyone should be forced
|
||||||
|
# to build Plots, while it will just include exactly the below line
|
||||||
|
# as long as `ENV["PLOTS_HOST_DEPENDENCY_LOCAL"] = "true"` is not set.
|
||||||
|
# If the above env is set + `plotly_local_file_path == ""``,
|
||||||
|
# it will warn in the __init__ function to run build
|
||||||
|
const plotly_local_file_path = ""
|
||||||
|
end
|
||||||
|
|
||||||
export
|
export
|
||||||
grid,
|
grid,
|
||||||
bbox,
|
bbox,
|
||||||
@ -72,7 +86,6 @@ export
|
|||||||
backends,
|
backends,
|
||||||
backend_name,
|
backend_name,
|
||||||
backend_object,
|
backend_object,
|
||||||
add_backend,
|
|
||||||
aliases,
|
aliases,
|
||||||
|
|
||||||
Shape,
|
Shape,
|
||||||
@ -168,103 +181,15 @@ include("arg_desc.jl")
|
|||||||
include("plotattr.jl")
|
include("plotattr.jl")
|
||||||
include("backends.jl")
|
include("backends.jl")
|
||||||
include("output.jl")
|
include("output.jl")
|
||||||
|
include("ijulia.jl")
|
||||||
|
include("fileio.jl")
|
||||||
include("init.jl")
|
include("init.jl")
|
||||||
|
|
||||||
include("backends/plotly.jl")
|
include("backends/plotly.jl")
|
||||||
include("backends/gr.jl")
|
include("backends/gr.jl")
|
||||||
include("backends/web.jl")
|
include("backends/web.jl")
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
include("shorthands.jl")
|
||||||
|
|
||||||
@shorthands scatter
|
|
||||||
@shorthands bar
|
|
||||||
@shorthands barh
|
|
||||||
@shorthands histogram
|
|
||||||
@shorthands barhist
|
|
||||||
@shorthands stephist
|
|
||||||
@shorthands scatterhist
|
|
||||||
@shorthands histogram2d
|
|
||||||
@shorthands density
|
|
||||||
@shorthands heatmap
|
|
||||||
@shorthands plots_heatmap
|
|
||||||
@shorthands hexbin
|
|
||||||
@shorthands sticks
|
|
||||||
@shorthands hline
|
|
||||||
@shorthands vline
|
|
||||||
@shorthands hspan
|
|
||||||
@shorthands vspan
|
|
||||||
@shorthands ohlc
|
|
||||||
@shorthands contour
|
|
||||||
@shorthands contourf
|
|
||||||
@shorthands contour3d
|
|
||||||
@shorthands surface
|
|
||||||
@shorthands wireframe
|
|
||||||
@shorthands path3d
|
|
||||||
@shorthands scatter3d
|
|
||||||
@shorthands boxplot
|
|
||||||
@shorthands violin
|
|
||||||
@shorthands quiver
|
|
||||||
@shorthands curves
|
|
||||||
|
|
||||||
"Plot a pie diagram"
|
|
||||||
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
|
||||||
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
|
||||||
|
|
||||||
"Plot with seriestype :path3d"
|
|
||||||
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
|
|
||||||
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
|
|
||||||
|
|
||||||
"Add title to an existing plot"
|
|
||||||
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
|
|
||||||
|
|
||||||
"Add xlabel to an existing plot"
|
|
||||||
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
|
|
||||||
|
|
||||||
"Add ylabel to an existing plot"
|
|
||||||
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
|
|
||||||
|
|
||||||
"Set xlims for an existing plot"
|
|
||||||
xlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; xlims = lims, kw...)
|
|
||||||
|
|
||||||
"Set ylims for an existing plot"
|
|
||||||
ylims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; ylims = lims, kw...)
|
|
||||||
|
|
||||||
"Set zlims for an existing plot"
|
|
||||||
zlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; zlims = lims, kw...)
|
|
||||||
|
|
||||||
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
|
|
||||||
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
|
|
||||||
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin,zmax), kw...)
|
|
||||||
|
|
||||||
|
|
||||||
"Set xticks for an existing plot"
|
|
||||||
xticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; xticks = v, kw...)
|
|
||||||
|
|
||||||
"Set yticks for an existing plot"
|
|
||||||
yticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; yticks = v, kw...)
|
|
||||||
|
|
||||||
xticks!(
|
|
||||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; xticks = (ticks,labels), kw...)
|
|
||||||
yticks!(
|
|
||||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; yticks = (ticks,labels), kw...)
|
|
||||||
|
|
||||||
"Add annotations to an existing plot"
|
|
||||||
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
|
|
||||||
annotate!(anns::AVec{T}; kw...) where {T<:Tuple} = plot!(; annotation = anns, kw...)
|
|
||||||
|
|
||||||
"Flip the current plots' x axis"
|
|
||||||
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
|
|
||||||
|
|
||||||
"Flip the current plots' y axis"
|
|
||||||
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
|
|
||||||
|
|
||||||
"Specify x axis attributes for an existing plot"
|
|
||||||
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
|
||||||
|
|
||||||
"Specify x axis attributes for an existing plot"
|
|
||||||
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
|
||||||
xgrid!(args...; kw...) = plot!(; xgrid = args, kw...)
|
|
||||||
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
|
|
||||||
|
|
||||||
let PlotOrSubplot = Union{Plot, Subplot}
|
let PlotOrSubplot = Union{Plot, Subplot}
|
||||||
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||||
|
|||||||
@ -76,15 +76,15 @@ function buildanimation(animdir::AbstractString, fn::AbstractString,
|
|||||||
if variable_palette
|
if variable_palette
|
||||||
# generate a colorpalette for each frame for highest quality, but larger filesize
|
# generate a colorpalette for each frame for highest quality, but larger filesize
|
||||||
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
|
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
|
||||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -lavfi "$palette" -y $fn`)
|
ffmpeg_exe(`-v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -lavfi "$palette" -y $fn`)
|
||||||
else
|
else
|
||||||
# generate a colorpalette first so ffmpeg does not have to guess it
|
# generate a colorpalette first so ffmpeg does not have to guess it
|
||||||
run(`ffmpeg -v 0 -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
|
ffmpeg_exe(`-v 0 -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
|
||||||
# then apply the palette to get better results
|
# then apply the palette to get better results
|
||||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
|
ffmpeg_exe(` -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
ffmpeg_exe(`-v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
||||||
end
|
end
|
||||||
|
|
||||||
show_msg && @info("Saved animation to ", fn)
|
show_msg && @info("Saved animation to ", fn)
|
||||||
@ -93,16 +93,25 @@ end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
|
# write out html to view the gif
|
||||||
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
||||||
ext = file_extension(agif.filename)
|
ext = file_extension(agif.filename)
|
||||||
write(io, if ext == "gif"
|
write(io, if ext == "gif"
|
||||||
"<img src=\"$(relpath(agif.filename))?$(rand())>\" />"
|
"<img src=\"$(relpath(agif.filename))\" />"
|
||||||
elseif ext in ("mov", "mp4")
|
elseif ext in ("mov", "mp4")
|
||||||
"<video controls><source src=\"$(relpath(agif.filename))?$(rand())>\" type=\"video/$ext\"></video>"
|
"<video controls><source src=\"$(relpath(agif.filename)) type=\"video/$ext\"></video>"
|
||||||
else
|
else
|
||||||
error("Cannot show animation with extension $ext: $agif")
|
error("Cannot show animation with extension $ext: $agif")
|
||||||
end)
|
end)
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Only gifs can be shown via image/gif
|
||||||
|
Base.showable(::MIME"image/gif", agif::AnimatedGif) = file_extension(agif.filename) == "gif"
|
||||||
|
|
||||||
|
function Base.show(io::IO, ::MIME"image/gif", agif::AnimatedGif)
|
||||||
|
open(fio-> write(io, fio), agif.filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const _arg_desc = KW(
|
|||||||
:linewidth => "Number. Width of the line (in pixels)",
|
:linewidth => "Number. Width of the line (in pixels)",
|
||||||
:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).",
|
:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).",
|
||||||
:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.",
|
:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.",
|
||||||
:fillrange => "Number or AbstractVector. Fills area from this to y for line-types, sets the base for bar/stick types, and similar for other types.",
|
:fillrange => "Number or AbstractVector. Fills area between fillrange and y for line-types, sets the base for bar/stick types, and similar for other types.",
|
||||||
:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.",
|
:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.",
|
||||||
:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.",
|
:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.",
|
||||||
:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).",
|
:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).",
|
||||||
@ -21,7 +21,7 @@ const _arg_desc = KW(
|
|||||||
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
|
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
|
||||||
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
|
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
|
||||||
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
|
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
|
||||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(min(x), stop = extrema(x), length = 25)`",
|
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(minimum(x), stop = maximum(x), length = 25)`",
|
||||||
:smooth => "Bool. Add a regression line?",
|
:smooth => "Bool. Add a regression line?",
|
||||||
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
|
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
|
||||||
:x => "Various. Input data. First Dimension",
|
:x => "Various. Input data. First Dimension",
|
||||||
@ -30,7 +30,7 @@ const _arg_desc = KW(
|
|||||||
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
|
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
|
||||||
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
|
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
|
||||||
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
|
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
|
||||||
:levels => "Integer, NTuple{2,Integer}. Number of levels (or x-levels/y-levels) for a contour type.",
|
:levels => "Integer, NTuple{2,Integer}, or AbstractVector. Levels or number of levels (or x-levels/y-levels) for a contour type.",
|
||||||
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
|
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
|
||||||
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
|
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
|
||||||
:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
|
:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
|
||||||
@ -49,6 +49,7 @@ const _arg_desc = KW(
|
|||||||
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
|
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
|
||||||
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
|
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
|
||||||
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
|
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
|
||||||
|
:colorbar_entry => "Bool. Include this series in the color bar? Set to `false` to exclude.",
|
||||||
|
|
||||||
# plot args
|
# plot args
|
||||||
:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
|
:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
|
||||||
@ -63,6 +64,7 @@ const _arg_desc = KW(
|
|||||||
:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).",
|
:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).",
|
||||||
:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).",
|
:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).",
|
||||||
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
|
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
|
||||||
|
:tex_output_standalone => "Bool. When writing tex output, should the source include a preamble for a standalone document class.",
|
||||||
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
|
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
|
||||||
:dpi => "Number. Dots Per Inch of output figures",
|
:dpi => "Number. Dots Per Inch of output figures",
|
||||||
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
|
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
|
||||||
@ -98,7 +100,7 @@ const _arg_desc = KW(
|
|||||||
:legendfont => "Font. Font of legend items.",
|
:legendfont => "Font. Font of legend items.",
|
||||||
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
|
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
|
||||||
:projection => "Symbol or String. '3d' or 'polar'",
|
:projection => "Symbol or String. '3d' or 'polar'",
|
||||||
:aspect_ratio => "Symbol (:equal) or Number. Plot area is resized so that 1 y-unit is the same size as `apect_ratio` x-units.",
|
:aspect_ratio => "Symbol (:equal) or Number. Plot area is resized so that 1 y-unit is the same size as `aspect_ratio` x-units.",
|
||||||
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
|
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
|
||||||
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
|
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
|
||||||
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
|
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
|
||||||
@ -117,7 +119,7 @@ const _arg_desc = KW(
|
|||||||
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
|
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
|
||||||
:rotation => "Number. Degrees rotation of tick labels.",
|
:rotation => "Number. Degrees rotation of tick labels.",
|
||||||
:flip => "Bool. Should we flip (reverse) the axis?",
|
:flip => "Bool. Should we flip (reverse) the axis?",
|
||||||
:formatter => "Function, :scientific, or :auto. A method which converts a number to a string for tick labeling.",
|
:formatter => "Function, :scientific, :plain or :auto. A method which converts a number to a string for tick labeling.",
|
||||||
:tickfontfamily => "String or Symbol. Font family of tick labels.",
|
:tickfontfamily => "String or Symbol. Font family of tick labels.",
|
||||||
:tickfontsize => "Integer. Font pointsize of tick labels.",
|
:tickfontsize => "Integer. Font pointsize of tick labels.",
|
||||||
:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center",
|
:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center",
|
||||||
@ -149,4 +151,5 @@ const _arg_desc = KW(
|
|||||||
:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`",
|
:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`",
|
||||||
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
|
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
|
||||||
:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.",
|
:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.",
|
||||||
|
:draw_arrow => "Bool. Draw arrow at the end of the axis.",
|
||||||
)
|
)
|
||||||
|
|||||||
43
src/args.jl
43
src/args.jl
@ -232,6 +232,7 @@ const _bar_width = 0.8
|
|||||||
|
|
||||||
const _series_defaults = KW(
|
const _series_defaults = KW(
|
||||||
:label => "AUTO",
|
:label => "AUTO",
|
||||||
|
:colorbar_entry => true,
|
||||||
:seriescolor => :auto,
|
:seriescolor => :auto,
|
||||||
:seriesalpha => nothing,
|
:seriesalpha => nothing,
|
||||||
:seriestype => :path,
|
:seriestype => :path,
|
||||||
@ -298,6 +299,7 @@ const _plot_defaults = KW(
|
|||||||
:link => :none,
|
:link => :none,
|
||||||
:overwrite_figure => true,
|
:overwrite_figure => true,
|
||||||
:html_output_format => :auto,
|
:html_output_format => :auto,
|
||||||
|
:tex_output_standalone => false,
|
||||||
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
|
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
|
||||||
# the parent layout and the relative bounding box of inset subplots
|
# the parent layout and the relative bounding box of inset subplots
|
||||||
:dpi => DPI, # dots per inch for images, etc
|
:dpi => DPI, # dots per inch for images, etc
|
||||||
@ -390,6 +392,7 @@ const _axis_defaults = KW(
|
|||||||
:minorgrid => false,
|
:minorgrid => false,
|
||||||
:showaxis => true,
|
:showaxis => true,
|
||||||
:widen => true,
|
:widen => true,
|
||||||
|
:draw_arrow => false,
|
||||||
)
|
)
|
||||||
|
|
||||||
const _suppress_warnings = Set{Symbol}([
|
const _suppress_warnings = Set{Symbol}([
|
||||||
@ -735,6 +738,10 @@ function processMarkerArg(plotattributes::KW, arg)
|
|||||||
elseif allAlphas(arg)
|
elseif allAlphas(arg)
|
||||||
plotattributes[:markeralpha] = arg
|
plotattributes[:markeralpha] = arg
|
||||||
|
|
||||||
|
# bool
|
||||||
|
elseif typeof(arg) <: Bool
|
||||||
|
plotattributes[:markershape] = arg ? :circle : :none
|
||||||
|
|
||||||
# markersize
|
# markersize
|
||||||
elseif allReals(arg)
|
elseif allReals(arg)
|
||||||
plotattributes[:markersize] = arg
|
plotattributes[:markersize] = arg
|
||||||
@ -1023,7 +1030,7 @@ function preprocessArgs!(plotattributes::KW)
|
|||||||
arrow()
|
arrow()
|
||||||
elseif a in (false, nothing, :none)
|
elseif a in (false, nothing, :none)
|
||||||
nothing
|
nothing
|
||||||
elseif !(typeof(a) <: Arrow)
|
elseif !(typeof(a) <: Arrow || typeof(a) <: AbstractArray{Arrow})
|
||||||
arrow(wraptuple(a)...)
|
arrow(wraptuple(a)...)
|
||||||
else
|
else
|
||||||
a
|
a
|
||||||
@ -1050,8 +1057,8 @@ function preprocessArgs!(plotattributes::KW)
|
|||||||
|
|
||||||
# warnings for moved recipes
|
# warnings for moved recipes
|
||||||
st = get(plotattributes, :seriestype, :path)
|
st = get(plotattributes, :seriestype, :path)
|
||||||
if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatPlots)
|
if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatsPlots)
|
||||||
@warn("seriestype $st has been moved to StatPlots. To use: \`Pkg.add(\"StatPlots\"); using StatPlots\`")
|
@warn("seriestype $st has been moved to StatsPlots. To use: \`Pkg.add(\"StatsPlots\"); using StatsPlots\`")
|
||||||
end
|
end
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -1180,7 +1187,7 @@ function convertLegendValue(val::Symbol)
|
|||||||
:best
|
:best
|
||||||
elseif val in (:no, :none)
|
elseif val in (:no, :none)
|
||||||
:none
|
:none
|
||||||
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend, :topright, :topleft, :bottomleft, :bottomright, :outertopright)
|
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend, :topright, :topleft, :bottomleft, :bottomright, :outertopright, :outertopleft, :outertop, :outerright, :outerleft, :outerbottomright, :outerbottomleft, :outerbottom)
|
||||||
val
|
val
|
||||||
else
|
else
|
||||||
error("Invalid symbol for legend: $val")
|
error("Invalid symbol for legend: $val")
|
||||||
@ -1508,19 +1515,19 @@ function has_black_border_for_default(st::Symbol)
|
|||||||
like_histogram(st) || st in (:hexbin, :bar, :shape)
|
like_histogram(st) || st in (:hexbin, :bar, :shape)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# converts a symbol or string into a Colorant or ColorGradient
|
||||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
# and assigns a color automatically
|
||||||
function getSeriesRGBColor(c, sp::Subplot, n::Int)
|
function get_series_color(c, sp::Subplot, n::Int, seriestype)
|
||||||
if c == :auto
|
if c == :auto
|
||||||
c = autopick(sp[:color_palette], n)
|
c = like_surface(seriestype) ? cgrad() : autopick(sp[:color_palette], n)
|
||||||
elseif isa(c, Int)
|
elseif isa(c, Int)
|
||||||
c = autopick(sp[:color_palette], c)
|
c = autopick(sp[:color_palette], c)
|
||||||
end
|
end
|
||||||
plot_color(c)
|
plot_color(c)
|
||||||
end
|
end
|
||||||
|
|
||||||
function getSeriesRGBColor(c::AbstractArray, sp::Subplot, n::Int)
|
function get_series_color(c::AbstractArray, sp::Subplot, n::Int, seriestype)
|
||||||
map(x->getSeriesRGBColor(x, sp, n), c)
|
map(x->get_series_color(x, sp, n, seriestype), c)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ensure_gradient!(plotattributes::KW, csym::Symbol, asym::Symbol)
|
function ensure_gradient!(plotattributes::KW, csym::Symbol, asym::Symbol)
|
||||||
@ -1565,21 +1572,23 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# update series color
|
# update series color
|
||||||
plotattributes[:seriescolor] = getSeriesRGBColor(plotattributes[:seriescolor], sp, plotIndex)
|
scolor = plotattributes[:seriescolor]
|
||||||
|
stype = plotattributes[:seriestype]
|
||||||
|
plotattributes[:seriescolor] = scolor = get_series_color(scolor, sp, plotIndex, stype)
|
||||||
|
|
||||||
# update other colors
|
# update other colors
|
||||||
for s in (:line, :marker, :fill)
|
for s in (:line, :marker, :fill)
|
||||||
csym, asym = Symbol(s,:color), Symbol(s,:alpha)
|
csym, asym = Symbol(s,:color), Symbol(s,:alpha)
|
||||||
plotattributes[csym] = if plotattributes[csym] == :auto
|
plotattributes[csym] = if plotattributes[csym] == :auto
|
||||||
plot_color(if has_black_border_for_default(plotattributes[:seriestype]) && s == :line
|
plot_color(if has_black_border_for_default(stype) && s == :line
|
||||||
sp[:foreground_color_subplot]
|
sp[:foreground_color_subplot]
|
||||||
else
|
else
|
||||||
plotattributes[:seriescolor]
|
scolor
|
||||||
end)
|
end)
|
||||||
elseif plotattributes[csym] == :match
|
elseif plotattributes[csym] == :match
|
||||||
plot_color(plotattributes[:seriescolor])
|
plot_color(scolor)
|
||||||
else
|
else
|
||||||
getSeriesRGBColor(plotattributes[csym], sp, plotIndex)
|
get_series_color(plotattributes[csym], sp, plotIndex, stype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1587,9 +1596,9 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
|||||||
plotattributes[:markerstrokecolor] = if plotattributes[:markerstrokecolor] == :match
|
plotattributes[:markerstrokecolor] = if plotattributes[:markerstrokecolor] == :match
|
||||||
plot_color(sp[:foreground_color_subplot])
|
plot_color(sp[:foreground_color_subplot])
|
||||||
elseif plotattributes[:markerstrokecolor] == :auto
|
elseif plotattributes[:markerstrokecolor] == :auto
|
||||||
getSeriesRGBColor(plotattributes[:markercolor], sp, plotIndex)
|
get_series_color(plotattributes[:markercolor], sp, plotIndex, stype)
|
||||||
else
|
else
|
||||||
getSeriesRGBColor(plotattributes[:markerstrokecolor], sp, plotIndex)
|
get_series_color(plotattributes[:markerstrokecolor], sp, plotIndex, stype)
|
||||||
end
|
end
|
||||||
|
|
||||||
# if marker_z, fill_z or line_z are set, ensure we have a gradient
|
# if marker_z, fill_z or line_z are set, ensure we have a gradient
|
||||||
|
|||||||
84
src/axes.jl
84
src/axes.jl
@ -117,7 +117,7 @@ end
|
|||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
Base.show(io::IO, axis::Axis) = dumpdict(axis.plotattributes, "Axis", true)
|
Base.show(io::IO, axis::Axis) = dumpdict(io, axis.plotattributes, "Axis", true)
|
||||||
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.plotattributes, k)
|
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.plotattributes, k)
|
||||||
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.plotattributes, v, ks...)
|
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.plotattributes, v, ks...)
|
||||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.plotattributes, k)
|
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.plotattributes, k)
|
||||||
@ -152,8 +152,8 @@ scalefunc(scale::Symbol) = x -> get(_scale_funcs, scale, identity)(Float64(x))
|
|||||||
invscalefunc(scale::Symbol) = x -> get(_inv_scale_funcs, scale, identity)(Float64(x))
|
invscalefunc(scale::Symbol) = x -> get(_inv_scale_funcs, scale, identity)(Float64(x))
|
||||||
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
|
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
|
||||||
|
|
||||||
function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing)
|
||||||
amin,amax = axis_limits(axis)
|
amin, amax = axis_limits(sp, axis[:letter])
|
||||||
|
|
||||||
# scale the limits
|
# scale the limits
|
||||||
scale = axis[:scale]
|
scale = axis[:scale]
|
||||||
@ -238,9 +238,9 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# return (continuous_values, discrete_values) for the ticks on this axis
|
# return (continuous_values, discrete_values) for the ticks on this axis
|
||||||
function get_ticks(axis::Axis)
|
function get_ticks(sp::Subplot, axis::Axis)
|
||||||
ticks = _transform_ticks(axis[:ticks])
|
ticks = _transform_ticks(axis[:ticks])
|
||||||
ticks in (nothing, false) && return nothing
|
ticks in (:none, nothing, false) && return nothing
|
||||||
|
|
||||||
# treat :native ticks as :auto
|
# treat :native ticks as :auto
|
||||||
ticks = ticks == :native ? :auto : ticks
|
ticks = ticks == :native ? :auto : ticks
|
||||||
@ -251,7 +251,7 @@ function get_ticks(axis::Axis)
|
|||||||
# discrete ticks...
|
# discrete ticks...
|
||||||
n = length(dvals)
|
n = length(dvals)
|
||||||
rng = if ticks == :auto
|
rng = if ticks == :auto
|
||||||
Int[round(Int,i) for i in range(1, stop=n, length=15)]
|
Int[round(Int,i) for i in range(1, stop=n, length=min(n,15))]
|
||||||
else # if ticks == :all
|
else # if ticks == :all
|
||||||
1:n
|
1:n
|
||||||
end
|
end
|
||||||
@ -261,7 +261,7 @@ function get_ticks(axis::Axis)
|
|||||||
(collect(0:pi/4:7pi/4), string.(0:45:315))
|
(collect(0:pi/4:7pi/4), string.(0:45:315))
|
||||||
else
|
else
|
||||||
# compute optimal ticks and labels
|
# compute optimal ticks and labels
|
||||||
optimal_ticks_and_labels(axis)
|
optimal_ticks_and_labels(sp, axis)
|
||||||
end
|
end
|
||||||
elseif typeof(ticks) <: Union{AVec, Int}
|
elseif typeof(ticks) <: Union{AVec, Int}
|
||||||
if !isempty(dvals) && typeof(ticks) <: Int
|
if !isempty(dvals) && typeof(ticks) <: Int
|
||||||
@ -269,7 +269,7 @@ function get_ticks(axis::Axis)
|
|||||||
axis[:continuous_values][rng], dvals[rng]
|
axis[:continuous_values][rng], dvals[rng]
|
||||||
else
|
else
|
||||||
# override ticks, but get the labels
|
# override ticks, but get the labels
|
||||||
optimal_ticks_and_labels(axis, ticks)
|
optimal_ticks_and_labels(sp, axis, ticks)
|
||||||
end
|
end
|
||||||
elseif typeof(ticks) <: NTuple{2, Any}
|
elseif typeof(ticks) <: NTuple{2, Any}
|
||||||
# assuming we're passed (ticks, labels)
|
# assuming we're passed (ticks, labels)
|
||||||
@ -286,12 +286,12 @@ _transform_ticks(ticks) = ticks
|
|||||||
_transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks)
|
_transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks)
|
||||||
_transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2])
|
_transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2])
|
||||||
|
|
||||||
function get_minor_ticks(axis,ticks)
|
function get_minor_ticks(sp, axis, ticks)
|
||||||
axis[:minorticks] in (nothing, false) && !axis[:minorgrid] && return nothing
|
axis[:minorticks] in (:none, nothing, false) && !axis[:minorgrid] && return nothing
|
||||||
ticks = ticks[1]
|
ticks = ticks[1]
|
||||||
length(ticks) < 2 && return nothing
|
length(ticks) < 2 && return nothing
|
||||||
|
|
||||||
amin, amax = axis_limits(axis)
|
amin, amax = axis_limits(sp, axis[:letter])
|
||||||
#Add one phantom tick either side of the ticks to ensure minor ticks extend to the axis limits
|
#Add one phantom tick either side of the ticks to ensure minor ticks extend to the axis limits
|
||||||
if length(ticks) > 2
|
if length(ticks) > 2
|
||||||
ratio = (ticks[3] - ticks[2])/(ticks[2] - ticks[1])
|
ratio = (ticks[3] - ticks[2])/(ticks[2] - ticks[1])
|
||||||
@ -459,7 +459,7 @@ const _widen_seriestypes = (:line, :path, :steppre, :steppost, :sticks, :scatter
|
|||||||
|
|
||||||
function default_should_widen(axis::Axis)
|
function default_should_widen(axis::Axis)
|
||||||
should_widen = false
|
should_widen = false
|
||||||
if !is_2tuple(axis[:lims])
|
if !(is_2tuple(axis[:lims]) || axis[:lims] == :round)
|
||||||
for sp in axis.sps
|
for sp in axis.sps
|
||||||
for series in series_list(sp)
|
for series in series_list(sp)
|
||||||
if series.plotattributes[:seriestype] in _widen_seriestypes
|
if series.plotattributes[:seriestype] in _widen_seriestypes
|
||||||
@ -479,11 +479,13 @@ function round_limits(amin,amax)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# using the axis extrema and limit overrides, return the min/max value for this axis
|
# using the axis extrema and limit overrides, return the min/max value for this axis
|
||||||
function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis))
|
function axis_limits(sp, letter, should_widen = default_should_widen(sp[Symbol(letter, :axis)]), consider_aspect = true)
|
||||||
|
axis = sp[Symbol(letter, :axis)]
|
||||||
ex = axis[:extrema]
|
ex = axis[:extrema]
|
||||||
amin, amax = ex.emin, ex.emax
|
amin, amax = ex.emin, ex.emax
|
||||||
lims = axis[:lims]
|
lims = axis[:lims]
|
||||||
if (isa(lims, Tuple) || isa(lims, AVec)) && length(lims) == 2
|
has_user_lims = (isa(lims, Tuple) || isa(lims, AVec)) && length(lims) == 2
|
||||||
|
if has_user_lims
|
||||||
if isfinite(lims[1])
|
if isfinite(lims[1])
|
||||||
amin = lims[1]
|
amin = lims[1]
|
||||||
end
|
end
|
||||||
@ -497,12 +499,12 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
|
|||||||
if !isfinite(amin) && !isfinite(amax)
|
if !isfinite(amin) && !isfinite(amax)
|
||||||
amin, amax = 0.0, 1.0
|
amin, amax = 0.0, 1.0
|
||||||
end
|
end
|
||||||
if ispolar(axis.sps[1])
|
amin, amax = if ispolar(axis.sps[1])
|
||||||
if axis[:letter] == :x
|
if axis[:letter] == :x
|
||||||
amin, amax = 0, 2pi
|
amin, amax = 0, 2pi
|
||||||
elseif lims == :auto
|
elseif lims == :auto
|
||||||
#widen max radius so ticks dont overlap with theta axis
|
#widen max radius so ticks dont overlap with theta axis
|
||||||
amin, amax + 0.1 * abs(amax - amin)
|
0, amax + 0.1 * abs(amax - amin)
|
||||||
else
|
else
|
||||||
amin, amax
|
amin, amax
|
||||||
end
|
end
|
||||||
@ -513,6 +515,32 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
|
|||||||
else
|
else
|
||||||
amin, amax
|
amin, amax
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if !has_user_lims && consider_aspect && letter in (:x, :y) && !(sp[:aspect_ratio] in (:none, :auto) || is3d(:sp))
|
||||||
|
aspect_ratio = isa(sp[:aspect_ratio], Number) ? sp[:aspect_ratio] : 1
|
||||||
|
plot_ratio = height(plotarea(sp)) / width(plotarea(sp))
|
||||||
|
dist = amax - amin
|
||||||
|
|
||||||
|
if letter == :x
|
||||||
|
yamin, yamax = axis_limits(sp, :y, default_should_widen(sp[:yaxis]), false)
|
||||||
|
ydist = yamax - yamin
|
||||||
|
axis_ratio = aspect_ratio * ydist / dist
|
||||||
|
factor = axis_ratio / plot_ratio
|
||||||
|
else
|
||||||
|
xamin, xamax = axis_limits(sp, :x, default_should_widen(sp[:xaxis]), false)
|
||||||
|
xdist = xamax - xamin
|
||||||
|
axis_ratio = aspect_ratio * dist / xdist
|
||||||
|
factor = plot_ratio / axis_ratio
|
||||||
|
end
|
||||||
|
|
||||||
|
if factor > 1
|
||||||
|
center = (amin + amax) / 2
|
||||||
|
amin = center + factor * (amin - center)
|
||||||
|
amax = center + factor * (amax - center)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return amin, amax
|
||||||
end
|
end
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
@ -586,12 +614,12 @@ end
|
|||||||
# compute the line segments which should be drawn for this axis
|
# compute the line segments which should be drawn for this axis
|
||||||
function axis_drawing_info(sp::Subplot)
|
function axis_drawing_info(sp::Subplot)
|
||||||
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
|
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
|
||||||
xmin, xmax = axis_limits(xaxis)
|
xmin, xmax = axis_limits(sp, :x)
|
||||||
ymin, ymax = axis_limits(yaxis)
|
ymin, ymax = axis_limits(sp, :y)
|
||||||
xticks = get_ticks(xaxis)
|
xticks = get_ticks(sp, xaxis)
|
||||||
yticks = get_ticks(yaxis)
|
yticks = get_ticks(sp, yaxis)
|
||||||
xminorticks = get_minor_ticks(xaxis,xticks)
|
xminorticks = get_minor_ticks(sp, xaxis, xticks)
|
||||||
yminorticks = get_minor_ticks(yaxis,yticks)
|
yminorticks = get_minor_ticks(sp, yaxis, yticks)
|
||||||
xaxis_segs = Segments(2)
|
xaxis_segs = Segments(2)
|
||||||
yaxis_segs = Segments(2)
|
yaxis_segs = Segments(2)
|
||||||
xtick_segs = Segments(2)
|
xtick_segs = Segments(2)
|
||||||
@ -614,14 +642,14 @@ function axis_drawing_info(sp::Subplot)
|
|||||||
end
|
end
|
||||||
push!(xaxis_segs, (xmin, y1), (xmax, y1))
|
push!(xaxis_segs, (xmin, y1), (xmax, y1))
|
||||||
# don't show the 0 tick label for the origin framestyle
|
# don't show the 0 tick label for the origin framestyle
|
||||||
if sp[:framestyle] == :origin && !(xticks in (nothing,false)) && length(xticks) > 1
|
if sp[:framestyle] == :origin && !(xticks in (:none, nothing, false)) && length(xticks) > 1
|
||||||
showticks = xticks[1] .!= 0
|
showticks = xticks[1] .!= 0
|
||||||
xticks = (xticks[1][showticks], xticks[2][showticks])
|
xticks = (xticks[1][showticks], xticks[2][showticks])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin, y2), (xmax, y2)) # top spine
|
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin, y2), (xmax, y2)) # top spine
|
||||||
end
|
end
|
||||||
if !(xaxis[:ticks] in (nothing, false))
|
if !(xaxis[:ticks] in (:none, nothing, false))
|
||||||
f = scalefunc(yaxis[:scale])
|
f = scalefunc(yaxis[:scale])
|
||||||
invf = invscalefunc(yaxis[:scale])
|
invf = invscalefunc(yaxis[:scale])
|
||||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||||
@ -642,7 +670,7 @@ function axis_drawing_info(sp::Subplot)
|
|||||||
xaxis[:grid] && push!(xgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid
|
xaxis[:grid] && push!(xgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if !(xaxis[:minorticks] in (nothing, false)) || xaxis[:minorgrid]
|
if !(xaxis[:minorticks] in (:none, nothing, false)) || xaxis[:minorgrid]
|
||||||
f = scalefunc(yaxis[:scale])
|
f = scalefunc(yaxis[:scale])
|
||||||
invf = invscalefunc(yaxis[:scale])
|
invf = invscalefunc(yaxis[:scale])
|
||||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||||
@ -675,14 +703,14 @@ function axis_drawing_info(sp::Subplot)
|
|||||||
end
|
end
|
||||||
push!(yaxis_segs, (x1, ymin), (x1, ymax))
|
push!(yaxis_segs, (x1, ymin), (x1, ymax))
|
||||||
# don't show the 0 tick label for the origin framestyle
|
# don't show the 0 tick label for the origin framestyle
|
||||||
if sp[:framestyle] == :origin && !(yticks in (nothing,false)) && length(yticks) > 1
|
if sp[:framestyle] == :origin && !(yticks in (:none, nothing,false)) && length(yticks) > 1
|
||||||
showticks = yticks[1] .!= 0
|
showticks = yticks[1] .!= 0
|
||||||
yticks = (yticks[1][showticks], yticks[2][showticks])
|
yticks = (yticks[1][showticks], yticks[2][showticks])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (x2, ymin), (x2, ymax)) # right spine
|
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (x2, ymin), (x2, ymax)) # right spine
|
||||||
end
|
end
|
||||||
if !(yaxis[:ticks] in (nothing, false))
|
if !(yaxis[:ticks] in (:none, nothing, false))
|
||||||
f = scalefunc(xaxis[:scale])
|
f = scalefunc(xaxis[:scale])
|
||||||
invf = invscalefunc(xaxis[:scale])
|
invf = invscalefunc(xaxis[:scale])
|
||||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||||
@ -703,7 +731,7 @@ function axis_drawing_info(sp::Subplot)
|
|||||||
yaxis[:grid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
yaxis[:grid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if !(yaxis[:minorticks] in (nothing, false)) || yaxis[:minorgrid]
|
if !(yaxis[:minorticks] in (:none, nothing, false)) || yaxis[:minorgrid]
|
||||||
f = scalefunc(xaxis[:scale])
|
f = scalefunc(xaxis[:scale])
|
||||||
invf = invscalefunc(xaxis[:scale])
|
invf = invscalefunc(xaxis[:scale])
|
||||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||||
|
|||||||
489
src/backends.jl
489
src/backends.jl
@ -7,16 +7,20 @@ const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none)
|
|||||||
const _backends = Symbol[]
|
const _backends = Symbol[]
|
||||||
const _initialized_backends = Set{Symbol}()
|
const _initialized_backends = Set{Symbol}()
|
||||||
const _default_backends = (:none, :gr, :plotly)
|
const _default_backends = (:none, :gr, :plotly)
|
||||||
const _backendPackage = Dict{Symbol, Symbol}()
|
|
||||||
|
const _backend_packages = Dict{Symbol, Symbol}()
|
||||||
|
|
||||||
"Returns a list of supported backends"
|
"Returns a list of supported backends"
|
||||||
backends() = _backends
|
backends() = _backends
|
||||||
|
|
||||||
"Returns the name of the current backend"
|
"Returns the name of the current backend"
|
||||||
backend_name() = CURRENT_BACKEND.sym
|
backend_name() = CURRENT_BACKEND.sym
|
||||||
_backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
|
|
||||||
backend_package(pkg::Symbol) = pkg in _default_backends ? :Plots : Symbol("Plots", _backendPackage[pkg])
|
function _backend_instance(sym::Symbol)::AbstractBackend
|
||||||
backend_package_name(sym::Symbol) = sym in _default_backends ? :Plots : _backendPackage[sym]
|
haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
|
||||||
|
end
|
||||||
|
|
||||||
|
backend_package_name(sym::Symbol) = _backend_packages[sym]
|
||||||
|
|
||||||
macro init_backend(s)
|
macro init_backend(s)
|
||||||
package_str = string(s)
|
package_str = string(s)
|
||||||
@ -26,13 +30,13 @@ macro init_backend(s)
|
|||||||
esc(quote
|
esc(quote
|
||||||
struct $T <: AbstractBackend end
|
struct $T <: AbstractBackend end
|
||||||
export $sym
|
export $sym
|
||||||
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
|
$sym(; kw...) = (default(; kw...); backend($T()))
|
||||||
backend_name(::$T) = Symbol($str)
|
backend_name(::$T) = Symbol($str)
|
||||||
backend_package_name(pkg::$T) = backend_package_name(Symbol($str))
|
backend_package_name(pkg::$T) = backend_package_name(Symbol($str))
|
||||||
push!(_backends, Symbol($str))
|
push!(_backends, Symbol($str))
|
||||||
_backendType[Symbol($str)] = $T
|
_backendType[Symbol($str)] = $T
|
||||||
_backendSymbol[$T] = Symbol($str)
|
_backendSymbol[$T] = Symbol($str)
|
||||||
_backendPackage[Symbol($str)] = Symbol($package_str)
|
_backend_packages[Symbol($str)] = Symbol($package_str)
|
||||||
# include("backends/" * $str * ".jl")
|
# include("backends/" * $str * ".jl")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
@ -42,12 +46,6 @@ end
|
|||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
function add_backend(pkg::Symbol)
|
|
||||||
@info("To do a standard install of $pkg, copy and run this:\n\n")
|
|
||||||
println(add_backend_string(_backend_instance(pkg)))
|
|
||||||
println()
|
|
||||||
end
|
|
||||||
|
|
||||||
# don't do anything as a default
|
# don't do anything as a default
|
||||||
_create_backend_figure(plt::Plot) = nothing
|
_create_backend_figure(plt::Plot) = nothing
|
||||||
_prepare_plot_object(plt::Plot) = nothing
|
_prepare_plot_object(plt::Plot) = nothing
|
||||||
@ -77,8 +75,8 @@ end
|
|||||||
text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(lab), sz, rot)
|
text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(lab), sz, rot)
|
||||||
|
|
||||||
# account for the size/length/rotation of tick labels
|
# account for the size/length/rotation of tick labels
|
||||||
function tick_padding(axis::Axis)
|
function tick_padding(sp::Subplot, axis::Axis)
|
||||||
ticks = get_ticks(axis)
|
ticks = get_ticks(sp, axis)
|
||||||
if ticks == nothing
|
if ticks == nothing
|
||||||
0mm
|
0mm
|
||||||
else
|
else
|
||||||
@ -108,10 +106,10 @@ end
|
|||||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||||
function _update_min_padding!(sp::Subplot)
|
function _update_min_padding!(sp::Subplot)
|
||||||
# TODO: something different when `is3d(sp) == true`
|
# TODO: something different when `is3d(sp) == true`
|
||||||
leftpad = tick_padding(sp[:yaxis]) + sp[:left_margin] + guide_padding(sp[:yaxis])
|
leftpad = tick_padding(sp, sp[:yaxis]) + sp[:left_margin] + guide_padding(sp[:yaxis])
|
||||||
toppad = sp[:top_margin] + title_padding(sp)
|
toppad = sp[:top_margin] + title_padding(sp)
|
||||||
rightpad = sp[:right_margin]
|
rightpad = sp[:right_margin]
|
||||||
bottompad = tick_padding(sp[:xaxis]) + sp[:bottom_margin] + guide_padding(sp[:xaxis])
|
bottompad = tick_padding(sp, sp[:xaxis]) + sp[:bottom_margin] + guide_padding(sp[:xaxis])
|
||||||
|
|
||||||
# switch them?
|
# switch them?
|
||||||
if sp[:xaxis][:mirror]
|
if sp[:xaxis][:mirror]
|
||||||
@ -138,33 +136,22 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
|
|||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
function pickDefaultBackend()
|
_fallback_default_backend() = backend(GRBackend())
|
||||||
|
|
||||||
|
function _pick_default_backend()
|
||||||
env_default = get(ENV, "PLOTS_DEFAULT_BACKEND", "")
|
env_default = get(ENV, "PLOTS_DEFAULT_BACKEND", "")
|
||||||
if env_default != ""
|
if env_default != ""
|
||||||
sym = Symbol(lowercase(env_default))
|
sym = Symbol(lowercase(env_default))
|
||||||
if sym in _backends
|
if sym in _backends
|
||||||
if sym in _initialized_backends
|
backend(sym)
|
||||||
return backend(sym)
|
|
||||||
else
|
else
|
||||||
@warn("You have set `PLOTS_DEFAULT_BACKEND=$env_default` but `$(backend_package_name(sym))` is not loaded.")
|
@warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t" *
|
||||||
end
|
|
||||||
else
|
|
||||||
@warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t",
|
|
||||||
join(sort(_backends), "\n\t"))
|
join(sort(_backends), "\n\t"))
|
||||||
|
_fallback_default_backend()
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
_fallback_default_backend()
|
||||||
end
|
end
|
||||||
|
|
||||||
# the ordering/inclusion of this package list is my semi-arbitrary guess at
|
|
||||||
# which one someone will want to use if they have the package installed...accounting for
|
|
||||||
# features, speed, and robustness
|
|
||||||
# for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize")
|
|
||||||
# if pkgstr in keys(Pkg.installed())
|
|
||||||
# return backend(Symbol(lowercase(pkgstr)))
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# the default if nothing else is installed
|
|
||||||
backend(:gr)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -174,10 +161,8 @@ end
|
|||||||
Returns the current plotting package name. Initializes package on first call.
|
Returns the current plotting package name. Initializes package on first call.
|
||||||
"""
|
"""
|
||||||
function backend()
|
function backend()
|
||||||
|
|
||||||
global CURRENT_BACKEND
|
|
||||||
if CURRENT_BACKEND.sym == :none
|
if CURRENT_BACKEND.sym == :none
|
||||||
pickDefaultBackend()
|
_pick_default_backend()
|
||||||
end
|
end
|
||||||
|
|
||||||
CURRENT_BACKEND.pkg
|
CURRENT_BACKEND.pkg
|
||||||
@ -188,20 +173,13 @@ Set the plot backend.
|
|||||||
"""
|
"""
|
||||||
function backend(pkg::AbstractBackend)
|
function backend(pkg::AbstractBackend)
|
||||||
sym = backend_name(pkg)
|
sym = backend_name(pkg)
|
||||||
if sym in _initialized_backends
|
if !(sym in _initialized_backends)
|
||||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
|
||||||
CURRENT_BACKEND.pkg = pkg
|
|
||||||
else
|
|
||||||
# try
|
|
||||||
_initialize_backend(pkg)
|
_initialize_backend(pkg)
|
||||||
push!(_initialized_backends, sym)
|
push!(_initialized_backends, sym)
|
||||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
|
||||||
CURRENT_BACKEND.pkg = pkg
|
|
||||||
# catch
|
|
||||||
# add_backend(sym)
|
|
||||||
# end
|
|
||||||
end
|
end
|
||||||
backend()
|
CURRENT_BACKEND.sym = sym
|
||||||
|
CURRENT_BACKEND.pkg = pkg
|
||||||
|
pkg
|
||||||
end
|
end
|
||||||
|
|
||||||
function backend(sym::Symbol)
|
function backend(sym::Symbol)
|
||||||
@ -209,15 +187,15 @@ function backend(sym::Symbol)
|
|||||||
backend(_backend_instance(sym))
|
backend(_backend_instance(sym))
|
||||||
else
|
else
|
||||||
@warn("`:$sym` is not a supported backend.")
|
@warn("`:$sym` is not a supported backend.")
|
||||||
end
|
|
||||||
backend()
|
backend()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse]
|
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse, :glvisualize]
|
||||||
|
|
||||||
function warn_on_deprecated_backend(bsym::Symbol)
|
function warn_on_deprecated_backend(bsym::Symbol)
|
||||||
if bsym in _deprecated_backends
|
if bsym in _deprecated_backends
|
||||||
@warn("Backend $bsym has been deprecated. It may not work as originally intended.")
|
@warn("Backend $bsym has been deprecated.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -268,17 +246,11 @@ end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# @init_backend Immerse
|
|
||||||
# @init_backend Gadfly
|
|
||||||
@init_backend PyPlot
|
@init_backend PyPlot
|
||||||
# @init_backend Qwt
|
|
||||||
@init_backend UnicodePlots
|
@init_backend UnicodePlots
|
||||||
# @init_backend Winston
|
|
||||||
# @init_backend Bokeh
|
|
||||||
@init_backend Plotly
|
@init_backend Plotly
|
||||||
@init_backend PlotlyJS
|
@init_backend PlotlyJS
|
||||||
@init_backend GR
|
@init_backend GR
|
||||||
@init_backend GLVisualize
|
|
||||||
@init_backend PGFPlots
|
@init_backend PGFPlots
|
||||||
@init_backend InspectDR
|
@init_backend InspectDR
|
||||||
@init_backend HDF5
|
@init_backend HDF5
|
||||||
@ -322,72 +294,191 @@ function _initialize_backend(pkg::AbstractBackend)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function add_backend_string(pkg::AbstractBackend)
|
_initialize_backend(pkg::GRBackend) = nothing
|
||||||
sym = backend_package_name(pkg)
|
|
||||||
"""
|
_initialize_backend(pkg::PlotlyBackend) = nothing
|
||||||
using Pkg
|
|
||||||
Pkg.add("$sym")
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# glvisualize
|
# gr
|
||||||
|
|
||||||
function _initialize_backend(::GLVisualizeBackend; kw...)
|
const _gr_attr = merge_with_base_supported([
|
||||||
@eval Main begin
|
:annotations,
|
||||||
import GLVisualize, GeometryTypes, Reactive, GLAbstraction, GLWindow, Contour
|
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||||
import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0, GLNormalMesh, SimpleRectangle, Point, Vec
|
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||||
import FileIO, Images
|
:foreground_color_text, :foreground_color_border,
|
||||||
export GLVisualize
|
:label,
|
||||||
import Reactive: Signal
|
:seriescolor, :seriesalpha,
|
||||||
import GLAbstraction: Style
|
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||||
import GLVisualize: visualize
|
:markershape, :markercolor, :markersize, :markeralpha,
|
||||||
import Plots.GL
|
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||||
import UnicodeFun
|
:fillrange, :fillcolor, :fillalpha,
|
||||||
end
|
:bins,
|
||||||
end
|
:layout,
|
||||||
|
: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,
|
||||||
|
:orientation,
|
||||||
|
:overwrite_figure,
|
||||||
|
:polar,
|
||||||
|
:aspect_ratio,
|
||||||
|
:normalize, :weights,
|
||||||
|
:inset_subplots,
|
||||||
|
:bar_width,
|
||||||
|
:arrow,
|
||||||
|
:framestyle,
|
||||||
|
:tick_direction,
|
||||||
|
:camera,
|
||||||
|
:contour_labels,
|
||||||
|
])
|
||||||
|
const _gr_seriestype = [
|
||||||
|
:path, :scatter, :straightline,
|
||||||
|
:heatmap, :pie, :image,
|
||||||
|
:contour, :path3d, :scatter3d, :surface, :wireframe, :volume,
|
||||||
|
:shape
|
||||||
|
]
|
||||||
|
const _gr_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||||
|
const _gr_marker = _allMarkers
|
||||||
|
const _gr_scale = [:identity, :log10]
|
||||||
|
is_marker_supported(::GRBackend, shape::Shape) = true
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# hdf5
|
# plotly
|
||||||
|
|
||||||
function _initialize_backend(::HDF5Backend)
|
const _plotly_attr = merge_with_base_supported([
|
||||||
@eval Main begin
|
:annotations,
|
||||||
import HDF5
|
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||||
export HDF5
|
:foreground_color_legend, :foreground_color_guide,
|
||||||
end
|
:foreground_color_grid, :foreground_color_axis,
|
||||||
end
|
:foreground_color_text, :foreground_color_border,
|
||||||
|
:foreground_color_title,
|
||||||
|
:label,
|
||||||
|
:seriescolor, :seriesalpha,
|
||||||
|
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||||
|
:markershape, :markercolor, :markersize, :markeralpha,
|
||||||
|
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||||
|
:fillrange, :fillcolor, :fillalpha,
|
||||||
|
:bins,
|
||||||
|
:title, :title_location,
|
||||||
|
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
|
||||||
|
:titlefontcolor,
|
||||||
|
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||||
|
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||||
|
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||||
|
:window_title,
|
||||||
|
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||||
|
:tickfont, :guidefont, :legendfont,
|
||||||
|
:grid, :gridalpha, :gridlinewidth,
|
||||||
|
:legend, :colorbar, :colorbar_title, :colorbar_entry,
|
||||||
|
:marker_z, :fill_z, :line_z, :levels,
|
||||||
|
:ribbon, :quiver,
|
||||||
|
:orientation,
|
||||||
|
# :overwrite_figure,
|
||||||
|
:polar,
|
||||||
|
:normalize, :weights,
|
||||||
|
# :contours,
|
||||||
|
:aspect_ratio,
|
||||||
|
:hover,
|
||||||
|
:inset_subplots,
|
||||||
|
:bar_width,
|
||||||
|
:clims,
|
||||||
|
:framestyle,
|
||||||
|
:tick_direction,
|
||||||
|
:camera,
|
||||||
|
:contour_labels,
|
||||||
|
])
|
||||||
|
|
||||||
|
const _plotly_seriestype = [
|
||||||
|
:path, :scatter, :pie, :heatmap,
|
||||||
|
:contour, :surface, :wireframe, :path3d, :scatter3d, :shape, :scattergl,
|
||||||
|
:straightline
|
||||||
|
]
|
||||||
|
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||||
|
const _plotly_marker = [
|
||||||
|
:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle,
|
||||||
|
:cross, :xcross, :pentagon, :hexagon, :octagon, :vline, :hline
|
||||||
|
]
|
||||||
|
const _plotly_scale = [:identity, :log10]
|
||||||
|
|
||||||
|
defaultOutputFormat(plt::Plot{Plots.PlotlyBackend}) = "html"
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# PGFPLOTS
|
# pgfplots
|
||||||
|
|
||||||
function add_backend_string(::PGFPlotsBackend)
|
const _pgfplots_attr = merge_with_base_supported([
|
||||||
"""
|
:annotations,
|
||||||
using Pkg
|
:background_color_legend,
|
||||||
Pkg.add("PGFPlots")
|
:background_color_inside,
|
||||||
Pkg.build("PGFPlots")
|
# :background_color_outside,
|
||||||
"""
|
# :foreground_color_legend,
|
||||||
end
|
:foreground_color_grid, :foreground_color_axis,
|
||||||
|
:foreground_color_text, :foreground_color_border,
|
||||||
|
:label,
|
||||||
|
:seriescolor, :seriesalpha,
|
||||||
|
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||||
|
:markershape, :markercolor, :markersize, :markeralpha,
|
||||||
|
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||||
|
:fillrange, :fillcolor, :fillalpha,
|
||||||
|
:bins,
|
||||||
|
# :bar_width, :bar_edges,
|
||||||
|
:title,
|
||||||
|
# :window_title,
|
||||||
|
:guide, :guide_position, :lims, :ticks, :scale, :flip, :rotation,
|
||||||
|
:tickfont, :guidefont, :legendfont,
|
||||||
|
:grid, :legend,
|
||||||
|
:colorbar, :colorbar_title,
|
||||||
|
:fill_z, :line_z, :marker_z, :levels,
|
||||||
|
# :ribbon, :quiver, :arrow,
|
||||||
|
# :orientation,
|
||||||
|
# :overwrite_figure,
|
||||||
|
:polar,
|
||||||
|
# :normalize, :weights, :contours,
|
||||||
|
:aspect_ratio,
|
||||||
|
# :match_dimensions,
|
||||||
|
:tick_direction,
|
||||||
|
:framestyle,
|
||||||
|
:camera,
|
||||||
|
:contour_labels,
|
||||||
|
])
|
||||||
|
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape, :straightline,]
|
||||||
|
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||||
|
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline, :vline] #vcat(_allMarkers, Shape)
|
||||||
|
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# plotlyjs
|
# plotlyjs
|
||||||
|
|
||||||
function add_backend_string(::PlotlyJSBackend)
|
function _initialize_backend(pkg::PlotlyJSBackend)
|
||||||
"""
|
@eval Main begin
|
||||||
using Pkg
|
import PlotlyJS, ORCA
|
||||||
Pkg.add("PlotlyJS")
|
export PlotlyJS
|
||||||
Pkg.add("Rsvg")
|
|
||||||
import Blink
|
|
||||||
Blink.AtomShell.install()
|
|
||||||
"""
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
const _plotlyjs_attr = _plotly_attr
|
||||||
|
const _plotlyjs_seriestype = _plotly_seriestype
|
||||||
|
const _plotlyjs_style = _plotly_style
|
||||||
|
const _plotlyjs_marker = _plotly_marker
|
||||||
|
const _plotlyjs_scale = _plotly_scale
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# pyplot
|
# pyplot
|
||||||
|
|
||||||
function _initialize_backend(::PyPlotBackend)
|
function _initialize_backend(::PyPlotBackend)
|
||||||
@eval Main begin
|
@eval Main begin
|
||||||
import PyPlot, PyCall
|
import PyPlot
|
||||||
import LaTeXStrings
|
|
||||||
|
|
||||||
export PyPlot
|
export PyPlot
|
||||||
|
|
||||||
@ -396,25 +487,177 @@ function _initialize_backend(::PyPlotBackend)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function add_backend_string(::PyPlotBackend)
|
const _pyplot_attr = merge_with_base_supported([
|
||||||
"""
|
:annotations,
|
||||||
using Pkg
|
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||||
Pkg.add("PyPlot")
|
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||||
Pkg.add("PyCall")
|
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||||
Pkg.add("LaTeXStrings")
|
:label,
|
||||||
withenv("PYTHON" => "") do
|
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||||
Pkg.build("PyCall")
|
:markershape, :markercolor, :markersize, :markeralpha,
|
||||||
Pkg.build("PyPlot")
|
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||||
end
|
:fillrange, :fillcolor, :fillalpha,
|
||||||
"""
|
:bins, :bar_width, :bar_edges, :bar_position,
|
||||||
end
|
:title, :title_location, :titlefont,
|
||||||
|
:window_title,
|
||||||
|
:guide, :guide_position, :lims, :ticks, :scale, :flip, :rotation,
|
||||||
|
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||||
|
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||||
|
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||||
|
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||||
|
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||||
|
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||||
|
:marker_z, :line_z, :fill_z,
|
||||||
|
:levels,
|
||||||
|
:ribbon, :quiver, :arrow,
|
||||||
|
:orientation,
|
||||||
|
:overwrite_figure,
|
||||||
|
:polar,
|
||||||
|
:normalize, :weights,
|
||||||
|
:contours, :aspect_ratio,
|
||||||
|
:match_dimensions,
|
||||||
|
:clims,
|
||||||
|
:inset_subplots,
|
||||||
|
:dpi,
|
||||||
|
:stride,
|
||||||
|
:framestyle,
|
||||||
|
:tick_direction,
|
||||||
|
:camera,
|
||||||
|
:contour_labels,
|
||||||
|
])
|
||||||
|
const _pyplot_seriestype = [
|
||||||
|
:path, :steppre, :steppost, :shape, :straightline,
|
||||||
|
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||||
|
# :bar,
|
||||||
|
:heatmap, :pie, :image,
|
||||||
|
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||||
|
]
|
||||||
|
const _pyplot_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||||
|
const _pyplot_marker = vcat(_allMarkers, :pixel)
|
||||||
|
const _pyplot_scale = [:identity, :ln, :log2, :log10]
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# unicodeplots
|
# unicodeplots
|
||||||
function add_backend_string(::UnicodePlotsBackend)
|
|
||||||
"""
|
const _unicodeplots_attr = merge_with_base_supported([
|
||||||
using Pkg
|
:label,
|
||||||
Pkg.add("UnicodePlots")
|
:legend,
|
||||||
Pkg.build("UnicodePlots")
|
:seriescolor,
|
||||||
"""
|
:seriesalpha,
|
||||||
end
|
:linestyle,
|
||||||
|
:markershape,
|
||||||
|
:bins,
|
||||||
|
:title,
|
||||||
|
:guide, :lims,
|
||||||
|
])
|
||||||
|
const _unicodeplots_seriestype = [
|
||||||
|
:path, :scatter, :straightline,
|
||||||
|
# :bar,
|
||||||
|
:shape,
|
||||||
|
:histogram2d,
|
||||||
|
:spy
|
||||||
|
]
|
||||||
|
const _unicodeplots_style = [:auto, :solid]
|
||||||
|
const _unicodeplots_marker = [:none, :auto, :circle]
|
||||||
|
const _unicodeplots_scale = [:identity]
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# hdf5
|
||||||
|
|
||||||
|
const _hdf5_attr = merge_with_base_supported([
|
||||||
|
:annotations,
|
||||||
|
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||||
|
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||||
|
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||||
|
:label,
|
||||||
|
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||||
|
:markershape, :markercolor, :markersize, :markeralpha,
|
||||||
|
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||||
|
:fillrange, :fillcolor, :fillalpha,
|
||||||
|
:bins, :bar_width, :bar_edges, :bar_position,
|
||||||
|
:title, :title_location, :titlefont,
|
||||||
|
:window_title,
|
||||||
|
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||||
|
:tickfont, :guidefont, :legendfont,
|
||||||
|
:grid, :legend, :colorbar,
|
||||||
|
:marker_z, :line_z, :fill_z,
|
||||||
|
:levels,
|
||||||
|
:ribbon, :quiver, :arrow,
|
||||||
|
:orientation,
|
||||||
|
:overwrite_figure,
|
||||||
|
:polar,
|
||||||
|
:normalize, :weights,
|
||||||
|
:contours, :aspect_ratio,
|
||||||
|
:match_dimensions,
|
||||||
|
:clims,
|
||||||
|
:inset_subplots,
|
||||||
|
:dpi,
|
||||||
|
:colorbar_title,
|
||||||
|
])
|
||||||
|
const _hdf5_seriestype = [
|
||||||
|
:path, :steppre, :steppost, :shape, :straightline,
|
||||||
|
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||||
|
# :bar,
|
||||||
|
:heatmap, :pie, :image,
|
||||||
|
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||||
|
]
|
||||||
|
const _hdf5_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||||
|
const _hdf5_marker = vcat(_allMarkers, :pixel)
|
||||||
|
const _hdf5_scale = [:identity, :ln, :log2, :log10]
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# inspectdr
|
||||||
|
|
||||||
|
const _inspectdr_attr = merge_with_base_supported([
|
||||||
|
:annotations,
|
||||||
|
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||||
|
# :foreground_color_grid,
|
||||||
|
:foreground_color_legend, :foreground_color_title,
|
||||||
|
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||||
|
:label,
|
||||||
|
:seriescolor, :seriesalpha,
|
||||||
|
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||||
|
:markershape, :markercolor, :markersize, :markeralpha,
|
||||||
|
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||||
|
:markerstrokestyle, #Causes warning not to have it... what is this?
|
||||||
|
:fillcolor, :fillalpha, #:fillrange,
|
||||||
|
# :bins, :bar_width, :bar_edges, :bar_position,
|
||||||
|
:title, :title_location,
|
||||||
|
:window_title,
|
||||||
|
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
||||||
|
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||||
|
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||||
|
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||||
|
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||||
|
:grid, :legend, #:colorbar,
|
||||||
|
# :marker_z,
|
||||||
|
# :line_z,
|
||||||
|
# :levels,
|
||||||
|
# :ribbon, :quiver, :arrow,
|
||||||
|
# :orientation,
|
||||||
|
:overwrite_figure,
|
||||||
|
:polar,
|
||||||
|
# :normalize, :weights,
|
||||||
|
# :contours, :aspect_ratio,
|
||||||
|
:match_dimensions,
|
||||||
|
# :clims,
|
||||||
|
# :inset_subplots,
|
||||||
|
:dpi,
|
||||||
|
# :colorbar_title,
|
||||||
|
])
|
||||||
|
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||||
|
const _inspectdr_seriestype = [
|
||||||
|
:path, :scatter, :shape, :straightline, #, :steppre, :steppost
|
||||||
|
]
|
||||||
|
#see: _allMarkers, _shape_keys
|
||||||
|
const _inspectdr_marker = Symbol[
|
||||||
|
:none, :auto,
|
||||||
|
:circle, :rect, :diamond,
|
||||||
|
:cross, :xcross,
|
||||||
|
:utriangle, :dtriangle, :rtriangle, :ltriangle,
|
||||||
|
:pentagon, :hexagon, :heptagon, :octagon,
|
||||||
|
:star4, :star5, :star6, :star7, :star8,
|
||||||
|
:vline, :hline, :+, :x,
|
||||||
|
]
|
||||||
|
|
||||||
|
const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -16,16 +16,19 @@ Read from .hdf5 file using:
|
|||||||
|
|
||||||
#==TODO
|
#==TODO
|
||||||
===============================================================================
|
===============================================================================
|
||||||
1. Support more features
|
1. Support more features.
|
||||||
- SeriesAnnotations & GridLayout known to be missing.
|
- GridLayout known not to be working.
|
||||||
3. Improve error handling.
|
2. Improve error handling.
|
||||||
- Will likely crash if file format is off.
|
- Will likely crash if file format is off.
|
||||||
2. Save data in a folder parallel to "plot".
|
3. Save data in a folder parallel to "plot".
|
||||||
- Will make it easier for users to locate data.
|
- Will make it easier for users to locate data.
|
||||||
- Use HDF5 reference to link data?
|
- Use HDF5 reference to link data?
|
||||||
3. Develop an actual versioned file format.
|
4. Develop an actual versioned file format.
|
||||||
- Should have some form of backward compatibility.
|
- Should have some form of backward compatibility.
|
||||||
- Should be reliable for archival purposes.
|
- Should be reliable for archival purposes.
|
||||||
|
5. Fix construction of plot object with hdf5plot_read.
|
||||||
|
- Not building object correctly when backends do not natively support
|
||||||
|
a certain feature (ex: :steppre)
|
||||||
==#
|
==#
|
||||||
|
|
||||||
import FixedPointNumbers: N0f8 #In core Julia
|
import FixedPointNumbers: N0f8 #In core Julia
|
||||||
@ -56,53 +59,13 @@ const HDF5PLOT_PLOTREF = HDF5Plot_PlotRef(nothing)
|
|||||||
|
|
||||||
#Simple sub-structures that can just be written out using _hdf5plot_gwritefields:
|
#Simple sub-structures that can just be written out using _hdf5plot_gwritefields:
|
||||||
const HDF5PLOT_SIMPLESUBSTRUCT = Union{Font, BoundingBox,
|
const HDF5PLOT_SIMPLESUBSTRUCT = Union{Font, BoundingBox,
|
||||||
GridLayout, RootLayout, ColorGradient, SeriesAnnotations, PlotText
|
GridLayout, RootLayout, ColorGradient, SeriesAnnotations, PlotText,
|
||||||
|
Shape,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#==
|
#==
|
||||||
===============================================================================#
|
===============================================================================#
|
||||||
|
|
||||||
const _hdf5_attr = merge_with_base_supported([
|
|
||||||
:annotations,
|
|
||||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
|
||||||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
|
||||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
|
||||||
:label,
|
|
||||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
|
||||||
:markershape, :markercolor, :markersize, :markeralpha,
|
|
||||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
|
||||||
:fillrange, :fillcolor, :fillalpha,
|
|
||||||
:bins, :bar_width, :bar_edges, :bar_position,
|
|
||||||
:title, :title_location, :titlefont,
|
|
||||||
:window_title,
|
|
||||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
|
||||||
:tickfont, :guidefont, :legendfont,
|
|
||||||
:grid, :legend, :colorbar,
|
|
||||||
:marker_z, :line_z, :fill_z,
|
|
||||||
:levels,
|
|
||||||
:ribbon, :quiver, :arrow,
|
|
||||||
:orientation,
|
|
||||||
:overwrite_figure,
|
|
||||||
:polar,
|
|
||||||
:normalize, :weights,
|
|
||||||
:contours, :aspect_ratio,
|
|
||||||
:match_dimensions,
|
|
||||||
:clims,
|
|
||||||
:inset_subplots,
|
|
||||||
:dpi,
|
|
||||||
:colorbar_title,
|
|
||||||
])
|
|
||||||
const _hdf5_seriestype = [
|
|
||||||
:path, :steppre, :steppost, :shape, :straightline,
|
|
||||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
|
||||||
# :bar,
|
|
||||||
:heatmap, :pie, :image,
|
|
||||||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
|
||||||
]
|
|
||||||
const _hdf5_style = [:auto, :solid, :dash, :dot, :dashdot]
|
|
||||||
const _hdf5_marker = vcat(_allMarkers, :pixel)
|
|
||||||
const _hdf5_scale = [:identity, :ln, :log2, :log10]
|
|
||||||
is_marker_supported(::HDF5Backend, shape::Shape) = true
|
is_marker_supported(::HDF5Backend, shape::Shape) = true
|
||||||
|
|
||||||
if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||||
@ -125,7 +88,8 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
|||||||
"GRIDLAYOUT" => GridLayout,
|
"GRIDLAYOUT" => GridLayout,
|
||||||
"ROOTLAYOUT" => RootLayout,
|
"ROOTLAYOUT" => RootLayout,
|
||||||
"SERIESANNOTATIONS" => SeriesAnnotations,
|
"SERIESANNOTATIONS" => SeriesAnnotations,
|
||||||
# "PLOTTEXT" => PlotText,
|
"PLOTTEXT" => PlotText,
|
||||||
|
"SHAPE" => Shape,
|
||||||
"COLORGRADIENT" => ColorGradient,
|
"COLORGRADIENT" => ColorGradient,
|
||||||
"AXIS" => Axis,
|
"AXIS" => Axis,
|
||||||
"SURFACE" => Surface,
|
"SURFACE" => Surface,
|
||||||
@ -284,6 +248,14 @@ end
|
|||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
function _hdf5plot_gwrite(grp, k::String, v) #Default
|
function _hdf5plot_gwrite(grp, k::String, v) #Default
|
||||||
|
T = typeof(v)
|
||||||
|
if !(T <: Number || T <: String)
|
||||||
|
tstr = string(T)
|
||||||
|
path = HDF5.name(grp) * "/" * k
|
||||||
|
@info("Type not supported: $tstr\npath: $path")
|
||||||
|
# @show v
|
||||||
|
return
|
||||||
|
end
|
||||||
grp[k] = v
|
grp[k] = v
|
||||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||||
end
|
end
|
||||||
@ -338,10 +310,6 @@ function _hdf5plot_gwrite(grp, k::String, v::Colorant)
|
|||||||
end
|
end
|
||||||
#Custom vector (when not using simple numeric type):
|
#Custom vector (when not using simple numeric type):
|
||||||
function _hdf5plot_gwritearray(grp, k::String, v::Array{T}) where T
|
function _hdf5plot_gwritearray(grp, k::String, v::Array{T}) where T
|
||||||
if "annotations" == k;
|
|
||||||
return #Hack. Does not yet support annotations.
|
|
||||||
end
|
|
||||||
|
|
||||||
vgrp = HDF5.g_create(grp, k)
|
vgrp = HDF5.g_create(grp, k)
|
||||||
_hdf5plot_writetype(vgrp, Array) #ANY
|
_hdf5plot_writetype(vgrp, Array) #ANY
|
||||||
sz = size(v)
|
sz = size(v)
|
||||||
@ -351,7 +319,7 @@ function _hdf5plot_gwritearray(grp, k::String, v::Array{T}) where T
|
|||||||
coord = lidx[iter]
|
coord = lidx[iter]
|
||||||
elem = v[iter]
|
elem = v[iter]
|
||||||
idxstr = join(coord, "_")
|
idxstr = join(coord, "_")
|
||||||
_hdf5plot_gwrite(vgrp, "v$idxstr", v[iter])
|
_hdf5plot_gwrite(vgrp, "v$idxstr", elem)
|
||||||
end
|
end
|
||||||
|
|
||||||
_hdf5plot_gwrite(vgrp, "dim", [sz...])
|
_hdf5plot_gwrite(vgrp, "dim", [sz...])
|
||||||
@ -402,10 +370,6 @@ end
|
|||||||
# return
|
# return
|
||||||
# end
|
# end
|
||||||
|
|
||||||
function _hdf5plot_gwrite(grp, k::String, v::SeriesAnnotations)
|
|
||||||
#Currently no support for SeriesAnnotations
|
|
||||||
return
|
|
||||||
end
|
|
||||||
function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
||||||
grp = HDF5.g_create(grp, k)
|
grp = HDF5.g_create(grp, k)
|
||||||
_hdf5plot_gwrite(grp, "index", v[:subplot_index])
|
_hdf5plot_gwrite(grp, "index", v[:subplot_index])
|
||||||
@ -528,6 +492,29 @@ function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple}, dtid)
|
|||||||
v = _hdf5plot_read(grp, k, Array, dtid)
|
v = _hdf5plot_read(grp, k, Array, dtid)
|
||||||
return tuple(v...)
|
return tuple(v...)
|
||||||
end
|
end
|
||||||
|
function _hdf5plot_read(grp, k::String, T::Type{PlotText}, dtid)
|
||||||
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
|
str = _hdf5plot_read(grp, "str")
|
||||||
|
font = _hdf5plot_read(grp, "font")
|
||||||
|
return PlotText(str, font)
|
||||||
|
end
|
||||||
|
function _hdf5plot_read(grp, k::String, T::Type{SeriesAnnotations}, dtid)
|
||||||
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
|
strs = _hdf5plot_read(grp, "strs")
|
||||||
|
font = _hdf5plot_read(grp, "font")
|
||||||
|
baseshape = _hdf5plot_read(grp, "baseshape")
|
||||||
|
scalefactor = _hdf5plot_read(grp, "scalefactor")
|
||||||
|
return SeriesAnnotations(strs, font, baseshape, scalefactor)
|
||||||
|
end
|
||||||
|
function _hdf5plot_read(grp, k::String, T::Type{Shape}, dtid)
|
||||||
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
|
x = _hdf5plot_read(grp, "x")
|
||||||
|
y = _hdf5plot_read(grp, "y")
|
||||||
|
return Shape(x, y)
|
||||||
|
end
|
||||||
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient}, dtid)
|
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient}, dtid)
|
||||||
grp = HDF5.g_open(grp, k)
|
grp = HDF5.g_open(grp, k)
|
||||||
|
|
||||||
@ -601,11 +588,6 @@ end
|
|||||||
function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
||||||
f = f::HDF5.HDF5File #Assert
|
f = f::HDF5.HDF5File #Assert
|
||||||
|
|
||||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
|
|
||||||
kwlist = KW()
|
|
||||||
_hdf5plot_read(grp, kwlist)
|
|
||||||
_hdf5_merge!(sp.attr, kwlist)
|
|
||||||
|
|
||||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list"))
|
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list"))
|
||||||
nseries = _hdf5plot_readcount(grp)
|
nseries = _hdf5plot_readcount(grp)
|
||||||
|
|
||||||
@ -617,6 +599,12 @@ function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
|||||||
_hdf5_merge!(sp.series_list[end].plotattributes, kwlist)
|
_hdf5_merge!(sp.series_list[end].plotattributes, kwlist)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#Perform after adding series... otherwise values get overwritten:
|
||||||
|
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
|
||||||
|
kwlist = KW()
|
||||||
|
_hdf5plot_read(grp, kwlist)
|
||||||
|
_hdf5_merge!(sp.attr, kwlist)
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -14,61 +14,6 @@ Add in functionality to Plots.jl:
|
|||||||
=#
|
=#
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
#TODO: remove features
|
|
||||||
const _inspectdr_attr = merge_with_base_supported([
|
|
||||||
:annotations,
|
|
||||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
|
||||||
# :foreground_color_grid,
|
|
||||||
:foreground_color_legend, :foreground_color_title,
|
|
||||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
|
||||||
:label,
|
|
||||||
:seriescolor, :seriesalpha,
|
|
||||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
|
||||||
:markershape, :markercolor, :markersize, :markeralpha,
|
|
||||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
|
||||||
:markerstrokestyle, #Causes warning not to have it... what is this?
|
|
||||||
:fillcolor, :fillalpha, #:fillrange,
|
|
||||||
# :bins, :bar_width, :bar_edges, :bar_position,
|
|
||||||
:title, :title_location,
|
|
||||||
:window_title,
|
|
||||||
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
|
||||||
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
|
||||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
|
||||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
|
||||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
|
||||||
:grid, #:gridalpha, :gridstyle, :gridlinewidth, #alhpa & linewidth are per plot - not per subplot
|
|
||||||
:legend, #:legendtitle, :colorbar,
|
|
||||||
# :marker_z,
|
|
||||||
# :line_z,
|
|
||||||
# :levels,
|
|
||||||
# :ribbon, :quiver, :arrow,
|
|
||||||
# :orientation,
|
|
||||||
:overwrite_figure,
|
|
||||||
:polar,
|
|
||||||
# :normalize, :weights,
|
|
||||||
# :contours, :aspect_ratio,
|
|
||||||
:match_dimensions,
|
|
||||||
# :clims,
|
|
||||||
# :inset_subplots,
|
|
||||||
:dpi,
|
|
||||||
# :colorbar_title,
|
|
||||||
])
|
|
||||||
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
|
||||||
const _inspectdr_seriestype = [
|
|
||||||
:path, :scatter, :shape, :straightline, #, :steppre, :steppost
|
|
||||||
]
|
|
||||||
#see: _allMarkers, _shape_keys
|
|
||||||
const _inspectdr_marker = Symbol[
|
|
||||||
:none, :auto,
|
|
||||||
:circle, :rect, :diamond,
|
|
||||||
:cross, :xcross,
|
|
||||||
:utriangle, :dtriangle, :rtriangle, :ltriangle,
|
|
||||||
:pentagon, :hexagon, :heptagon, :octagon,
|
|
||||||
:star4, :star5, :star6, :star7, :star8,
|
|
||||||
:vline, :hline, :+, :x,
|
|
||||||
]
|
|
||||||
|
|
||||||
const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
|
||||||
|
|
||||||
is_marker_supported(::InspectDRBackend, shape::Shape) = true
|
is_marker_supported(::InspectDRBackend, shape::Shape) = true
|
||||||
|
|
||||||
@ -349,8 +294,8 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
|||||||
|
|
||||||
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
|
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
|
||||||
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
|
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
|
||||||
xmin, xmax = axis_limits(xaxis)
|
xmin, xmax = axis_limits(sp, :x)
|
||||||
ymin, ymax = axis_limits(yaxis)
|
ymin, ymax = axis_limits(sp, :y)
|
||||||
if ispolar(sp)
|
if ispolar(sp)
|
||||||
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
|
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
|
||||||
rmax = NaNMath.max(abs(ymin), abs(ymax))
|
rmax = NaNMath.max(abs(ymin), abs(ymax))
|
||||||
|
|||||||
@ -2,47 +2,6 @@
|
|||||||
|
|
||||||
# significant contributions by: @pkofod
|
# significant contributions by: @pkofod
|
||||||
|
|
||||||
const _pgfplots_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,
|
|
||||||
:label,
|
|
||||||
:seriescolor, :seriesalpha,
|
|
||||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
|
||||||
:markershape, :markercolor, :markersize, :markeralpha,
|
|
||||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
|
||||||
:fillrange, :fillcolor, :fillalpha,
|
|
||||||
:bins,
|
|
||||||
# :bar_width, :bar_edges,
|
|
||||||
:title,
|
|
||||||
# :window_title,
|
|
||||||
:guide, :guide_position, :lims, :ticks, :scale, :flip, :rotation,
|
|
||||||
:tickfont, :guidefont, :legendfont,
|
|
||||||
:grid, :legend,
|
|
||||||
:colorbar, :colorbar_title,
|
|
||||||
:fill_z, :line_z, :marker_z, :levels,
|
|
||||||
# :ribbon, :quiver, :arrow,
|
|
||||||
# :orientation,
|
|
||||||
# :overwrite_figure,
|
|
||||||
:polar,
|
|
||||||
# :normalize, :weights, :contours,
|
|
||||||
:aspect_ratio,
|
|
||||||
# :match_dimensions,
|
|
||||||
:tick_direction,
|
|
||||||
:framestyle,
|
|
||||||
:camera,
|
|
||||||
:contour_labels,
|
|
||||||
])
|
|
||||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape, :straightline,]
|
|
||||||
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
|
||||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline] #vcat(_allMarkers, Shape)
|
|
||||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
const _pgfplots_linestyles = KW(
|
const _pgfplots_linestyles = KW(
|
||||||
@ -67,7 +26,8 @@ const _pgfplots_markers = KW(
|
|||||||
:star6 => "asterisk",
|
:star6 => "asterisk",
|
||||||
:diamond => "diamond*",
|
:diamond => "diamond*",
|
||||||
:pentagon => "pentagon*",
|
:pentagon => "pentagon*",
|
||||||
:hline => "-"
|
:hline => "-",
|
||||||
|
:vline => "|"
|
||||||
)
|
)
|
||||||
|
|
||||||
const _pgfplots_legend_pos = KW(
|
const _pgfplots_legend_pos = KW(
|
||||||
@ -211,7 +171,7 @@ function pgf_series(sp::Subplot, series::Series)
|
|||||||
elseif st == :shape
|
elseif st == :shape
|
||||||
shape_data(series)
|
shape_data(series)
|
||||||
elseif ispolar(sp)
|
elseif ispolar(sp)
|
||||||
theta, r = filter_radial_data(plotattributes[:x], plotattributes[:y], axis_limits(sp[:yaxis]))
|
theta, r = plotattributes[:x], plotattributes[:y]
|
||||||
rad2deg.(theta), r
|
rad2deg.(theta), r
|
||||||
else
|
else
|
||||||
plotattributes[:x], plotattributes[:y]
|
plotattributes[:x], plotattributes[:y]
|
||||||
@ -401,13 +361,13 @@ function pgf_axis(sp::Subplot, letter)
|
|||||||
# limits
|
# limits
|
||||||
# TODO: support zlims
|
# TODO: support zlims
|
||||||
if letter != :z
|
if letter != :z
|
||||||
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(axis)) : axis_limits(axis)
|
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) : axis_limits(sp, letter)
|
||||||
kw[Symbol(letter,:min)] = lims[1]
|
kw[Symbol(letter,:min)] = lims[1]
|
||||||
kw[Symbol(letter,:max)] = lims[2]
|
kw[Symbol(letter,:max)] = lims[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
|
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
|
||||||
ticks = get_ticks(axis)
|
ticks = get_ticks(sp, axis)
|
||||||
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
|
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
|
||||||
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
|
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
|
||||||
push!(style, string(letter, "tick = {", join(tick_values,","), "}"))
|
push!(style, string(letter, "tick = {", join(tick_values,","), "}"))
|
||||||
@ -439,14 +399,18 @@ function pgf_axis(sp::Subplot, letter)
|
|||||||
# framestyle
|
# framestyle
|
||||||
if framestyle in (:axes, :origin)
|
if framestyle in (:axes, :origin)
|
||||||
axispos = framestyle == :axes ? "left" : "middle"
|
axispos = framestyle == :axes ? "left" : "middle"
|
||||||
# the * after lines disables the arrows at the axes
|
if axis[:draw_arrow]
|
||||||
push!(style, string("axis lines* = ", axispos))
|
push!(style, string("axis ", letter, " line = ", axispos))
|
||||||
|
else
|
||||||
|
# the * after line disables the arrow at the axis
|
||||||
|
push!(style, string("axis ", letter, " line* = ", axispos))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if framestyle == :zerolines
|
if framestyle == :zerolines
|
||||||
push!(style, string("extra ", letter, " ticks = 0"))
|
push!(style, string("extra ", letter, " ticks = 0"))
|
||||||
push!(style, string("extra ", letter, " tick labels = "))
|
push!(style, string("extra ", letter, " tick labels = "))
|
||||||
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}}"))
|
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}}"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if !axis[:showaxis]
|
if !axis[:showaxis]
|
||||||
@ -455,7 +419,7 @@ function pgf_axis(sp::Subplot, letter)
|
|||||||
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
|
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
|
||||||
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
|
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
|
||||||
else
|
else
|
||||||
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}"))
|
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}"))
|
||||||
end
|
end
|
||||||
|
|
||||||
# return the style list and KW args
|
# return the style list and KW args
|
||||||
@ -604,7 +568,7 @@ end
|
|||||||
|
|
||||||
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
|
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
|
||||||
fn = tempname()*".tex"
|
fn = tempname()*".tex"
|
||||||
PGFPlots.save(fn, backend_object(plt), include_preamble=false)
|
PGFPlots.save(fn, backend_object(plt), include_preamble=plt.attr[:tex_output_standalone])
|
||||||
write(io, read(open(fn), String))
|
write(io, read(open(fn), String))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,60 +1,6 @@
|
|||||||
|
|
||||||
# https://plot.ly/javascript/getting-started
|
# https://plot.ly/javascript/getting-started
|
||||||
|
|
||||||
const _plotly_attr = merge_with_base_supported([
|
|
||||||
:annotations,
|
|
||||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
|
||||||
:foreground_color_legend, :foreground_color_guide,
|
|
||||||
:foreground_color_grid, :foreground_color_axis,
|
|
||||||
:foreground_color_text, :foreground_color_border,
|
|
||||||
:foreground_color_title,
|
|
||||||
:label,
|
|
||||||
:seriescolor, :seriesalpha,
|
|
||||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
|
||||||
:markershape, :markercolor, :markersize, :markeralpha,
|
|
||||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
|
||||||
:fillrange, :fillcolor, :fillalpha,
|
|
||||||
:bins,
|
|
||||||
:title, :title_location,
|
|
||||||
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
|
|
||||||
:titlefontcolor,
|
|
||||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
|
||||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
|
||||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
|
||||||
:window_title,
|
|
||||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
|
||||||
:tickfont, :guidefont, :legendfont,
|
|
||||||
:grid, :gridalpha, :gridlinewidth,
|
|
||||||
:legend, :colorbar, :colorbar_title,
|
|
||||||
:marker_z, :fill_z, :line_z, :levels,
|
|
||||||
:ribbon, :quiver,
|
|
||||||
:orientation,
|
|
||||||
# :overwrite_figure,
|
|
||||||
:polar,
|
|
||||||
:normalize, :weights,
|
|
||||||
# :contours,
|
|
||||||
:aspect_ratio,
|
|
||||||
:hover,
|
|
||||||
:inset_subplots,
|
|
||||||
:bar_width,
|
|
||||||
:clims,
|
|
||||||
:framestyle,
|
|
||||||
:tick_direction,
|
|
||||||
:camera,
|
|
||||||
:contour_labels,
|
|
||||||
])
|
|
||||||
|
|
||||||
const _plotly_seriestype = [
|
|
||||||
:path, :scatter, :pie, :heatmap,
|
|
||||||
:contour, :surface, :wireframe, :path3d, :scatter3d, :shape, :scattergl,
|
|
||||||
:straightline
|
|
||||||
]
|
|
||||||
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
|
|
||||||
const _plotly_marker = [
|
|
||||||
:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle,
|
|
||||||
:cross, :xcross, :pentagon, :hexagon, :octagon, :vline, :hline
|
|
||||||
]
|
|
||||||
const _plotly_scale = [:identity, :log10]
|
|
||||||
is_subplot_supported(::PlotlyBackend) = true
|
is_subplot_supported(::PlotlyBackend) = true
|
||||||
# is_string_supported(::PlotlyBackend) = true
|
# is_string_supported(::PlotlyBackend) = true
|
||||||
const _plotly_framestyles = [:box, :axes, :zerolines, :grid, :none]
|
const _plotly_framestyles = [:box, :axes, :zerolines, :grid, :none]
|
||||||
@ -71,30 +17,9 @@ end
|
|||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------
|
||||||
|
const plotly_remote_file_path = "https://cdn.plot.ly/plotly-latest.min.js"
|
||||||
|
|
||||||
|
|
||||||
const _plotly_js_path = joinpath(dirname(@__FILE__), "..", "..", "deps", "plotly-latest.min.js")
|
|
||||||
const _plotly_js_path_remote = "https://cdn.plot.ly/plotly-latest.min.js"
|
|
||||||
|
|
||||||
_js_code = open(read, _plotly_js_path, "r")
|
|
||||||
|
|
||||||
# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2
|
|
||||||
_js_script = """
|
|
||||||
<script type='text/javascript'>
|
|
||||||
define('plotly', function(require, exports, module) {
|
|
||||||
$(_js_code)
|
|
||||||
});
|
|
||||||
require(['plotly'], function(Plotly) {
|
|
||||||
window.Plotly = Plotly;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
"""
|
|
||||||
|
|
||||||
# if we're in IJulia call setupnotebook to load js and css
|
|
||||||
if isijulia()
|
|
||||||
display("text/html", _js_script)
|
|
||||||
end
|
|
||||||
|
|
||||||
# if isatom()
|
# if isatom()
|
||||||
# import Atom
|
# import Atom
|
||||||
# Atom.@msg evaljs(_js_code)
|
# Atom.@msg evaljs(_js_code)
|
||||||
@ -102,8 +27,6 @@ end
|
|||||||
using UUIDs
|
using UUIDs
|
||||||
|
|
||||||
push!(_initialized_backends, :plotly)
|
push!(_initialized_backends, :plotly)
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
const _plotly_legend_pos = KW(
|
const _plotly_legend_pos = KW(
|
||||||
@ -191,8 +114,8 @@ function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
|
|||||||
if aspect_ratio == :equal
|
if aspect_ratio == :equal
|
||||||
aspect_ratio = 1.0
|
aspect_ratio = 1.0
|
||||||
end
|
end
|
||||||
xmin,xmax = axis_limits(sp[:xaxis])
|
xmin,xmax = axis_limits(sp, :x)
|
||||||
ymin,ymax = axis_limits(sp[:yaxis])
|
ymin,ymax = axis_limits(sp, :y)
|
||||||
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
|
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
|
||||||
parea_ratio = width(plotarea) / height(plotarea)
|
parea_ratio = width(plotarea) / height(plotarea)
|
||||||
if want_ratio > parea_ratio
|
if want_ratio > parea_ratio
|
||||||
@ -251,7 +174,7 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
|
|||||||
|
|
||||||
ax[:tickangle] = -axis[:rotation]
|
ax[:tickangle] = -axis[:rotation]
|
||||||
ax[:type] = plotly_scale(axis[:scale])
|
ax[:type] = plotly_scale(axis[:scale])
|
||||||
lims = axis_limits(axis)
|
lims = axis_limits(sp, letter)
|
||||||
|
|
||||||
if axis[:ticks] != :native || axis[:lims] != :auto
|
if axis[:ticks] != :native || axis[:lims] != :auto
|
||||||
ax[:range] = map(scalefunc(axis[:scale]), lims)
|
ax[:range] = map(scalefunc(axis[:scale]), lims)
|
||||||
@ -263,14 +186,9 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
|
|||||||
ax[:tickcolor] = framestyle in (:zerolines, :grid) || !axis[:showaxis] ? rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
|
ax[:tickcolor] = framestyle in (:zerolines, :grid) || !axis[:showaxis] ? rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
|
||||||
ax[:linecolor] = rgba_string(axis[:foreground_color_axis])
|
ax[:linecolor] = rgba_string(axis[:foreground_color_axis])
|
||||||
|
|
||||||
# flip
|
|
||||||
if axis[:flip]
|
|
||||||
ax[:range] = reverse(ax[:range])
|
|
||||||
end
|
|
||||||
|
|
||||||
# ticks
|
# ticks
|
||||||
if axis[:ticks] != :native
|
if axis[:ticks] != :native
|
||||||
ticks = get_ticks(axis)
|
ticks = get_ticks(sp, axis)
|
||||||
ttype = ticksType(ticks)
|
ttype = ticksType(ticks)
|
||||||
if ttype == :ticks
|
if ttype == :ticks
|
||||||
ax[:tickmode] = "array"
|
ax[:tickmode] = "array"
|
||||||
@ -285,20 +203,24 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
|
|||||||
ax[:showgrid] = false
|
ax[:showgrid] = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# flip
|
||||||
|
if axis[:flip]
|
||||||
|
ax[:range] = reverse(ax[:range])
|
||||||
|
end
|
||||||
|
|
||||||
ax
|
ax
|
||||||
end
|
end
|
||||||
|
|
||||||
function plotly_polaraxis(axis::Axis)
|
function plotly_polaraxis(sp::Subplot, axis::Axis)
|
||||||
ax = KW(
|
ax = KW(
|
||||||
:visible => axis[:showaxis],
|
:visible => axis[:showaxis],
|
||||||
:showline => axis[:grid],
|
:showline => axis[:grid],
|
||||||
)
|
)
|
||||||
|
|
||||||
if axis[:letter] == :x
|
if axis[:letter] == :x
|
||||||
ax[:range] = rad2deg.(axis_limits(axis))
|
ax[:range] = rad2deg.(axis_limits(sp, :x))
|
||||||
else
|
else
|
||||||
ax[:range] = axis_limits(axis)
|
ax[:range] = axis_limits(sp, :y)
|
||||||
ax[:orientation] = -90
|
ax[:orientation] = -90
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -360,8 +282,8 @@ function plotly_layout(plt::Plot)
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
elseif ispolar(sp)
|
elseif ispolar(sp)
|
||||||
plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp[:xaxis])
|
plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp, sp[:xaxis])
|
||||||
plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp[:yaxis])
|
plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp, sp[:yaxis])
|
||||||
else
|
else
|
||||||
plotattributes_out[Symbol("xaxis$(x_idx)")] = plotly_axis(plt, sp[:xaxis], sp)
|
plotattributes_out[Symbol("xaxis$(x_idx)")] = plotly_axis(plt, sp[:xaxis], sp)
|
||||||
# don't allow yaxis to be reupdated/reanchored in a linked subplot
|
# don't allow yaxis to be reupdated/reanchored in a linked subplot
|
||||||
@ -431,7 +353,7 @@ end
|
|||||||
function plotly_colorscale(grad::ColorGradient, α)
|
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 1:length(grad.colors)]
|
||||||
end
|
end
|
||||||
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
|
plotly_colorscale(c::Colorant,α) = plotly_colorscale(_as_gradient(c),α)
|
||||||
function plotly_colorscale(c::AbstractVector{<:RGBA}, α)
|
function plotly_colorscale(c::AbstractVector{<:RGBA}, α)
|
||||||
if length(c) == 1
|
if length(c) == 1
|
||||||
return [[0.0, rgba_string(plot_color(c[1], α))], [1.0, rgba_string(plot_color(c[1], α))]]
|
return [[0.0, rgba_string(plot_color(c[1], α))], [1.0, rgba_string(plot_color(c[1], α))]]
|
||||||
@ -513,7 +435,7 @@ plotly_native_data(axis::Axis, a::Surface) = Surface(plotly_native_data(axis, a.
|
|||||||
|
|
||||||
function plotly_convert_to_datetime(x::AbstractArray, formatter::Function)
|
function plotly_convert_to_datetime(x::AbstractArray, formatter::Function)
|
||||||
if formatter == datetimeformatter
|
if formatter == datetimeformatter
|
||||||
map(xi -> replace(formatter(xi), "T", " "), x)
|
map(xi -> replace(formatter(xi), "T" => " "), x)
|
||||||
elseif formatter == dateformatter
|
elseif formatter == dateformatter
|
||||||
map(xi -> string(formatter(xi), " 00:00:00"), x)
|
map(xi -> string(formatter(xi), " 00:00:00"), x)
|
||||||
elseif formatter == timeformatter
|
elseif formatter == timeformatter
|
||||||
@ -583,13 +505,13 @@ function plotly_series(plt::Plot, series::Series)
|
|||||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||||
|
|
||||||
elseif st == :contour
|
elseif st == :contour
|
||||||
|
filled = isfilledcontour(series)
|
||||||
plotattributes_out[:type] = "contour"
|
plotattributes_out[:type] = "contour"
|
||||||
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
|
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
|
||||||
# plotattributes_out[:showscale] = series[:colorbar] != :none
|
plotattributes_out[:ncontours] = series[:levels] + 2
|
||||||
plotattributes_out[:ncontours] = series[:levels]
|
plotattributes_out[:contours] = KW(:coloring => filled ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
||||||
plotattributes_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
|
||||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
|
plotattributes_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
|
||||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
plotattributes_out[:showscale] = hascolorbar(sp) && hascolorbar(series)
|
||||||
|
|
||||||
elseif st in (:surface, :wireframe)
|
elseif st in (:surface, :wireframe)
|
||||||
plotattributes_out[:type] = "surface"
|
plotattributes_out[:type] = "surface"
|
||||||
@ -851,7 +773,7 @@ end
|
|||||||
function plotly_polar!(plotattributes_out::KW, series::Series)
|
function plotly_polar!(plotattributes_out::KW, series::Series)
|
||||||
# convert polar plots x/y to theta/radius
|
# convert polar plots x/y to theta/radius
|
||||||
if ispolar(series[:subplot])
|
if ispolar(series[:subplot])
|
||||||
theta, r = filter_radial_data(pop!(plotattributes_out, :x), pop!(plotattributes_out, :y), axis_limits(series[:subplot][:yaxis]))
|
theta, r = pop!(plotattributes_out, :x), pop!(plotattributes_out, :y)
|
||||||
plotattributes_out[:t] = rad2deg.(theta)
|
plotattributes_out[:t] = rad2deg.(theta)
|
||||||
plotattributes_out[:r] = r
|
plotattributes_out[:r] = r
|
||||||
end
|
end
|
||||||
@ -881,12 +803,27 @@ plotly_series_json(plt::Plot) = JSON.json(plotly_series(plt))
|
|||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
const _use_remote = Ref(false)
|
const ijulia_initialized = Ref(false)
|
||||||
|
|
||||||
function html_head(plt::Plot{PlotlyBackend})
|
function html_head(plt::Plot{PlotlyBackend})
|
||||||
jsfilename = _use_remote[] ? _plotly_js_path_remote : ("file://" * _plotly_js_path)
|
local_file = ("file://" * plotly_local_file_path)
|
||||||
# "<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
|
plotly = use_local_dependencies[] ? local_file : plotly_remote_file_path
|
||||||
"<script src=\"$jsfilename\"></script>"
|
if isijulia() && !ijulia_initialized[]
|
||||||
|
# using requirejs seems to be key to load a js depency in IJulia!
|
||||||
|
# https://requirejs.org/docs/start.html
|
||||||
|
# https://github.com/JuliaLang/IJulia.jl/issues/345
|
||||||
|
display("text/html", """
|
||||||
|
<script type="text/javascript">
|
||||||
|
requirejs([$(repr(plotly))], function(p) {
|
||||||
|
window.Plotly = p
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
ijulia_initialized[] = true
|
||||||
|
end
|
||||||
|
# IJulia just needs one initialization
|
||||||
|
isijulia() && return ""
|
||||||
|
return "<script src=$(repr(plotly))></script>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||||
@ -929,10 +866,15 @@ end
|
|||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{PlotlyBackend})
|
||||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
|
data = []
|
||||||
write(io, html_head(plt) * html_body(plt))
|
for series in plot.series_list
|
||||||
|
append!(data, plotly_series(plot, series))
|
||||||
end
|
end
|
||||||
|
layout = plotly_layout(plot)
|
||||||
|
JSON.print(io, Dict(:data => data, :layout => layout))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function _display(plt::Plot{PlotlyBackend})
|
function _display(plt::Plot{PlotlyBackend})
|
||||||
standalone_html_window(plt)
|
standalone_html_window(plt)
|
||||||
|
|||||||
@ -1,18 +1,11 @@
|
|||||||
|
# https://github.com/sglyon/PlotlyJS.jl
|
||||||
# https://github.com/spencerlyon2/PlotlyJS.jl
|
|
||||||
|
|
||||||
const _plotlyjs_attr = _plotly_attr
|
|
||||||
const _plotlyjs_seriestype = _plotly_seriestype
|
|
||||||
const _plotlyjs_style = _plotly_style
|
|
||||||
const _plotlyjs_marker = _plotly_marker
|
|
||||||
const _plotlyjs_scale = _plotly_scale
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||||
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||||
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
|
PlotlyJS.SyncPlot(PlotlyJS.Plot(), options = current().o.options)
|
||||||
else
|
else
|
||||||
PlotlyJS.plot()
|
PlotlyJS.plot()
|
||||||
end
|
end
|
||||||
@ -56,23 +49,16 @@ end
|
|||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
|
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = show(io, MIME("text/html"), plt.o)
|
||||||
if isijulia() && !_use_remote[]
|
_show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="svg")
|
||||||
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
|
_show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="png")
|
||||||
else
|
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="pdf")
|
||||||
show(io, MIME("text/html"), plt.o)
|
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="eps")
|
||||||
end
|
|
||||||
|
function _show(io::IO, m::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend})
|
||||||
|
show(io, m, plt.o)
|
||||||
end
|
end
|
||||||
|
|
||||||
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
|
|
||||||
tmpfn = tempname() * "." * ext
|
|
||||||
PlotlyJS.savefig(plt.o, tmpfn)
|
|
||||||
write(io, read(open(tmpfn)))
|
|
||||||
end
|
|
||||||
_show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "svg")
|
|
||||||
_show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "png")
|
|
||||||
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "pdf")
|
|
||||||
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "eps")
|
|
||||||
|
|
||||||
function write_temp_html(plt::Plot{PlotlyJSBackend})
|
function write_temp_html(plt::Plot{PlotlyJSBackend})
|
||||||
filename = string(tempname(), ".html")
|
filename = string(tempname(), ".html")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,28 +1,6 @@
|
|||||||
|
|
||||||
# https://github.com/Evizero/UnicodePlots.jl
|
# https://github.com/Evizero/UnicodePlots.jl
|
||||||
|
|
||||||
const _unicodeplots_attr = merge_with_base_supported([
|
|
||||||
:label,
|
|
||||||
:legend,
|
|
||||||
:seriescolor,
|
|
||||||
:seriesalpha,
|
|
||||||
:linestyle,
|
|
||||||
:markershape,
|
|
||||||
:bins,
|
|
||||||
:title,
|
|
||||||
:guide, :lims,
|
|
||||||
])
|
|
||||||
const _unicodeplots_seriestype = [
|
|
||||||
:path, :scatter, :straightline,
|
|
||||||
# :bar,
|
|
||||||
:shape,
|
|
||||||
:histogram2d,
|
|
||||||
:spy
|
|
||||||
]
|
|
||||||
const _unicodeplots_style = [:auto, :solid]
|
|
||||||
const _unicodeplots_marker = [:none, :auto, :circle]
|
|
||||||
const _unicodeplots_scale = [:identity]
|
|
||||||
|
|
||||||
|
|
||||||
# don't warn on unsupported... there's just too many warnings!!
|
# don't warn on unsupported... there's just too many warnings!!
|
||||||
warnOnUnsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing
|
warnOnUnsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing
|
||||||
@ -49,8 +27,8 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
|||||||
for sp in plt.subplots
|
for sp in plt.subplots
|
||||||
xaxis = sp[:xaxis]
|
xaxis = sp[:xaxis]
|
||||||
yaxis = sp[:yaxis]
|
yaxis = sp[:yaxis]
|
||||||
xlim = axis_limits(xaxis)
|
xlim = axis_limits(sp, :x)
|
||||||
ylim = axis_limits(yaxis)
|
ylim = axis_limits(sp, :y)
|
||||||
|
|
||||||
# make vectors
|
# make vectors
|
||||||
xlim = [xlim[1], xlim[2]]
|
xlim = [xlim[1], xlim[2]]
|
||||||
@ -138,7 +116,7 @@ function addUnicodeSeries!(o, plotattributes::KW, addlegend::Bool, xlim, ylim)
|
|||||||
x, y = if st == :straightline
|
x, y = if st == :straightline
|
||||||
straightline_data(plotattributes)
|
straightline_data(plotattributes)
|
||||||
elseif st == :shape
|
elseif st == :shape
|
||||||
shape_data(series)
|
shape_data(plotattributes)
|
||||||
else
|
else
|
||||||
[collect(float(plotattributes[s])) for s in (:x, :y)]
|
[collect(float(plotattributes[s])) for s in (:x, :y)]
|
||||||
end
|
end
|
||||||
|
|||||||
@ -42,8 +42,14 @@ function write_temp_html(plt::AbstractPlot)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function standalone_html_window(plt::AbstractPlot)
|
function standalone_html_window(plt::AbstractPlot)
|
||||||
|
old = use_local_dependencies[] # save state to restore afterwards
|
||||||
|
# if we open a browser ourself, we can host local files, so
|
||||||
|
# when we have a local plotly downloaded this is the way to go!
|
||||||
|
use_local_dependencies[] = isfile(plotly_local_file_path)
|
||||||
filename = write_temp_html(plt)
|
filename = write_temp_html(plt)
|
||||||
open_browser_window(filename)
|
open_browser_window(filename)
|
||||||
|
# restore for other backends
|
||||||
|
use_local_dependencies[] = old
|
||||||
end
|
end
|
||||||
|
|
||||||
# uses wkhtmltopdf/wkhtmltoimage: http://wkhtmltopdf.org/downloads.html
|
# uses wkhtmltopdf/wkhtmltoimage: http://wkhtmltopdf.org/downloads.html
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
const P2 = FixedSizeArrays.Vec{2,Float64}
|
const P2 = GeometryTypes.Point2{Float64}
|
||||||
const P3 = FixedSizeArrays.Vec{3,Float64}
|
const P3 = GeometryTypes.Point3{Float64}
|
||||||
|
|
||||||
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
|
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
|
||||||
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
|
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
|
||||||
@ -256,8 +256,24 @@ mutable struct Font
|
|||||||
color::Colorant
|
color::Colorant
|
||||||
end
|
end
|
||||||
|
|
||||||
"Create a Font from a list of unordered features"
|
"""
|
||||||
function font(args...)
|
font(args...)
|
||||||
|
Create a Font from a list of features. Values may be specified either as
|
||||||
|
arguments (which are distinguished by type/value) or as keyword arguments.
|
||||||
|
# Arguments
|
||||||
|
- `family`: AbstractString. "serif" or "sans-serif" or "monospace"
|
||||||
|
- `pointsize`: Integer. Size of font in points
|
||||||
|
- `halign`: Symbol. Horizontal alignment (:hcenter, :left, or :right)
|
||||||
|
- `valign`: Symbol. Vertical aligment (:vcenter, :top, or :bottom)
|
||||||
|
- `rotation`: Real. Angle of rotation for text in degrees (use a non-integer type)
|
||||||
|
- `color`: Colorant or Symbol
|
||||||
|
# Examples
|
||||||
|
```julia-repl
|
||||||
|
julia> font(8)
|
||||||
|
julia> font(family="serif",halign=:center,rotation=45.0)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
function font(args...;kw...)
|
||||||
|
|
||||||
# defaults
|
# defaults
|
||||||
family = "sans-serif"
|
family = "sans-serif"
|
||||||
@ -301,6 +317,32 @@ function font(args...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for symbol in keys(kw)
|
||||||
|
if symbol == :family
|
||||||
|
family = kw[:family]
|
||||||
|
elseif symbol == :pointsize
|
||||||
|
pointsize = kw[:pointsize]
|
||||||
|
elseif symbol == :halign
|
||||||
|
halign = kw[:halign]
|
||||||
|
if halign == :center
|
||||||
|
halign = :hcenter
|
||||||
|
end
|
||||||
|
@assert halign in (:hcenter, :left, :right)
|
||||||
|
elseif symbol == :valign
|
||||||
|
valign = kw[:valign]
|
||||||
|
if valign == :center
|
||||||
|
valign = :vcenter
|
||||||
|
end
|
||||||
|
@assert valign in (:vcenter, :top, :bottom)
|
||||||
|
elseif symbol == :rotation
|
||||||
|
rotation = kw[:rotation]
|
||||||
|
elseif symbol == :color
|
||||||
|
color = parse(Colorant, kw[:color])
|
||||||
|
else
|
||||||
|
@warn("Unused font kwarg: $symbol")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Font(family, pointsize, halign, valign, rotation, color)
|
Font(family, pointsize, halign, valign, rotation, color)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -344,15 +386,16 @@ end
|
|||||||
PlotText(str) = PlotText(string(str), font())
|
PlotText(str) = PlotText(string(str), font())
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text(string, args...)
|
text(string, args...; kw...)
|
||||||
|
|
||||||
Create a PlotText object wrapping a string with font info, for plot annotations
|
Create a PlotText object wrapping a string with font info, for plot annotations.
|
||||||
|
`args` and `kw` are passed to `font`.
|
||||||
"""
|
"""
|
||||||
text(t::PlotText) = t
|
text(t::PlotText) = t
|
||||||
text(t::PlotText, font::Font) = PlotText(t.str, font)
|
text(t::PlotText, font::Font) = PlotText(t.str, font)
|
||||||
text(str::AbstractString, f::Font) = PlotText(str, f)
|
text(str::AbstractString, f::Font) = PlotText(str, f)
|
||||||
function text(str, args...)
|
function text(str, args...;kw...)
|
||||||
PlotText(string(str), font(args...))
|
PlotText(string(str), font(args...;kw...))
|
||||||
end
|
end
|
||||||
|
|
||||||
Base.length(t::PlotText) = length(t.str)
|
Base.length(t::PlotText) = length(t.str)
|
||||||
@ -491,7 +534,7 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
|||||||
# with a list of custom shapes for each
|
# with a list of custom shapes for each
|
||||||
msw,msh = anns.scalefactor
|
msw,msh = anns.scalefactor
|
||||||
msize = Float64[]
|
msize = Float64[]
|
||||||
shapes = Vector{Shape}(length(anns.strs))
|
shapes = Vector{Shape}(undef, length(anns.strs))
|
||||||
for i in eachindex(anns.strs)
|
for i in eachindex(anns.strs)
|
||||||
str = _cycle(anns.strs,i)
|
str = _cycle(anns.strs,i)
|
||||||
|
|
||||||
@ -509,7 +552,7 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
|||||||
# and then re-scale a copy of baseshape to match the w/h ratio
|
# and then re-scale a copy of baseshape to match the w/h ratio
|
||||||
maxscale = max(xscale, yscale)
|
maxscale = max(xscale, yscale)
|
||||||
push!(msize, maxscale)
|
push!(msize, maxscale)
|
||||||
baseshape = _cycle(get(anns.baseshape),i)
|
baseshape = _cycle(anns.baseshape, i)
|
||||||
shapes[i] = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
|
shapes[i] = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
|
||||||
end
|
end
|
||||||
series[:markershape] = shapes
|
series[:markershape] = shapes
|
||||||
@ -554,7 +597,7 @@ function process_annotation(sp::Subplot, xs, ys, labs, font = font())
|
|||||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||||
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||||
else
|
else
|
||||||
push!(anns, (x, y, isa(lab, PlotText) ? lab : text(lab, font)))
|
push!(anns, (x, y, isa(lab, PlotText) ? lab : isa(lab, Tuple) ? text(lab...) : text(lab, font)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
anns
|
anns
|
||||||
@ -569,7 +612,7 @@ function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol},
|
|||||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||||
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||||
else
|
else
|
||||||
push!(anns, (pos, isa(lab, PlotText) ? lab : text(lab, font)))
|
push!(anns, (pos, isa(lab, PlotText) ? lab : isa(lab, Tuple) ? text(lab...) : text(lab, font)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
anns
|
anns
|
||||||
@ -736,7 +779,7 @@ end
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
"create a BezierCurve for plotting"
|
"create a BezierCurve for plotting"
|
||||||
mutable struct BezierCurve{T <: FixedSizeArrays.Vec}
|
mutable struct BezierCurve{T <: GeometryTypes.Point}
|
||||||
control_points::Vector{T}
|
control_points::Vector{T}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -749,9 +792,6 @@ function (bc::BezierCurve)(t::Real)
|
|||||||
p
|
p
|
||||||
end
|
end
|
||||||
|
|
||||||
# mean(x::Real, y::Real) = 0.5*(x+y) #commented out as I cannot see this used anywhere and it overwrites a Base method with different functionality
|
|
||||||
# mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps) # I also could not see this used anywhere, and it's type piracy - implementing a NaNMath version for this would just involve converting to a standard array
|
|
||||||
|
|
||||||
@deprecate curve_points coords
|
@deprecate curve_points coords
|
||||||
|
|
||||||
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, range(range..., stop=n, length=50))
|
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, range(range..., stop=n, length=50))
|
||||||
|
|||||||
@ -1,208 +0,0 @@
|
|||||||
|
|
||||||
# https://github.com/bokeh/Bokeh.jl
|
|
||||||
|
|
||||||
|
|
||||||
supported_attrs(::BokehBackend) = merge_with_base_supported([
|
|
||||||
# :annotations,
|
|
||||||
# :axis,
|
|
||||||
# :background_color,
|
|
||||||
:linecolor,
|
|
||||||
# :color_palette,
|
|
||||||
# :fillrange,
|
|
||||||
# :fillcolor,
|
|
||||||
# :fillalpha,
|
|
||||||
# :foreground_color,
|
|
||||||
:group,
|
|
||||||
# :label,
|
|
||||||
# :layout,
|
|
||||||
# :legend,
|
|
||||||
:seriescolor, :seriesalpha,
|
|
||||||
:linestyle,
|
|
||||||
:seriestype,
|
|
||||||
:linewidth,
|
|
||||||
# :linealpha,
|
|
||||||
:markershape,
|
|
||||||
:markercolor,
|
|
||||||
:markersize,
|
|
||||||
# :markeralpha,
|
|
||||||
# :markerstrokewidth,
|
|
||||||
# :markerstrokecolor,
|
|
||||||
# :markerstrokestyle,
|
|
||||||
# :n,
|
|
||||||
# :bins,
|
|
||||||
# :nc,
|
|
||||||
# :nr,
|
|
||||||
# :pos,
|
|
||||||
# :smooth,
|
|
||||||
# :show,
|
|
||||||
:size,
|
|
||||||
:title,
|
|
||||||
# :window_title,
|
|
||||||
:x,
|
|
||||||
# :xguide,
|
|
||||||
# :xlims,
|
|
||||||
# :xticks,
|
|
||||||
:y,
|
|
||||||
# :yguide,
|
|
||||||
# :ylims,
|
|
||||||
# :yrightlabel,
|
|
||||||
# :yticks,
|
|
||||||
# :xscale,
|
|
||||||
# :yscale,
|
|
||||||
# :xflip,
|
|
||||||
# :yflip,
|
|
||||||
# :z,
|
|
||||||
# :tickfont,
|
|
||||||
# :guidefont,
|
|
||||||
# :legendfont,
|
|
||||||
# :grid,
|
|
||||||
# :surface,
|
|
||||||
# :levels,
|
|
||||||
])
|
|
||||||
supported_types(::BokehBackend) = [:path, :scatter]
|
|
||||||
supported_styles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
|
||||||
supported_markers(::BokehBackend) = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
|
||||||
supported_scales(::BokehBackend) = [:identity, :ln]
|
|
||||||
is_subplot_supported(::BokehBackend) = false
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _initialize_backend(::BokehBackend; kw...)
|
|
||||||
@eval begin
|
|
||||||
@warn("Bokeh is no longer supported... many features will likely be broken.")
|
|
||||||
import Bokeh
|
|
||||||
export Bokeh
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
const _glyphtypes = KW(
|
|
||||||
:circle => :Circle,
|
|
||||||
:rect => :Square,
|
|
||||||
:diamond => :Diamond,
|
|
||||||
:utriangle => :Triangle,
|
|
||||||
:dtriangle => :InvertedTriangle,
|
|
||||||
# :pentagon =>
|
|
||||||
# :hexagon =>
|
|
||||||
# :heptagon =>
|
|
||||||
# :octagon =>
|
|
||||||
:cross => :Cross,
|
|
||||||
:xcross => :X,
|
|
||||||
:star5 => :Asterisk,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
function bokeh_glyph_type(plotattributes::KW)
|
|
||||||
st = plotattributes[:seriestype]
|
|
||||||
mt = plotattributes[:markershape]
|
|
||||||
if st == :scatter && mt == :none
|
|
||||||
mt = :circle
|
|
||||||
end
|
|
||||||
|
|
||||||
# if we have a marker, use that
|
|
||||||
if st == :scatter || mt != :none
|
|
||||||
return _glyphtypes[mt]
|
|
||||||
end
|
|
||||||
|
|
||||||
# otherwise return a line
|
|
||||||
return :Line
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_stroke_vector(linestyle::Symbol)
|
|
||||||
dash = 12
|
|
||||||
dot = 3
|
|
||||||
gap = 2
|
|
||||||
linestyle == :solid && return Int[]
|
|
||||||
linestyle == :dash && return Int[dash, gap]
|
|
||||||
linestyle == :dot && return Int[dot, gap]
|
|
||||||
linestyle == :dashdot && return Int[dash, gap, dot, gap]
|
|
||||||
linestyle == :dashdotdot && return Int[dash, gap, dot, gap, dot, gap]
|
|
||||||
error("unsupported linestyle: ", linestyle)
|
|
||||||
end
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# function _create_plot(pkg::BokehBackend, plotattributes::KW)
|
|
||||||
function _create_backend_figure(plt::Plot{BokehBackend})
|
|
||||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
|
||||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
|
||||||
|
|
||||||
datacolumns = Bokeh.BokehDataSet[]
|
|
||||||
tools = Bokeh.tools()
|
|
||||||
filename = tempname() * ".html"
|
|
||||||
title = plt.attr[:title]
|
|
||||||
w, h = plt.attr[:size]
|
|
||||||
xaxis_type = plt.attr[:xscale] == :log10 ? :log : :auto
|
|
||||||
yaxis_type = plt.attr[:yscale] == :log10 ? :log : :auto
|
|
||||||
# legend = plt.attr[:legend] ? xxxx : nothing
|
|
||||||
legend = nothing
|
|
||||||
extra_args = KW() # TODO: we'll put extra settings (xlim, etc) here
|
|
||||||
Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
|
|
||||||
|
|
||||||
# Plot(bplt, pkg, 0, plotattributes, KW[])
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# function _series_added(::BokehBackend, plt::Plot, plotattributes::KW)
|
|
||||||
function _series_added(plt::Plot{BokehBackend}, series::Series)
|
|
||||||
bdata = Dict{Symbol, Vector}(:x => collect(series.plotattributes[:x]), :y => collect(series.plotattributes[:y]))
|
|
||||||
|
|
||||||
glyph = Bokeh.Bokehjs.Glyph(
|
|
||||||
glyphtype = bokeh_glyph_type(plotattributes),
|
|
||||||
linecolor = webcolor(plotattributes[:linecolor]), # shape's stroke or line color
|
|
||||||
linewidth = plotattributes[:linewidth], # shape's stroke width or line width
|
|
||||||
fillcolor = webcolor(plotattributes[:markercolor]),
|
|
||||||
size = ceil(Int, plotattributes[:markersize] * 2.5), # magic number 2.5 to keep in same scale as other backends
|
|
||||||
dash = get_stroke_vector(plotattributes[:linestyle])
|
|
||||||
)
|
|
||||||
|
|
||||||
legend = nothing # TODO
|
|
||||||
push!(plt.o.datacolumns, Bokeh.BokehDataSet(bdata, glyph, legend))
|
|
||||||
|
|
||||||
# push!(plt.seriesargs, plotattributes)
|
|
||||||
# plt
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
|
||||||
function _update_plot_object(plt::Plot{BokehBackend}, plotattributes::KW)
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
# accessors for x/y data
|
|
||||||
|
|
||||||
# function getxy(plt::Plot{BokehBackend}, i::Int)
|
|
||||||
# series = plt.o.datacolumns[i].data
|
|
||||||
# series[:x], series[:y]
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# function setxy!(plt::Plot{BokehBackend}, xy::Tuple{X,Y}, i::Integer)
|
|
||||||
# series = plt.o.datacolumns[i].data
|
|
||||||
# series[:x], series[:y] = xy
|
|
||||||
# plt
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
|
|
||||||
# TODO: write a png to io
|
|
||||||
@warn("mime png not implemented")
|
|
||||||
end
|
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
|
|
||||||
Bokeh.showplot(plt.o)
|
|
||||||
end
|
|
||||||
|
|
||||||
# function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
|
|
||||||
# # TODO: display/show the subplot
|
|
||||||
# end
|
|
||||||
@ -1,744 +0,0 @@
|
|||||||
|
|
||||||
# https://github.com/dcjones/Gadfly.jl
|
|
||||||
|
|
||||||
|
|
||||||
supported_attrs(::GadflyBackend) = merge_with_base_supported([
|
|
||||||
:annotations,
|
|
||||||
:background_color, :foreground_color, :color_palette,
|
|
||||||
:group, :label, :seriestype,
|
|
||||||
:seriescolor, :seriesalpha,
|
|
||||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
|
||||||
:markershape, :markercolor, :markersize, :markeralpha,
|
|
||||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
|
||||||
:fillrange, :fillcolor, :fillalpha,
|
|
||||||
:bins, :n, :nc, :nr, :layout, :smooth,
|
|
||||||
:title, :window_title, :show, :size,
|
|
||||||
:x, :xguide, :xlims, :xticks, :xscale, :xflip,
|
|
||||||
:y, :yguide, :ylims, :yticks, :yscale, :yflip,
|
|
||||||
:z,
|
|
||||||
:tickfont, :guidefont, :legendfont,
|
|
||||||
:grid, :legend, :colorbar,
|
|
||||||
:marker_z, :levels,
|
|
||||||
:xerror, :yerror,
|
|
||||||
:ribbon, :quiver,
|
|
||||||
:orientation,
|
|
||||||
])
|
|
||||||
supported_types(::GadflyBackend) = [
|
|
||||||
:path,
|
|
||||||
:scatter, :hexbin,
|
|
||||||
:bar,
|
|
||||||
:contour, :shape
|
|
||||||
]
|
|
||||||
supported_styles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
|
||||||
supported_markers(::GadflyBackend) = vcat(_allMarkers, Shape)
|
|
||||||
supported_scales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
|
||||||
is_subplot_supported(::GadflyBackend) = true
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function _initialize_backend(::GadflyBackend; kw...)
|
|
||||||
@eval begin
|
|
||||||
import Gadfly, Compose
|
|
||||||
export Gadfly, Compose
|
|
||||||
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# immutable MissingVec <: AbstractVector{Float64} end
|
|
||||||
# Base.size(v::MissingVec) = (1,)
|
|
||||||
# Base.getindex(v::MissingVec, i::Integer) = 0.0
|
|
||||||
|
|
||||||
function createGadflyPlotObject(plotattributes::KW)
|
|
||||||
gplt = Gadfly.Plot()
|
|
||||||
gplt.mapping = Dict()
|
|
||||||
gplt.data_source = Gadfly.DataFrames.DataFrame()
|
|
||||||
# gplt.layers = gplt.layers[1:0]
|
|
||||||
gplt.layers = [Gadfly.layer(Gadfly.Geom.point(tag=:remove), x=zeros(1), y=zeros(1));] # x=MissingVec(), y=MissingVec());]
|
|
||||||
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(plotattributes[:xguide]),
|
|
||||||
Gadfly.Guide.ylabel(plotattributes[:yguide]),
|
|
||||||
Gadfly.Guide.title(plotattributes[:title])]
|
|
||||||
gplt
|
|
||||||
end
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
function getLineGeom(plotattributes::KW)
|
|
||||||
st = plotattributes[:seriestype]
|
|
||||||
xbins, ybins = maketuple(plotattributes[:bins])
|
|
||||||
if st == :hexb
|
|
||||||
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
|
|
||||||
elseif st == :histogram2d
|
|
||||||
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
|
|
||||||
elseif st == :histogram
|
|
||||||
Gadfly.Geom.histogram(bincount = xbins,
|
|
||||||
orientation = isvertical(plotattributes) ? :vertical : :horizontal,
|
|
||||||
position = plotattributes[:bar_position] == :stack ? :stack : :dodge)
|
|
||||||
elseif st == :path
|
|
||||||
Gadfly.Geom.path
|
|
||||||
elseif st in (:bar, :sticks)
|
|
||||||
Gadfly.Geom.bar
|
|
||||||
elseif st == :steppost
|
|
||||||
Gadfly.Geom.step
|
|
||||||
elseif st == :steppre
|
|
||||||
Gadfly.Geom.step(direction = :vh)
|
|
||||||
elseif st == :hline
|
|
||||||
Gadfly.Geom.hline
|
|
||||||
elseif st == :vline
|
|
||||||
Gadfly.Geom.vline
|
|
||||||
elseif st == :contour
|
|
||||||
Gadfly.Geom.contour(levels = plotattributes[:levels])
|
|
||||||
# elseif st == :shape
|
|
||||||
# Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
|
||||||
else
|
|
||||||
nothing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_extra_theme_args(plotattributes::KW, k::Symbol)
|
|
||||||
# gracefully handles old Gadfly versions
|
|
||||||
extra_theme_args = KW()
|
|
||||||
try
|
|
||||||
extra_theme_args[:line_style] = Gadfly.get_stroke_vector(plotattributes[k])
|
|
||||||
catch err
|
|
||||||
if string(err) == "UndefVarError(:get_stroke_vector)"
|
|
||||||
Base.warn_once("Gadfly.get_stroke_vector failed... do you have an old version of Gadfly?")
|
|
||||||
else
|
|
||||||
rethrow()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
extra_theme_args
|
|
||||||
end
|
|
||||||
|
|
||||||
function getGadflyLineTheme(plotattributes::KW)
|
|
||||||
st = plotattributes[:seriestype]
|
|
||||||
lc = convertColor(getColor(plotattributes[:linecolor]), plotattributes[:linealpha])
|
|
||||||
fc = convertColor(getColor(plotattributes[:fillcolor]), plotattributes[:fillalpha])
|
|
||||||
|
|
||||||
Gadfly.Theme(;
|
|
||||||
default_color = (st in (:histogram,:histogram2d,:hexbin,:bar,:sticks) ? fc : lc),
|
|
||||||
line_width = (st == :sticks ? 1 : plotattributes[:linewidth]) * Gadfly.px,
|
|
||||||
# line_style = Gadfly.get_stroke_vector(plotattributes[:linestyle]),
|
|
||||||
lowlight_color = x->RGB(fc), # fill/ribbon
|
|
||||||
lowlight_opacity = alpha(fc), # fill/ribbon
|
|
||||||
bar_highlight = RGB(lc), # bars
|
|
||||||
get_extra_theme_args(plotattributes, :linestyle)...
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# add a line as a new layer
|
|
||||||
function addGadflyLine!(plt::Plot, numlayers::Int, plotattributes::KW, geoms...)
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
gfargs = vcat(geoms..., getGadflyLineTheme(plotattributes))
|
|
||||||
kwargs = KW()
|
|
||||||
st = plotattributes[:seriestype]
|
|
||||||
|
|
||||||
# add a fill?
|
|
||||||
if plotattributes[:fillrange] != nothing && st != :contour
|
|
||||||
fillmin, fillmax = map(makevec, maketuple(plotattributes[:fillrange]))
|
|
||||||
nmin, nmax = length(fillmin), length(fillmax)
|
|
||||||
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(plotattributes[:y])]
|
|
||||||
kwargs[:ymax] = Float64[max(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(plotattributes[:y])]
|
|
||||||
push!(gfargs, Gadfly.Geom.ribbon)
|
|
||||||
end
|
|
||||||
|
|
||||||
if st in (:hline, :vline)
|
|
||||||
kwargs[st == :hline ? :yintercept : :xintercept] = plotattributes[:y]
|
|
||||||
|
|
||||||
else
|
|
||||||
if st == :sticks
|
|
||||||
w = 0.01 * mean(diff(plotattributes[:x]))
|
|
||||||
kwargs[:xmin] = plotattributes[:x] - w
|
|
||||||
kwargs[:xmax] = plotattributes[:x] + w
|
|
||||||
elseif st == :contour
|
|
||||||
kwargs[:z] = plotattributes[:z].surf
|
|
||||||
addGadflyContColorScale(plt, plotattributes[:linecolor])
|
|
||||||
end
|
|
||||||
|
|
||||||
kwargs[:x] = plotattributes[st == :histogram ? :y : :x]
|
|
||||||
kwargs[:y] = plotattributes[:y]
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# # add the layer
|
|
||||||
Gadfly.layer(gfargs...; order=numlayers, kwargs...)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
get_shape(sym::Symbol) = _shapes[sym]
|
|
||||||
get_shape(shape::Shape) = shape
|
|
||||||
|
|
||||||
# extract the underlying ShapeGeometry object(s)
|
|
||||||
getMarkerGeom(shapes::AVec) = gadflyshape(map(get_shape, shapes))
|
|
||||||
getMarkerGeom(other) = gadflyshape(get_shape(other))
|
|
||||||
|
|
||||||
# getMarkerGeom(shape::Shape) = gadflyshape(shape)
|
|
||||||
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
|
||||||
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
|
|
||||||
function getMarkerGeom(plotattributes::KW)
|
|
||||||
if plotattributes[:seriestype] == :shape
|
|
||||||
Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
|
||||||
else
|
|
||||||
getMarkerGeom(plotattributes[:markershape])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function getGadflyMarkerTheme(plotattributes::KW, attr::KW)
|
|
||||||
c = getColor(plotattributes[:markercolor])
|
|
||||||
α = plotattributes[:markeralpha]
|
|
||||||
if α != nothing
|
|
||||||
c = RGBA(RGB(c), α)
|
|
||||||
end
|
|
||||||
|
|
||||||
ms = plotattributes[:markersize]
|
|
||||||
ms = if typeof(ms) <: AVec
|
|
||||||
@warn("Gadfly doesn't support variable marker sizes... using the average: $(mean(ms))")
|
|
||||||
mean(ms) * Gadfly.px
|
|
||||||
else
|
|
||||||
ms * Gadfly.px
|
|
||||||
end
|
|
||||||
|
|
||||||
Gadfly.Theme(;
|
|
||||||
default_color = c,
|
|
||||||
default_point_size = ms,
|
|
||||||
discrete_highlight_color = c -> RGB(getColor(plotattributes[:markerstrokecolor])),
|
|
||||||
highlight_width = plotattributes[:markerstrokewidth] * Gadfly.px,
|
|
||||||
line_width = plotattributes[:markerstrokewidth] * Gadfly.px,
|
|
||||||
# get_extra_theme_args(plotattributes, :markerstrokestyle)...
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function addGadflyContColorScale(plt::Plot{GadflyBackend}, c)
|
|
||||||
plt.attr[:colorbar] == :none && return
|
|
||||||
if !isa(c, ColorGradient)
|
|
||||||
c = default_gradient()
|
|
||||||
end
|
|
||||||
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(c, p))))
|
|
||||||
end
|
|
||||||
|
|
||||||
function addGadflyMarker!(plt::Plot, numlayers::Int, plotattributes::KW, attr::KW, geoms...)
|
|
||||||
gfargs = vcat(geoms..., getGadflyMarkerTheme(plotattributes, attr), getMarkerGeom(plotattributes))
|
|
||||||
kwargs = KW()
|
|
||||||
|
|
||||||
# handle continuous color scales for the markers
|
|
||||||
zcolor = plotattributes[:marker_z]
|
|
||||||
if zcolor != nothing && typeof(zcolor) <: AVec
|
|
||||||
kwargs[:color] = zcolor
|
|
||||||
addGadflyContColorScale(plt, plotattributes[:markercolor])
|
|
||||||
end
|
|
||||||
|
|
||||||
Gadfly.layer(gfargs...; x = plotattributes[:x], y = plotattributes[:y], order=numlayers, kwargs...)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function addToGadflyLegend(plt::Plot, plotattributes::KW)
|
|
||||||
if plt.attr[:legend] != :none && plotattributes[:label] != ""
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
|
|
||||||
# add the legend if needed
|
|
||||||
if all(g -> !isa(g, Gadfly.Guide.ManualColorKey), gplt.guides)
|
|
||||||
pushfirst!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
|
|
||||||
end
|
|
||||||
|
|
||||||
# now add the series to the legend
|
|
||||||
for guide in gplt.guides
|
|
||||||
if isa(guide, Gadfly.Guide.ManualColorKey)
|
|
||||||
# TODO: there's a BUG in gadfly if you pass in the same color more than once,
|
|
||||||
# since gadfly will call unique(colors), but doesn't also merge the rows that match
|
|
||||||
# Should ensure from this side that colors which are the same are merged together
|
|
||||||
|
|
||||||
c = getColor(plotattributes[plotattributes[:markershape] == :none ? :linecolor : :markercolor])
|
|
||||||
foundit = false
|
|
||||||
|
|
||||||
# extend the label if we found this color
|
|
||||||
for i in 1:length(guide.colors)
|
|
||||||
if RGB(c) == guide.colors[i]
|
|
||||||
guide.labels[i] *= ", " * plotattributes[:label]
|
|
||||||
foundit = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# didn't find the color, so add a new entry into the legend
|
|
||||||
if !foundit
|
|
||||||
push!(guide.labels, plotattributes[:label])
|
|
||||||
push!(guide.colors, c)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
getGadflySmoothing(smooth::Bool) = smooth ? [Gadfly.Geom.smooth(method=:lm)] : Any[]
|
|
||||||
getGadflySmoothing(smooth::Real) = [Gadfly.Geom.smooth(method=:loess, smoothing=float(smooth))]
|
|
||||||
|
|
||||||
|
|
||||||
function addGadflySeries!(plt::Plot, plotattributes::KW)
|
|
||||||
layers = Gadfly.Layer[]
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
|
|
||||||
# add a regression line?
|
|
||||||
# TODO: make more flexible
|
|
||||||
smooth = getGadflySmoothing(plotattributes[:smooth])
|
|
||||||
|
|
||||||
# lines
|
|
||||||
geom = getLineGeom(plotattributes)
|
|
||||||
if geom != nothing
|
|
||||||
prepend!(layers, addGadflyLine!(plt, length(gplt.layers), plotattributes, geom, smooth...))
|
|
||||||
smooth = Any[] # don't add a regression for markers too
|
|
||||||
end
|
|
||||||
|
|
||||||
# special handling for ohlc and scatter
|
|
||||||
st = plotattributes[:seriestype]
|
|
||||||
# if st == :ohlc
|
|
||||||
# error("Haven't re-implemented after refactoring")
|
|
||||||
if st in (:histogram2d, :hexbin) && (isa(plotattributes[:fillcolor], ColorGradient) || isa(plotattributes[:fillcolor], ColorFunction))
|
|
||||||
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(plotattributes[:fillcolor], p))))
|
|
||||||
elseif st == :scatter && plotattributes[:markershape] == :none
|
|
||||||
plotattributes[:markershape] = :circle
|
|
||||||
end
|
|
||||||
|
|
||||||
# markers
|
|
||||||
if plotattributes[:markershape] != :none || st == :shape
|
|
||||||
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), plotattributes, plt.attr, smooth...))
|
|
||||||
end
|
|
||||||
|
|
||||||
st in (:histogram2d, :hexbin, :contour) || addToGadflyLegend(plt, plotattributes)
|
|
||||||
|
|
||||||
# now save the layers that apply to this series
|
|
||||||
plotattributes[:gadflylayers] = layers
|
|
||||||
prepend!(gplt.layers, layers)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# NOTE: I'm leaving this here and commented out just in case I want to implement again... it was hacky code to create multi-colored line segments
|
|
||||||
|
|
||||||
# # colorgroup
|
|
||||||
# z = plotattributes[:z]
|
|
||||||
|
|
||||||
# # handle line segments of different colors
|
|
||||||
# cscheme = plotattributes[:linecolor]
|
|
||||||
# if isa(cscheme, ColorVector)
|
|
||||||
# # create a color scale, and set the color group to the index of the color
|
|
||||||
# push!(gplt.scales, Gadfly.Scale.color_discrete_manual(cscheme.v...))
|
|
||||||
|
|
||||||
# # this is super weird, but... oh well... for some reason this creates n separate line segments...
|
|
||||||
# # create a list of vertices that go: [x1,x2,x2,x3,x3, ... ,xi,xi, ... xn,xn] (same for y)
|
|
||||||
# # then the vector passed to the "color" keyword should be a vector: [1,1,2,2,3,3,4,4, ..., i,i, ... , n,n]
|
|
||||||
# csindices = Int[mod1(i,length(cscheme.v)) for i in 1:length(plotattributes[:y])]
|
|
||||||
# cs = collect(repeat(csindices', 2, 1))[1:end-1]
|
|
||||||
# grp = collect(repeat((1:length(plotattributes[:y]))', 2, 1))[1:end-1]
|
|
||||||
# plotattributes[:x], plotattributes[:y] = map(createSegments, (plotattributes[:x], plotattributes[:y]))
|
|
||||||
# colorgroup = [(:linecolor, cs), (:group, grp)]
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
function addGadflyTicksGuide(gplt, ticks, isx::Bool)
|
|
||||||
ticks == :auto && return
|
|
||||||
|
|
||||||
# remove the ticks?
|
|
||||||
if ticks in (:none, false, nothing)
|
|
||||||
return addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
|
||||||
end
|
|
||||||
|
|
||||||
ttype = ticksType(ticks)
|
|
||||||
|
|
||||||
# just the values... put ticks here, but use standard labels
|
|
||||||
if ttype == :ticks
|
|
||||||
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
|
|
||||||
replaceType(gplt.guides, gtype(ticks = collect(ticks)))
|
|
||||||
|
|
||||||
# set the ticks and the labels
|
|
||||||
# Note: this is pretty convoluted, but I think it works. We set the ticks using Gadfly.Guide,
|
|
||||||
# and then set the label function (wraps a dict lookup) through a continuous Gadfly.Scale.
|
|
||||||
elseif ttype == :ticks_and_labels
|
|
||||||
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
|
|
||||||
replaceType(gplt.guides, gtype(ticks = collect(ticks[1])))
|
|
||||||
|
|
||||||
# # TODO add xtick_label function (given tick, return label??)
|
|
||||||
# # Scale.x_discrete(; labels=nothing, levels=nothing, order=nothing)
|
|
||||||
# filterGadflyScale(gplt, isx)
|
|
||||||
# gfunc = isx ? Gadfly.Scale.x_discrete : Gadfly.Scale.y_discrete
|
|
||||||
# labelmap = Dict(zip(ticks...))
|
|
||||||
# labelfunc = val -> labelmap[val]
|
|
||||||
# push!(gplt.scales, gfunc(levels = collect(ticks[1]), labels = labelfunc))
|
|
||||||
|
|
||||||
filterGadflyScale(gplt, isx)
|
|
||||||
gfunc = isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous
|
|
||||||
labelmap = Dict(zip(ticks...))
|
|
||||||
labelfunc = val -> labelmap[val]
|
|
||||||
push!(gplt.scales, gfunc(labels = labelfunc))
|
|
||||||
|
|
||||||
else
|
|
||||||
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
continuousAndSameAxis(scale, isx::Bool) = isa(scale, Gadfly.Scale.ContinuousScale) && scale.vars[1] == (isx ? :x : :y)
|
|
||||||
filterGadflyScale(gplt, isx::Bool) = filter!(scale -> !continuousAndSameAxis(scale, isx), gplt.scales)
|
|
||||||
|
|
||||||
|
|
||||||
function getGadflyScaleFunction(plotattributes::KW, isx::Bool)
|
|
||||||
scalekey = isx ? :xscale : :yscale
|
|
||||||
hasScaleKey = haskey(plotattributes, scalekey)
|
|
||||||
if hasScaleKey
|
|
||||||
scale = plotattributes[scalekey]
|
|
||||||
scale == :ln && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey, log
|
|
||||||
scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey, log2
|
|
||||||
scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey, log10
|
|
||||||
scale == :asinh && return isx ? Gadfly.Scale.x_asinh : Gadfly.Scale.y_asinh, hasScaleKey, asinh
|
|
||||||
scale == :sqrt && return isx ? Gadfly.Scale.x_sqrt : Gadfly.Scale.y_sqrt, hasScaleKey, sqrt
|
|
||||||
end
|
|
||||||
isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous, hasScaleKey, identity
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function addGadflyLimitsScale(gplt, plotattributes::KW, isx::Bool)
|
|
||||||
gfunc, hasScaleKey, func = getGadflyScaleFunction(plotattributes, isx)
|
|
||||||
|
|
||||||
# do we want to add min/max limits for the axis?
|
|
||||||
limsym = isx ? :xlims : :ylims
|
|
||||||
limargs = Any[]
|
|
||||||
|
|
||||||
# map :auto to nothing, otherwise add to limargs
|
|
||||||
lims = get(plotattributes, limsym, :auto)
|
|
||||||
if lims == :auto
|
|
||||||
lims = nothing
|
|
||||||
else
|
|
||||||
if limsType(lims) == :limits
|
|
||||||
push!(limargs, (:minvalue, min(lims...)))
|
|
||||||
push!(limargs, (:maxvalue, max(lims...)))
|
|
||||||
else
|
|
||||||
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# replace any current scales with this one
|
|
||||||
if hasScaleKey || !isempty(limargs)
|
|
||||||
filterGadflyScale(gplt, isx)
|
|
||||||
push!(gplt.scales, gfunc(; limargs...))
|
|
||||||
end
|
|
||||||
|
|
||||||
lims, func
|
|
||||||
end
|
|
||||||
|
|
||||||
function updateGadflyAxisFlips(gplt, plotattributes::KW, xlims, ylims, xfunc, yfunc)
|
|
||||||
if isa(gplt.coord, Gadfly.Coord.Cartesian)
|
|
||||||
gplt.coord = Gadfly.Coord.cartesian(
|
|
||||||
gplt.coord.xvars,
|
|
||||||
gplt.coord.yvars;
|
|
||||||
xmin = xlims == nothing ? gplt.coord.xmin : xfunc(minimum(xlims)),
|
|
||||||
xmax = xlims == nothing ? gplt.coord.xmax : xfunc(maximum(xlims)),
|
|
||||||
ymin = ylims == nothing ? gplt.coord.ymin : yfunc(minimum(ylims)),
|
|
||||||
ymax = ylims == nothing ? gplt.coord.ymax : yfunc(maximum(ylims)),
|
|
||||||
xflip = get(plotattributes, :xflip, gplt.coord.xflip),
|
|
||||||
yflip = get(plotattributes, :yflip, gplt.coord.yflip),
|
|
||||||
fixed = gplt.coord.fixed,
|
|
||||||
aspect_ratio = gplt.coord.aspect_ratio,
|
|
||||||
raster = gplt.coord.raster
|
|
||||||
)
|
|
||||||
else
|
|
||||||
gplt.coord = Gadfly.Coord.Cartesian(
|
|
||||||
xflip = get(plotattributes, :xflip, false),
|
|
||||||
yflip = get(plotattributes, :yflip, false)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function findGuideAndSet(gplt, t::DataType, args...; kw...)
|
|
||||||
for (i,guide) in enumerate(gplt.guides)
|
|
||||||
if isa(guide, t)
|
|
||||||
gplt.guides[i] = t(args...; kw...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function updateGadflyGuides(plt::Plot, plotattributes::KW)
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
haskey(plotattributes, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(plotattributes[:title]))
|
|
||||||
haskey(plotattributes, :xguide) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(plotattributes[:xguide]))
|
|
||||||
haskey(plotattributes, :yguide) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(plotattributes[:yguide]))
|
|
||||||
|
|
||||||
xlims, xfunc = addGadflyLimitsScale(gplt, plotattributes, true)
|
|
||||||
ylims, yfunc = addGadflyLimitsScale(gplt, plotattributes, false)
|
|
||||||
|
|
||||||
ticks = get(plotattributes, :xticks, :auto)
|
|
||||||
if ticks == :none
|
|
||||||
_remove_axis(plt, true)
|
|
||||||
else
|
|
||||||
addGadflyTicksGuide(gplt, ticks, true)
|
|
||||||
end
|
|
||||||
ticks = get(plotattributes, :yticks, :auto)
|
|
||||||
if ticks == :none
|
|
||||||
_remove_axis(plt, false)
|
|
||||||
else
|
|
||||||
addGadflyTicksGuide(gplt, ticks, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
updateGadflyAxisFlips(gplt, plotattributes, xlims, ylims, xfunc, yfunc)
|
|
||||||
end
|
|
||||||
|
|
||||||
function updateGadflyPlotTheme(plt::Plot, plotattributes::KW)
|
|
||||||
kwargs = KW()
|
|
||||||
|
|
||||||
# colors
|
|
||||||
insidecolor, gridcolor, textcolor, guidecolor, legendcolor =
|
|
||||||
map(s -> getColor(plotattributes[s]), (
|
|
||||||
:background_color_inside,
|
|
||||||
:foreground_color_grid,
|
|
||||||
:foreground_color_text,
|
|
||||||
:foreground_color_guide,
|
|
||||||
:foreground_color_legend
|
|
||||||
))
|
|
||||||
|
|
||||||
# # hide the legend?
|
|
||||||
leg = plotattributes[plotattributes[:legend] == :none ? :colorbar : :legend]
|
|
||||||
if leg != :best
|
|
||||||
kwargs[:key_position] = leg == :inside ? :right : leg
|
|
||||||
end
|
|
||||||
|
|
||||||
if !get(plotattributes, :grid, true)
|
|
||||||
kwargs[:grid_color] = gridcolor
|
|
||||||
end
|
|
||||||
|
|
||||||
# fonts
|
|
||||||
tfont, gfont, lfont = plotattributes[:tickfont], plotattributes[:guidefont], plotattributes[:legendfont]
|
|
||||||
|
|
||||||
getGadflyContext(plt).theme = Gadfly.Theme(;
|
|
||||||
background_color = insidecolor,
|
|
||||||
minor_label_color = textcolor,
|
|
||||||
minor_label_font = tfont.family,
|
|
||||||
minor_label_font_size = tfont.pointsize * Gadfly.pt,
|
|
||||||
major_label_color = guidecolor,
|
|
||||||
major_label_font = gfont.family,
|
|
||||||
major_label_font_size = gfont.pointsize * Gadfly.pt,
|
|
||||||
key_title_color = guidecolor,
|
|
||||||
key_title_font = gfont.family,
|
|
||||||
key_title_font_size = gfont.pointsize * Gadfly.pt,
|
|
||||||
key_label_color = legendcolor,
|
|
||||||
key_label_font = lfont.family,
|
|
||||||
key_label_font_size = lfont.pointsize * Gadfly.pt,
|
|
||||||
plot_padding = 1 * Gadfly.mm,
|
|
||||||
kwargs...
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
function createGadflyAnnotationObject(x, y, val::AbstractString)
|
|
||||||
Gadfly.Guide.annotation(Compose.compose(
|
|
||||||
Compose.context(),
|
|
||||||
Compose.text(x, y, val)
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
function createGadflyAnnotationObject(x, y, txt::PlotText)
|
|
||||||
halign = (txt.font.halign == :hcenter ? Compose.hcenter : (txt.font.halign == :left ? Compose.hleft : Compose.hright))
|
|
||||||
valign = (txt.font.valign == :vcenter ? Compose.vcenter : (txt.font.valign == :top ? Compose.vtop : Compose.vbottom))
|
|
||||||
rotations = (txt.font.rotation == 0.0 ? [] : [Compose.Rotation(txt.font.rotation, Compose.Point(Compose.x_measure(x), Compose.y_measure(y)))])
|
|
||||||
Gadfly.Guide.annotation(Compose.compose(
|
|
||||||
Compose.context(),
|
|
||||||
Compose.text(x, y, txt.str, halign, valign, rotations...),
|
|
||||||
Compose.font(string(txt.font.family)),
|
|
||||||
Compose.fontsize(txt.font.pointsize * Gadfly.pt),
|
|
||||||
Compose.stroke(txt.font.color),
|
|
||||||
Compose.fill(txt.font.color)
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
function _add_annotations(plt::Plot{GadflyBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
|
||||||
for ann in anns
|
|
||||||
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# create a blank Gadfly.Plot object
|
|
||||||
# function _create_plot(pkg::GadflyBackend, plotattributes::KW)
|
|
||||||
# gplt = createGadflyPlotObject(plotattributes)
|
|
||||||
# Plot(gplt, pkg, 0, plotattributes, KW[])
|
|
||||||
# end
|
|
||||||
function _create_backend_figure(plt::Plot{GadflyBackend})
|
|
||||||
createGadflyPlotObject(plt.attr)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# plot one data series
|
|
||||||
# function _series_added(::GadflyBackend, plt::Plot, plotattributes::KW)
|
|
||||||
function _series_added(plt::Plot{GadflyBackend}, series::Series)
|
|
||||||
# first clear out the temporary layer
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
if gplt.layers[1].geom.tag == :remove
|
|
||||||
gplt.layers = gplt.layers[2:end]
|
|
||||||
end
|
|
||||||
|
|
||||||
addGadflySeries!(plt, series.plotattributes)
|
|
||||||
# push!(plt.seriesargs, plotattributes)
|
|
||||||
# plt
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _update_plot_object(plt::Plot{GadflyBackend}, plotattributes::KW)
|
|
||||||
updateGadflyGuides(plt, plotattributes)
|
|
||||||
updateGadflyPlotTheme(plt, plotattributes)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
# accessors for x/y data
|
|
||||||
|
|
||||||
# TODO: need to save all the layer indices which apply to this series
|
|
||||||
function getGadflyMappings(plt::Plot, i::Integer)
|
|
||||||
@assert i > 0 && i <= plt.n
|
|
||||||
mappings = [l.mapping for l in plt.seriesargs[i][:gadflylayers]]
|
|
||||||
end
|
|
||||||
|
|
||||||
function getxy(plt::Plot{GadflyBackend}, i::Integer)
|
|
||||||
mapping = getGadflyMappings(plt, i)[1]
|
|
||||||
mapping[:x], mapping[:y]
|
|
||||||
end
|
|
||||||
|
|
||||||
function setxy!(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
|
||||||
for mapping in getGadflyMappings(plt, i)
|
|
||||||
mapping[:x], mapping[:y] = xy
|
|
||||||
end
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
# # create the underlying object (each backend will do this differently)
|
|
||||||
# function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
|
|
||||||
# isbefore && return false # wait until after plotting to create the subplots
|
|
||||||
# subplt.o = nothing
|
|
||||||
# true
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
function _remove_axis(plt::Plot{GadflyBackend}, isx::Bool)
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
|
||||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
function _expand_limits(lims, plt::Plot{GadflyBackend}, isx::Bool)
|
|
||||||
for l in getGadflyContext(plt).layers
|
|
||||||
_expand_limits(lims, l.mapping[isx ? :x : :y])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
getGadflyContext(plt::Plot{GadflyBackend}) = plt.o
|
|
||||||
# getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
|
|
||||||
|
|
||||||
# # create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
|
|
||||||
# function buildGadflySubplotContext(subplt::Subplot)
|
|
||||||
# rows = Any[]
|
|
||||||
# row = Any[]
|
|
||||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
|
||||||
#
|
|
||||||
# # add the Plot object to the row
|
|
||||||
# push!(row, getGadflyContext(subplt.plts[i]))
|
|
||||||
#
|
|
||||||
# # add the row
|
|
||||||
# if c == ncols(subplt.layout, r)
|
|
||||||
# push!(rows, Gadfly.hstack(row...))
|
|
||||||
# row = Any[]
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # stack the rows
|
|
||||||
# Gadfly.vstack(rows...)
|
|
||||||
# end
|
|
||||||
|
|
||||||
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
|
|
||||||
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.attr[:size]...)
|
|
||||||
# setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getattr(subplt, 1)[:size]...)
|
|
||||||
# -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
function doshow(io::IO, func, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
setGadflyDisplaySize(plt)
|
|
||||||
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
|
|
||||||
end
|
|
||||||
|
|
||||||
getGadflyWriteFunc(::MIME"image/png") = Gadfly.PNG
|
|
||||||
getGadflyWriteFunc(::MIME"image/svg+xml") = Gadfly.SVG
|
|
||||||
# getGadflyWriteFunc(::MIME"text/html") = Gadfly.SVGJS
|
|
||||||
getGadflyWriteFunc(::MIME"application/pdf") = Gadfly.PDF
|
|
||||||
getGadflyWriteFunc(::MIME"application/postscript") = Gadfly.PS
|
|
||||||
getGadflyWriteFunc(::MIME"application/x-tex") = Gadfly.PGF
|
|
||||||
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
|
|
||||||
|
|
||||||
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript", MIME"application/x-tex")
|
|
||||||
@eval function Base.show(io::IO, ::$mime, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
|
|
||||||
func = getGadflyWriteFunc($mime())
|
|
||||||
doshow(io, func, plt)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::Plot{GadflyBackend})
|
|
||||||
setGadflyDisplaySize(plt.attr[:size]...)
|
|
||||||
display(plt.o)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
|
|
||||||
# setGadflyDisplaySize(getattr(subplt,1)[:size]...)
|
|
||||||
# ctx = buildGadflySubplotContext(subplt)
|
|
||||||
#
|
|
||||||
# # taken from Gadfly since I couldn't figure out how to do it directly
|
|
||||||
#
|
|
||||||
# filename = string(Gadfly.tempname(), ".html")
|
|
||||||
# output = open(filename, "w")
|
|
||||||
#
|
|
||||||
# plot_output = IOBuffer()
|
|
||||||
# Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
|
|
||||||
# Compose.default_graphic_height, false), ctx)
|
|
||||||
# plotsvg = takebuf_string(plot_output)
|
|
||||||
#
|
|
||||||
# write(output,
|
|
||||||
# """
|
|
||||||
# <!DOCTYPE html>
|
|
||||||
# <html>
|
|
||||||
# <head>
|
|
||||||
# <title>Gadfly Plot</title>
|
|
||||||
# <meta charset="utf-8">
|
|
||||||
# </head>
|
|
||||||
# <body>
|
|
||||||
# <script charset="utf-8">
|
|
||||||
# $(readall(Compose.snapsvgjs))
|
|
||||||
# </script>
|
|
||||||
# <script charset="utf-8">
|
|
||||||
# $(readall(Gadfly.gadflyjs))
|
|
||||||
# </script>
|
|
||||||
# $(plotsvg)
|
|
||||||
# </body>
|
|
||||||
# </html>
|
|
||||||
# """)
|
|
||||||
# close(output)
|
|
||||||
# Gadfly.open_file(filename)
|
|
||||||
# end
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
# Geometry which displays arbitrary shapes at given (x, y) positions.
|
|
||||||
# note: vertices is a list of shapes
|
|
||||||
struct ShapeGeometry <: Gadfly.GeometryElement
|
|
||||||
vertices::AbstractVector #{Tuple{Float64,Float64}}
|
|
||||||
tag::Symbol
|
|
||||||
|
|
||||||
function ShapeGeometry(shape; tag::Symbol=Gadfly.Geom.empty_tag)
|
|
||||||
new(shape, tag)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: add for PR
|
|
||||||
# const shape = ShapeGeometry
|
|
||||||
|
|
||||||
|
|
||||||
function Gadfly.element_aesthetics(::ShapeGeometry)
|
|
||||||
[:x, :y, :size, :color]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Generate a form for a shape geometry.
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# geom: shape geometry.
|
|
||||||
# theme: the plot's theme.
|
|
||||||
# aes: aesthetics.
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# A compose Form.
|
|
||||||
#
|
|
||||||
function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics)
|
|
||||||
|
|
||||||
# TODO: add for PR
|
|
||||||
# Gadfly.assert_aesthetics_defined("Geom.shape", aes, :x, :y)
|
|
||||||
# Gadfly.assert_aesthetics_equal_length("Geom.shape", aes,
|
|
||||||
# element_aesthetics(geom)...)
|
|
||||||
|
|
||||||
default_aes = Gadfly.Aesthetics()
|
|
||||||
default_aes.color = Gadfly.DataFrames.PooledDataArray(RGBA{Float32}[theme.default_color])
|
|
||||||
default_aes.size = Compose.Measure[theme.default_point_size]
|
|
||||||
aes = Gadfly.inherit(aes, default_aes)
|
|
||||||
|
|
||||||
lw_hover_scale = 10
|
|
||||||
lw_ratio = theme.line_width / aes.size[1]
|
|
||||||
|
|
||||||
aes_x, aes_y = Gadfly.concretize(aes.x, aes.y)
|
|
||||||
|
|
||||||
ctx = Compose.compose!(
|
|
||||||
Compose.context(),
|
|
||||||
make_polygon(geom, aes.x, aes.y, aes.size),
|
|
||||||
Compose.fill(aes.color),
|
|
||||||
Compose.linewidth(theme.highlight_width))
|
|
||||||
|
|
||||||
if aes.color_key_continuous != nothing && aes.color_key_continuous
|
|
||||||
Compose.compose!(ctx,
|
|
||||||
Compose.stroke(map(theme.continuous_highlight_color, aes.color)))
|
|
||||||
else
|
|
||||||
Compose.compose!(ctx,
|
|
||||||
Compose.stroke(map(theme.discrete_highlight_color, aes.color)),
|
|
||||||
Compose.svgclass([Gadfly.svg_color_class_from_label(Gadfly.escape_id(aes.color_label([c])[1]))
|
|
||||||
for c in aes.color]))
|
|
||||||
end
|
|
||||||
|
|
||||||
return Compose.compose!(Compose.context(order=4), Compose.svgclass("geometry"), ctx)
|
|
||||||
end
|
|
||||||
|
|
||||||
function gadflyshape(sv::Shape)
|
|
||||||
ShapeGeometry(Any[vertices(sv)])
|
|
||||||
end
|
|
||||||
|
|
||||||
function gadflyshape(sv::AVec{Shape})
|
|
||||||
ShapeGeometry(Any[vertices(s) for s in sv])
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# create a Compose context given a ShapeGeometry and the xs/ys/sizes
|
|
||||||
function make_polygon(geom::ShapeGeometry, xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
|
|
||||||
n = max(length(xs), length(ys), length(rs))
|
|
||||||
T = Tuple{Compose.Measure, Compose.Measure}
|
|
||||||
polys = Array(Vector{T}, n)
|
|
||||||
for i in 1:n
|
|
||||||
x = Compose.x_measure(xs[mod1(i, length(xs))])
|
|
||||||
y = Compose.y_measure(ys[mod1(i, length(ys))])
|
|
||||||
r = rs[mod1(i, length(rs))]
|
|
||||||
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in _cycle(geom.vertices, i)]
|
|
||||||
end
|
|
||||||
Gadfly.polygon(polys, geom.tag)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------
|
|
||||||
@ -1,186 +0,0 @@
|
|||||||
|
|
||||||
# https://github.com/JuliaGraphics/Immerse.jl
|
|
||||||
|
|
||||||
supported_attrs(::ImmerseBackend) = supported_attrs(GadflyBackend())
|
|
||||||
supported_types(::ImmerseBackend) = supported_types(GadflyBackend())
|
|
||||||
supported_styles(::ImmerseBackend) = supported_styles(GadflyBackend())
|
|
||||||
supported_markers(::ImmerseBackend) = supported_markers(GadflyBackend())
|
|
||||||
supported_scales(::ImmerseBackend) = supported_scales(GadflyBackend())
|
|
||||||
is_subplot_supported(::ImmerseBackend) = true
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function _initialize_backend(::ImmerseBackend; kw...)
|
|
||||||
@eval begin
|
|
||||||
import Immerse, Gadfly, Compose, Gtk
|
|
||||||
export Immerse, Gadfly, Compose, Gtk
|
|
||||||
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function createImmerseFigure(plotattributes::KW)
|
|
||||||
w,h = plotattributes[:size]
|
|
||||||
figidx = Immerse.figure(; name = plotattributes[:window_title], width = w, height = h)
|
|
||||||
Immerse.Figure(figidx)
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
# create a blank Gadfly.Plot object
|
|
||||||
# function _create_plot(pkg::ImmerseBackend, plotattributes::KW)
|
|
||||||
# # create the underlying Gadfly.Plot object
|
|
||||||
# gplt = createGadflyPlotObject(plotattributes)
|
|
||||||
#
|
|
||||||
# # save both the Immerse.Figure and the Gadfly.Plot
|
|
||||||
# Plot((nothing,gplt), pkg, 0, plotattributes, KW[])
|
|
||||||
# end
|
|
||||||
function _create_backend_figure(plt::Plot{ImmerseBackend})
|
|
||||||
(nothing, createGadflyPlotObject(plt.attr))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# # plot one data series
|
|
||||||
# function _series_added(::ImmerseBackend, plt::Plot, plotattributes::KW)
|
|
||||||
# addGadflySeries!(plt, plotattributes)
|
|
||||||
# push!(plt.seriesargs, plotattributes)
|
|
||||||
# plt
|
|
||||||
# end
|
|
||||||
|
|
||||||
function _series_added(plt::Plot{ImmerseBackend}, series::Series)
|
|
||||||
addGadflySeries!(plt, series.plotattributes)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function _update_plot_object(plt::Plot{ImmerseBackend}, plotattributes::KW)
|
|
||||||
updateGadflyGuides(plt, plotattributes)
|
|
||||||
updateGadflyPlotTheme(plt, plotattributes)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
function _add_annotations(plt::Plot{ImmerseBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
|
||||||
for ann in anns
|
|
||||||
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
# accessors for x/y data
|
|
||||||
|
|
||||||
function getxy(plt::Plot{ImmerseBackend}, i::Integer)
|
|
||||||
mapping = getGadflyMappings(plt, i)[1]
|
|
||||||
mapping[:x], mapping[:y]
|
|
||||||
end
|
|
||||||
|
|
||||||
function setxy!(plt::Plot{ImmerseBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
|
||||||
for mapping in getGadflyMappings(plt, i)
|
|
||||||
mapping[:x], mapping[:y] = xy
|
|
||||||
end
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
# function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
|
|
||||||
# return false
|
|
||||||
# # isbefore && return false
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# function showSubplotObject(subplt::Subplot{ImmerseBackend})
|
|
||||||
# # create the Gtk window with vertical box vsep
|
|
||||||
# plotattributes = getattr(subplt,1)
|
|
||||||
# w,h = plotattributes[:size]
|
|
||||||
# vsep = Gtk.GtkBoxLeaf(:v)
|
|
||||||
# win = Gtk.GtkWindowLeaf(vsep, plotattributes[:window_title], w, h)
|
|
||||||
#
|
|
||||||
# figindices = []
|
|
||||||
# row = Gtk.GtkBoxLeaf(:h)
|
|
||||||
# push!(vsep, row)
|
|
||||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
|
||||||
# plt = subplt.plts[i]
|
|
||||||
#
|
|
||||||
# # get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
|
|
||||||
# box, toolbar, canvas = Immerse.createPlotGuiComponents()
|
|
||||||
#
|
|
||||||
# # add the plot's box to the row
|
|
||||||
# push!(row, box)
|
|
||||||
#
|
|
||||||
# # create the figure and store the index returned for destruction later
|
|
||||||
# figidx = Immerse.figure(canvas)
|
|
||||||
# push!(figindices, figidx)
|
|
||||||
#
|
|
||||||
# fig = Immerse.figure(figidx)
|
|
||||||
# plt.o = (fig, plt.o[2])
|
|
||||||
#
|
|
||||||
# # add the row
|
|
||||||
# if c == ncols(subplt.layout, r)
|
|
||||||
# row = Gtk.GtkBoxLeaf(:h)
|
|
||||||
# push!(vsep, row)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # destructor... clean up plots
|
|
||||||
# Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
|
|
||||||
#
|
|
||||||
# subplt.o = win
|
|
||||||
# true
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
function _remove_axis(plt::Plot{ImmerseBackend}, isx::Bool)
|
|
||||||
gplt = getGadflyContext(plt)
|
|
||||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
|
||||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
function _expand_limits(lims, plt::Plot{ImmerseBackend}, isx::Bool)
|
|
||||||
for l in getGadflyContext(plt).layers
|
|
||||||
_expand_limits(lims, l.mapping[isx ? :x : :y])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
getGadflyContext(plt::Plot{ImmerseBackend}) = plt.o[2]
|
|
||||||
# getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
|
|
||||||
|
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
|
|
||||||
|
|
||||||
fig, gplt = plt.o
|
|
||||||
if fig == nothing
|
|
||||||
fig = createImmerseFigure(plt.attr)
|
|
||||||
Gtk.on_signal_destroy((x...) -> (Immerse.dropfig(Immerse._display, fig.figno); plt.o = (nothing,gplt)), fig.canvas)
|
|
||||||
plt.o = (fig, gplt)
|
|
||||||
end
|
|
||||||
|
|
||||||
Immerse.figure(fig.figno; displayfig = false)
|
|
||||||
display(gplt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
|
|
||||||
#
|
|
||||||
# # if we haven't created the window yet, do it
|
|
||||||
# if subplt.o == nothing
|
|
||||||
# showSubplotObject(subplt)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
|
|
||||||
# for plt in subplt.plts
|
|
||||||
# fig, gplt = plt.o
|
|
||||||
# Immerse.figure(fig.figno; displayfig = false)
|
|
||||||
# display(gplt)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # o is the window... show it
|
|
||||||
# showall(subplt.o)
|
|
||||||
# end
|
|
||||||
@ -1,308 +0,0 @@
|
|||||||
|
|
||||||
# https://github.com/tbreloff/Qwt.jl
|
|
||||||
|
|
||||||
|
|
||||||
supported_attrs(::QwtBackend) = merge_with_base_supported([
|
|
||||||
:annotations,
|
|
||||||
:linecolor,
|
|
||||||
:fillrange,
|
|
||||||
:fillcolor,
|
|
||||||
:label,
|
|
||||||
:legend,
|
|
||||||
:seriescolor, :seriesalpha,
|
|
||||||
:linestyle,
|
|
||||||
:linewidth,
|
|
||||||
:markershape,
|
|
||||||
:markercolor,
|
|
||||||
:markersize,
|
|
||||||
:bins,
|
|
||||||
:pos,
|
|
||||||
:title,
|
|
||||||
:window_title,
|
|
||||||
:guide, :lims, :ticks, :scale,
|
|
||||||
])
|
|
||||||
supported_types(::QwtBackend) = [:path, :scatter, :hexbin, :bar]
|
|
||||||
supported_markers(::QwtBackend) = [:none, :auto, :rect, :circle, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
|
|
||||||
supported_scales(::QwtBackend) = [:identity, :log10]
|
|
||||||
is_subplot_supported(::QwtBackend) = true
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function _initialize_backend(::QwtBackend; kw...)
|
|
||||||
@eval begin
|
|
||||||
@warn("Qwt is no longer supported... many features will likely be broken.")
|
|
||||||
import Qwt
|
|
||||||
export Qwt
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# -------------------------------
|
|
||||||
|
|
||||||
const _qwtAliases = KW(
|
|
||||||
:bins => :heatmap_n,
|
|
||||||
:fillrange => :fillto,
|
|
||||||
:linewidth => :width,
|
|
||||||
:markershape => :marker,
|
|
||||||
:hexbin => :heatmap,
|
|
||||||
:path => :line,
|
|
||||||
:steppost => :step,
|
|
||||||
:steppre => :stepinverted,
|
|
||||||
:star5 => :star1,
|
|
||||||
:star8 => :star2,
|
|
||||||
)
|
|
||||||
|
|
||||||
function fixcolors(plotattributes::KW)
|
|
||||||
for (k,v) in plotattributes
|
|
||||||
if typeof(v) <: ColorScheme
|
|
||||||
plotattributes[k] = getColor(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function replaceQwtAliases(plotattributes, s)
|
|
||||||
if haskey(_qwtAliases, plotattributes[s])
|
|
||||||
plotattributes[s] = _qwtAliases[plotattributes[s]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
|
|
||||||
plotattributes = KW(kw)
|
|
||||||
st = plotattributes[:seriestype]
|
|
||||||
if st == :scatter
|
|
||||||
plotattributes[:seriestype] = :none
|
|
||||||
if plotattributes[:markershape] == :none
|
|
||||||
plotattributes[:markershape] = :circle
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif st in (:hline, :vline)
|
|
||||||
addLineMarker(plt, plotattributes)
|
|
||||||
plotattributes[:seriestype] = :none
|
|
||||||
plotattributes[:markershape] = :circle
|
|
||||||
plotattributes[:markersize] = 1
|
|
||||||
if st == :vline
|
|
||||||
plotattributes[:x], plotattributes[:y] = plotattributes[:y], plotattributes[:x]
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif !iscreating && st == :bar
|
|
||||||
plotattributes = barHack(; kw...)
|
|
||||||
elseif !iscreating && st == :histogram
|
|
||||||
plotattributes = barHack(; histogramHack(; kw...)...)
|
|
||||||
end
|
|
||||||
|
|
||||||
replaceQwtAliases(plotattributes, :seriestype)
|
|
||||||
replaceQwtAliases(plotattributes, :markershape)
|
|
||||||
|
|
||||||
for k in keys(plotattributes)
|
|
||||||
if haskey(_qwtAliases, k)
|
|
||||||
plotattributes[_qwtAliases[k]] = plotattributes[k]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
plotattributes[:x] = collect(plotattributes[:x])
|
|
||||||
plotattributes[:y] = collect(plotattributes[:y])
|
|
||||||
|
|
||||||
plotattributes
|
|
||||||
end
|
|
||||||
|
|
||||||
# function _create_plot(pkg::QwtBackend, plotattributes::KW)
|
|
||||||
function _create_backend_figure(plt::Plot{QwtBackend})
|
|
||||||
fixcolors(plt.attr)
|
|
||||||
dumpdict(plt.attr,"\n\n!!! plot")
|
|
||||||
o = Qwt.plot(zeros(0,0); plt.attr..., show=false)
|
|
||||||
# plt = Plot(o, pkg, 0, plotattributes, KW[])
|
|
||||||
# plt
|
|
||||||
end
|
|
||||||
|
|
||||||
# function _series_added(::QwtBackend, plt::Plot, plotattributes::KW)
|
|
||||||
function _series_added(plt::Plot{QwtBackend}, series::Series)
|
|
||||||
plotattributes = adjustQwtKeywords(plt, false; series.plotattributes...)
|
|
||||||
fixcolors(plotattributes)
|
|
||||||
dumpdict(plotattributes,"\n\n!!! plot!")
|
|
||||||
Qwt.oplot(plt.o; plotattributes...)
|
|
||||||
# push!(plt.seriesargs, plotattributes)
|
|
||||||
# plt
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
function updateLimsAndTicks(plt::Plot{QwtBackend}, plotattributes::KW, isx::Bool)
|
|
||||||
lims = get(plotattributes, isx ? :xlims : :ylims, nothing)
|
|
||||||
ticks = get(plotattributes, isx ? :xticks : :yticks, nothing)
|
|
||||||
w = plt.o.widget
|
|
||||||
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
|
|
||||||
|
|
||||||
if typeof(lims) <: Union{Tuple,AVec} && length(lims) == 2
|
|
||||||
if isx
|
|
||||||
plt.o.autoscale_x = false
|
|
||||||
else
|
|
||||||
plt.o.autoscale_y = false
|
|
||||||
end
|
|
||||||
w[:setAxisScale](axisid, lims...)
|
|
||||||
end
|
|
||||||
|
|
||||||
if typeof(ticks) <: AbstractRange
|
|
||||||
if isx
|
|
||||||
plt.o.autoscale_x = false
|
|
||||||
else
|
|
||||||
plt.o.autoscale_y = false
|
|
||||||
end
|
|
||||||
w[:setAxisScale](axisid, float(minimum(ticks)), float(maximum(ticks)), float(step(ticks)))
|
|
||||||
elseif !(ticks in (nothing, :none, :auto))
|
|
||||||
@warn("Only Range types are supported for Qwt xticks/yticks. typeof(ticks)=$(typeof(ticks))")
|
|
||||||
end
|
|
||||||
|
|
||||||
# change the scale
|
|
||||||
scalesym = isx ? :xscale : :yscale
|
|
||||||
if haskey(plotattributes, scalesym)
|
|
||||||
scaletype = plotattributes[scalesym]
|
|
||||||
scaletype == :identity && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLinearScaleEngine())
|
|
||||||
# scaletype == :log && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(e))
|
|
||||||
# scaletype == :log2 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(2))
|
|
||||||
scaletype == :log10 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLog10ScaleEngine())
|
|
||||||
scaletype in supported_scales() || @warn("Unsupported scale type: ", scaletype)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function _update_plot_object(plt::Plot{QwtBackend}, plotattributes::KW)
|
|
||||||
haskey(plotattributes, :title) && Qwt.title(plt.o, plotattributes[:title])
|
|
||||||
haskey(plotattributes, :xguide) && Qwt.xlabel(plt.o, plotattributes[:xguide])
|
|
||||||
haskey(plotattributes, :yguide) && Qwt.ylabel(plt.o, plotattributes[:yguide])
|
|
||||||
updateLimsAndTicks(plt, plotattributes, true)
|
|
||||||
updateLimsAndTicks(plt, plotattributes, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
function _update_plot_pos_size(plt::AbstractPlot{QwtBackend}, plotattributes::KW)
|
|
||||||
haskey(plotattributes, :size) && Qwt.resizewidget(plt.o, plotattributes[:size]...)
|
|
||||||
haskey(plotattributes, :pos) && Qwt.movewidget(plt.o, plotattributes[:pos]...)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
# curve.setPen(Qt.QPen(Qt.QColor(color), linewidth, self.getLineStyle(linestyle)))
|
|
||||||
function addLineMarker(plt::Plot{QwtBackend}, plotattributes::KW)
|
|
||||||
for yi in plotattributes[:y]
|
|
||||||
marker = Qwt.QWT.QwtPlotMarker()
|
|
||||||
ishorizontal = (plotattributes[:seriestype] == :hline)
|
|
||||||
marker[:setLineStyle](ishorizontal ? 1 : 2)
|
|
||||||
marker[ishorizontal ? :setYValue : :setXValue](yi)
|
|
||||||
qcolor = Qwt.convertRGBToQColor(getColor(plotattributes[:linecolor]))
|
|
||||||
linestyle = plt.o.widget[:getLineStyle](string(plotattributes[:linestyle]))
|
|
||||||
marker[:setLinePen](Qwt.QT.QPen(qcolor, plotattributes[:linewidth], linestyle))
|
|
||||||
marker[:attach](plt.o.widget)
|
|
||||||
end
|
|
||||||
|
|
||||||
# marker[:setValue](x, y)
|
|
||||||
# marker[:setLabel](Qwt.QWT.QwtText(val))
|
|
||||||
# marker[:attach](plt.o.widget)
|
|
||||||
end
|
|
||||||
|
|
||||||
function createQwtAnnotation(plt::Plot, x, y, val::PlotText)
|
|
||||||
marker = Qwt.QWT.QwtPlotMarker()
|
|
||||||
marker[:setValue](x, y)
|
|
||||||
qwttext = Qwt.QWT.QwtText(val.str)
|
|
||||||
qwttext[:setFont](Qwt.QT.QFont(val.font.family, val.font.pointsize))
|
|
||||||
qwttext[:setColor](Qwt.convertRGBToQColor(getColor(val.font.color)))
|
|
||||||
marker[:setLabel](qwttext)
|
|
||||||
marker[:attach](plt.o.widget)
|
|
||||||
end
|
|
||||||
|
|
||||||
function createQwtAnnotation(plt::Plot, x, y, val::AbstractString)
|
|
||||||
marker = Qwt.QWT.QwtPlotMarker()
|
|
||||||
marker[:setValue](x, y)
|
|
||||||
marker[:setLabel](Qwt.QWT.QwtText(val))
|
|
||||||
marker[:attach](plt.o.widget)
|
|
||||||
end
|
|
||||||
|
|
||||||
function _add_annotations(plt::Plot{QwtBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
|
||||||
for ann in anns
|
|
||||||
createQwtAnnotation(plt, ann...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
# accessors for x/y data
|
|
||||||
|
|
||||||
function getxy(plt::Plot{QwtBackend}, i::Int)
|
|
||||||
series = plt.o.lines[i]
|
|
||||||
series.x, series.y
|
|
||||||
end
|
|
||||||
|
|
||||||
function setxy!(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
|
||||||
series = plt.o.lines[i]
|
|
||||||
series.x, series.y = xy
|
|
||||||
plt
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------
|
|
||||||
|
|
||||||
# -------------------------------
|
|
||||||
|
|
||||||
# # create the underlying object (each backend will do this differently)
|
|
||||||
# function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
|
|
||||||
# isbefore && return false
|
|
||||||
# i = 0
|
|
||||||
# rows = Any[]
|
|
||||||
# row = Any[]
|
|
||||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
|
||||||
# push!(row, subplt.plts[i].o)
|
|
||||||
# if c == ncols(subplt.layout, r)
|
|
||||||
# push!(rows, Qwt.hsplitter(row...))
|
|
||||||
# row = Any[]
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# # for rowcnt in subplt.layout.rowcounts
|
|
||||||
# # push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
|
|
||||||
# # i += rowcnt
|
|
||||||
# # end
|
|
||||||
# subplt.o = Qwt.vsplitter(rows...)
|
|
||||||
# # Qwt.resizewidget(subplt.o, getattr(subplt,1)[:size]...)
|
|
||||||
# # Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
|
|
||||||
# true
|
|
||||||
# end
|
|
||||||
|
|
||||||
function _expand_limits(lims, plt::Plot{QwtBackend}, isx::Bool)
|
|
||||||
for series in plt.o.lines
|
|
||||||
_expand_limits(lims, isx ? series.x : series.y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function _remove_axis(plt::Plot{QwtBackend}, isx::Bool)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
function Base.show(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
|
|
||||||
Qwt.refresh(plt.o)
|
|
||||||
Qwt.savepng(plt.o, "/tmp/dfskjdhfkh.png")
|
|
||||||
write(io, readall("/tmp/dfskjdhfkh.png"))
|
|
||||||
end
|
|
||||||
|
|
||||||
# function Base.show(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
|
|
||||||
# for plt in subplt.plts
|
|
||||||
# Qwt.refresh(plt.o)
|
|
||||||
# end
|
|
||||||
# Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
|
|
||||||
# write(io, readall("/tmp/dfskjdhfkh.png"))
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
|
|
||||||
Qwt.refresh(plt.o)
|
|
||||||
Qwt.showwidget(plt.o)
|
|
||||||
end
|
|
||||||
|
|
||||||
# function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
|
|
||||||
# for plt in subplt.plts
|
|
||||||
# Qwt.refresh(plt.o)
|
|
||||||
# end
|
|
||||||
# Qwt.showwidget(subplt.o)
|
|
||||||
# end
|
|
||||||
@ -1,272 +0,0 @@
|
|||||||
|
|
||||||
# https://github.com/nolta/Winston.jl
|
|
||||||
|
|
||||||
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
|
|
||||||
|
|
||||||
supported_attrs(::WinstonBackend) = merge_with_base_supported([
|
|
||||||
:annotations,
|
|
||||||
:linecolor,
|
|
||||||
:fillrange,
|
|
||||||
:fillcolor,
|
|
||||||
:label,
|
|
||||||
:legend,
|
|
||||||
:seriescolor, :seriesalpha,
|
|
||||||
:linestyle,
|
|
||||||
:linewidth,
|
|
||||||
:markershape,
|
|
||||||
:markercolor,
|
|
||||||
:markersize,
|
|
||||||
:bins,
|
|
||||||
:title,
|
|
||||||
:window_title,
|
|
||||||
:guide, :lims, :scale,
|
|
||||||
])
|
|
||||||
supported_types(::WinstonBackend) = [:path, :scatter, :bar]
|
|
||||||
supported_styles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
|
||||||
supported_markers(::WinstonBackend) = [:none, :auto, :rect, :circle, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
|
||||||
supported_scales(::WinstonBackend) = [:identity, :log10]
|
|
||||||
is_subplot_supported(::WinstonBackend) = false
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
function _initialize_backend(::WinstonBackend; kw...)
|
|
||||||
@eval begin
|
|
||||||
# ENV["WINSTON_OUTPUT"] = "gtk"
|
|
||||||
@warn("Winston is no longer supported... many features will likely be broken.")
|
|
||||||
import Winston, Gtk
|
|
||||||
export Winston, Gtk
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
## dictionaries for conversion of Plots.jl names to Winston ones.
|
|
||||||
const winston_linestyle = KW(:solid=>"solid",
|
|
||||||
:dash=>"dash",
|
|
||||||
:dot=>"dotted",
|
|
||||||
:dashdot=>"dotdashed"
|
|
||||||
)
|
|
||||||
|
|
||||||
const winston_marker = KW(:none=>".",
|
|
||||||
:rect => "square",
|
|
||||||
:circle=>"circle",
|
|
||||||
:diamond=>"diamond",
|
|
||||||
:utriangle=>"triangle",
|
|
||||||
:dtriangle=>"down-triangle",
|
|
||||||
:cross => "plus",
|
|
||||||
:xcross => "cross",
|
|
||||||
:star5 => "asterisk"
|
|
||||||
)
|
|
||||||
|
|
||||||
function _before_update(plt::Plot{WinstonBackend})
|
|
||||||
Winston.ghf(plt.o)
|
|
||||||
end
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function _create_backend_figure(plt::Plot{WinstonBackend})
|
|
||||||
Winston.FramedPlot(
|
|
||||||
title = plt.attr[:title],
|
|
||||||
xlabel = plt.attr[:xguide],
|
|
||||||
ylabel = plt.attr[:yguide]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
copy_remove(plotattributes::KW, s::Symbol) = delete!(copy(plotattributes), s)
|
|
||||||
|
|
||||||
function addRegressionLineWinston(plotattributes::KW, wplt)
|
|
||||||
xs, ys = regressionXY(plotattributes[:x], plotattributes[:y])
|
|
||||||
Winston.add(wplt, Winston.Curve(xs, ys, kind="dotted"))
|
|
||||||
end
|
|
||||||
|
|
||||||
function getWinstonItems(plt::Plot)
|
|
||||||
if isa(plt.o, Winston.FramedPlot)
|
|
||||||
wplt = plt.o
|
|
||||||
window, canvas = nothing, nothing
|
|
||||||
else
|
|
||||||
window, canvas, wplt = plt.o
|
|
||||||
end
|
|
||||||
window, canvas, wplt
|
|
||||||
end
|
|
||||||
|
|
||||||
function _series_added(plt::Plot{WinstonBackend}, series::Series)
|
|
||||||
plotattributes = series.plotattributes
|
|
||||||
window, canvas, wplt = getWinstonItems(plt)
|
|
||||||
|
|
||||||
# until we call it normally, do the hack
|
|
||||||
if plotattributes[:seriestype] == :bar
|
|
||||||
plotattributes = barHack(;plotattributes...)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
e = KW()
|
|
||||||
e[:color] = getColor(plotattributes[:linecolor])
|
|
||||||
e[:linewidth] = plotattributes[:linewidth]
|
|
||||||
e[:kind] = winston_linestyle[plotattributes[:linestyle]]
|
|
||||||
e[:symbolkind] = winston_marker[plotattributes[:markershape]]
|
|
||||||
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
|
|
||||||
e[:symbolsize] = plotattributes[:markersize] / 5
|
|
||||||
|
|
||||||
# pos # (Int,Int), move the enclosing window to this position
|
|
||||||
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :histogram2d, :hexbin, :histogram, :bar
|
|
||||||
if plotattributes[:seriestype] == :none
|
|
||||||
Winston.add(wplt, Winston.Points(plotattributes[:x], plotattributes[:y]; copy_remove(e, :kind)..., color=getColor(plotattributes[:markercolor])))
|
|
||||||
|
|
||||||
elseif plotattributes[:seriestype] == :path
|
|
||||||
x, y = plotattributes[:x], plotattributes[:y]
|
|
||||||
Winston.add(wplt, Winston.Curve(x, y; e...))
|
|
||||||
|
|
||||||
fillrange = plotattributes[:fillrange]
|
|
||||||
if fillrange != nothing
|
|
||||||
if isa(fillrange, AbstractVector)
|
|
||||||
y2 = fillrange
|
|
||||||
else
|
|
||||||
y2 = Float64[fillrange for yi in y]
|
|
||||||
end
|
|
||||||
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(plotattributes[:fillcolor])))
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif plotattributes[:seriestype] == :scatter
|
|
||||||
if plotattributes[:markershape] == :none
|
|
||||||
plotattributes[:markershape] = :circle
|
|
||||||
end
|
|
||||||
|
|
||||||
# elseif plotattributes[:seriestype] == :step
|
|
||||||
# fn = Winston.XXX
|
|
||||||
|
|
||||||
# elseif plotattributes[:seriestype] == :stepinverted
|
|
||||||
# fn = Winston.XXX
|
|
||||||
|
|
||||||
elseif plotattributes[:seriestype] == :sticks
|
|
||||||
Winston.add(wplt, Winston.Stems(plotattributes[:x], plotattributes[:y]; e...))
|
|
||||||
|
|
||||||
# elseif plotattributes[:seriestype] == :dots
|
|
||||||
# fn = Winston.XXX
|
|
||||||
|
|
||||||
# elseif plotattributes[:seriestype] == :histogram2d
|
|
||||||
# fn = Winston.XXX
|
|
||||||
|
|
||||||
# elseif plotattributes[:seriestype] == :hexbin
|
|
||||||
# fn = Winston.XXX
|
|
||||||
|
|
||||||
elseif plotattributes[:seriestype] == :histogram
|
|
||||||
hst = hist(plotattributes[:y], plotattributes[:bins])
|
|
||||||
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
|
|
||||||
|
|
||||||
# elseif plotattributes[:seriestype] == :bar
|
|
||||||
# # fn = Winston.XXX
|
|
||||||
|
|
||||||
else
|
|
||||||
error("seriestype $(plotattributes[:seriestype]) not supported by Winston.")
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# markershape
|
|
||||||
if plotattributes[:markershape] != :none
|
|
||||||
Winston.add(wplt, Winston.Points(plotattributes[:x], plotattributes[:y]; copy_remove(e, :kind)..., color=getColor(plotattributes[:markercolor])))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# optionally add a regression line
|
|
||||||
plotattributes[:smooth] && plotattributes[:seriestype] != :histogram && addRegressionLineWinston(plotattributes, wplt)
|
|
||||||
|
|
||||||
# push!(plt.seriesargs, plotattributes)
|
|
||||||
# plt
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
const _winstonNames = KW(
|
|
||||||
:xlims => :xrange,
|
|
||||||
:ylims => :yrange,
|
|
||||||
:xscale => :xlog,
|
|
||||||
:yscale => :ylog,
|
|
||||||
)
|
|
||||||
|
|
||||||
function _update_plot_object(plt::Plot{WinstonBackend}, plotattributes::KW)
|
|
||||||
window, canvas, wplt = getWinstonItems(plt)
|
|
||||||
for k in (:xguide, :yguide, :title, :xlims, :ylims)
|
|
||||||
if haskey(plotattributes, k)
|
|
||||||
Winston.setattr(wplt, string(get(_winstonNames, k, k)), plotattributes[k])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for k in (:xscale, :yscale)
|
|
||||||
if haskey(plotattributes, k)
|
|
||||||
islogscale = plotattributes[k] == :log10
|
|
||||||
Winston.setattr(wplt, (k == :xscale ? :xlog : :ylog), islogscale)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::AbstractString)
|
|
||||||
Winston.text(x, y, val)
|
|
||||||
end
|
|
||||||
|
|
||||||
function _add_annotations(plt::Plot{WinstonBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
|
||||||
for ann in anns
|
|
||||||
createWinstonAnnotationObject(plt, ann...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
# function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
|
|
||||||
# # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
|
||||||
# end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
function addWinstonLegend(plt::Plot, wplt)
|
|
||||||
if plt.attr[:legend] != :none
|
|
||||||
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{WinstonBackend})
|
|
||||||
window, canvas, wplt = getWinstonItems(plt)
|
|
||||||
addWinstonLegend(plt, wplt)
|
|
||||||
show(io, "image/png", wplt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
|
|
||||||
|
|
||||||
window, canvas, wplt = getWinstonItems(plt)
|
|
||||||
|
|
||||||
if window == nothing
|
|
||||||
if Winston.output_surface != :gtk
|
|
||||||
error("Gtk is the only supported display for Winston in Plots. Set `output_surface = gtk` in src/Winston.ini")
|
|
||||||
end
|
|
||||||
# initialize window
|
|
||||||
w,h = plt.attr[:size]
|
|
||||||
canvas = Gtk.GtkCanvasLeaf()
|
|
||||||
window = Gtk.GtkWindowLeaf(canvas, plt.attr[:window_title], w, h)
|
|
||||||
plt.o = (window, canvas, wplt)
|
|
||||||
end
|
|
||||||
|
|
||||||
addWinstonLegend(plt, wplt)
|
|
||||||
|
|
||||||
Winston.display(canvas, wplt)
|
|
||||||
Gtk.showall(window)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
|
|
||||||
# # TODO: display/show the Subplot object
|
|
||||||
# end
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,415 +0,0 @@
|
|||||||
|
|
||||||
abstract type ColorScheme end
|
|
||||||
|
|
||||||
Base.getindex(scheme::ColorScheme, i::Integer) = getColor(scheme, i)
|
|
||||||
|
|
||||||
export
|
|
||||||
cgrad
|
|
||||||
|
|
||||||
cgrad() = default_gradient()
|
|
||||||
|
|
||||||
function cgrad(arg, values = nothing; alpha = nothing, scale = :identity)
|
|
||||||
colors = ColorGradient(arg, alpha=alpha).colors
|
|
||||||
values = if values != nothing
|
|
||||||
values
|
|
||||||
elseif scale in (:log, :log10)
|
|
||||||
log10(range(1, stop=10, length=30))
|
|
||||||
elseif scale == :log2
|
|
||||||
log2(range(1, stop=2, length=30))
|
|
||||||
elseif scale == :ln
|
|
||||||
log(range(1, stop=pi, length=30))
|
|
||||||
elseif scale in (:exp, :exp10)
|
|
||||||
(exp10(range(0, stop=1, length=30)) - 1) / 9
|
|
||||||
else
|
|
||||||
range(0, stop=1, length=length(colors))
|
|
||||||
end
|
|
||||||
ColorGradient(colors, values)
|
|
||||||
end
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
getColor(scheme::ColorScheme) = getColor(scheme, 1)
|
|
||||||
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
|
|
||||||
|
|
||||||
colorscheme(scheme::ColorScheme) = scheme
|
|
||||||
colorscheme(s::AbstractString; kw...) = colorscheme(Symbol(s); kw...)
|
|
||||||
colorscheme(s::Symbol; kw...) = haskey(_gradients, s) ? ColorGradient(s; kw...) : ColorWrapper(convertColor(s); kw...)
|
|
||||||
colorscheme(s::Symbol, vals::AVec{T}; kw...) where {T<:Real} = ColorGradient(s, vals; kw...)
|
|
||||||
colorscheme(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
|
|
||||||
colorscheme(cs::AVec{T}; kw...) where {T<:Colorant} = ColorGradient(cs; kw...)
|
|
||||||
colorscheme(f::Function; kw...) = ColorFunction(f; kw...)
|
|
||||||
colorscheme(v::AVec; kw...) = ColorVector(v; kw...)
|
|
||||||
colorscheme(m::AMat; kw...) = size(m,1) == 1 ? map(c->colorscheme(c; kw...), m) : [colorscheme(m[:,i]; kw...) for i in 1:size(m,2)]'
|
|
||||||
colorscheme(c::Colorant; kw...) = ColorWrapper(c; kw...)
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
convertColor(c::AbstractString) = parse(Colorant, c)
|
|
||||||
convertColor(c::Symbol) = parse(Colorant, string(c))
|
|
||||||
convertColor(c::Colorant) = c
|
|
||||||
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
|
|
||||||
convertColor(c::ColorScheme) = c
|
|
||||||
convertColor(v::Nothing) = RGBA(0,0,0,0)
|
|
||||||
convertColor(b::Bool) = b ? RGBA(0,0,0,1) : RGBA(0,0,0,0)
|
|
||||||
|
|
||||||
function convertColor(c, α::Real)
|
|
||||||
c = convertColor(c)
|
|
||||||
RGBA(RGB(getColor(c)), α)
|
|
||||||
end
|
|
||||||
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
|
|
||||||
convertColor(c, α::Nothing) = convertColor(c)
|
|
||||||
|
|
||||||
# backup... try to convert
|
|
||||||
getColor(c) = convertColor(c)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
function darken(c, v=0.1)
|
|
||||||
rgba = convert(RGBA, c)
|
|
||||||
r = max(0, min(rgba.r - v, 1))
|
|
||||||
g = max(0, min(rgba.g - v, 1))
|
|
||||||
b = max(0, min(rgba.b - v, 1))
|
|
||||||
RGBA(r,g,b,rgba.alpha)
|
|
||||||
end
|
|
||||||
function lighten(c, v=0.3)
|
|
||||||
darken(c, -v)
|
|
||||||
end
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
const _rainbowColors = [colorant"purple", colorant"blue", colorant"green", colorant"orange", colorant"red"]
|
|
||||||
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
|
|
||||||
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
|
|
||||||
|
|
||||||
const _gradients = KW(
|
|
||||||
:blues => [colorant"lightblue", colorant"darkblue"],
|
|
||||||
:reds => [colorant"lightpink", colorant"darkred"],
|
|
||||||
:greens => [colorant"lightgreen", colorant"darkgreen"],
|
|
||||||
:redsblues => [colorant"darkred", RGB(0.8,0.85,0.8), colorant"darkblue"],
|
|
||||||
:bluesreds => [colorant"darkblue", RGB(0.8,0.85,0.8), colorant"darkred"],
|
|
||||||
:heat => [colorant"lightyellow", colorant"orange", colorant"darkred"],
|
|
||||||
:grays => [RGB(.95,.95,.95),RGB(.05,.05,.05)],
|
|
||||||
:rainbow => _rainbowColors,
|
|
||||||
:lightrainbow => map(lighten, _rainbowColors),
|
|
||||||
:darkrainbow => map(darken, _rainbowColors),
|
|
||||||
:darktest => _testColors,
|
|
||||||
:lighttest => map(c -> lighten(c, 0.3), _testColors),
|
|
||||||
)
|
|
||||||
|
|
||||||
function register_gradient_colors(name::Symbol, colors::AVec{C}) where C<:Colorant
|
|
||||||
_gradients[name] = colors
|
|
||||||
end
|
|
||||||
|
|
||||||
include("color_gradients.jl")
|
|
||||||
|
|
||||||
default_gradient() = ColorGradient(:inferno)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
|
|
||||||
struct ColorGradient <: ColorScheme
|
|
||||||
colors::Vector
|
|
||||||
values::Vector
|
|
||||||
|
|
||||||
function ColorGradient(cs::AVec, vals::AVec{S} = range(0, stop=1, length=length(cs)); alpha = nothing) where S<:Real
|
|
||||||
if length(cs) == length(vals)
|
|
||||||
return new(convertColor(cs,alpha), collect(vals))
|
|
||||||
end
|
|
||||||
|
|
||||||
# # otherwise interpolate evenly between the minval and maxval
|
|
||||||
# minval, maxval = minimum(vals), maximum(vals)
|
|
||||||
# vs = Float64[interpolate(minval, maxval, w) for w in range(0, stop = 1, length = length(cs))]
|
|
||||||
# new(convertColor(cs,alpha), vs)
|
|
||||||
|
|
||||||
# interpolate the colors for each value
|
|
||||||
vals = merge(range(0, stop=1, length=length(cs)), vals)
|
|
||||||
grad = ColorGradient(cs)
|
|
||||||
cs = [getColorZ(grad, z) for z in range(0, stop=1, length=length(vals))]
|
|
||||||
new(convertColor(cs, alpha), vals)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Base.getindex(cs::ColorGradient, i::Integer) = getColor(cs, i)
|
|
||||||
Base.getindex(cs::ColorGradient, z::Number) = getColorZ(cs, z)
|
|
||||||
|
|
||||||
|
|
||||||
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
|
|
||||||
function ColorGradient(s::Symbol, vals::AVec{T} = 0:0; kw...) where T<:Real
|
|
||||||
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
|
|
||||||
cs = _gradients[s]
|
|
||||||
if vals == 0:0
|
|
||||||
vals = range(0, stop=1, length=length(cs))
|
|
||||||
end
|
|
||||||
ColorGradient(cs, vals; kw...)
|
|
||||||
end
|
|
||||||
|
|
||||||
# function ColorGradient{T<:Real}(cs::AVec, vals::AVec{T} = range(0, stop = 1, length = length(cs)); kw...)
|
|
||||||
# ColorGradient(map(convertColor, cs), vals; kw...)
|
|
||||||
# end
|
|
||||||
|
|
||||||
function ColorGradient(grad::ColorGradient; alpha = nothing)
|
|
||||||
ColorGradient(convertColor(grad.colors, alpha), grad.values)
|
|
||||||
end
|
|
||||||
|
|
||||||
# anything else just gets the default gradient
|
|
||||||
function ColorGradient(cw; alpha=nothing)
|
|
||||||
ColorGradient(default_gradient(), alpha=alpha)
|
|
||||||
end
|
|
||||||
|
|
||||||
getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]
|
|
||||||
|
|
||||||
function getColorZ(gradient::ColorGradient, z::Real)
|
|
||||||
cs = gradient.colors
|
|
||||||
vs = gradient.values
|
|
||||||
n = length(cs)
|
|
||||||
@assert n > 0 && n == length(vs)
|
|
||||||
|
|
||||||
# can we just return the first color?
|
|
||||||
if z <= vs[1] || n == 1
|
|
||||||
return cs[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
# find the bounding colors and interpolate
|
|
||||||
for i in 2:n
|
|
||||||
if z <= vs[i]
|
|
||||||
return interpolate_rgb(cs[i-1], cs[i], (z - vs[i-1]) / (vs[i] - vs[i-1]))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# if we get here, return the last color
|
|
||||||
cs[end]
|
|
||||||
end
|
|
||||||
|
|
||||||
getColorVector(gradient::ColorGradient) = gradient.colors
|
|
||||||
|
|
||||||
# for 0.3
|
|
||||||
Colors.RGBA(c::Colorant) = RGBA(red(c), green(c), blue(c), alpha(c))
|
|
||||||
Colors.RGB(c::Colorant) = RGB(red(c), green(c), blue(c))
|
|
||||||
|
|
||||||
function interpolate_rgb(c1::Colorant, c2::Colorant, w::Real)
|
|
||||||
rgb1 = RGBA(c1)
|
|
||||||
rgb2 = RGBA(c2)
|
|
||||||
r = interpolate(rgb1.r, rgb2.r, w)
|
|
||||||
g = interpolate(rgb1.g, rgb2.g, w)
|
|
||||||
b = interpolate(rgb1.b, rgb2.b, w)
|
|
||||||
a = interpolate(rgb1.alpha, rgb2.alpha, w)
|
|
||||||
RGBA(r, g, b, a)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function interpolate(v1::Real, v2::Real, w::Real)
|
|
||||||
(1-w) * v1 + w * v2
|
|
||||||
end
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
"Wraps a function, taking an index and returning a Colorant"
|
|
||||||
struct ColorFunction <: ColorScheme
|
|
||||||
f::Function
|
|
||||||
end
|
|
||||||
|
|
||||||
getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
"Wraps a function, taking an z-value and returning a Colorant"
|
|
||||||
struct ColorZFunction <: ColorScheme
|
|
||||||
f::Function
|
|
||||||
end
|
|
||||||
|
|
||||||
getColorZ(scheme::ColorZFunction, z::Real) = scheme.f(z)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
"Wraps a vector of colors... may be vector of Symbol/String/Colorant"
|
|
||||||
struct ColorVector <: ColorScheme
|
|
||||||
v::Vector{Colorant}
|
|
||||||
ColorVector(v::AVec; alpha = nothing) = new(convertColor(v,alpha))
|
|
||||||
end
|
|
||||||
|
|
||||||
getColor(scheme::ColorVector, idx::Int) = convertColor(scheme.v[mod1(idx, length(scheme.v))])
|
|
||||||
getColorVector(scheme::ColorVector) = scheme.v
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
"Wraps a single color"
|
|
||||||
struct ColorWrapper <: ColorScheme
|
|
||||||
c::RGBA
|
|
||||||
ColorWrapper(c::Colorant; alpha = nothing) = new(convertColor(c, alpha))
|
|
||||||
end
|
|
||||||
|
|
||||||
ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Colorant, s), alpha))
|
|
||||||
|
|
||||||
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
|
|
||||||
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
|
|
||||||
convertColor(c::ColorWrapper, α::Nothing) = c.c
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
isbackgrounddark(bgcolor::Color) = Lab(bgcolor).l < 0.5
|
|
||||||
|
|
||||||
# move closer to lighter/darker depending on background value
|
|
||||||
function adjustAway(val, bgval, vmin=0., vmax=100.)
|
|
||||||
if bgval < 0.5 * (vmax+vmin)
|
|
||||||
tmp = max(val, bgval)
|
|
||||||
return 0.5 * (tmp + max(tmp, vmax))
|
|
||||||
else
|
|
||||||
tmp = min(val, bgval)
|
|
||||||
return 0.5 * (tmp + min(tmp, vmin))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# borrowed from http://stackoverflow.com/a/1855903:
|
|
||||||
lightnessLevel(c::Colorant) = 0.299 * red(c) + 0.587 * green(c) + 0.114 * blue(c)
|
|
||||||
|
|
||||||
isdark(c::Colorant) = lightnessLevel(c) < 0.5
|
|
||||||
islight(c::Colorant) = !isdark(c)
|
|
||||||
|
|
||||||
function convertHexToRGB(h::Unsigned)
|
|
||||||
mask = 0x0000FF
|
|
||||||
RGB([(x & mask) / 0xFF for x in (h >> 16, h >> 8, h)]...)
|
|
||||||
end
|
|
||||||
|
|
||||||
# note: I found this list of hex values in a comment by Tatarize here: http://stackoverflow.com/a/12224359
|
|
||||||
const _masterColorList = [
|
|
||||||
0xFFFFFF, 0x000000, 0x0000FF, 0x00FF00, 0xFF0000, 0x01FFFE, 0xFFA6FE, 0xFFDB66, 0x006401, 0x010067,
|
|
||||||
0x95003A, 0x007DB5, 0xFF00F6, 0xFFEEE8, 0x774D00, 0x90FB92, 0x0076FF, 0xD5FF00, 0xFF937E, 0x6A826C,
|
|
||||||
0xFF029D, 0xFE8900, 0x7A4782, 0x7E2DD2, 0x85A900, 0xFF0056, 0xA42400, 0x00AE7E, 0x683D3B, 0xBDC6FF,
|
|
||||||
0x263400, 0xBDD393, 0x00B917, 0x9E008E, 0x001544, 0xC28C9F, 0xFF74A3, 0x01D0FF, 0x004754, 0xE56FFE,
|
|
||||||
0x788231, 0x0E4CA1, 0x91D0CB, 0xBE9970, 0x968AE8, 0xBB8800, 0x43002C, 0xDEFF74, 0x00FFC6, 0xFFE502,
|
|
||||||
0x620E00, 0x008F9C, 0x98FF52, 0x7544B1, 0xB500FF, 0x00FF78, 0xFF6E41, 0x005F39, 0x6B6882, 0x5FAD4E,
|
|
||||||
0xA75740, 0xA5FFD2, 0xFFB167, 0x009BFF, 0xE85EBE
|
|
||||||
]
|
|
||||||
const _allColors = map(convertHexToRGB, _masterColorList)
|
|
||||||
const _darkColors = filter(isdark, _allColors)
|
|
||||||
const _lightColors = filter(islight, _allColors)
|
|
||||||
const _sortedColorsForDarkBackground = vcat(_lightColors, reverse(_darkColors[2:end]))
|
|
||||||
const _sortedColorsForLightBackground = vcat(_darkColors, reverse(_lightColors[2:end]))
|
|
||||||
|
|
||||||
const _defaultNumColors = 17
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
# Methods to automatically generate gradients for color selection based on
|
|
||||||
# background color and a short list of seed colors
|
|
||||||
|
|
||||||
# here are some magic constants that could be changed if you really want
|
|
||||||
const _lightness_darkbg = [80.0]
|
|
||||||
const _lightness_lightbg = [60.0]
|
|
||||||
const _lch_c_const = [60]
|
|
||||||
|
|
||||||
function adjust_lch(color, l, c)
|
|
||||||
lch = convert(LCHab, color)
|
|
||||||
convert(RGB, LCHab(l, c, lch.h))
|
|
||||||
end
|
|
||||||
|
|
||||||
function lightness_from_background(bgcolor)
|
|
||||||
bglight = convert(LCHab, bgcolor).l
|
|
||||||
bglight < 50.0 ? _lightness_darkbg[1] : _lightness_lightbg[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
function gradient_from_list(cs)
|
|
||||||
zvalues = Plots.get_zvalues(length(cs))
|
|
||||||
indices = sortperm(zvalues)
|
|
||||||
sorted_colors = map(RGBA, cs[indices])
|
|
||||||
sorted_zvalues = zvalues[indices]
|
|
||||||
ColorGradient(sorted_colors, sorted_zvalues)
|
|
||||||
end
|
|
||||||
|
|
||||||
function generate_colorgradient(bgcolor = colorant"white";
|
|
||||||
color_bases = color_bases=[colorant"steelblue",colorant"orangered"],
|
|
||||||
lightness = lightness_from_background(bgcolor),
|
|
||||||
chroma = _lch_c_const[1],
|
|
||||||
n = _defaultNumColors)
|
|
||||||
seed_colors = vcat(bgcolor, map(c -> adjust_lch(c, lightness, chroma), color_bases))
|
|
||||||
colors = distinguishable_colors(n,
|
|
||||||
seed_colors,
|
|
||||||
lchoices=Float64[lightness],
|
|
||||||
cchoices=Float64[chroma],
|
|
||||||
hchoices=range(0, stop=340, length=20)
|
|
||||||
)[2:end]
|
|
||||||
gradient_from_list(colors)
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_color_palette(palette, bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
|
|
||||||
grad = if palette == :auto
|
|
||||||
generate_colorgradient(bgcolor)
|
|
||||||
else
|
|
||||||
ColorGradient(palette)
|
|
||||||
end
|
|
||||||
zrng = get_zvalues(numcolors)
|
|
||||||
RGBA[getColorZ(grad, z) for z in zrng]
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_color_palette(palette::Vector{C},
|
|
||||||
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer) where C<:Colorant
|
|
||||||
palette
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
function getpctrange(n::Int)
|
|
||||||
n > 0 || error()
|
|
||||||
n == 1 && return zeros(1)
|
|
||||||
zs = [0.0, 1.0]
|
|
||||||
for i in 3:n
|
|
||||||
sorted = sort(zs)
|
|
||||||
diffs = diff(sorted)
|
|
||||||
widestj = 0
|
|
||||||
widest = 0.0
|
|
||||||
for (j,d) in enumerate(diffs)
|
|
||||||
if d > widest
|
|
||||||
widest = d
|
|
||||||
widestj = j
|
|
||||||
end
|
|
||||||
end
|
|
||||||
push!(zs, sorted[widestj] + 0.5 * diffs[widestj])
|
|
||||||
end
|
|
||||||
zs
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_zvalues(n::Int)
|
|
||||||
offsets = getpctrange(ceil(Int,n/4)+1)/4
|
|
||||||
offsets = vcat(offsets[1], offsets[3:end])
|
|
||||||
zvalues = Float64[]
|
|
||||||
for offset in offsets
|
|
||||||
append!(zvalues, offset + [0.0, 0.5, 0.25, 0.75])
|
|
||||||
end
|
|
||||||
vcat(zvalues[1], 1.0, zvalues[2:n-1])
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
make255(x) = round(Int, 255 * x)
|
|
||||||
|
|
||||||
function webcolor(c::Color)
|
|
||||||
@sprintf("rgb(%d, %d, %d)", [make255(f(c)) for f in [red,green,blue]]...)
|
|
||||||
end
|
|
||||||
function webcolor(c::TransparentColor)
|
|
||||||
@sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
|
|
||||||
end
|
|
||||||
webcolor(cs::ColorScheme) = webcolor(getColor(cs))
|
|
||||||
webcolor(c) = webcolor(convertColor(c))
|
|
||||||
webcolor(c, α) = webcolor(convertColor(getColor(c), α))
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
|
||||||
function getSeriesRGBColor(c, sp::Subplot, n::Int)
|
|
||||||
|
|
||||||
if c == :auto
|
|
||||||
c = autopick(sp[:color_palette], n)
|
|
||||||
end
|
|
||||||
|
|
||||||
# c should now be a subtype of ColorScheme
|
|
||||||
colorscheme(c)
|
|
||||||
end
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
"""
|
|
||||||
- load Contours.jl similar to DataFrames
|
|
||||||
- method to build grid from x/y/z vectors
|
|
||||||
- method to wrap contours creation
|
|
||||||
- method to plot contours as custom shapes (TODO: create Stroke and Fill types and add markerstroke/markerfill args)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# # ----------------------------------------------------------
|
|
||||||
# # ----------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
# immutable Vertex
|
|
||||||
# x::Float64
|
|
||||||
# y::Float64
|
|
||||||
# z::Float64
|
|
||||||
# end
|
|
||||||
|
|
||||||
# immutable Edge
|
|
||||||
# v::Vertex
|
|
||||||
# u::Vertex
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # ----------------------------------------------------------
|
|
||||||
|
|
||||||
# # one rectangle's z-values and the center vertex
|
|
||||||
# # z is ordered: topleft, topright, bottomright, bottomleft
|
|
||||||
# immutable GridRect
|
|
||||||
# z::Vector{Float64}
|
|
||||||
# center::Vertex
|
|
||||||
# data::Vector{Vertex}
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# type Grid
|
|
||||||
# xs::Vector{Float64}
|
|
||||||
# ys::Vector{Float64}
|
|
||||||
# rects::Matrix{GridRect}
|
|
||||||
# end
|
|
||||||
|
|
||||||
# function splitDataEvenly(v::AbstractVector{Float64}, n::Int)
|
|
||||||
# vs = sort(v)
|
|
||||||
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # the goal here is to create the vertical and horizontal partitions
|
|
||||||
# # which define the grid, so that the data is somewhat evenly split
|
|
||||||
# function bucketData(x, y, z)
|
|
||||||
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# function buildGrid(x, y, z)
|
|
||||||
# # create
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
|
|
||||||
# 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 = Union{Function, AVec{Function}}
|
|
||||||
|
|
||||||
all3D(plotattributes::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(plotattributes, :seriestype, :none))
|
|
||||||
|
|
||||||
# missing
|
|
||||||
convertToAnyVector(v::Nothing, plotattributes::KW) = Any[nothing], nothing
|
|
||||||
|
|
||||||
# fixed number of blank series
|
|
||||||
convertToAnyVector(n::Integer, plotattributes::KW) = Any[zeros(0) for i in 1:n], nothing
|
|
||||||
|
|
||||||
# numeric vector
|
|
||||||
convertToAnyVector(v::AVec{T}, plotattributes::KW) where {T<:Number} = Any[v], nothing
|
|
||||||
|
|
||||||
# string vector
|
|
||||||
convertToAnyVector(v::AVec{T}, plotattributes::KW) where {T<:AbstractString} = Any[v], nothing
|
|
||||||
|
|
||||||
function convertToAnyVector(v::AMat, plotattributes::KW)
|
|
||||||
if all3D(plotattributes)
|
|
||||||
Any[Surface(v)]
|
|
||||||
else
|
|
||||||
Any[v[:,i] for i in 1:size(v,2)]
|
|
||||||
end, nothing
|
|
||||||
end
|
|
||||||
|
|
||||||
# function
|
|
||||||
convertToAnyVector(f::Function, plotattributes::KW) = Any[f], nothing
|
|
||||||
|
|
||||||
# surface
|
|
||||||
convertToAnyVector(s::Surface, plotattributes::KW) = Any[s], nothing
|
|
||||||
|
|
||||||
# # vector of OHLC
|
|
||||||
# convertToAnyVector(v::AVec{OHLC}, plotattributes::KW) = Any[v], nothing
|
|
||||||
|
|
||||||
# dates
|
|
||||||
convertToAnyVector(dts::AVec{D}, plotattributes::KW) where {D<:Union{Date,DateTime}} = Any[dts], nothing
|
|
||||||
|
|
||||||
# list of things (maybe other vectors, functions, or something else)
|
|
||||||
function convertToAnyVector(v::AVec, plotattributes::KW)
|
|
||||||
if all(x -> typeof(x) <: Number, v)
|
|
||||||
# all real numbers wrap the whole vector as one item
|
|
||||||
Any[convert(Vector{Float64}, v)], nothing
|
|
||||||
else
|
|
||||||
# something else... treat each element as an item
|
|
||||||
vcat(Any[convertToAnyVector(vi, plotattributes)[1] for vi in v]...), nothing
|
|
||||||
# Any[vi for vi in v], nothing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
convertToAnyVector(t::Tuple, plotattributes::KW) = Any[t], nothing
|
|
||||||
|
|
||||||
|
|
||||||
function convertToAnyVector(args...)
|
|
||||||
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
|
|
||||||
end
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
|
|
||||||
# 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::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, y::Function, z) = map(y, x)
|
|
||||||
compute_y(x, y, z) = copy(y)
|
|
||||||
|
|
||||||
compute_z(x, y, z::Function) = map(z, x, y)
|
|
||||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
|
||||||
compute_z(x, y, z::Nothing) = nothing
|
|
||||||
compute_z(x, y, z) = copy(z)
|
|
||||||
|
|
||||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
|
||||||
nobigs(v::AVec{BigInt}) = map(Int64, v)
|
|
||||||
nobigs(v) = v
|
|
||||||
|
|
||||||
@noinline function compute_xyz(x, y, z)
|
|
||||||
x = compute_x(x,y,z)
|
|
||||||
y = compute_y(x,y,z)
|
|
||||||
z = compute_z(x,y,z)
|
|
||||||
nobigs(x), nobigs(y), nobigs(z)
|
|
||||||
end
|
|
||||||
|
|
||||||
# not allowed
|
|
||||||
compute_xyz(x::Nothing, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
|
||||||
compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
|
||||||
compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!")
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
@ -86,8 +86,9 @@ yaxis!("YLABEL", :log10)
|
|||||||
PlotExample("Images",
|
PlotExample("Images",
|
||||||
"Plot an image. y-axis is set to flipped",
|
"Plot an image. y-axis is set to flipped",
|
||||||
[:(begin
|
[:(begin
|
||||||
import FileIO, PlotReferenceImages
|
import FileIO
|
||||||
img = FileIO.load(joinpath(dirname(pathof(PlotReferenceImages)), "..", "Plots","pyplot","0.7.0","ref1.png"))
|
path = download("http://juliaplots.org/PlotReferenceImages.jl/Plots/pyplot/0.7.0/ref1.png")
|
||||||
|
img = FileIO.load(path)
|
||||||
plot(img)
|
plot(img)
|
||||||
end)]
|
end)]
|
||||||
),
|
),
|
||||||
@ -435,8 +436,37 @@ each line segment or marker in the plot.
|
|||||||
end)]
|
end)]
|
||||||
),
|
),
|
||||||
|
|
||||||
|
PlotExample("Portfolio Composition maps",
|
||||||
|
"""
|
||||||
|
see: http://stackoverflow.com/a/37732384/5075246
|
||||||
|
""",
|
||||||
|
[:(begin
|
||||||
|
using Random
|
||||||
|
Random.seed!(111)
|
||||||
|
tickers = ["IBM", "Google", "Apple", "Intel"]
|
||||||
|
N = 10
|
||||||
|
D = length(tickers)
|
||||||
|
weights = rand(N,D)
|
||||||
|
weights ./= sum(weights, dims = 2)
|
||||||
|
returns = sort!((1:N) + D*randn(N))
|
||||||
|
|
||||||
|
portfoliocomposition(weights, returns, labels = permutedims(tickers))
|
||||||
|
end)]
|
||||||
|
),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Some constants for PlotDocs and PlotReferenceImages
|
||||||
|
_animation_examples = [2, 30]
|
||||||
|
_backend_skips = Dict(
|
||||||
|
:gr => [25, 30],
|
||||||
|
:pyplot => [25, 30],
|
||||||
|
:plotlyjs => [2, 21, 25, 30, 31],
|
||||||
|
:pgfplots => [2, 5, 6, 10, 16, 20, 22, 23, 25, 28, 30],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------
|
||||||
|
|
||||||
# make and display one plot
|
# make and display one plot
|
||||||
@ -473,7 +503,7 @@ function test_examples(pkgname::Symbol; debug = false, disp = true, sleep = noth
|
|||||||
plts[i] = plt
|
plts[i] = plt
|
||||||
catch ex
|
catch ex
|
||||||
# TODO: put error info into markdown?
|
# TODO: put error info into markdown?
|
||||||
warn("Example $pkgname:$i:$(_examples[i].header) failed with: $ex")
|
@warn("Example $pkgname:$i:$(_examples[i].header) failed with: $ex")
|
||||||
end
|
end
|
||||||
if sleep != nothing
|
if sleep != nothing
|
||||||
Base.sleep(sleep)
|
Base.sleep(sleep)
|
||||||
|
|||||||
24
src/fileio.jl
Normal file
24
src/fileio.jl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# ---------------------------------------------------------
|
||||||
|
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||||
|
|
||||||
|
_fileio_load(@nospecialize(filename::AbstractString)) = FileIO.load(filename::AbstractString)
|
||||||
|
_fileio_save(@nospecialize(filename::AbstractString), @nospecialize(x)) = FileIO.save(filename::AbstractString, x)
|
||||||
|
|
||||||
|
function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
|
||||||
|
fn = tempname()
|
||||||
|
|
||||||
|
# first save a pdf file
|
||||||
|
pdf(plt, fn)
|
||||||
|
|
||||||
|
# load that pdf into a FileIO Stream
|
||||||
|
s = _fileio_load(fn * ".pdf")
|
||||||
|
|
||||||
|
# save a png
|
||||||
|
pngfn = fn * ".png"
|
||||||
|
_fileio_save(pngfn, s)
|
||||||
|
|
||||||
|
# now write from the file
|
||||||
|
write(io, read(open(pngfn), String))
|
||||||
|
end
|
||||||
|
|
||||||
|
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
||||||
59
src/ijulia.jl
Normal file
59
src/ijulia.jl
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
const use_local_dependencies = Ref(false)
|
||||||
|
const use_local_plotlyjs = Ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
function _init_ijulia_plotting()
|
||||||
|
# IJulia is more stable with local file
|
||||||
|
use_local_plotlyjs[] = isfile(plotly_local_file_path)
|
||||||
|
|
||||||
|
ENV["MPLBACKEND"] = "Agg"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
||||||
|
|
||||||
|
The default is nothing, except for plotly based backends, where it
|
||||||
|
adds data for `application/vnd.plotly.v1+json` that is used in
|
||||||
|
frontends like jupyterlab and nteract.
|
||||||
|
"""
|
||||||
|
_ijulia__extra_mime_info!(plt::Plot, out::Dict) = out
|
||||||
|
|
||||||
|
function _ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
||||||
|
out["application/vnd.plotly.v1+json"] = JSON.lower(plt.o)
|
||||||
|
out
|
||||||
|
end
|
||||||
|
|
||||||
|
function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
|
||||||
|
out["application/vnd.plotly.v1+json"] = Dict(
|
||||||
|
:data => plotly_series(plt),
|
||||||
|
:layout => plotly_layout(plt)
|
||||||
|
)
|
||||||
|
out
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _ijulia_display_dict(plt::Plot)
|
||||||
|
output_type = Symbol(plt.attr[:html_output_format])
|
||||||
|
if output_type == :auto
|
||||||
|
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||||
|
end
|
||||||
|
out = Dict()
|
||||||
|
if output_type == :txt
|
||||||
|
mime = "text/plain"
|
||||||
|
out[mime] = sprint(show, MIME(mime), plt)
|
||||||
|
elseif output_type == :png
|
||||||
|
mime = "image/png"
|
||||||
|
out[mime] = base64encode(show, MIME(mime), plt)
|
||||||
|
elseif output_type == :svg
|
||||||
|
mime = "image/svg+xml"
|
||||||
|
out[mime] = sprint(show, MIME(mime), plt)
|
||||||
|
elseif output_type == :html
|
||||||
|
mime = "text/html"
|
||||||
|
out[mime] = sprint(show, MIME(mime), plt)
|
||||||
|
else
|
||||||
|
error("Unsupported output type $output_type")
|
||||||
|
end
|
||||||
|
_ijulia__extra_mime_info!(plt, out)
|
||||||
|
out
|
||||||
|
end
|
||||||
87
src/init.jl
87
src/init.jl
@ -1,22 +1,33 @@
|
|||||||
function __init__()
|
using REPL
|
||||||
|
|
||||||
|
|
||||||
|
function _plots_defaults()
|
||||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||||
if haskey(Main.PLOTS_DEFAULTS, :theme)
|
Dict{Symbol,Any}(Main.PLOTS_DEFAULTS)
|
||||||
theme(Main.PLOTS_DEFAULTS[:theme])
|
else
|
||||||
|
Dict{Symbol,Any}()
|
||||||
end
|
end
|
||||||
for (k,v) in Main.PLOTS_DEFAULTS
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function __init__()
|
||||||
|
user_defaults = _plots_defaults()
|
||||||
|
if haskey(user_defaults, :theme)
|
||||||
|
theme(user_defaults[:theme])
|
||||||
|
end
|
||||||
|
for (k,v) in user_defaults
|
||||||
k == :theme || default(k, v)
|
k == :theme || default(k, v)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
pushdisplay(PlotsDisplay())
|
insert!(Base.Multimedia.displays, findlast(x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
|
||||||
|
|
||||||
atreplinit(i -> begin
|
atreplinit(i -> begin
|
||||||
if PlotDisplay() in Base.Multimedia.displays
|
while PlotsDisplay() in Base.Multimedia.displays
|
||||||
popdisplay(PlotsDisplay())
|
popdisplay(PlotsDisplay())
|
||||||
end
|
end
|
||||||
pushdisplay(PlotsDisplay())
|
insert!(Base.Multimedia.displays, findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@require GLVisualize = "4086de5b-f4b6-55f3-abb0-b8c73827585f" include(joinpath(@__DIR__, "backends", "glvisualize.jl"))
|
|
||||||
@require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" include(joinpath(@__DIR__, "backends", "hdf5.jl"))
|
@require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" include(joinpath(@__DIR__, "backends", "hdf5.jl"))
|
||||||
@require InspectDR = "d0351b0e-4b05-5898-87b3-e2a8edfddd1d" include(joinpath(@__DIR__, "backends", "inspectdr.jl"))
|
@require InspectDR = "d0351b0e-4b05-5898-87b3-e2a8edfddd1d" include(joinpath(@__DIR__, "backends", "inspectdr.jl"))
|
||||||
@require PGFPlots = "3b7a836e-365b-5785-a47d-02c71176b4aa" include(joinpath(@__DIR__, "backends", "pgfplots.jl"))
|
@require PGFPlots = "3b7a836e-365b-5785-a47d-02c71176b4aa" include(joinpath(@__DIR__, "backends", "pgfplots.jl"))
|
||||||
@ -24,60 +35,26 @@ function __init__()
|
|||||||
@require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" include(joinpath(@__DIR__, "backends", "pyplot.jl"))
|
@require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" include(joinpath(@__DIR__, "backends", "pyplot.jl"))
|
||||||
@require UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" include(joinpath(@__DIR__, "backends", "unicodeplots.jl"))
|
@require UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" include(joinpath(@__DIR__, "backends", "unicodeplots.jl"))
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
# IJulia
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
|
|
||||||
@require IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" begin
|
@require IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" begin
|
||||||
if IJulia.inited
|
if IJulia.inited
|
||||||
|
_init_ijulia_plotting()
|
||||||
|
|
||||||
"""
|
IJulia.display_dict(plt::Plot) = _ijulia_display_dict(plt)
|
||||||
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
end
|
||||||
|
|
||||||
The default is nothing, except for plotly based backends, where it
|
|
||||||
adds data for `application/vnd.plotly.v1+json` that is used in
|
|
||||||
frontends like jupyterlab and nteract.
|
|
||||||
"""
|
|
||||||
_extra_mime_info!(plt::Plot, out::Dict) = out
|
|
||||||
function _extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
|
||||||
out["application/vnd.plotly.v1+json"] = JSON.lower(plt.o)
|
|
||||||
out
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function _extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
|
if haskey(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL")
|
||||||
out["application/vnd.plotly.v1+json"] = Dict(
|
use_local_plotlyjs[] = ENV["PLOTS_HOST_DEPENDENCY_LOCAL"] == "true"
|
||||||
:data => plotly_series(plt),
|
use_local_dependencies[] = isfile(plotly_local_file_path) && use_local_plotlyjs[]
|
||||||
:layout => plotly_layout(plt)
|
if use_local_plotlyjs[] && !isfile(plotly_local_file_path)
|
||||||
)
|
@warn("PLOTS_HOST_DEPENDENCY_LOCAL is set to true, but no local plotly file found. run Pkg.build(\"Plots\") and make sure PLOTS_HOST_DEPENDENCY_LOCAL is set to true")
|
||||||
out
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function IJulia.display_dict(plt::Plot)
|
|
||||||
output_type = Symbol(plt.attr[:html_output_format])
|
|
||||||
if output_type == :auto
|
|
||||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
|
||||||
end
|
|
||||||
out = Dict()
|
|
||||||
if output_type == :txt
|
|
||||||
mime = "text/plain"
|
|
||||||
out[mime] = sprint(show, MIME(mime), plt)
|
|
||||||
elseif output_type == :png
|
|
||||||
mime = "image/png"
|
|
||||||
out[mime] = base64encode(show, MIME(mime), plt)
|
|
||||||
elseif output_type == :svg
|
|
||||||
mime = "image/svg+xml"
|
|
||||||
out[mime] = sprint(show, MIME(mime), plt)
|
|
||||||
elseif output_type == :html
|
|
||||||
mime = "text/html"
|
|
||||||
out[mime] = sprint(show, MIME(mime), plt)
|
|
||||||
else
|
else
|
||||||
error("Unsupported output type $output_type")
|
use_local_dependencies[] = use_local_plotlyjs[]
|
||||||
end
|
|
||||||
_extra_mime_info!(plt, out)
|
|
||||||
out
|
|
||||||
end
|
end
|
||||||
|
|
||||||
ENV["MPLBACKEND"] = "Agg"
|
|
||||||
end
|
@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin
|
||||||
|
_show(io::IO, mime::MIME"image/png", plt::Plot{<:PDFBackends}) = _show_pdfbackends(io, mime, plt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -111,7 +111,7 @@ function resolve_mixed(mix::MixedMeasures, sp::Subplot, letter::Symbol)
|
|||||||
pct += mix.len / totlen
|
pct += mix.len / totlen
|
||||||
end
|
end
|
||||||
if pct != 0
|
if pct != 0
|
||||||
amin, amax = axis_limits(sp[Symbol(letter,:axis)])
|
amin, amax = axis_limits(sp, letter)
|
||||||
xy += pct * (amax-amin)
|
xy += pct * (amax-amin)
|
||||||
end
|
end
|
||||||
xy
|
xy
|
||||||
|
|||||||
@ -55,9 +55,7 @@ tex(fn::AbstractString) = tex(current(), fn)
|
|||||||
function html(plt::Plot, fn::AbstractString)
|
function html(plt::Plot, fn::AbstractString)
|
||||||
fn = addExtension(fn, "html")
|
fn = addExtension(fn, "html")
|
||||||
io = open(fn, "w")
|
io = open(fn, "w")
|
||||||
_use_remote[] = true
|
|
||||||
show(io, MIME("text/html"), plt)
|
show(io, MIME("text/html"), plt)
|
||||||
_use_remote[] = false
|
|
||||||
close(io)
|
close(io)
|
||||||
end
|
end
|
||||||
html(fn::AbstractString) = html(current(), fn)
|
html(fn::AbstractString) = html(current(), fn)
|
||||||
@ -156,7 +154,6 @@ end
|
|||||||
const _best_html_output_type = KW(
|
const _best_html_output_type = KW(
|
||||||
:pyplot => :png,
|
:pyplot => :png,
|
||||||
:unicodeplots => :txt,
|
:unicodeplots => :txt,
|
||||||
:glvisualize => :png,
|
|
||||||
:plotlyjs => :html,
|
:plotlyjs => :html,
|
||||||
:plotly => :html
|
:plotly => :html
|
||||||
)
|
)
|
||||||
@ -192,7 +189,7 @@ end
|
|||||||
# for writing to io streams... first prepare, then callback
|
# for writing to io streams... first prepare, then callback
|
||||||
for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml",
|
for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml",
|
||||||
"application/eps", "application/pdf", "application/postscript",
|
"application/eps", "application/pdf", "application/postscript",
|
||||||
"application/x-tex")
|
"application/x-tex", "application/vnd.plotly.v1+json")
|
||||||
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
|
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
|
||||||
if haskey(io, :juno_plotsize)
|
if haskey(io, :juno_plotsize)
|
||||||
showjuno(io, m, plt)
|
showjuno(io, m, plt)
|
||||||
@ -200,6 +197,7 @@ for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml
|
|||||||
prepare_output(plt)
|
prepare_output(plt)
|
||||||
_show(io, m, plt)
|
_show(io, m, plt)
|
||||||
end
|
end
|
||||||
|
return nothing
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -210,30 +208,6 @@ _show(io::IO, ::MIME{Symbol("text/plain")}, plt::Plot) = show(io, plt)
|
|||||||
closeall() = closeall(backend())
|
closeall() = closeall(backend())
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
|
||||||
|
|
||||||
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
|
||||||
if is_installed("FileIO")
|
|
||||||
@eval import FileIO
|
|
||||||
function _show(io::IO, ::MIME"image/png", plt::Plot{<:PDFBackends})
|
|
||||||
fn = tempname()
|
|
||||||
|
|
||||||
# first save a pdf file
|
|
||||||
pdf(plt, fn)
|
|
||||||
|
|
||||||
# load that pdf into a FileIO Stream
|
|
||||||
s = FileIO.load(fn * ".pdf")
|
|
||||||
|
|
||||||
# save a png
|
|
||||||
pngfn = fn * ".png"
|
|
||||||
FileIO.save(pngfn, s)
|
|
||||||
|
|
||||||
# now write from the file
|
|
||||||
write(io, read(open(pngfn), String))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# function html_output_format(fmt)
|
# function html_output_format(fmt)
|
||||||
# if fmt == "png"
|
# if fmt == "png"
|
||||||
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
||||||
|
|||||||
@ -166,9 +166,9 @@ end
|
|||||||
function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||||
plotattributes[:plot_object] = plt
|
plotattributes[:plot_object] = plt
|
||||||
|
|
||||||
if !isempty(args) && !isdefined(Main, :StatPlots) &&
|
if !isempty(args) && !isdefined(Main, :StatsPlots) &&
|
||||||
first(split(string(typeof(args[1])), ".")) == "DataFrames"
|
first(split(string(typeof(args[1])), ".")) == "DataFrames"
|
||||||
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatPlots")
|
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots")
|
||||||
end
|
end
|
||||||
|
|
||||||
# --------------------------------
|
# --------------------------------
|
||||||
|
|||||||
@ -18,7 +18,7 @@ end
|
|||||||
plotattr([attr])
|
plotattr([attr])
|
||||||
|
|
||||||
Look up the properties of a Plots attribute, or specify an attribute type. Call `plotattr()` for options.
|
Look up the properties of a Plots attribute, or specify an attribute type. Call `plotattr()` for options.
|
||||||
The information is the same as that given on https://juliaplots.github.io/attributes/.
|
The information is the same as that given on https://docs.juliaplots.org/latest/attributes/.
|
||||||
"""
|
"""
|
||||||
function plotattr()
|
function plotattr()
|
||||||
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
|
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
|
||||||
@ -50,8 +50,13 @@ function plotattr(attrtype::Symbol, attribute::AbstractString)
|
|||||||
|
|
||||||
desc = get(_arg_desc, attribute, "")
|
desc = get(_arg_desc, attribute, "")
|
||||||
first_period_idx = findfirst(isequal('.'), desc)
|
first_period_idx = findfirst(isequal('.'), desc)
|
||||||
|
if isnothing(first_period_idx)
|
||||||
|
typedesc = ""
|
||||||
|
desc = strip(desc)
|
||||||
|
else
|
||||||
typedesc = desc[1:first_period_idx-1]
|
typedesc = desc[1:first_period_idx-1]
|
||||||
desc = strip(desc[first_period_idx+1:end])
|
desc = strip(desc[first_period_idx+1:end])
|
||||||
|
end
|
||||||
als = keys(filter(x->x[2]==attribute, _keyAliases)) |> collect |> sort
|
als = keys(filter(x->x[2]==attribute, _keyAliases)) |> collect |> sort
|
||||||
als = join(map(string,als), ", ")
|
als = join(map(string,als), ", ")
|
||||||
def = _attribute_defaults[attrtype][attribute]
|
def = _attribute_defaults[attrtype][attribute]
|
||||||
|
|||||||
131
src/recipes.jl
131
src/recipes.jl
@ -47,16 +47,47 @@ end
|
|||||||
num_series(x::AMat) = size(x,2)
|
num_series(x::AMat) = size(x,2)
|
||||||
num_series(x) = 1
|
num_series(x) = 1
|
||||||
|
|
||||||
RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError("Unmatched plot recipe: $T"))
|
RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# for seriestype `line`, need to sort by x values
|
# 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
|
||||||
|
]
|
||||||
|
|
||||||
@recipe function f(::Type{Val{:line}}, x, y, z)
|
@recipe function f(::Type{Val{:line}}, x, y, z)
|
||||||
indices = sortperm(x)
|
indices = sortperm(x)
|
||||||
x := x[indices]
|
x := x[indices]
|
||||||
y := y[indices]
|
y := y[indices]
|
||||||
|
|
||||||
|
# sort vector arguments
|
||||||
|
for arg in POTENTIAL_VECTOR_ARGUMENTS
|
||||||
|
if typeof(plotattributes[arg]) <: AVec
|
||||||
|
plotattributes[arg] = _cycle(plotattributes[arg], indices)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# a tuple as fillrange has to be handled differently
|
||||||
|
if typeof(plotattributes[:fillrange]) <: Tuple
|
||||||
|
lower, upper = plotattributes[:fillrange]
|
||||||
|
if typeof(lower) <: AVec
|
||||||
|
lower = _cycle(lower, indices)
|
||||||
|
end
|
||||||
|
if typeof(upper) <: AVec
|
||||||
|
upper = _cycle(upper, indices)
|
||||||
|
end
|
||||||
|
plotattributes[:fillrange] = (lower, upper)
|
||||||
|
end
|
||||||
|
|
||||||
if typeof(z) <: AVec
|
if typeof(z) <: AVec
|
||||||
z := z[indices]
|
z := z[indices]
|
||||||
end
|
end
|
||||||
@ -65,19 +96,6 @@ RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where
|
|||||||
end
|
end
|
||||||
@deps line path
|
@deps line path
|
||||||
|
|
||||||
|
|
||||||
function hvline_limits(axis::Axis)
|
|
||||||
vmin, vmax = axis_limits(axis)
|
|
||||||
if vmin >= vmax
|
|
||||||
if isfinite(vmin)
|
|
||||||
vmax = vmin + 1
|
|
||||||
else
|
|
||||||
vmin, vmax = 0.0, 1.1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vmin, vmax
|
|
||||||
end
|
|
||||||
|
|
||||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||||
n = length(y)
|
n = length(y)
|
||||||
newx = repeat(Float64[-1, 1, NaN], n)
|
newx = repeat(Float64[-1, 1, NaN], n)
|
||||||
@ -222,11 +240,12 @@ end
|
|||||||
n = length(x)
|
n = length(x)
|
||||||
fr = plotattributes[:fillrange]
|
fr = plotattributes[:fillrange]
|
||||||
if fr == nothing
|
if fr == nothing
|
||||||
yaxis = plotattributes[:subplot][:yaxis]
|
sp = plotattributes[:subplot]
|
||||||
|
yaxis = sp[:yaxis]
|
||||||
fr = if yaxis[:scale] == :identity
|
fr = if yaxis[:scale] == :identity
|
||||||
0.0
|
0.0
|
||||||
else
|
else
|
||||||
NaNMath.min(axis_limits(yaxis)[1], ignorenan_minimum(y))
|
NaNMath.min(axis_limits(sp, :y)[1], ignorenan_minimum(y))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
newx, newy = zeros(3n), zeros(3n)
|
newx, newy = zeros(3n), zeros(3n)
|
||||||
@ -519,13 +538,15 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
|||||||
w, it_state_w = it_tuple_w
|
w, it_state_w = it_tuple_w
|
||||||
|
|
||||||
if (log_scale_x && a ≈ 0)
|
if (log_scale_x && a ≈ 0)
|
||||||
a = b/_logScaleBases[xscale]^3
|
a = oftype(a, b/_logScaleBases[xscale]^3)
|
||||||
end
|
end
|
||||||
|
|
||||||
if isnan(w)
|
if isnan(w)
|
||||||
if !isnan(last_w)
|
if !isnan(last_w)
|
||||||
push!(x, a)
|
push!(x, a)
|
||||||
push!(y, baseline)
|
push!(y, baseline)
|
||||||
|
push!(x, NaN)
|
||||||
|
push!(y, NaN)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if isnan(last_w)
|
if isnan(last_w)
|
||||||
@ -538,8 +559,8 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
|||||||
push!(y, w)
|
push!(y, w)
|
||||||
end
|
end
|
||||||
|
|
||||||
a = b
|
a = oftype(a, b)
|
||||||
last_w = w
|
last_w = oftype(last_w, w)
|
||||||
|
|
||||||
it_tuple_e = iterate(edge, it_state_e)
|
it_tuple_e = iterate(edge, it_state_e)
|
||||||
it_tuple_w = iterate(weights, it_state_w)
|
it_tuple_w = iterate(weights, it_state_w)
|
||||||
@ -586,12 +607,13 @@ end
|
|||||||
end
|
end
|
||||||
Plots.@deps stepbins path
|
Plots.@deps stepbins path
|
||||||
|
|
||||||
wand_edges(x...) = (@warn("Load the StatPlots 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
|
||||||
_cl(x) = ceil(Int, NaNMath.max(x, one(x)))
|
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))
|
_iqr(v) = (q = quantile(v, 0.75) - quantile(v, 0.25); q > 0 ? q : oftype(q, 1))
|
||||||
_span(v) = ignorenan_maximum(v) - ignorenan_minimum(v)
|
_span(v) = maximum(v) - minimum(v)
|
||||||
|
|
||||||
n_samples = length(LinearIndices(first(vs)))
|
n_samples = length(LinearIndices(first(vs)))
|
||||||
|
|
||||||
@ -616,7 +638,7 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
|||||||
elseif mode == :fd # Freedman–Diaconis rule
|
elseif mode == :fd # Freedman–Diaconis rule
|
||||||
_cl(_span(v) / (2 * _iqr(v) / nd))
|
_cl(_span(v) / (2 * _iqr(v) / nd))
|
||||||
elseif mode == :wand
|
elseif mode == :wand
|
||||||
wand_edges(v) # this makes this function not type stable, but the type instability does not propagate
|
_cl(wand_edges(v)) # this makes this function not type stable, but the type instability does not propagate
|
||||||
else
|
else
|
||||||
error("Unknown auto-binning mode $mode")
|
error("Unknown auto-binning mode $mode")
|
||||||
end
|
end
|
||||||
@ -635,11 +657,19 @@ _hist_edges(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, Abstra
|
|||||||
_hist_norm_mode(mode::Symbol) = mode
|
_hist_norm_mode(mode::Symbol) = mode
|
||||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
_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)]
|
||||||
|
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
|
||||||
edges = _hist_edges(vs, binning)
|
localvs = _filternans(vs)
|
||||||
|
edges = _hist_edges(localvs, binning)
|
||||||
h = float( weights == nothing ?
|
h = float( weights == nothing ?
|
||||||
StatsBase.fit(StatsBase.Histogram, vs, edges, closed = :left) :
|
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||||
StatsBase.fit(StatsBase.Histogram, vs, StatsBase.Weights(weights), edges, closed = :left)
|
StatsBase.fit(StatsBase.Histogram, localvs, StatsBase.Weights(weights), edges, closed = :left)
|
||||||
)
|
)
|
||||||
normalize!(h, mode = _hist_norm_mode(normed))
|
normalize!(h, mode = _hist_norm_mode(normed))
|
||||||
end
|
end
|
||||||
@ -1053,7 +1083,7 @@ end
|
|||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
|
|
||||||
"Adds a+bx... straight line over the current plot, without changing the axis limits"
|
"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...)
|
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||||
@ -1069,6 +1099,7 @@ timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
|||||||
@recipe f(::Type{Date}, dt::Date) = (dt -> Dates.value(dt), dateformatter)
|
@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{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)))
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# Complex Numbers
|
# Complex Numbers
|
||||||
@ -1131,3 +1162,49 @@ end
|
|||||||
title := string(grad.args[1])
|
title := string(grad.args[1])
|
||||||
z
|
z
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Moved in from PlotRecipes - see: http://stackoverflow.com/a/37732384/5075246
|
||||||
|
@userplot PortfolioComposition
|
||||||
|
|
||||||
|
# this shows the shifting composition of a basket of something over a variable
|
||||||
|
# - "returns" are the dependent variable
|
||||||
|
# - "weights" are a matrix where the ith column is the composition for returns[i]
|
||||||
|
# - since each polygon is its own series, you can assign labels easily
|
||||||
|
@recipe function f(pc::PortfolioComposition)
|
||||||
|
weights, returns = pc.args
|
||||||
|
n = length(returns)
|
||||||
|
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]))
|
||||||
|
sy = vcat(returns, reverse(returns))
|
||||||
|
@series Plots.isvertical(plotattributes) ? (sx, sy) : (sy, sx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
areaplot([x,] y)
|
||||||
|
areaplot!([x,] y)
|
||||||
|
|
||||||
|
Draw a stacked area plot of the matrix y.
|
||||||
|
# Examples
|
||||||
|
```julia-repl
|
||||||
|
julia> areaplot(1:3, [1 2 3; 7 8 9; 4 5 6], seriescolor = [:red :green :blue], fillalpha = [0.2 0.3 0.4])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@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]
|
||||||
|
seriestype := :line
|
||||||
|
for i in 1:size(data, 2)
|
||||||
|
@series begin
|
||||||
|
fillrange := i > 1 ? data[:,i-1] : 0
|
||||||
|
x, data[:,i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
169
src/series.jl
169
src/series.jl
@ -7,72 +7,33 @@
|
|||||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||||
|
|
||||||
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
|
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
|
||||||
|
const DataPoint = Union{Number, AbstractString, Missing}
|
||||||
|
const SeriesData = Union{AVec{<:DataPoint}, Function, Surface, Volume}
|
||||||
|
|
||||||
all3D(plotattributes::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(plotattributes, :seriestype, :none))
|
prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plotting")
|
||||||
|
prepareSeriesData(::Nothing) = nothing
|
||||||
|
prepareSeriesData(s::SeriesData) = handlemissings(s)
|
||||||
|
|
||||||
# unknown
|
handlemissings(v) = v
|
||||||
convertToAnyVector(x, plotattributes::KW) = error("No user recipe defined for $(typeof(x))")
|
handlemissings(v::AbstractArray{Union{T,Missing}}) where T <: Number = replace(v, missing => NaN)
|
||||||
|
handlemissings(v::AbstractArray{Union{T,Missing}}) where T <: AbstractString = replace(v, missing => "")
|
||||||
|
handlemissings(s::Surface) = Surface(handlemissings(s.surf))
|
||||||
|
handlemissings(v::Volume) = Volume(handlemissings(v.v), v.x_extents, v.y_extents, v.z_extents)
|
||||||
|
|
||||||
# missing
|
# default: assume x represents a single series
|
||||||
convertToAnyVector(v::Nothing, plotattributes::KW) = Any[nothing], nothing
|
convertToAnyVector(x) = Any[prepareSeriesData(x)]
|
||||||
|
|
||||||
# fixed number of blank series
|
# fixed number of blank series
|
||||||
convertToAnyVector(n::Integer, plotattributes::KW) = Any[zeros(0) for i in 1:n], nothing
|
convertToAnyVector(n::Integer) = Any[zeros(0) for i in 1:n]
|
||||||
|
|
||||||
# numeric vector
|
# vector of data points is a single series
|
||||||
convertToAnyVector(v::AVec{T}, plotattributes::KW) where {T<:Number} = Any[v], nothing
|
convertToAnyVector(v::AVec{<:DataPoint}) = Any[prepareSeriesData(v)]
|
||||||
convertToAnyVector(v::AVec{Union{Missing, T}}, plotattributes::KW) where {T<:Number} = Any[replace(v, missing => NaN)], nothing
|
|
||||||
|
|
||||||
# string vector
|
|
||||||
convertToAnyVector(v::AVec{T}, plotattributes::KW) where {T<:AbstractString} = Any[v], nothing
|
|
||||||
convertToAnyVector(v::AVec{Union{Missing, T}}, plotattributes::KW) where {T<:AbstractString} = Any[replace(v, missing => "")], nothing
|
|
||||||
|
|
||||||
function convertToAnyVector(v::AMat, plotattributes::KW)
|
|
||||||
v = handlemissings(v)
|
|
||||||
if all3D(plotattributes)
|
|
||||||
Any[Surface(v)]
|
|
||||||
else
|
|
||||||
Any[v[:,i] for i in 1:size(v,2)]
|
|
||||||
end, nothing
|
|
||||||
end
|
|
||||||
|
|
||||||
handlemissings(v::AMat) = v
|
|
||||||
handlemissings(v::AMat{T}) where T <: Number = replace(v, missing => NaN)
|
|
||||||
handlemissings(v::AMat{T}) where T <: String = replace(v, missing => "")
|
|
||||||
|
|
||||||
# function
|
|
||||||
convertToAnyVector(f::Function, plotattributes::KW) = Any[f], nothing
|
|
||||||
|
|
||||||
# surface
|
|
||||||
convertToAnyVector(s::Surface, plotattributes::KW) = Any[s], nothing
|
|
||||||
|
|
||||||
# volume
|
|
||||||
convertToAnyVector(v::Volume, plotattributes::KW) = Any[v], nothing
|
|
||||||
|
|
||||||
# # vector of OHLC
|
|
||||||
# convertToAnyVector(v::AVec{OHLC}, plotattributes::KW) = Any[v], nothing
|
|
||||||
|
|
||||||
# # dates
|
|
||||||
convertToAnyVector(dts::AVec{D}, plotattributes::KW) where {D<:Union{Date,DateTime}} = Any[dts], nothing
|
|
||||||
|
|
||||||
# list of things (maybe other vectors, functions, or something else)
|
# list of things (maybe other vectors, functions, or something else)
|
||||||
function convertToAnyVector(v::AVec, plotattributes::KW)
|
convertToAnyVector(v::AVec) = vcat((convertToAnyVector(vi) for vi in v)...)
|
||||||
if all(x -> typeof(x) <: Number, v)
|
|
||||||
# all real numbers wrap the whole vector as one item
|
|
||||||
Any[convert(Vector{Float64}, v)], nothing
|
|
||||||
else
|
|
||||||
# something else... treat each element as an item
|
|
||||||
vcat(Any[convertToAnyVector(vi, plotattributes)[1] for vi in v]...), nothing
|
|
||||||
# Any[vi for vi in v], nothing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
convertToAnyVector(t::Tuple, plotattributes::KW) = Any[t], nothing
|
# Matrix is split into columns
|
||||||
|
convertToAnyVector(v::AMat{<:DataPoint}) = Any[prepareSeriesData(v[:,i]) for i in 1:size(v,2)]
|
||||||
|
|
||||||
function convertToAnyVector(args...)
|
|
||||||
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
|
|
||||||
end
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
@ -135,23 +96,24 @@ struct SliceIt end
|
|||||||
z = z.data
|
z = z.data
|
||||||
end
|
end
|
||||||
|
|
||||||
xs, _ = convertToAnyVector(x, plotattributes)
|
xs = convertToAnyVector(x)
|
||||||
ys, _ = convertToAnyVector(y, plotattributes)
|
ys = convertToAnyVector(y)
|
||||||
zs, _ = convertToAnyVector(z, plotattributes)
|
zs = convertToAnyVector(z)
|
||||||
|
|
||||||
|
|
||||||
fr = pop!(plotattributes, :fillrange, nothing)
|
fr = pop!(plotattributes, :fillrange, nothing)
|
||||||
fillranges, _ = if typeof(fr) <: Number
|
fillranges = if typeof(fr) <: Number
|
||||||
([fr],nothing)
|
[fr]
|
||||||
else
|
else
|
||||||
convertToAnyVector(fr, plotattributes)
|
convertToAnyVector(fr)
|
||||||
end
|
end
|
||||||
mf = length(fillranges)
|
mf = length(fillranges)
|
||||||
|
|
||||||
rib = pop!(plotattributes, :ribbon, nothing)
|
rib = pop!(plotattributes, :ribbon, nothing)
|
||||||
ribbons, _ = if typeof(rib) <: Number
|
ribbons = if typeof(rib) <: Number
|
||||||
([fr],nothing)
|
[rib]
|
||||||
else
|
else
|
||||||
convertToAnyVector(rib, plotattributes)
|
convertToAnyVector(rib)
|
||||||
end
|
end
|
||||||
mr = length(ribbons)
|
mr = length(ribbons)
|
||||||
|
|
||||||
@ -193,8 +155,9 @@ _apply_type_recipe(plotattributes, v) = RecipesBase.apply_recipe(plotattributes,
|
|||||||
# This sort of recipe should return a pair of functions... one to convert to number,
|
# This sort of recipe should return a pair of functions... one to convert to number,
|
||||||
# and one to format tick values.
|
# and one to format tick values.
|
||||||
function _apply_type_recipe(plotattributes, v::AbstractArray)
|
function _apply_type_recipe(plotattributes, v::AbstractArray)
|
||||||
isempty(v) && return Float64[]
|
isempty(skipmissing(v)) && return Float64[]
|
||||||
args = RecipesBase.apply_recipe(plotattributes, typeof(v[1]), v[1])[1].args
|
x = first(skipmissing(v))
|
||||||
|
args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args
|
||||||
if length(args) == 2 && typeof(args[1]) <: Function && typeof(args[2]) <: Function
|
if length(args) == 2 && typeof(args[1]) <: Function && typeof(args[2]) <: Function
|
||||||
numfunc, formatter = args
|
numfunc, formatter = args
|
||||||
Formatted(map(numfunc, v), formatter)
|
Formatted(map(numfunc, v), formatter)
|
||||||
@ -292,8 +255,10 @@ end
|
|||||||
|
|
||||||
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||||
|
|
||||||
|
all3D(plotattributes::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
|
# 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}
|
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat,Missing}
|
||||||
if all3D(plotattributes)
|
if all3D(plotattributes)
|
||||||
n,m = size(mat)
|
n,m = size(mat)
|
||||||
wrap_surfaces(plotattributes)
|
wrap_surfaces(plotattributes)
|
||||||
@ -316,26 +281,33 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
# assume this is a Volume, so construct one
|
# assume this is a Volume, so construct one
|
||||||
@recipe function f(vol::AbstractArray{T,3}, args...) where T<:Number
|
@recipe function f(vol::AbstractArray{T,3}, args...) where T<:Union{Number,Missing}
|
||||||
seriestype := :volume
|
seriestype := :volume
|
||||||
SliceIt, nothing, Volume(vol, args...), nothing
|
SliceIt, nothing, Volume(vol, args...), nothing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# # images - grays
|
# # images - grays
|
||||||
|
function clamp_greys!(mat::AMat{T}) where T<:Gray
|
||||||
|
for i in eachindex(mat)
|
||||||
|
mat[i].val < 0 && (mat[i] = Gray(0))
|
||||||
|
mat[i].val > 1 && (mat[i] = Gray(1))
|
||||||
|
end
|
||||||
|
mat
|
||||||
|
end
|
||||||
|
|
||||||
@recipe function f(mat::AMat{T}) where T<:Gray
|
@recipe function f(mat::AMat{T}) where T<:Gray
|
||||||
n, m = size(mat)
|
n, m = size(mat)
|
||||||
if is_seriestype_supported(:image)
|
if is_seriestype_supported(:image)
|
||||||
seriestype := :image
|
seriestype := :image
|
||||||
yflip --> true
|
yflip --> true
|
||||||
SliceIt, 1:m, 1:n, Surface(mat)
|
SliceIt, 1:m, 1:n, Surface(clamp_greys!(mat))
|
||||||
else
|
else
|
||||||
seriestype := :heatmap
|
seriestype := :heatmap
|
||||||
yflip --> true
|
yflip --> true
|
||||||
cbar --> false
|
cbar --> false
|
||||||
fillcolor --> ColorGradient([:black, :white])
|
fillcolor --> ColorGradient([:black, :white])
|
||||||
SliceIt, 1:m, 1:n, Surface(convert(Matrix{Float64}, mat))
|
SliceIt, 1:m, 1:n, Surface(clamp!(convert(Matrix{Float64}, mat), 0., 1.))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -384,10 +356,11 @@ end
|
|||||||
@recipe function f(f::FuncOrFuncs{F}) where F<:Function
|
@recipe function f(f::FuncOrFuncs{F}) where F<:Function
|
||||||
plt = plotattributes[:plot_object]
|
plt = plotattributes[:plot_object]
|
||||||
xmin, xmax = try
|
xmin, xmax = try
|
||||||
axis_limits(plt[1][:xaxis])
|
axis_limits(plt[1], :x)
|
||||||
catch
|
catch
|
||||||
xm = tryrange(f, [-5,-1,0,0.01])
|
xinv = invscalefunc(get(plotattributes, :xscale, :identity))
|
||||||
xm, tryrange(f, filter(x->x>xm, [5,1,0.99, 0, -0.01]))
|
xm = tryrange(f, xinv.([-5,-1,0,0.01]))
|
||||||
|
xm, tryrange(f, filter(x->x>xm, xinv.([5,1,0.99, 0, -0.01])))
|
||||||
end
|
end
|
||||||
|
|
||||||
f, xmin, xmax
|
f, xmin, xmax
|
||||||
@ -517,16 +490,23 @@ end
|
|||||||
#
|
#
|
||||||
# # special handling... xmin/xmax with parametric function(s)
|
# # special handling... xmin/xmax with parametric function(s)
|
||||||
@recipe function f(f::Function, xmin::Number, xmax::Number)
|
@recipe function f(f::Function, xmin::Number, xmax::Number)
|
||||||
xs = adapted_grid(f, (xmin, xmax))
|
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||||
|
xs = _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||||
xs, f
|
xs, f
|
||||||
end
|
end
|
||||||
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
|
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
|
||||||
xs = Any[adapted_grid(f, (xmin, xmax)) for f in fs]
|
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||||
|
xs = Any[_scaled_adapted_grid(f, xscale, yscale, xmin, xmax) for f in fs]
|
||||||
xs, fs
|
xs, fs
|
||||||
end
|
end
|
||||||
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||||
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, range(umin, stop = umax, length = n)
|
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, range(umin, stop = umax, length = n)
|
||||||
|
|
||||||
|
function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||||
|
(xf, xinv), (yf, yinv) = ((scalefunc(s),invscalefunc(s)) for s in (xscale,yscale))
|
||||||
|
xinv.(adapted_grid(yf∘f∘xinv, xf.((xmin, xmax))))
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# # special handling... 3D parametric function(s)
|
# # special handling... 3D parametric function(s)
|
||||||
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function}
|
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function}
|
||||||
@ -539,36 +519,17 @@ end
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
# # --------------------------------------------------------------------
|
# # --------------------------------------------------------------------
|
||||||
# # Lists of tuples and FixedSizeArrays
|
# # Lists of tuples and GeometryTypes.Points
|
||||||
# # --------------------------------------------------------------------
|
# # --------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# # if we get an unhandled tuple, just splat it in
|
|
||||||
@recipe f(tup::Tuple) = tup
|
|
||||||
|
|
||||||
#
|
@recipe f(v::AVec{<:Tuple}) = unzip(v)
|
||||||
# # (x,y) tuples
|
@recipe f(v::AVec{<:GeometryTypes.Point}) = unzip(v)
|
||||||
@recipe f(xy::AVec{Tuple{R1,R2}}) where {R1<:Number,R2<:Number} = unzip(xy)
|
@recipe f(tup::Tuple) = [tup]
|
||||||
@recipe f(xy::Tuple{R1,R2}) where {R1<:Number,R2<:Number} = [xy[1]], [xy[2]]
|
@recipe f(p::GeometryTypes.Point) = [p]
|
||||||
|
|
||||||
#
|
# Special case for 4-tuples in :ohlc series
|
||||||
# # (x,y,z) tuples
|
@recipe f(xyuv::AVec{<:Tuple{R1,R2,R3,R4}}) where {R1,R2,R3,R4} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
||||||
@recipe f(xyz::AVec{Tuple{R1,R2,R3}}) where {R1<:Number,R2<:Number,R3<:Number} = unzip(xyz)
|
|
||||||
@recipe f(xyz::Tuple{R1,R2,R3}) where {R1<:Number,R2<:Number,R3<:Number} = [xyz[1]], [xyz[2]], [xyz[3]]
|
|
||||||
|
|
||||||
# these might be points+velocity, or OHLC or something else
|
|
||||||
@recipe f(xyuv::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
|
||||||
@recipe f(xyuv::Tuple{R1,R2,R3,R4}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# # 2D FixedSizeArrays
|
|
||||||
@recipe f(xy::AVec{FixedSizeArrays.Vec{2,T}}) where {T<:Number} = unzip(xy)
|
|
||||||
@recipe f(xy::FixedSizeArrays.Vec{2,T}) where {T<:Number} = [xy[1]], [xy[2]]
|
|
||||||
|
|
||||||
#
|
|
||||||
# # 3D FixedSizeArrays
|
|
||||||
@recipe f(xyz::AVec{FixedSizeArrays.Vec{3,T}}) where {T<:Number} = unzip(xyz)
|
|
||||||
@recipe f(xyz::FixedSizeArrays.Vec{3,T}) where {T<:Number} = [xyz[1]], [xyz[2]], [xyz[3]]
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# # --------------------------------------------------------------------
|
# # --------------------------------------------------------------------
|
||||||
@ -589,7 +550,7 @@ end
|
|||||||
# end
|
# end
|
||||||
|
|
||||||
splittable_kw(key, val, lengthGroup) = false
|
splittable_kw(key, val, lengthGroup) = false
|
||||||
splittable_kw(key, val::AbstractArray, lengthGroup) = (key != :group) && size(val,1) == lengthGroup
|
splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && size(val,1) == lengthGroup
|
||||||
splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, 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)
|
splittable_kw(key, val::SeriesAnnotations, lengthGroup) = splittable_kw(key, val.strs, lengthGroup)
|
||||||
|
|
||||||
|
|||||||
455
src/shorthands.jl
Normal file
455
src/shorthands.jl
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
"""
|
||||||
|
scatter(x,y)
|
||||||
|
scatter!(x,y)
|
||||||
|
|
||||||
|
Make a scatter plot of y vs x.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
```julia-repl
|
||||||
|
julia> scatter([1,2,3],[4,5,6],markersize=[3,4,5],markercolor=[:red,:green,:blue])
|
||||||
|
julia> scatter([(1,4),(2,5),(3,6)])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands scatter
|
||||||
|
|
||||||
|
"""
|
||||||
|
bar(x,y)
|
||||||
|
bar!(x,y)
|
||||||
|
|
||||||
|
Make a bar plot of y vs x.
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
|
||||||
|
- $(_document_argument("bar_position"))
|
||||||
|
- $(_document_argument("bar_width"))
|
||||||
|
- $(_document_argument("bar_edges"))
|
||||||
|
- $(_document_argument("orientation"))
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
```julia-repl
|
||||||
|
julia> bar([1,2,3],[4,5,6],fillcolor=[:red,:green,:blue],fillalpha=[0.2,0.4,0.6])
|
||||||
|
julia> bar([(1,4),(2,5),(3,6)])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands bar
|
||||||
|
|
||||||
|
@shorthands barh
|
||||||
|
|
||||||
|
"""
|
||||||
|
histogram(x)
|
||||||
|
histogram!(x)
|
||||||
|
|
||||||
|
Plot a histogram.
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
|
||||||
|
- `x`: AbstractVector of values to be binned
|
||||||
|
- $(_document_argument("bins"))
|
||||||
|
- `weights`: Vector of weights for the values in `x`, for weighted bin counts
|
||||||
|
- $(_document_argument("normalize"))
|
||||||
|
- $(_document_argument("bar_position"))
|
||||||
|
- $(_document_argument("bar_width"))
|
||||||
|
- $(_document_argument("bar_edges"))
|
||||||
|
- $(_document_argument("orientation"))
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> histogram([1,2,1,1,4,3,8],bins=0:8)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands histogram
|
||||||
|
|
||||||
|
"""
|
||||||
|
barhist(x)
|
||||||
|
barhist!(x)
|
||||||
|
|
||||||
|
Make a histogram bar plot. See `histogram`.
|
||||||
|
"""
|
||||||
|
@shorthands barhist
|
||||||
|
|
||||||
|
"""
|
||||||
|
stephist(x)
|
||||||
|
stephist(x)
|
||||||
|
|
||||||
|
Make a histogram step plot (bin counts are represented using horizontal lines
|
||||||
|
instead of bars). See `histogram`.
|
||||||
|
"""
|
||||||
|
@shorthands stephist
|
||||||
|
|
||||||
|
"""
|
||||||
|
scatterhist(x)
|
||||||
|
scatterhist!(x)
|
||||||
|
|
||||||
|
Make a histogram scatter plot (bin counts are represented using points
|
||||||
|
instead of bars). See `histogram`.
|
||||||
|
"""
|
||||||
|
@shorthands scatterhist
|
||||||
|
|
||||||
|
"""
|
||||||
|
histogram2d(x,y)
|
||||||
|
histogram2d!(x,y)
|
||||||
|
|
||||||
|
Plot a two-dimensional histogram.
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
|
||||||
|
- `bins`: Number of bins (if an `Integer`) or bin edges (if an `AbtractVector`)
|
||||||
|
- `weights`: Vector of weights for the values in `x`. Each entry of x contributes
|
||||||
|
its weight to the height of its bin.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> histogram2d(randn(10_000),randn(10_000))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands histogram2d
|
||||||
|
|
||||||
|
"""
|
||||||
|
density(x)
|
||||||
|
density!(x)
|
||||||
|
|
||||||
|
Make a line plot of a kernel density estimate of x.
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
|
||||||
|
- `x`: AbstractVector of samples for probability density estimation
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> using StatsPlots
|
||||||
|
julia> density(randn(100_000))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands density
|
||||||
|
|
||||||
|
"""
|
||||||
|
heatmap(x,y,z)
|
||||||
|
heatmap!(x,y,z)
|
||||||
|
|
||||||
|
Plot a heatmap of the rectangular array `z`.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> heatmap(randn(10,10))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands heatmap
|
||||||
|
@shorthands plots_heatmap
|
||||||
|
|
||||||
|
"""
|
||||||
|
hexbin(x,y)
|
||||||
|
hexbin!(x,y)
|
||||||
|
|
||||||
|
Make a hexagonal binning plot (a histogram of the observations `(x[i],y[i])`
|
||||||
|
with hexagonal bins)
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> hexbin(randn(10_000), randn(10_000))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands hexbin
|
||||||
|
|
||||||
|
"""
|
||||||
|
sticks(x,y)
|
||||||
|
sticks!(x,y)
|
||||||
|
|
||||||
|
Draw a stick plot of y vs x.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> sticks(1:10)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands sticks
|
||||||
|
|
||||||
|
"""
|
||||||
|
hline(y)
|
||||||
|
hline!(y)
|
||||||
|
|
||||||
|
Draw horizontal lines at positions specified by the values in
|
||||||
|
the AbstractVector `y`
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> hline([-1,0,2])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands hline
|
||||||
|
|
||||||
|
"""
|
||||||
|
vline(x)
|
||||||
|
vline!(x)
|
||||||
|
|
||||||
|
Draw vertical lines at positions specified by the values in
|
||||||
|
the AbstractVector `x`
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> vline([-1,0,2])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands vline
|
||||||
|
|
||||||
|
"""
|
||||||
|
hspan(y)
|
||||||
|
|
||||||
|
Draw a rectangle between the horizontal line at position `y[1]`
|
||||||
|
and the horizontal line at position `y[2]`. If `length(y) ≥ 4`,
|
||||||
|
then further rectangles are drawn between `y[3]` and `y[4]`,
|
||||||
|
`y[5]` and `y[6]`, and so on. If `length(y)` is odd, then the
|
||||||
|
last entry of `y` is ignored.
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> hspan(1:6)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands hspan
|
||||||
|
|
||||||
|
"""
|
||||||
|
vspan(x)
|
||||||
|
|
||||||
|
Draw a rectangle between the vertical line at position `x[1]`
|
||||||
|
and the vertical line at position `x[2]`. If `length(x) ≥ 4`,
|
||||||
|
then further rectangles are drawn between `x[3]` and `x[4]`,
|
||||||
|
`x[5]` and `x[6]`, and so on. If `length(x)` is odd, then the
|
||||||
|
last entry of `x` is ignored.
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> vspan(1:6)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands vspan
|
||||||
|
|
||||||
|
"""
|
||||||
|
ohlc(x,y::Vector{OHLC})
|
||||||
|
ohlc!(x,y::Vector{OHLC})
|
||||||
|
|
||||||
|
Make open-high-low-close plot. Each entry of y is represented by a vertical
|
||||||
|
segment extending from the low value to the high value, with short horizontal
|
||||||
|
segments on the left and right indicating the open and close values, respectively.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> meanprices = cumsum(randn(100))
|
||||||
|
julia> y = OHLC[(p+rand(),p+1,p-1,p+rand()) for p in meanprices]
|
||||||
|
julia> ohlc(y)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands ohlc
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
contour(x,y,z)
|
||||||
|
contour!(x,y,z)
|
||||||
|
|
||||||
|
Draw contour lines of the `Surface` z.
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
- `levels`: Contour levels (if `AbstractVector`) or number of levels (if `Integer`)
|
||||||
|
- `fill`: Bool. Fill area between contours or draw contours only (false by default)
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> x = y = range(-20, 20, length = 100)
|
||||||
|
julia> contour(x, y, (x, y) -> x^2 + y^2)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands contour
|
||||||
|
|
||||||
|
"An alias for `contour` with fill = true."
|
||||||
|
@shorthands contourf
|
||||||
|
|
||||||
|
|
||||||
|
@shorthands contour3d
|
||||||
|
|
||||||
|
"""
|
||||||
|
surface(x,y,z)
|
||||||
|
surface!(x,y,z)
|
||||||
|
|
||||||
|
Draw a 3D surface plot.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> x = y = range(-3, 3, length = 100)
|
||||||
|
julia> surface(x, y, (x, y) -> sinc(norm([x, y])))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands surface
|
||||||
|
|
||||||
|
"""
|
||||||
|
wireframe(x,y,z)
|
||||||
|
wireframe!(x,y,z)
|
||||||
|
|
||||||
|
Draw a 3D wireframe plot.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> wireframe(1:10,1:10,randn(10,10))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands wireframe
|
||||||
|
|
||||||
|
"""
|
||||||
|
path3d(x,y,z)
|
||||||
|
path3d!(x,y,z)
|
||||||
|
|
||||||
|
Plot a 3D path from `(x[1],y[1],z[1])` to `(x[2],y[2],z[2])`,
|
||||||
|
..., to `(x[end],y[end],z[end])`.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> path3d([0,1,2,3],[0,1,4,9],[0,1,8,27])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands path3d
|
||||||
|
|
||||||
|
"""
|
||||||
|
scatter3d(x,y,z)
|
||||||
|
scatter3d!(x,y,z)
|
||||||
|
|
||||||
|
Make a 3D scatter plot.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> scatter3d([0,1,2,3],[0,1,4,9],[0,1,8,27])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands scatter3d
|
||||||
|
|
||||||
|
"""
|
||||||
|
boxplot(x, y)
|
||||||
|
boxplot!(x, y)
|
||||||
|
|
||||||
|
Make a box and whisker plot.
|
||||||
|
|
||||||
|
# Keyword arguments
|
||||||
|
- `notch`: Bool. Notch the box plot? (false)
|
||||||
|
- `range`: Real. Values more than range*IQR below the first quartile
|
||||||
|
or above the third quartile are shown as outliers (1.5)
|
||||||
|
- `outliers`: Bool. Show outliers? (true)
|
||||||
|
- `whisker_width`: Real or Symbol. Length of whiskers (:match)
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> using StatsPlots
|
||||||
|
julia> boxplot(repeat([1,2,3],outer=100),randn(300))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands boxplot
|
||||||
|
|
||||||
|
"""
|
||||||
|
violin(x,y,z)
|
||||||
|
violin!(x,y,z)
|
||||||
|
|
||||||
|
Make a violin plot.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> violin(repeat([1,2,3],outer=100),randn(300))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands violin
|
||||||
|
|
||||||
|
"""
|
||||||
|
quiver(x,y,quiver=(u,v))
|
||||||
|
quiver!(x,y,quiver=(u,v))
|
||||||
|
|
||||||
|
Make a quiver (vector field) plot. The `i`th vector extends
|
||||||
|
from `(x[i],y[i])` to `(x[i] + u[i], y[i] + v[i])`.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> quiver([1,2,3],[3,2,1],quiver=([1,1,1],[1,2,3]))
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands quiver
|
||||||
|
|
||||||
|
"""
|
||||||
|
curves(x,y)
|
||||||
|
curves!(x,y)
|
||||||
|
|
||||||
|
Draw a Bezier curve from `(x[1],y[1])` to `(x[end],y[end])`
|
||||||
|
with control points `(x[2],y[2]), ..., (x[end-1],y[end]-1)`
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> curves([1,2,3,4],[1,1,2,4])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
@shorthands curves
|
||||||
|
|
||||||
|
"Plot a pie diagram"
|
||||||
|
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||||
|
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||||
|
|
||||||
|
"Plot with seriestype :path3d"
|
||||||
|
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
|
||||||
|
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
|
||||||
|
|
||||||
|
"Add title to an existing plot"
|
||||||
|
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
|
||||||
|
|
||||||
|
"Add xlabel to an existing plot"
|
||||||
|
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
|
||||||
|
|
||||||
|
"Add ylabel to an existing plot"
|
||||||
|
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
|
||||||
|
|
||||||
|
"Set xlims for an existing plot"
|
||||||
|
xlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; xlims = lims, kw...)
|
||||||
|
|
||||||
|
"Set ylims for an existing plot"
|
||||||
|
ylims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; ylims = lims, kw...)
|
||||||
|
|
||||||
|
"Set zlims for an existing plot"
|
||||||
|
zlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; zlims = lims, kw...)
|
||||||
|
|
||||||
|
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
|
||||||
|
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
|
||||||
|
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin,zmax), kw...)
|
||||||
|
|
||||||
|
|
||||||
|
"Set xticks for an existing plot"
|
||||||
|
xticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; xticks = v, kw...)
|
||||||
|
|
||||||
|
"Set yticks for an existing plot"
|
||||||
|
yticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; yticks = v, kw...)
|
||||||
|
|
||||||
|
xticks!(
|
||||||
|
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; xticks = (ticks,labels), kw...)
|
||||||
|
yticks!(
|
||||||
|
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; yticks = (ticks,labels), kw...)
|
||||||
|
|
||||||
|
"""
|
||||||
|
annotate!(anns...)
|
||||||
|
|
||||||
|
Add annotations to an existing plot.
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
|
||||||
|
- `anns`: An `AbstractVector` of tuples of the form (x,y,text). The text object
|
||||||
|
can be an String or PlotText
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```julia-repl
|
||||||
|
julia> plot(1:10)
|
||||||
|
julia> annotate!([(7,3,"(7,3)"),(3,7,text("hey", 14, :left, :top, :green))])
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
|
||||||
|
annotate!(anns::AVec{T}; kw...) where {T<:Tuple} = plot!(; annotation = anns, kw...)
|
||||||
|
|
||||||
|
"Flip the current plots' x axis"
|
||||||
|
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
|
||||||
|
|
||||||
|
"Flip the current plots' y axis"
|
||||||
|
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
|
||||||
|
|
||||||
|
"Specify x axis attributes for an existing plot"
|
||||||
|
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
||||||
|
|
||||||
|
"Specify y axis attributes for an existing plot"
|
||||||
|
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
||||||
|
xgrid!(args...; kw...) = plot!(; xgrid = args, kw...)
|
||||||
|
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
|
||||||
@ -100,7 +100,7 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
srand(1)
|
Random.seed!(1)
|
||||||
|
|
||||||
label := ""
|
label := ""
|
||||||
colorbar := false
|
colorbar := false
|
||||||
|
|||||||
228
src/utils.jl
228
src/utils.jl
@ -194,7 +194,9 @@ end
|
|||||||
|
|
||||||
function iter_segments(series::Series)
|
function iter_segments(series::Series)
|
||||||
x, y, z = series[:x], series[:y], series[:z]
|
x, y, z = series[:x], series[:y], series[:z]
|
||||||
if has_attribute_segments(series)
|
if x == nothing
|
||||||
|
return UnitRange{Int}[]
|
||||||
|
elseif has_attribute_segments(series)
|
||||||
if series[:seriestype] in (:scatter, :scatter3d)
|
if series[:seriestype] in (:scatter, :scatter3d)
|
||||||
return [[i] for i in 1:length(y)]
|
return [[i] for i in 1:length(y)]
|
||||||
else
|
else
|
||||||
@ -212,36 +214,19 @@ end
|
|||||||
|
|
||||||
# helpers to figure out if there are NaN values in a list of array types
|
# helpers to figure out if there are NaN values in a list of array types
|
||||||
anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, args)
|
anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, args)
|
||||||
anynan(istart::Int, iend::Int, args::Tuple) = any(i -> anynan(i, args), istart:iend)
|
anynan(args::Tuple) = i -> anynan(i,args)
|
||||||
allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:iend)
|
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)
|
function Base.iterate(itr::SegmentsIterator, nextidx::Int = 1)
|
||||||
nextidx > itr.n && return nothing
|
i = findfirst(!anynan(itr.args), nextidx:itr.n)
|
||||||
if nextidx == 1 && !any(isempty,itr.args) && anynan(1, itr.args)
|
i === nothing && return nothing
|
||||||
nextidx = 2
|
nextval = nextidx + i - 1
|
||||||
end
|
|
||||||
|
|
||||||
i = istart = iend = nextidx
|
j = findfirst(anynan(itr.args), nextval:itr.n)
|
||||||
|
nextnan = j === nothing ? itr.n + 1 : nextval + j - 1
|
||||||
|
|
||||||
# find the next NaN, and iend is the one before
|
nextval:nextnan-1, nextnan
|
||||||
while i <= itr.n + 1
|
|
||||||
if i > itr.n || anynan(i, itr.args)
|
|
||||||
# done... array end or found NaN
|
|
||||||
iend = i-1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# find the next non-NaN, and set nextidx
|
|
||||||
while i <= itr.n
|
|
||||||
if !anynan(i, itr.args)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
istart:iend, i
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Find minimal type that can contain NaN and x
|
# Find minimal type that can contain NaN and x
|
||||||
@ -273,6 +258,9 @@ _cycle(v, indices::AVec{Int}) = fill(v, length(indices))
|
|||||||
_cycle(grad::ColorGradient, idx::Int) = _cycle(grad.colors, idx)
|
_cycle(grad::ColorGradient, idx::Int) = _cycle(grad.colors, idx)
|
||||||
_cycle(grad::ColorGradient, indices::AVec{Int}) = _cycle(grad.colors, indices)
|
_cycle(grad::ColorGradient, indices::AVec{Int}) = _cycle(grad.colors, indices)
|
||||||
|
|
||||||
|
_as_gradient(grad::ColorGradient) = grad
|
||||||
|
_as_gradient(c::Colorant) = ColorGradient([c,c])
|
||||||
|
|
||||||
makevec(v::AVec) = v
|
makevec(v::AVec) = v
|
||||||
makevec(v::T) where {T} = T[v]
|
makevec(v::T) where {T} = T[v]
|
||||||
|
|
||||||
@ -283,18 +271,17 @@ maketuple(x::Tuple{T,S}) where {T,S} = x
|
|||||||
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
||||||
mapFuncOrFuncs(fs::AVec{F}, u::AVec) where {F<:Function} = [map(f, u) for f in fs]
|
mapFuncOrFuncs(fs::AVec{F}, u::AVec) where {F<:Function} = [map(f, u) for f in fs]
|
||||||
|
|
||||||
unzip(xy::AVec{Tuple{X,Y}}) where {X,Y} = [t[1] for t in xy], [t[2] for t in xy]
|
for i in 2:4
|
||||||
unzip(xyz::AVec{Tuple{X,Y,Z}}) where {X,Y,Z} = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
|
@eval begin
|
||||||
unzip(xyuv::AVec{Tuple{X,Y,U,V}}) where {X,Y,U,V} = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
|
unzip(v::Union{AVec{<:Tuple{Vararg{T,$i} where T}},
|
||||||
|
AVec{<:GeometryTypes.Point{$i}}}) = $(Expr(:tuple, (:([t[$j] for t in v]) for j=1:i)...))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
unzip(xy::AVec{FixedSizeArrays.Vec{2,T}}) where {T} = T[t[1] for t in xy], T[t[2] for t in xy]
|
unzip(v::Union{AVec{<:GeometryTypes.Point{N}},
|
||||||
unzip(xy::FixedSizeArrays.Vec{2,T}) where {T} = T[xy[1]], T[xy[2]]
|
AVec{<:Tuple{Vararg{T,N} where T}}}) where N = error("$N-dimensional unzip not implemented.")
|
||||||
|
unzip(v::Union{AVec{<:GeometryTypes.Point},
|
||||||
unzip(xyz::AVec{FixedSizeArrays.Vec{3,T}}) where {T} = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
|
AVec{<:Tuple}}) = error("Can't unzip points of different dimensions.")
|
||||||
unzip(xyz::FixedSizeArrays.Vec{3,T}) where {T} = T[xyz[1]], T[xyz[2]], T[xyz[3]]
|
|
||||||
|
|
||||||
unzip(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) where {T} = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
|
|
||||||
unzip(xyuv::FixedSizeArrays.Vec{4,T}) where {T} = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
|
|
||||||
|
|
||||||
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
||||||
function _expand_limits(lims, x)
|
function _expand_limits(lims, x)
|
||||||
@ -357,6 +344,7 @@ const _scale_base = Dict{Symbol, Real}(
|
|||||||
)
|
)
|
||||||
|
|
||||||
function _heatmap_edges(v::AVec)
|
function _heatmap_edges(v::AVec)
|
||||||
|
length(v) == 1 && return v[1] .+ [-0.5, 0.5]
|
||||||
vmin, vmax = ignorenan_extrema(v)
|
vmin, vmax = ignorenan_extrema(v)
|
||||||
extra_min = (v[2] - v[1]) / 2
|
extra_min = (v[2] - v[1]) / 2
|
||||||
extra_max = (v[end] - v[end - 1]) / 2
|
extra_max = (v[end] - v[end - 1]) / 2
|
||||||
@ -369,38 +357,14 @@ function heatmap_edges(v::AVec, scale::Symbol = :identity)
|
|||||||
map(invf, _heatmap_edges(map(f,v)))
|
map(invf, _heatmap_edges(map(f,v)))
|
||||||
end
|
end
|
||||||
|
|
||||||
function calc_r_extrema(x, y)
|
function convert_to_polar(theta, r, r_extrema = ignorenan_extrema(r))
|
||||||
xmin, xmax = ignorenan_extrema(x)
|
|
||||||
ymin, ymax = ignorenan_extrema(y)
|
|
||||||
r = 0.5 * NaNMath.min(xmax - xmin, ymax - ymin)
|
|
||||||
ignorenan_extrema(r)
|
|
||||||
end
|
|
||||||
|
|
||||||
function convert_to_polar(x, y, r_extrema = calc_r_extrema(x, y))
|
|
||||||
rmin, rmax = r_extrema
|
rmin, rmax = r_extrema
|
||||||
theta, r = filter_radial_data(x, y, r_extrema)
|
|
||||||
r = (r .- rmin) ./ (rmax .- rmin)
|
r = (r .- rmin) ./ (rmax .- rmin)
|
||||||
x = r.*cos.(theta)
|
x = r.*cos.(theta)
|
||||||
y = r.*sin.(theta)
|
y = r.*sin.(theta)
|
||||||
x, y
|
x, y
|
||||||
end
|
end
|
||||||
|
|
||||||
# Filters radial data for points within the axis limits
|
|
||||||
function filter_radial_data(theta, r, r_extrema::Tuple{Real, Real})
|
|
||||||
n = max(length(theta), length(r))
|
|
||||||
rmin, rmax = r_extrema
|
|
||||||
x, y = zeros(n), zeros(n)
|
|
||||||
for i in 1:n
|
|
||||||
x[i] = _cycle(theta, i)
|
|
||||||
y[i] = _cycle(r, i)
|
|
||||||
end
|
|
||||||
points = map((a, b) -> (a, b), x, y)
|
|
||||||
filter!(a -> a[2] >= rmin && a[2] <= rmax, points)
|
|
||||||
x = map(a -> a[1], points)
|
|
||||||
y = map(a -> a[2], points)
|
|
||||||
x, y
|
|
||||||
end
|
|
||||||
|
|
||||||
function fakedata(sz...)
|
function fakedata(sz...)
|
||||||
y = zeros(sz...)
|
y = zeros(sz...)
|
||||||
for r in 2:size(y,1)
|
for r in 2:size(y,1)
|
||||||
@ -412,14 +376,6 @@ end
|
|||||||
isijulia() = :IJulia in nameof.(collect(values(Base.loaded_modules)))
|
isijulia() = :IJulia in nameof.(collect(values(Base.loaded_modules)))
|
||||||
isatom() = :Atom in nameof.(collect(values(Base.loaded_modules)))
|
isatom() = :Atom in nameof.(collect(values(Base.loaded_modules)))
|
||||||
|
|
||||||
function is_installed(pkgstr::AbstractString)
|
|
||||||
try
|
|
||||||
Pkg.installed(pkgstr) === nothing ? false : true
|
|
||||||
catch
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
istuple(::Tuple) = true
|
istuple(::Tuple) = true
|
||||||
istuple(::Any) = false
|
istuple(::Any) = false
|
||||||
isvector(::AVec) = true
|
isvector(::AVec) = true
|
||||||
@ -438,7 +394,7 @@ isvertical(series::Series) = isvertical(series.plotattributes)
|
|||||||
|
|
||||||
ticksType(ticks::AVec{T}) where {T<:Real} = :ticks
|
ticksType(ticks::AVec{T}) where {T<:Real} = :ticks
|
||||||
ticksType(ticks::AVec{T}) where {T<:AbstractString} = :labels
|
ticksType(ticks::AVec{T}) where {T<:AbstractString} = :labels
|
||||||
ticksType(ticks::Tuple{T,S}) where {T<:AVec,S<:AVec} = :ticks_and_labels
|
ticksType(ticks::Tuple{T,S}) where {T<:Union{AVec,Tuple},S<:Union{AVec,Tuple}} = :ticks_and_labels
|
||||||
ticksType(ticks) = :invalid
|
ticksType(ticks) = :invalid
|
||||||
|
|
||||||
limsType(lims::Tuple{T,S}) where {T<:Real,S<:Real} = :limits
|
limsType(lims::Tuple{T,S}) where {T<:Real,S<:Real} = :limits
|
||||||
@ -534,7 +490,7 @@ function concatenate_fillrange(x,y::Tuple)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function get_sp_lims(sp::Subplot, letter::Symbol)
|
function get_sp_lims(sp::Subplot, letter::Symbol)
|
||||||
axis_limits(sp[Symbol(letter, :axis)])
|
axis_limits(sp, letter)
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -571,9 +527,9 @@ function get_clims(sp::Subplot)
|
|||||||
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface)
|
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface)
|
||||||
for series in series_list(sp)
|
for series in series_list(sp)
|
||||||
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
||||||
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Real)
|
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing, Real})
|
||||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals.surf)...)
|
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals.surf)...)
|
||||||
elseif (vals != nothing) && (eltype(vals) <: Real)
|
elseif (vals != nothing) && (eltype(vals) <: Union{Missing, Real})
|
||||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals)...)
|
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals)...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -588,34 +544,49 @@ end
|
|||||||
|
|
||||||
_update_clims(zmin, zmax, emin, emax) = min(zmin, emin), max(zmax, emax)
|
_update_clims(zmin, zmax, emin, emax) = min(zmin, emin), max(zmax, emax)
|
||||||
|
|
||||||
function hascolorbar(series::Series)
|
@enum ColorbarStyle cbar_gradient cbar_fill cbar_lines
|
||||||
st = series[:seriestype]
|
|
||||||
hascbar = st == :heatmap
|
function colorbar_style(series::Series)
|
||||||
if st == :contour
|
colorbar_entry = series[:colorbar_entry]
|
||||||
hascbar = (isscalar(series[:levels]) ? (series[:levels] > 1) : (length(series[:levels]) > 1)) && (length(unique(Array(series[:z]))) > 1)
|
if !(colorbar_entry isa Bool)
|
||||||
end
|
@warn "Non-boolean colorbar_entry ignored."
|
||||||
if series[:marker_z] != nothing || series[:line_z] != nothing || series[:fill_z] != nothing
|
colorbar_entry = true
|
||||||
hascbar = true
|
|
||||||
end
|
|
||||||
# no colorbar if we are creating a surface LightSource
|
|
||||||
if xor(st == :surface, series[:fill_z] != nothing)
|
|
||||||
hascbar = true
|
|
||||||
end
|
|
||||||
return hascbar
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function hascolorbar(sp::Subplot)
|
if !colorbar_entry
|
||||||
cbar = sp[:colorbar]
|
nothing
|
||||||
hascbar = false
|
elseif isfilledcontour(series)
|
||||||
if cbar != :none
|
cbar_fill
|
||||||
for series in series_list(sp)
|
elseif iscontour(series)
|
||||||
if hascolorbar(series)
|
cbar_lines
|
||||||
hascbar = true
|
elseif series[:seriestype] ∈ (:heatmap,:surface) ||
|
||||||
|
any(series[z] !== nothing for z ∈ [:marker_z,:line_z,:fill_z])
|
||||||
|
cbar_gradient
|
||||||
|
else
|
||||||
|
nothing
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
hascolorbar(series::Series) = colorbar_style(series) !== nothing
|
||||||
|
hascolorbar(sp::Subplot) = sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp))
|
||||||
|
|
||||||
|
iscontour(series::Series) = series[:seriestype] == :contour
|
||||||
|
isfilledcontour(series::Series) = iscontour(series) && series[:fillrange] !== nothing
|
||||||
|
|
||||||
|
function contour_levels(series::Series, clims)
|
||||||
|
iscontour(series) || error("Not a contour series")
|
||||||
|
zmin, zmax = clims
|
||||||
|
levels = series[:levels]
|
||||||
|
if levels isa Integer
|
||||||
|
levels = range(zmin, stop=zmax, length=levels+2)
|
||||||
|
if !isfilledcontour(series)
|
||||||
|
levels = levels[2:end-1]
|
||||||
end
|
end
|
||||||
hascbar
|
|
||||||
end
|
end
|
||||||
|
levels
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for comp in (:line, :fill, :marker)
|
for comp in (:line, :fill, :marker)
|
||||||
|
|
||||||
@ -653,6 +624,9 @@ for comp in (:line, :fill, :marker)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
single_color(c, v = 0.5) = c
|
||||||
|
single_color(grad::ColorGradient, v = 0.5) = grad[v]
|
||||||
|
|
||||||
function get_linewidth(series, i::Int = 1)
|
function get_linewidth(series, i::Int = 1)
|
||||||
_cycle(series[:linewidth], i)
|
_cycle(series[:linewidth], i)
|
||||||
end
|
end
|
||||||
@ -680,7 +654,7 @@ function has_attribute_segments(series::Series)
|
|||||||
end
|
end
|
||||||
series[:seriestype] == :shape && return false
|
series[:seriestype] == :shape && return false
|
||||||
# ... else we check relevant attributes if they have multiple inputs
|
# ... else we check relevant attributes if they have multiple inputs
|
||||||
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray{<:Real} for attr in (:line_z, :fill_z, :marker_z))
|
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z))
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
@ -733,7 +707,7 @@ function with(f::Function, args...; kw...)
|
|||||||
|
|
||||||
# save the backend
|
# save the backend
|
||||||
if CURRENT_BACKEND.sym == :none
|
if CURRENT_BACKEND.sym == :none
|
||||||
pickDefaultBackend()
|
_pick_default_backend()
|
||||||
end
|
end
|
||||||
oldbackend = CURRENT_BACKEND.sym
|
oldbackend = CURRENT_BACKEND.sym
|
||||||
|
|
||||||
@ -745,7 +719,7 @@ function with(f::Function, args...; kw...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# # TODO: generalize this strategy to allow args as much as possible
|
# # TODO: generalize this strategy to allow args as much as possible
|
||||||
# # as in: with(:gadfly, :scatter, :legend, :grid) do; ...; end
|
# # as in: with(:gr, :scatter, :legend, :grid) do; ...; end
|
||||||
# # TODO: can we generalize this enough to also do something similar in the plot commands??
|
# # TODO: can we generalize this enough to also do something similar in the plot commands??
|
||||||
|
|
||||||
# k = :seriestype
|
# k = :seriestype
|
||||||
@ -803,24 +777,24 @@ function debugplots(on = true)
|
|||||||
_debugMode.on = on
|
_debugMode.on = on
|
||||||
end
|
end
|
||||||
|
|
||||||
debugshow(x) = show(x)
|
debugshow(io, x) = show(io, x)
|
||||||
debugshow(x::AbstractArray) = print(summary(x))
|
debugshow(io, x::AbstractArray) = print(io, summary(x))
|
||||||
|
|
||||||
function dumpdict(plotattributes::KW, prefix = "", alwaysshow = false)
|
function dumpdict(io::IO, plotattributes::KW, prefix = "", alwaysshow = false)
|
||||||
_debugMode.on || alwaysshow || return
|
_debugMode.on || alwaysshow || return
|
||||||
println()
|
println(io)
|
||||||
if prefix != ""
|
if prefix != ""
|
||||||
println(prefix, ":")
|
println(io, prefix, ":")
|
||||||
end
|
end
|
||||||
for k in sort(collect(keys(plotattributes)))
|
for k in sort(collect(keys(plotattributes)))
|
||||||
@printf("%14s: ", k)
|
@printf("%14s: ", k)
|
||||||
debugshow(plotattributes[k])
|
debugshow(io, plotattributes[k])
|
||||||
println()
|
println(io)
|
||||||
end
|
end
|
||||||
println()
|
println(io)
|
||||||
end
|
end
|
||||||
DD(plotattributes::KW, prefix = "") = dumpdict(plotattributes, prefix, true)
|
DD(io::IO, plotattributes::KW, prefix = "") = dumpdict(io, plotattributes, prefix, true)
|
||||||
|
DD(plotattributes::KW, prefix = "") = DD(stdout, plotattributes, prefix)
|
||||||
|
|
||||||
function dumpcallstack()
|
function dumpcallstack()
|
||||||
error() # well... you wanted the stacktrace, didn't you?!?
|
error() # well... you wanted the stacktrace, didn't you?!?
|
||||||
@ -1206,3 +1180,37 @@ end
|
|||||||
function construct_categorical_data(x::AbstractArray, axis::Axis)
|
function construct_categorical_data(x::AbstractArray, axis::Axis)
|
||||||
map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x)
|
map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_fmt_paragraph(paragraph::AbstractString;kwargs...) = _fmt_paragraph(IOBuffer(),paragraph,0;kwargs...)
|
||||||
|
|
||||||
|
function _fmt_paragraph(io::IOBuffer,
|
||||||
|
remaining_text::AbstractString,
|
||||||
|
column_count::Integer;
|
||||||
|
fillwidth=60,
|
||||||
|
leadingspaces=0)
|
||||||
|
|
||||||
|
kwargs = (fillwidth = fillwidth, leadingspaces = leadingspaces)
|
||||||
|
|
||||||
|
m = match(r"(.*?) (.*)",remaining_text)
|
||||||
|
if isa(m,Nothing)
|
||||||
|
if column_count + length(remaining_text) ≤ fillwidth
|
||||||
|
print(io,remaining_text)
|
||||||
|
String(take!(io))
|
||||||
|
else
|
||||||
|
print(io,"\n"*" "^leadingspaces*remaining_text)
|
||||||
|
String(take!(io))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if column_count + length(m[1]) ≤ fillwidth
|
||||||
|
print(io,"$(m[1]) ")
|
||||||
|
_fmt_paragraph(io,m[2],column_count + length(m[1]) + 1;kwargs...)
|
||||||
|
else
|
||||||
|
print(io,"\n"*" "^leadingspaces*"$(m[1]) ")
|
||||||
|
_fmt_paragraph(io,m[2],leadingspaces;kwargs...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _document_argument(S::AbstractString)
|
||||||
|
_fmt_paragraph("`$S`: "*_arg_desc[Symbol(S)],leadingspaces = 6 + length(S))
|
||||||
|
end
|
||||||
|
|||||||
1
test/.gitignore
vendored
Normal file
1
test/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
reference_images
|
||||||
10
test/REQUIRE
10
test/REQUIRE
@ -1,10 +0,0 @@
|
|||||||
StatPlots
|
|
||||||
Images
|
|
||||||
ImageMagick
|
|
||||||
@osx QuartzImageIO
|
|
||||||
FileIO
|
|
||||||
GR 0.31.0
|
|
||||||
RDatasets
|
|
||||||
VisualRegressionTests
|
|
||||||
UnicodePlots
|
|
||||||
LaTeXStrings
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
using Pkg
|
|
||||||
|
|
||||||
# need this to use Conda
|
|
||||||
# ENV["PYTHON"] = ""
|
|
||||||
|
|
||||||
to_add = [
|
|
||||||
PackageSpec(url="https://github.com/JuliaPlots/PlotReferenceImages.jl.git"),
|
|
||||||
# PackageSpec(url="https://github.com/JuliaStats/KernelDensity.jl.git"),
|
|
||||||
PackageSpec(name="PlotUtils", rev="master"),
|
|
||||||
PackageSpec(name="RecipesBase", rev="master"),
|
|
||||||
# PackageSpec(name="Blink", rev="master"),
|
|
||||||
# PackageSpec(name="Rsvg", rev="master"),
|
|
||||||
# PackageSpec(name="PlotlyJS", rev="master"),
|
|
||||||
# PackageSpec(name="VisualRegressionTests", rev="master"),
|
|
||||||
# PackageSpec("PyPlot"),
|
|
||||||
# PackageSpec("InspectDR"),
|
|
||||||
]
|
|
||||||
|
|
||||||
if isinteractive()
|
|
||||||
append!(to_add, [
|
|
||||||
PackageSpec(name="FileIO"),
|
|
||||||
PackageSpec(name="ImageMagick"),
|
|
||||||
PackageSpec(name="UnicodePlots"),
|
|
||||||
PackageSpec(name="VisualRegressionTests"),
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
Pkg.add(to_add)
|
|
||||||
|
|
||||||
Pkg.build("ImageMagick")
|
|
||||||
# Pkg.build("GR")
|
|
||||||
# Pkg.build("Blink")
|
|
||||||
# import Blink
|
|
||||||
# Blink.AtomShell.install()
|
|
||||||
# Pkg.build("PyPlot")
|
|
||||||
@ -1,50 +1,46 @@
|
|||||||
|
import Plots._current_plots_version
|
||||||
|
|
||||||
using VisualRegressionTests
|
# Taken from MakieGallery
|
||||||
# using ExamplePlots
|
"""
|
||||||
|
Downloads the reference images from ReferenceImages for a specific version
|
||||||
if isinteractive()
|
"""
|
||||||
@eval Main import Gtk
|
function download_reference(version = v"0.0.1")
|
||||||
|
download_dir = abspath(@__DIR__, "reference_images")
|
||||||
|
isdir(download_dir) || mkpath(download_dir)
|
||||||
|
tarfile = joinpath(download_dir, "reference_images.zip")
|
||||||
|
url = "https://github.com/JuliaPlots/PlotReferenceImages.jl/archive/v$(version).tar.gz"
|
||||||
|
refpath = joinpath(download_dir, "PlotReferenceImages.jl-$(version)")
|
||||||
|
if !isdir(refpath) # if not yet downloaded
|
||||||
|
@info "downloading reference images for version $version"
|
||||||
|
download(url, tarfile)
|
||||||
|
BinaryProvider.unpack(tarfile, download_dir)
|
||||||
|
# check again after download
|
||||||
|
if !isdir(refpath)
|
||||||
|
error("Something went wrong while downloading reference images. Plots can't be compared to references")
|
||||||
|
else
|
||||||
|
rm(tarfile, force = true)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@info "using reference images for version $version (already downloaded)"
|
||||||
|
end
|
||||||
|
refpath
|
||||||
end
|
end
|
||||||
|
|
||||||
# import DataFrames, RDatasets
|
const ref_image_dir = download_reference()
|
||||||
|
|
||||||
# don't let pyplot use a gui... it'll crash
|
|
||||||
# note: Agg will set gui -> :none in PyPlot
|
|
||||||
# ENV["MPLBACKEND"] = "Agg"
|
|
||||||
# try
|
|
||||||
# @eval import PyPlot
|
|
||||||
# @info("Matplotlib version: $(PyPlot.matplotlib[:__version__])")
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
using Plots
|
|
||||||
# using StatPlots
|
|
||||||
import PlotReferenceImages
|
|
||||||
using Random
|
|
||||||
using Test
|
|
||||||
|
|
||||||
default(size=(500,300))
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
|
||||||
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
|
||||||
|
|
||||||
const _current_plots_version = v"0.20.3"
|
|
||||||
|
|
||||||
|
|
||||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], tol = 1e-2)
|
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], tol = 1e-2)
|
||||||
Plots._debugMode.on = debug
|
Plots._debugMode.on = debug
|
||||||
example = Plots._examples[idx]
|
example = Plots._examples[idx]
|
||||||
|
Plots.theme(:default)
|
||||||
@info("Testing plot: $pkg:$idx:$(example.header)")
|
@info("Testing plot: $pkg:$idx:$(example.header)")
|
||||||
backend(pkg)
|
backend(pkg)
|
||||||
backend()
|
backend()
|
||||||
|
default(size=(500,300))
|
||||||
# ensure consistent results
|
# ensure consistent results
|
||||||
Random.seed!(1234)
|
Random.seed!(1234)
|
||||||
|
|
||||||
# reference image directory setup
|
# reference image directory setup
|
||||||
# refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
|
refdir = joinpath(ref_image_dir, "Plots", string(pkg))
|
||||||
refdir = joinpath(dirname(pathof(PlotReferenceImages)), "..", "Plots", string(pkg))
|
|
||||||
fn = "ref$idx.png"
|
fn = "ref$idx.png"
|
||||||
|
|
||||||
# firgure out version info
|
# firgure out version info
|
||||||
@ -76,7 +72,9 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
|
|||||||
|
|
||||||
# test function
|
# test function
|
||||||
func = (fn, idx) -> begin
|
func = (fn, idx) -> begin
|
||||||
map(eval, example.exprs)
|
expr = Expr(:block)
|
||||||
|
append!(expr.args, example.exprs)
|
||||||
|
eval(expr)
|
||||||
png(fn)
|
png(fn)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
186
test/runtests.jl
186
test/runtests.jl
@ -1,8 +1,12 @@
|
|||||||
module PlotsTests
|
using VisualRegressionTests
|
||||||
|
using Plots
|
||||||
|
using Random
|
||||||
|
using BinaryProvider
|
||||||
|
using Test
|
||||||
|
using FileIO
|
||||||
|
using GeometryTypes
|
||||||
|
|
||||||
include("add_packages.jl")
|
|
||||||
include("imgcomp.jl")
|
include("imgcomp.jl")
|
||||||
|
|
||||||
# don't actually show the plots
|
# don't actually show the plots
|
||||||
Random.seed!(1234)
|
Random.seed!(1234)
|
||||||
default(show=false, reuse=true)
|
default(show=false, reuse=true)
|
||||||
@ -14,109 +18,25 @@ img_tol = isinteractive() ? 1e-2 : 10e-2
|
|||||||
@test gr() == Plots.GRBackend()
|
@test gr() == Plots.GRBackend()
|
||||||
@test backend() == Plots.GRBackend()
|
@test backend() == Plots.GRBackend()
|
||||||
|
|
||||||
|
@static if Sys.islinux()
|
||||||
image_comparison_facts(:gr, tol=img_tol, skip = [25, 30])
|
image_comparison_facts(:gr, tol=img_tol, skip = [25, 30])
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
#@testset "PyPlot" begin
|
|
||||||
# @test pyplot() == Plots.PyPlotBackend()
|
|
||||||
# @test backend() == Plots.PyPlotBackend()
|
|
||||||
#
|
|
||||||
# image_comparison_facts(:pyplot, tol=img_tol)
|
|
||||||
#end
|
|
||||||
|
|
||||||
@testset "UnicodePlots" begin
|
@testset "UnicodePlots" begin
|
||||||
@test unicodeplots() == Plots.UnicodePlotsBackend()
|
@test unicodeplots() == Plots.UnicodePlotsBackend()
|
||||||
@test backend() == Plots.UnicodePlotsBackend()
|
@test backend() == Plots.UnicodePlotsBackend()
|
||||||
|
|
||||||
# lets just make sure it runs without error
|
# lets just make sure it runs without error
|
||||||
@test isa(plot(rand(10)), Plots.Plot) == true
|
p = plot(rand(10))
|
||||||
|
@test isa(p, Plots.Plot) == true
|
||||||
|
@test isa(display(p), Nothing) == true
|
||||||
|
p = bar(randn(10))
|
||||||
|
@test isa(p, Plots.Plot) == true
|
||||||
|
@test isa(display(p), Nothing) == true
|
||||||
end
|
end
|
||||||
|
|
||||||
# The plotlyjs testimages return a connection error on travis:
|
|
||||||
# connect: connection refused (ECONNREFUSED)
|
|
||||||
|
|
||||||
# @testset "PlotlyJS" begin
|
|
||||||
# @test plotlyjs() == Plots.PlotlyJSBackend()
|
|
||||||
# @test backend() == Plots.PlotlyJSBackend()
|
|
||||||
#
|
|
||||||
# if Sys.islinux() && isinteractive()
|
|
||||||
# image_comparison_facts(:plotlyjs,
|
|
||||||
# skip=[
|
|
||||||
# 2, # animation (skipped for speed)
|
|
||||||
# 27, # (polar plots) takes very long / not working
|
|
||||||
# 31, # animation (skipped for speed)
|
|
||||||
# ],
|
|
||||||
# tol=img_tol)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# InspectDR returns that error on travis:
|
|
||||||
# ERROR: LoadError: InitError: Cannot open display:
|
|
||||||
# in Gtk.GLib.GError(::Gtk.##229#230) at /home/travis/.julia/v0.5/Gtk/src/GLib/gerror.jl:17
|
|
||||||
|
|
||||||
# @testset "InspectDR" begin
|
|
||||||
# @test inspectdr() == Plots.InspectDRBackend()
|
|
||||||
# @test backend() == Plots.InspectDRBackend()
|
|
||||||
#
|
|
||||||
# image_comparison_facts(:inspectdr,
|
|
||||||
# skip=[
|
|
||||||
# 2, # animation
|
|
||||||
# 6, # heatmap not defined
|
|
||||||
# 10, # heatmap not defined
|
|
||||||
# 22, # contour not defined
|
|
||||||
# 23, # pie not defined
|
|
||||||
# 27, # polar plot not working
|
|
||||||
# 28, # heatmap not defined
|
|
||||||
# 31, # animation
|
|
||||||
# ],
|
|
||||||
# tol=img_tol)
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# @testset "Plotly" begin
|
|
||||||
# @test plotly() == Plots.PlotlyBackend()
|
|
||||||
# @test backend() == Plots.PlotlyBackend()
|
|
||||||
#
|
|
||||||
# # # until png generation is reliable on OSX, just test on linux
|
|
||||||
# # @static Sys.islinux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], tol=img_tol)
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# @testset "Immerse" begin
|
|
||||||
# @test immerse() == Plots.ImmerseBackend()
|
|
||||||
# @test backend() == Plots.ImmerseBackend()
|
|
||||||
#
|
|
||||||
# # as long as we can plot anything without error, it should be the same as Gadfly
|
|
||||||
# image_comparison_facts(:immerse, only=[1], tol=img_tol)
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# @testset "PlotlyJS" begin
|
|
||||||
# @test plotlyjs() == Plots.PlotlyJSBackend()
|
|
||||||
# @test backend() == Plots.PlotlyJSBackend()
|
|
||||||
#
|
|
||||||
# # as long as we can plot anything without error, it should be the same as Plotly
|
|
||||||
# image_comparison_facts(:plotlyjs, only=[1], tol=img_tol)
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# @testset "Gadfly" begin
|
|
||||||
# @test gadfly() == Plots.GadflyBackend()
|
|
||||||
# @test backend() == Plots.GadflyBackend()
|
|
||||||
#
|
|
||||||
# @test typeof(plot(1:10)) == Plots.Plot{Plots.GadflyBackend}
|
|
||||||
# @test plot(Int[1,2,3], rand(3)) == not(nothing)
|
|
||||||
# @test plot(sort(rand(10)), rand(Int, 10, 3)) == not(nothing)
|
|
||||||
# @test plot!(rand(10,3), rand(10,3)) == not(nothing)
|
|
||||||
#
|
|
||||||
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], tol=img_tol)
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@testset "Axes" begin
|
@testset "Axes" begin
|
||||||
p = plot()
|
p = plot()
|
||||||
axis = p.subplots[1][:xaxis]
|
axis = p.subplots[1][:xaxis]
|
||||||
@ -132,46 +52,44 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "NoFail" begin
|
@testset "NoFail" begin
|
||||||
histogram([1, 0, 0, 0, 0, 0])
|
plots = [histogram([1, 0, 0, 0, 0, 0]),
|
||||||
|
plot([missing]),
|
||||||
|
plot([missing; 1:4]),
|
||||||
|
plot([fill(missing,10); 1:4]),
|
||||||
|
plot([1 1; 1 missing]),
|
||||||
|
plot(["a" "b"; missing "d"], [1 2; 3 4])]
|
||||||
|
for plt in plots
|
||||||
|
display(plt)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# tests for preprocessing recipes
|
@testset "Segments" begin
|
||||||
|
function segments(args...)
|
||||||
|
segs = UnitRange{Int}[]
|
||||||
|
for seg in iter_segments(args...)
|
||||||
|
push!(segs,seg)
|
||||||
|
end
|
||||||
|
segs
|
||||||
|
end
|
||||||
|
|
||||||
# @testset "recipes" begin
|
nan10 = fill(NaN,10)
|
||||||
|
@test segments(11:20) == [1:10]
|
||||||
|
@test segments([NaN]) == []
|
||||||
|
@test segments(nan10) == []
|
||||||
|
@test segments([nan10; 1:5]) == [11:15]
|
||||||
|
@test segments([1:5;nan10]) == [1:5]
|
||||||
|
@test segments([nan10; 1:5; nan10; 1:5; nan10]) == [11:15, 26:30]
|
||||||
|
@test segments([NaN; 1], 1:10) == [2:2, 4:4, 6:6, 8:8, 10:10]
|
||||||
|
@test segments([nan10; 1:15], [1:15; nan10]) == [11:15]
|
||||||
|
end
|
||||||
|
|
||||||
# user recipe
|
@testset "Utils" begin
|
||||||
|
zipped = ([(1,2)], [("a","b")], [(1,"a"),(2,"b")],
|
||||||
# type T end
|
[(1,2),(3,4)], [(1,2,3),(3,4,5)], [(1,2,3,4),(3,4,5,6)],
|
||||||
# @recipe function f(::T)
|
[(1,2.0),(missing,missing)], [(1,missing),(missing,"a")],
|
||||||
# line := (3,0.3,:red)
|
[(missing,missing)], [(missing,missing,missing),("a","b","c")])
|
||||||
# marker := (20,0.5,:blue,:o)
|
for z in zipped
|
||||||
# bg := :yellow
|
@test isequal(collect(zip(Plots.unzip(z)...)), z)
|
||||||
# rand(10)
|
@test isequal(collect(zip(Plots.unzip(Point.(z))...)), z)
|
||||||
# end
|
end
|
||||||
# plot(T())
|
end
|
||||||
|
|
||||||
# plot recipe
|
|
||||||
|
|
||||||
# @recipe function f(::Type{Val{:hiplt}},plt::Plot)
|
|
||||||
# line := (3,0.3,:red)
|
|
||||||
# marker := (20,0.5,:blue,:o)
|
|
||||||
# t := :path
|
|
||||||
# bg:=:green
|
|
||||||
# ()
|
|
||||||
# end
|
|
||||||
# plot(rand(10),t=:hiplt)
|
|
||||||
|
|
||||||
# series recipe
|
|
||||||
|
|
||||||
# @recipe function f(::Type{Val{:hi}},x,y,z)
|
|
||||||
# line := (3,0.3,:red)
|
|
||||||
# marker := (20,0.5,:blue,:o)
|
|
||||||
# t := :path
|
|
||||||
# ()
|
|
||||||
# end
|
|
||||||
# plot(rand(10),t=:hiplt)
|
|
||||||
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
end # module
|
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
import SnoopCompile
|
|
||||||
|
|
||||||
### Log the compiles
|
|
||||||
# This only needs to be run once (to generate "/tmp/plots_compiles.csv")
|
|
||||||
|
|
||||||
# SnoopCompile.@snoop "/tmp/plots_compiles.csv" begin
|
|
||||||
# include(joinpath(dirname(@__FILE__), "runtests.jl"))
|
|
||||||
# end
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
|
||||||
|
|
||||||
### Parse the compiles and generate precompilation scripts
|
|
||||||
# This can be run repeatedly to tweak the scripts
|
|
||||||
|
|
||||||
# IMPORTANT: we must have the module(s) defined for the parcelation
|
|
||||||
# step, otherwise we will get no precompiles for the Plots module
|
|
||||||
using Plots
|
|
||||||
|
|
||||||
data = SnoopCompile.read("/tmp/plots_compiles.csv")
|
|
||||||
|
|
||||||
# The Plots tests are run inside a module PlotsTest, so all
|
|
||||||
# the precompiles get credited to PlotsTest. Credit them to Plots instead.
|
|
||||||
subst = Dict("PlotsTests"=>"Plots")
|
|
||||||
|
|
||||||
# Blacklist helps fix problems:
|
|
||||||
# - MIME uses type-parameters with symbols like :image/png, which is
|
|
||||||
# not parseable
|
|
||||||
blacklist = ["MIME"]
|
|
||||||
|
|
||||||
# Use these two lines if you want to create precompile functions for
|
|
||||||
# individual packages
|
|
||||||
pc, discards = SnoopCompile.parcel(data[end:-1:1,2], subst=subst, blacklist=blacklist)
|
|
||||||
SnoopCompile.write("/tmp/precompile", pc)
|
|
||||||
|
|
||||||
pdir = joinpath(dirname(@__FILE__), "..")
|
|
||||||
run(`cp /tmp/precompile/precompile_Plots.jl $pdir/src/precompile.jl`)
|
|
||||||
Loading…
x
Reference in New Issue
Block a user