diff --git a/.gitignore b/.gitignore
index 69fe281c..1bac5f19 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@ examples/.ipynb_checkpoints/*
examples/meetup/.ipynb_checkpoints/*
deps/plotly-latest.min.js
deps/build.log
+deps/deps.jl
+Manifest.toml
diff --git a/.travis.yml b/.travis.yml
index e236fe4f..fc303b0f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,57 +4,17 @@ os:
- linux
# - osx
julia:
- # - 1.0
+ - 1.1
- nightly
-# matrix:
-# allow_failures:
-# - julia: nightly
-
-# # before install:
-# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
-# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install wkhtmltopdf; fi
-
-# ref: http://askubuntu.com/a/556672 for the wkhtmltopdf apt repository info
+
+matrix:
+ allow_failures:
+ - julia: nightly
sudo: required
before_install:
- 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 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:
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())'
diff --git a/NEWS.md b/NEWS.md
index 81c8b5a0..08a760c3 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -10,7 +10,113 @@
---
## (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
- 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
- update syntax to 0.6
- 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
- implement `title_location` for gr
- add `hline` marker to pgfplots - fixes errorbars
@@ -305,7 +411,7 @@ Many updates, min julia 1.0
- added dependency on PlotThemes
- set_theme --> theme
- remove Compat from REQUIRE
-- warning for DataFrames without StatPlots
+- warning for DataFrames without StatsPlots
- closeall exported and implemented for gr/pyplot
- fix DateTime recipe
- reset theme with theme(:none)
@@ -427,8 +533,8 @@ Many updates, min julia 1.0
#### 0.8.0
- added dependency on PlotUtils
-- BREAKING: removed DataFrames support (now in StatPlots.jl)
-- BREAKING: removed boxplot/violin/density recipes (now in StatPlots.jl)
+- BREAKING: removed DataFrames support (now in StatsPlots.jl)
+- BREAKING: removed boxplot/violin/density recipes (now in StatsPlots.jl)
- GR:
- inline iterm2 support
- trisurface support
diff --git a/Project.toml b/Project.toml
new file mode 100644
index 00000000..d61e259e
--- /dev/null
+++ b/Project.toml
@@ -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"]
diff --git a/README.md b/README.md
index 955a5f4b..f2225901 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
#### 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:
diff --git a/REQUIRE b/REQUIRE
deleted file mode 100644
index a2a2f67f..00000000
--- a/REQUIRE
+++ /dev/null
@@ -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
diff --git a/appveyor.yml b/appveyor.yml
index d2c44998..3490f07b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,6 @@
environment:
matrix:
- - julia_version: 0.7
+ # - julia_version: 0.7
- julia_version: 1
- julia_version: nightly
diff --git a/deps/build.jl b/deps/build.jl
index ad328049..a8cf81ff 100644
--- a/deps/build.jl
+++ b/deps/build.jl
@@ -1,8 +1,18 @@
#TODO: download https://cdn.plot.ly/plotly-latest.min.js to deps/ if it doesn't exist
-
-local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
-if !isfile(local_fn)
- @info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
- download("https://cdn.plot.ly/plotly-latest.min.js", local_fn)
+file_path = ""
+if get(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL", "false") == "true"
+ global file_path
+ local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
+ if !isfile(local_fn)
+ @info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
+ 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
diff --git a/src/Plots.jl b/src/Plots.jl
index 35b16001..4fd536b5 100644
--- a/src/Plots.jl
+++ b/src/Plots.jl
@@ -1,12 +1,15 @@
module Plots
+_current_plots_version = v"0.25.0"
+
using Reexport
-import StaticArrays
-using StaticArrays.FixedSizeArrays
-using Dates, Printf, Statistics, Base64, LinearAlgebra
+import GeometryTypes
+using Dates, Printf, Statistics, Base64, LinearAlgebra, Random
import SparseArrays: findnz
+using FFMPEG
+
@reexport using RecipesBase
import RecipesBase: plot, plot!, animate
using Base.Meta
@@ -18,6 +21,17 @@ import JSON
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
grid,
bbox,
@@ -72,7 +86,6 @@ export
backends,
backend_name,
backend_object,
- add_backend,
aliases,
Shape,
@@ -168,103 +181,15 @@ include("arg_desc.jl")
include("plotattr.jl")
include("backends.jl")
include("output.jl")
+include("ijulia.jl")
+include("fileio.jl")
include("init.jl")
include("backends/plotly.jl")
include("backends/gr.jl")
include("backends/web.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...)
+include("shorthands.jl")
let PlotOrSubplot = Union{Plot, Subplot}
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
diff --git a/src/animation.jl b/src/animation.jl
index e0b2f5ab..b6d45f36 100644
--- a/src/animation.jl
+++ b/src/animation.jl
@@ -76,15 +76,15 @@ function buildanimation(animdir::AbstractString, fn::AbstractString,
if variable_palette
# generate a colorpalette for each frame for highest quality, but larger filesize
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
- 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
# 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
- 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
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
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)
ext = file_extension(agif.filename)
write(io, if ext == "gif"
- "\" />"
+ "
"
elseif ext in ("mov", "mp4")
- ""
+ ""
else
error("Cannot show animation with extension $ext: $agif")
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
diff --git a/src/arg_desc.jl b/src/arg_desc.jl
index 45dcaaa5..120d1c36 100644
--- a/src/arg_desc.jl
+++ b/src/arg_desc.jl
@@ -2,7 +2,7 @@
const _arg_desc = KW(
# series args
-:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
+:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
:seriescolor => "Color Type. The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot",
:seriesalpha => "Number in [0,1]. The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color.",
:seriestype => "Symbol. This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined.",
@@ -10,7 +10,7 @@ const _arg_desc = KW(
: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).",
: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`.",
: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).",
@@ -21,7 +21,7 @@ const _arg_desc = KW(
: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`.",
: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?",
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
:x => "Various. Input data. First Dimension",
@@ -29,13 +29,13 @@ const _arg_desc = KW(
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
: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).",
-: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.",
+: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}, 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).",
: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_edges => "Bool. Align bars to edges (true), or centers (the default)?",
-:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)",
+:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
+:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?",
+:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)",
:yerror => "AbstractVector or 2-Tuple of Vectors. y (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)",
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
@@ -43,13 +43,14 @@ const _arg_desc = KW(
:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes).",
:weights => "AbstractVector. Used in histogram types for weighted counts.",
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
-:contour_labels => "Bool. Show labels at the contour lines?",
+:contour_labels => "Bool. Show labels at the contour lines?",
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
-: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.",
-:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
-
+: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.",
+: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_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
:background_color => "Color Type. Base color for all backgrounds.",
@@ -63,11 +64,12 @@ 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).",
: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.",
-: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",
+: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",
+: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.",
-:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
-:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
+:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
+:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides",
# subplot args
@@ -98,7 +100,7 @@ const _arg_desc = KW(
: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.",
: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.",
: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.",
@@ -110,14 +112,14 @@ const _arg_desc = KW(
:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots",
# axis args
-:guide => "String. Axis guide (label).",
+:guide => "String. Axis guide (label).",
:guide_position => "Symbol. Position of axis guides: :top, :bottom, :left or :right",
-:lims => "NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example). `:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]",
-:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
-:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
-:rotation => "Number. Degrees rotation of tick labels.",
-: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.",
+:lims => "NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example). `:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]",
+:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
+:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
+:rotation => "Number. Degrees rotation of tick labels.",
+:flip => "Bool. Should we flip (reverse) the axis?",
+: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.",
:tickfontsize => "Integer. Font pointsize of tick labels.",
: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`",
: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`.",
+:draw_arrow => "Bool. Draw arrow at the end of the axis.",
)
diff --git a/src/args.jl b/src/args.jl
index 1065f12e..6f719bae 100644
--- a/src/args.jl
+++ b/src/args.jl
@@ -232,6 +232,7 @@ const _bar_width = 0.8
const _series_defaults = KW(
:label => "AUTO",
+ :colorbar_entry => true,
:seriescolor => :auto,
:seriesalpha => nothing,
:seriestype => :path,
@@ -298,6 +299,7 @@ const _plot_defaults = KW(
:link => :none,
:overwrite_figure => true,
:html_output_format => :auto,
+ :tex_output_standalone => false,
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
# the parent layout and the relative bounding box of inset subplots
:dpi => DPI, # dots per inch for images, etc
@@ -390,6 +392,7 @@ const _axis_defaults = KW(
:minorgrid => false,
:showaxis => true,
:widen => true,
+ :draw_arrow => false,
)
const _suppress_warnings = Set{Symbol}([
@@ -735,6 +738,10 @@ function processMarkerArg(plotattributes::KW, arg)
elseif allAlphas(arg)
plotattributes[:markeralpha] = arg
+ # bool
+ elseif typeof(arg) <: Bool
+ plotattributes[:markershape] = arg ? :circle : :none
+
# markersize
elseif allReals(arg)
plotattributes[:markersize] = arg
@@ -1023,7 +1030,7 @@ function preprocessArgs!(plotattributes::KW)
arrow()
elseif a in (false, nothing, :none)
nothing
- elseif !(typeof(a) <: Arrow)
+ elseif !(typeof(a) <: Arrow || typeof(a) <: AbstractArray{Arrow})
arrow(wraptuple(a)...)
else
a
@@ -1050,8 +1057,8 @@ function preprocessArgs!(plotattributes::KW)
# warnings for moved recipes
st = get(plotattributes, :seriestype, :path)
- if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatPlots)
- @warn("seriestype $st has been moved to StatPlots. To use: \`Pkg.add(\"StatPlots\"); using StatPlots\`")
+ if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatsPlots)
+ @warn("seriestype $st has been moved to StatsPlots. To use: \`Pkg.add(\"StatsPlots\"); using StatsPlots\`")
end
return
@@ -1180,7 +1187,7 @@ function convertLegendValue(val::Symbol)
:best
elseif val in (:no, :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
else
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)
end
-
-# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
-function getSeriesRGBColor(c, sp::Subplot, n::Int)
+# converts a symbol or string into a Colorant or ColorGradient
+# and assigns a color automatically
+function get_series_color(c, sp::Subplot, n::Int, seriestype)
if c == :auto
- c = autopick(sp[:color_palette], n)
+ c = like_surface(seriestype) ? cgrad() : autopick(sp[:color_palette], n)
elseif isa(c, Int)
c = autopick(sp[:color_palette], c)
end
plot_color(c)
end
-function getSeriesRGBColor(c::AbstractArray, sp::Subplot, n::Int)
- map(x->getSeriesRGBColor(x, sp, n), c)
+function get_series_color(c::AbstractArray, sp::Subplot, n::Int, seriestype)
+ map(x->get_series_color(x, sp, n, seriestype), c)
end
function ensure_gradient!(plotattributes::KW, csym::Symbol, asym::Symbol)
@@ -1565,21 +1572,23 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
end
# 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
for s in (:line, :marker, :fill)
csym, asym = Symbol(s,:color), Symbol(s,:alpha)
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]
else
- plotattributes[:seriescolor]
+ scolor
end)
elseif plotattributes[csym] == :match
- plot_color(plotattributes[:seriescolor])
+ plot_color(scolor)
else
- getSeriesRGBColor(plotattributes[csym], sp, plotIndex)
+ get_series_color(plotattributes[csym], sp, plotIndex, stype)
end
end
@@ -1587,9 +1596,9 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
plotattributes[:markerstrokecolor] = if plotattributes[:markerstrokecolor] == :match
plot_color(sp[:foreground_color_subplot])
elseif plotattributes[:markerstrokecolor] == :auto
- getSeriesRGBColor(plotattributes[:markercolor], sp, plotIndex)
+ get_series_color(plotattributes[:markercolor], sp, plotIndex, stype)
else
- getSeriesRGBColor(plotattributes[:markerstrokecolor], sp, plotIndex)
+ get_series_color(plotattributes[:markerstrokecolor], sp, plotIndex, stype)
end
# if marker_z, fill_z or line_z are set, ensure we have a gradient
diff --git a/src/axes.jl b/src/axes.jl
index 6f00a27a..8ef97f7e 100644
--- a/src/axes.jl
+++ b/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.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.plotattributes, v, ks...)
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))
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
-function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
- amin,amax = axis_limits(axis)
+function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing)
+ amin, amax = axis_limits(sp, axis[:letter])
# scale the limits
scale = axis[:scale]
@@ -238,9 +238,9 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
end
# 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 in (nothing, false) && return nothing
+ ticks in (:none, nothing, false) && return nothing
# treat :native ticks as :auto
ticks = ticks == :native ? :auto : ticks
@@ -251,7 +251,7 @@ function get_ticks(axis::Axis)
# discrete ticks...
n = length(dvals)
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
1:n
end
@@ -261,7 +261,7 @@ function get_ticks(axis::Axis)
(collect(0:pi/4:7pi/4), string.(0:45:315))
else
# compute optimal ticks and labels
- optimal_ticks_and_labels(axis)
+ optimal_ticks_and_labels(sp, axis)
end
elseif typeof(ticks) <: Union{AVec, Int}
if !isempty(dvals) && typeof(ticks) <: Int
@@ -269,7 +269,7 @@ function get_ticks(axis::Axis)
axis[:continuous_values][rng], dvals[rng]
else
# override ticks, but get the labels
- optimal_ticks_and_labels(axis, ticks)
+ optimal_ticks_and_labels(sp, axis, ticks)
end
elseif typeof(ticks) <: NTuple{2, Any}
# 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::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2])
-function get_minor_ticks(axis,ticks)
- axis[:minorticks] in (nothing, false) && !axis[:minorgrid] && return nothing
+function get_minor_ticks(sp, axis, ticks)
+ axis[:minorticks] in (:none, nothing, false) && !axis[:minorgrid] && return nothing
ticks = ticks[1]
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
if length(ticks) > 2
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)
should_widen = false
- if !is_2tuple(axis[:lims])
+ if !(is_2tuple(axis[:lims]) || axis[:lims] == :round)
for sp in axis.sps
for series in series_list(sp)
if series.plotattributes[:seriestype] in _widen_seriestypes
@@ -479,11 +479,13 @@ function round_limits(amin,amax)
end
# 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]
amin, amax = ex.emin, ex.emax
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])
amin = lims[1]
end
@@ -497,12 +499,12 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
if !isfinite(amin) && !isfinite(amax)
amin, amax = 0.0, 1.0
end
- if ispolar(axis.sps[1])
+ amin, amax = if ispolar(axis.sps[1])
if axis[:letter] == :x
amin, amax = 0, 2pi
elseif lims == :auto
#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
amin, amax
end
@@ -513,6 +515,32 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
else
amin, amax
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
# -------------------------------------------------------------------------
@@ -586,12 +614,12 @@ end
# compute the line segments which should be drawn for this axis
function axis_drawing_info(sp::Subplot)
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
- xmin, xmax = axis_limits(xaxis)
- ymin, ymax = axis_limits(yaxis)
- xticks = get_ticks(xaxis)
- yticks = get_ticks(yaxis)
- xminorticks = get_minor_ticks(xaxis,xticks)
- yminorticks = get_minor_ticks(yaxis,yticks)
+ xmin, xmax = axis_limits(sp, :x)
+ ymin, ymax = axis_limits(sp, :y)
+ xticks = get_ticks(sp, xaxis)
+ yticks = get_ticks(sp, yaxis)
+ xminorticks = get_minor_ticks(sp, xaxis, xticks)
+ yminorticks = get_minor_ticks(sp, yaxis, yticks)
xaxis_segs = Segments(2)
yaxis_segs = Segments(2)
xtick_segs = Segments(2)
@@ -614,14 +642,14 @@ function axis_drawing_info(sp::Subplot)
end
push!(xaxis_segs, (xmin, y1), (xmax, y1))
# don't show the 0 tick label for the origin framestyle
- if sp[:framestyle] == :origin && !(xticks in (nothing,false)) && length(xticks) > 1
+ if sp[:framestyle] == :origin && !(xticks in (:none, nothing, false)) && length(xticks) > 1
showticks = xticks[1] .!= 0
xticks = (xticks[1][showticks], xticks[2][showticks])
end
end
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin, y2), (xmax, y2)) # top spine
end
- if !(xaxis[:ticks] in (nothing, false))
+ if !(xaxis[:ticks] in (:none, nothing, false))
f = scalefunc(yaxis[:scale])
invf = invscalefunc(yaxis[:scale])
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
end
end
- if !(xaxis[:minorticks] in (nothing, false)) || xaxis[:minorgrid]
+ if !(xaxis[:minorticks] in (:none, nothing, false)) || xaxis[:minorgrid]
f = scalefunc(yaxis[:scale])
invf = invscalefunc(yaxis[:scale])
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
@@ -675,14 +703,14 @@ function axis_drawing_info(sp::Subplot)
end
push!(yaxis_segs, (x1, ymin), (x1, ymax))
# don't show the 0 tick label for the origin framestyle
- if sp[:framestyle] == :origin && !(yticks in (nothing,false)) && length(yticks) > 1
+ if sp[:framestyle] == :origin && !(yticks in (:none, nothing,false)) && length(yticks) > 1
showticks = yticks[1] .!= 0
yticks = (yticks[1][showticks], yticks[2][showticks])
end
end
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (x2, ymin), (x2, ymax)) # right spine
end
- if !(yaxis[:ticks] in (nothing, false))
+ if !(yaxis[:ticks] in (:none, nothing, false))
f = scalefunc(xaxis[:scale])
invf = invscalefunc(xaxis[:scale])
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
end
end
- if !(yaxis[:minorticks] in (nothing, false)) || yaxis[:minorgrid]
+ if !(yaxis[:minorticks] in (:none, nothing, false)) || yaxis[:minorgrid]
f = scalefunc(xaxis[:scale])
invf = invscalefunc(xaxis[:scale])
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
diff --git a/src/backends.jl b/src/backends.jl
index 6a5dea31..a95b2dd2 100644
--- a/src/backends.jl
+++ b/src/backends.jl
@@ -7,16 +7,20 @@ const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none)
const _backends = Symbol[]
const _initialized_backends = Set{Symbol}()
const _default_backends = (:none, :gr, :plotly)
-const _backendPackage = Dict{Symbol, Symbol}()
+
+const _backend_packages = Dict{Symbol, Symbol}()
"Returns a list of supported backends"
backends() = _backends
"Returns the name of the current backend"
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])
-backend_package_name(sym::Symbol) = sym in _default_backends ? :Plots : _backendPackage[sym]
+
+function _backend_instance(sym::Symbol)::AbstractBackend
+ haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
+end
+
+backend_package_name(sym::Symbol) = _backend_packages[sym]
macro init_backend(s)
package_str = string(s)
@@ -26,13 +30,13 @@ macro init_backend(s)
esc(quote
struct $T <: AbstractBackend end
export $sym
- $sym(; kw...) = (default(; kw...); backend(Symbol($str)))
+ $sym(; kw...) = (default(; kw...); backend($T()))
backend_name(::$T) = Symbol($str)
backend_package_name(pkg::$T) = backend_package_name(Symbol($str))
push!(_backends, Symbol($str))
_backendType[Symbol($str)] = $T
_backendSymbol[$T] = Symbol($str)
- _backendPackage[Symbol($str)] = Symbol($package_str)
+ _backend_packages[Symbol($str)] = Symbol($package_str)
# include("backends/" * $str * ".jl")
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
_create_backend_figure(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)
# account for the size/length/rotation of tick labels
-function tick_padding(axis::Axis)
- ticks = get_ticks(axis)
+function tick_padding(sp::Subplot, axis::Axis)
+ ticks = get_ticks(sp, axis)
if ticks == nothing
0mm
else
@@ -108,10 +106,10 @@ end
# to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot)
# 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)
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?
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", "")
if env_default != ""
sym = Symbol(lowercase(env_default))
if sym in _backends
- if sym in _initialized_backends
- return backend(sym)
- else
- @warn("You have set `PLOTS_DEFAULT_BACKEND=$env_default` but `$(backend_package_name(sym))` is not loaded.")
- end
+ backend(sym)
else
- @warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t",
+ @warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t" *
join(sort(_backends), "\n\t"))
+ _fallback_default_backend()
end
+ else
+ _fallback_default_backend()
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
@@ -174,13 +161,11 @@ end
Returns the current plotting package name. Initializes package on first call.
"""
function backend()
+ if CURRENT_BACKEND.sym == :none
+ _pick_default_backend()
+ end
- global CURRENT_BACKEND
- if CURRENT_BACKEND.sym == :none
- pickDefaultBackend()
- end
-
- CURRENT_BACKEND.pkg
+ CURRENT_BACKEND.pkg
end
"""
@@ -188,20 +173,13 @@ Set the plot backend.
"""
function backend(pkg::AbstractBackend)
sym = backend_name(pkg)
- if sym in _initialized_backends
- CURRENT_BACKEND.sym = backend_name(pkg)
- CURRENT_BACKEND.pkg = pkg
- else
- # try
- _initialize_backend(pkg)
- push!(_initialized_backends, sym)
- CURRENT_BACKEND.sym = backend_name(pkg)
- CURRENT_BACKEND.pkg = pkg
- # catch
- # add_backend(sym)
- # end
+ if !(sym in _initialized_backends)
+ _initialize_backend(pkg)
+ push!(_initialized_backends, sym)
end
- backend()
+ CURRENT_BACKEND.sym = sym
+ CURRENT_BACKEND.pkg = pkg
+ pkg
end
function backend(sym::Symbol)
@@ -209,15 +187,15 @@ function backend(sym::Symbol)
backend(_backend_instance(sym))
else
@warn("`:$sym` is not a supported backend.")
+ backend()
end
- backend()
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)
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
@@ -268,17 +246,11 @@ end
-# @init_backend Immerse
-# @init_backend Gadfly
@init_backend PyPlot
-# @init_backend Qwt
@init_backend UnicodePlots
-# @init_backend Winston
-# @init_backend Bokeh
@init_backend Plotly
@init_backend PlotlyJS
@init_backend GR
-@init_backend GLVisualize
@init_backend PGFPlots
@init_backend InspectDR
@init_backend HDF5
@@ -322,72 +294,191 @@ function _initialize_backend(pkg::AbstractBackend)
end
end
-function add_backend_string(pkg::AbstractBackend)
- sym = backend_package_name(pkg)
- """
- using Pkg
- Pkg.add("$sym")
- """
-end
+_initialize_backend(pkg::GRBackend) = nothing
+
+_initialize_backend(pkg::PlotlyBackend) = nothing
+
# ------------------------------------------------------------------------------
-# glvisualize
+# gr
-function _initialize_backend(::GLVisualizeBackend; kw...)
- @eval Main begin
- import GLVisualize, GeometryTypes, Reactive, GLAbstraction, GLWindow, Contour
- import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0, GLNormalMesh, SimpleRectangle, Point, Vec
- import FileIO, Images
- export GLVisualize
- import Reactive: Signal
- import GLAbstraction: Style
- import GLVisualize: visualize
- import Plots.GL
- import UnicodeFun
- end
-end
+const _gr_attr = merge_with_base_supported([
+ :annotations,
+ :background_color_legend, :background_color_inside, :background_color_outside,
+ :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
+ :foreground_color_text, :foreground_color_border,
+ :label,
+ :seriescolor, :seriesalpha,
+ :linecolor, :linestyle, :linewidth, :linealpha,
+ :markershape, :markercolor, :markersize, :markeralpha,
+ :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
+ :fillrange, :fillcolor, :fillalpha,
+ :bins,
+ :layout,
+ :title, :window_title,
+ :guide, :lims, :ticks, :scale, :flip,
+ :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)
- @eval Main begin
- import HDF5
- export HDF5
- end
-end
+const _plotly_attr = merge_with_base_supported([
+ :annotations,
+ :background_color_legend, :background_color_inside, :background_color_outside,
+ :foreground_color_legend, :foreground_color_guide,
+ :foreground_color_grid, :foreground_color_axis,
+ :foreground_color_text, :foreground_color_border,
+ :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)
- """
- using Pkg
- Pkg.add("PGFPlots")
- Pkg.build("PGFPlots")
- """
-end
+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, :vline] #vcat(_allMarkers, Shape)
+const _pgfplots_scale = [:identity, :ln, :log2, :log10]
# ------------------------------------------------------------------------------
# plotlyjs
-function add_backend_string(::PlotlyJSBackend)
- """
- using Pkg
- Pkg.add("PlotlyJS")
- Pkg.add("Rsvg")
- import Blink
- Blink.AtomShell.install()
- """
+function _initialize_backend(pkg::PlotlyJSBackend)
+ @eval Main begin
+ import PlotlyJS, ORCA
+ export PlotlyJS
+ 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
function _initialize_backend(::PyPlotBackend)
@eval Main begin
- import PyPlot, PyCall
- import LaTeXStrings
+ import PyPlot
export PyPlot
@@ -396,25 +487,177 @@ function _initialize_backend(::PyPlotBackend)
end
end
-function add_backend_string(::PyPlotBackend)
- """
- using Pkg
- Pkg.add("PyPlot")
- Pkg.add("PyCall")
- Pkg.add("LaTeXStrings")
- withenv("PYTHON" => "") do
- Pkg.build("PyCall")
- Pkg.build("PyPlot")
- end
- """
-end
+const _pyplot_attr = merge_with_base_supported([
+ :annotations,
+ :background_color_legend, :background_color_inside, :background_color_outside,
+ :foreground_color_grid, :foreground_color_legend, :foreground_color_title,
+ :foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
+ :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, :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
-function add_backend_string(::UnicodePlotsBackend)
- """
- using Pkg
- Pkg.add("UnicodePlots")
- Pkg.build("UnicodePlots")
- """
-end
+
+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]
+
+# ------------------------------------------------------------------------------
+# 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]
diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl
deleted file mode 100644
index 72288380..00000000
--- a/src/backends/glvisualize.jl
+++ /dev/null
@@ -1,1518 +0,0 @@
-#=
-TODO
- * move all gl_ methods to GLPlot
- * integrate GLPlot UI
- * clean up corner cases
- * find a cleaner way for extracting properties
- * polar plots
- * labes and axis
- * fix units in all visuals (e.g dotted lines, marker scale, surfaces)
-=#
-
-const _glvisualize_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,
- :window_title,
- :guide, :lims, :ticks, :scale, :flip, :rotation,
- :titlefontsize, :titlefontcolor,
- :legendfontsize, :legendfontcolor,
- :tickfontsize,
- :guidefontsize, :guidefontcolor,
- :grid, :gridalpha, :gridstyle, :gridlinewidth,
- :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,
- :hover,
- :framestyle,
- :tick_direction,
-])
-const _glvisualize_seriestype = [
- :path, :shape, :straightline,
- :scatter, :hexbin,
- :bar, :boxplot,
- :heatmap, :image, :volume,
- :contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
-]
-const _glvisualize_style = [:auto, :solid, :dash, :dot, :dashdot]
-const _glvisualize_marker = _allMarkers
-const _glvisualize_scale = [:identity, :ln, :log2, :log10]
-
-slice_arg(img::Matrix{C}, idx::Int) where {C<:Colorant} = img
-is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true
-is_marker_supported(::GLVisualizeBackend, shape::Union{Vector{Matrix{C}}, Matrix{C}}) where {C<:Colorant} = true
-is_marker_supported(::GLVisualizeBackend, shape::Shape) = true
-GL = Plots
-
-# ---------------------------------------------------------------------------
-
-# initialize the figure/window
-# function _create_backend_figure(plt::Plot{GLVisualizeBackend})
-# # init a screen
-#
-# GLPlot.init()
-# end
-const _glplot_deletes = []
-
-
-function get_plot_screen(list::Vector, name, result = [])
- for elem in list
- get_plot_screen(elem, name, result)
- end
- return result
-end
-function get_plot_screen(screen, name, result = [])
- if screen.name == name
- push!(result, screen)
- return result
- end
- get_plot_screen(screen.children, name, result)
-end
-
-function create_window(plt::Plot{GLVisualizeBackend}, visible)
- name = Symbol("__Plots.jl")
- # make sure we have any screen open
- if isempty(GLVisualize.get_screens())
- # create a fresh, new screen
- parent_screen = GLVisualize.glscreen(
- "Plots",
- resolution = plt[:size],
- visible = visible
- )
- @async GLWindow.renderloop(parent_screen)
- GLVisualize.add_screen(parent_screen)
- end
- # now lets get ourselves a permanent Plotting screen
- plot_screens = get_plot_screen(GLVisualize.current_screen(), name)
- screen = if isempty(plot_screens) # no screen with `name`
- parent = GLVisualize.current_screen()
- screen = GLWindow.Screen(
- parent, area = map(GLWindow.zeroposition, parent.area),
- name = name
- )
- screen
- elseif length(plot_screens) == 1
- plot_screens[1]
- else
- # okay this is silly! Lets see if we can. There is an ID we could use
- # will not be fine for more than 255 screens though -.-.
- error("multiple Plot screens. Please don't use any screen with the name $name")
- end
- # Since we own this window, we can do deep cleansing
- empty!(screen)
- plt.o = screen
- GLWindow.set_visibility!(screen, visible)
- resize!(screen, plt[:size]...)
- screen
-end
-# ---------------------------------------------------------------------------
-
-const _gl_marker_map = KW(
- :rect => '■',
- :star5 => '★',
- :diamond => '◆',
- :hexagon => '⬢',
- :cross => '✚',
- :xcross => '❌',
- :utriangle => '▲',
- :dtriangle => '▼',
- :ltriangle => '◀',
- :rtriangle => '▶',
- :pentagon => '⬟',
- :octagon => '⯄',
- :star4 => '✦',
- :star6 => '🟋',
- :star8 => '✷',
- :vline => '┃',
- :hline => '━',
- :+ => '+',
- :x => 'x',
- :circle => '●'
-)
-
-function gl_marker(shape)
- shape
-end
-function gl_marker(shape::Shape)
- points = Point2f0[GeometryTypes.Vec{2, Float32}(p) for p in zip(shape.x, shape.y)]
- bb = GeometryTypes.AABB(points)
- mini, maxi = minimum(bb), maximum(bb)
- w3 = maxi-mini
- origin, width = Point2f0(mini[1], mini[2]), Point2f0(w3[1], w3[2])
- map!(p -> ((p - origin) ./ width) - 0.5f0, points, points) # normalize and center
- GeometryTypes.GLNormalMesh(points)
-end
-# create a marker/shape type
-function gl_marker(shape::Vector{Symbol})
- String(map(shape) do sym
- get(_gl_marker_map, sym, '●')
- end)
-end
-
-function gl_marker(shape::Symbol)
- if shape == :rect
- GeometryTypes.HyperRectangle(Vec2f0(0), Vec2f0(1))
- elseif shape == :circle || shape == :none
- GeometryTypes.HyperSphere(Point2f0(0), 1f0)
- elseif haskey(_gl_marker_map, shape)
- _gl_marker_map[shape]
- elseif haskey(_shapes, shape)
- gl_marker(_shapes[shape])
- else
- error("Shape $shape not supported by GLVisualize")
- end
-end
-
-function extract_limits(sp, plotattributes, kw_args)
- clims = sp[:clims]
- if is_2tuple(clims)
- if isfinite(clims[1]) && isfinite(clims[2])
- kw_args[:limits] = Vec2f0(clims)
- end
- end
- nothing
-end
-
-to_vec(::Type{T}, vec::T) where {T <: StaticArrays.StaticVector} = vec
-to_vec(::Type{T}, s::Number) where {T <: StaticArrays.StaticVector} = T(s)
-
-to_vec(::Type{T}, vec::StaticArrays.StaticVector{3}) where {T <: StaticArrays.StaticVector{2}} = T(vec[1], vec[2])
-to_vec(::Type{T}, vec::StaticArrays.StaticVector{2}) where {T <: StaticArrays.StaticVector{3}} = T(vec[1], vec[2], 0)
-
-to_vec(::Type{T}, vecs::AbstractVector) where {T <: StaticArrays.StaticVector} = map(x-> to_vec(T, x), vecs)
-
-function extract_marker(plotattributes, kw_args)
- dim = Plots.is3d(plotattributes) ? 3 : 2
- scaling = dim == 3 ? 0.003 : 2
- if haskey(plotattributes, :markershape)
- shape = plotattributes[:markershape]
- shape = gl_marker(shape)
- if shape != :none
- kw_args[:primitive] = shape
- end
- end
- dim = isa(kw_args[:primitive], GLVisualize.Sprites) ? 2 : 3
- if haskey(plotattributes, :markersize)
- msize = plotattributes[:markersize]
- kw_args[:scale] = to_vec(GeometryTypes.Vec{dim, Float32}, msize .* scaling)
- end
- if haskey(plotattributes, :offset)
- kw_args[:offset] = plotattributes[:offset]
- end
- # get the color
- key = :markercolor
- haskey(plotattributes, key) || return
- c = gl_color(plotattributes[key])
- if isa(c, AbstractVector) && plotattributes[:marker_z] != nothing
- extract_colornorm(plotattributes, kw_args)
- kw_args[:color] = nothing
- kw_args[:color_map] = c
- kw_args[:intensity] = convert(Vector{Float32}, plotattributes[:marker_z])
- else
- kw_args[:color] = c
- end
- key = :markerstrokecolor
- haskey(plotattributes, key) || return
- c = gl_color(plotattributes[key])
- if c != nothing
- if !(isa(c, Colorant) || (isa(c, Vector) && eltype(c) <: Colorant))
- error("Stroke Color not supported: $c")
- end
- kw_args[:stroke_color] = c
- kw_args[:stroke_width] = Float32(plotattributes[:markerstrokewidth])
- end
-end
-
-function _extract_surface(plotattributes::Plots.Surface)
- plotattributes.surf
-end
-function _extract_surface(plotattributes::AbstractArray)
- plotattributes
-end
-
-# TODO when to transpose??
-function extract_surface(plotattributes)
- map(_extract_surface, (plotattributes[:x], plotattributes[:y], plotattributes[:z]))
-end
-function topoints(::Type{P}, array) where P
- [P(x) for x in zip(array...)]
-end
-function extract_points(plotattributes)
- dim = is3d(plotattributes) ? 3 : 2
- array = if plotattributes[:seriestype] == :straightline
- straightline_data(plotattributes)
- elseif plotattributes[:seriestype] == :shape
- shape_data(plotattributes)
- else
- (plotattributes[:x], plotattributes[:y], plotattributes[:z])[1:dim]
- end
- topoints(Point{dim, Float32}, array)
-end
-function make_gradient(grad::Vector{C}) where C <: Colorant
- grad
-end
-function make_gradient(grad::ColorGradient)
- RGBA{Float32}[c for c in grad.colors]
-end
-make_gradient(c) = make_gradient(cgrad())
-
-function extract_any_color(plotattributes, kw_args)
- if plotattributes[:marker_z] == nothing
- c = scalar_color(plotattributes, :fill)
- extract_c(plotattributes, kw_args, :fill)
- if isa(c, Colorant)
- kw_args[:color] = c
- else
- kw_args[:color] = nothing
- kw_args[:color_map] = make_gradient(c)
- clims = plotattributes[:subplot][:clims]
- if Plots.is_2tuple(clims)
- if isfinite(clims[1]) && isfinite(clims[2])
- kw_args[:color_norm] = Vec2f0(clims)
- end
- elseif clims == :auto
- kw_args[:color_norm] = Vec2f0(ignorenan_extrema(plotattributes[:y]))
- end
- end
- else
- kw_args[:color] = nothing
- clims = plotattributes[:subplot][:clims]
- if Plots.is_2tuple(clims)
- if isfinite(clims[1]) && isfinite(clims[2])
- kw_args[:color_norm] = Vec2f0(clims)
- end
- elseif clims == :auto
- kw_args[:color_norm] = Vec2f0(ignorenan_extrema(plotattributes[:y]))
- else
- error("Unsupported limits: $clims")
- end
- kw_args[:intensity] = convert(Vector{Float32}, plotattributes[:marker_z])
- kw_args[:color_map] = gl_color_map(plotattributes, :marker)
- end
-end
-
-function extract_stroke(plotattributes, kw_args)
- extract_c(plotattributes, kw_args, :line)
- if haskey(plotattributes, :linewidth)
- kw_args[:thickness] = Float32(plotattributes[:linewidth] * 3)
- end
-end
-
-function extract_color(plotattributes, sym)
- plotattributes[Symbol("$(sym)color")]
-end
-
-gl_color(c::PlotUtils.ColorGradient) = c.colors
-gl_color(c::Vector{T}) where {T<:Colorant} = c
-gl_color(c::RGBA{Float32}) = c
-gl_color(c::Colorant) = RGBA{Float32}(c)
-
-function gl_color(tuple::Tuple)
- gl_color(tuple...)
-end
-
-# convert to RGBA
-function gl_color(c, a)
- c = convertColor(c, a)
- RGBA{Float32}(c)
-end
-function scalar_color(plotattributes, sym)
- gl_color(extract_color(plotattributes, sym))
-end
-
-function gl_color_map(plotattributes, sym)
- colors = extract_color(plotattributes, sym)
- _gl_color_map(colors)
-end
-function _gl_color_map(colors::PlotUtils.ColorGradient)
- colors.colors
-end
-function _gl_color_map(c)
- Plots.default_gradient()
-end
-
-
-
-dist(a, b) = abs(a-b)
-mindist(x, a, b) = NaNMath.min(dist(a, x), dist(b, x))
-
-function gappy(x, ps)
- n = length(ps)
- x <= first(ps) && return first(ps) - x
- for j=1:(n-1)
- p0 = ps[j]
- p1 = ps[NaNMath.min(j+1, n)]
- if p0 <= x && p1 >= x
- return mindist(x, p0, p1) * (isodd(j) ? 1 : -1)
- end
- end
- return last(ps) - x
-end
-function ticks(points, resolution)
- Float16[gappy(x, points) for x = range(first(points), stop=last(points), length=resolution)]
-end
-
-
-function insert_pattern!(points, kw_args)
- tex = GLAbstraction.Texture(ticks(points, 100), x_repeat=:repeat)
- kw_args[:pattern] = tex
- kw_args[:pattern_length] = Float32(last(points))
-end
-function extract_linestyle(plotattributes, kw_args)
- haskey(plotattributes, :linestyle) || return
- ls = plotattributes[:linestyle]
- lw = plotattributes[:linewidth]
- kw_args[:thickness] = Float32(lw)
- if ls == :dash
- points = [0.0, lw, 2lw, 3lw, 4lw]
- insert_pattern!(points, kw_args)
- elseif ls == :dot
- tick, gap = lw/2, lw/4
- points = [0.0, tick, tick+gap, 2tick+gap, 2tick+2gap]
- insert_pattern!(points, kw_args)
- elseif ls == :dashdot
- dtick, dgap = lw, lw
- ptick, pgap = lw/2, lw/4
- points = [0.0, dtick, dtick+dgap, dtick+dgap+ptick, dtick+dgap+ptick+pgap]
- insert_pattern!(points, kw_args)
- elseif ls == :dashdotdot
- dtick, dgap = lw, lw
- ptick, pgap = lw/2, lw/4
- points = [0.0, dtick, dtick+dgap, dtick+dgap+ptick, dtick+dgap+ptick+pgap, dtick+dgap+ptick+pgap+ptick, dtick+dgap+ptick+pgap+ptick+pgap]
- insert_pattern!(points, kw_args)
- end
- extract_c(plotattributes, kw_args, :line)
- nothing
-end
-
-function hover(to_hover::Vector, to_display, window)
- hover(to_hover[], to_display, window)
-end
-
-function get_cam(x)
- if isa(x, GLAbstraction.Context)
- return get_cam(x.children)
- elseif isa(x, Vector)
- return get_cam(first(x))
- elseif isa(x, GLAbstraction.RenderObject)
- return x[:preferred_camera]
- end
-end
-
-
-function hover(to_hover, to_display, window)
- if isa(to_hover, GLAbstraction.Context)
- return hover(to_hover.children, to_display, window)
- end
- area = map(window.inputs[:mouseposition]) do mp
- SimpleRectangle{Int}(round(Int, mp+10)..., 100, 70)
- end
- mh = GLWindow.mouse2id(window)
- popup = GLWindow.Screen(
- window,
- hidden = map(mh-> !(mh.id == to_hover.id), mh),
- area = area,
- stroke = (2f0, RGBA(0f0, 0f0, 0f0, 0.8f0))
- )
- cam = get!(popup.cameras, :perspective) do
- GLAbstraction.PerspectiveCamera(
- popup.inputs, Vec3f0(3), Vec3f0(0),
- keep = Signal(false),
- theta = Signal(Vec3f0(0)), trans = Signal(Vec3f0(0))
- )
- end
-
- map(enumerate(to_display)) do id
- i,d = id
- robj = visualize(d)
- viewit = Reactive.droprepeats(map(mh->mh.id == to_hover.id && mh.index == i, mh))
- camtype = get_cam(robj)
- Reactive.preserve(map(viewit) do vi
- if vi
- empty!(popup)
- if camtype == :perspective
- cam.projectiontype.value = GLVisualize.PERSPECTIVE
- else
- cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
- end
- GLVisualize._view(robj, popup, camera = cam)
- bb = GLAbstraction.boundingbox(robj).value
- mini = minimum(bb)
- w = GeometryTypes.widths(bb)
- wborder = w * 0.08f0 #8 percent border
- bb = GeometryTypes.AABB{Float32}(mini - wborder, w + 2 * wborder)
- GLAbstraction.center!(cam, bb)
- end
- end)
- end
- nothing
-end
-
-function extract_extrema(plotattributes, kw_args)
- xmin, xmax = ignorenan_extrema(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y])
- kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(xmin, ymin, xmax-xmin, ymax-ymin)
- nothing
-end
-
-function extract_font(font, kw_args)
- kw_args[:family] = font.family
- kw_args[:relative_scale] = pointsize(font)
- kw_args[:color] = gl_color(font.color)
-end
-
-function extract_colornorm(plotattributes, kw_args)
- clims = plotattributes[:subplot][:clims]
- if Plots.is_2tuple(clims)
- if isfinite(clims[1]) && isfinite(clims[2])
- kw_args[:color_norm] = Vec2f0(clims)
- end
- elseif clims == :auto
- z = if haskey(plotattributes, :marker_z) && plotattributes[:marker_z] != nothing
- plotattributes[:marker_z]
- elseif haskey(plotattributes, :line_z) && plotattributes[:line_z] != nothing
- plotattributes[:line_z]
- elseif isa(plotattributes[:z], Plots.Surface)
- plotattributes[:z].surf
- else
- plotattributes[:y]
- end
- kw_args[:color_norm] = Vec2f0(ignorenan_extrema(z))
- kw_args[:intensity] = map(Float32, collect(z))
- end
-end
-
-function extract_gradient(plotattributes, kw_args, sym)
- key = Symbol("$(sym)color")
- haskey(plotattributes, key) || return
- c = make_gradient(plotattributes[key])
- kw_args[:color] = nothing
- extract_colornorm(plotattributes, kw_args)
- kw_args[:color_map] = c
- return
-end
-
-function extract_c(plotattributes, kw_args, sym)
- key = Symbol("$(sym)color")
- haskey(plotattributes, key) || return
- c = gl_color(plotattributes[key])
- kw_args[:color] = nothing
- kw_args[:color_map] = nothing
- kw_args[:color_norm] = nothing
- if (
- isa(c, AbstractVector) &&
- ((haskey(plotattributes, :marker_z) && plotattributes[:marker_z] != nothing) ||
- (haskey(plotattributes, :line_z) && plotattributes[:line_z] != nothing))
- )
- extract_colornorm(plotattributes, kw_args)
- kw_args[:color_map] = c
- else
- kw_args[:color] = c
- end
- return
-end
-
-function extract_stroke(plotattributes, kw_args, sym)
- key = Symbol("$(sym)strokecolor")
- haskey(plotattributes, key) || return
- c = gl_color(plotattributes[key])
- if c != nothing
- if !isa(c, Colorant)
- error("Stroke Color not supported: $c")
- end
- kw_args[:stroke_color] = c
- kw_args[:stroke_width] = Float32(plotattributes[Symbol("$(sym)strokewidth")]) * 2
- end
- return
-end
-
-
-
-function draw_grid_lines(sp, grid_segs, thickness, style, model, color)
-
- kw_args = Dict{Symbol, Any}(
- :model => model
- )
- plotattributes = Dict(
- :linestyle => style,
- :linewidth => Float32(thickness),
- :linecolor => color
- )
- Plots.extract_linestyle(plotattributes, kw_args)
- GL.gl_lines(map(Point2f0, grid_segs.pts), kw_args)
-end
-
-function align_offset(startpos, lastpos, atlas, rscale, font, align)
- xscale, yscale = GLVisualize.glyph_scale!('X', rscale)
- xmove = (lastpos-startpos)[1] + xscale
- if isa(align, GeometryTypes.Vec)
- return -Vec2f0(xmove, yscale) .* align
- elseif align == :top
- return -Vec2f0(xmove/2f0, yscale)
- elseif align == :right
- return -Vec2f0(xmove, yscale/2f0)
- else
- error("Align $align not known")
- end
-end
-
-
-function alignment2num(x::Symbol)
- (x in (:hcenter, :vcenter)) && return 0.5
- (x in (:left, :bottom)) && return 0.0
- (x in (:right, :top)) && return 1.0
- 0.0 # 0 default, or better to error?
-end
-
-function alignment2num(font::Plots.Font)
- Vec2f0(map(alignment2num, (font.halign, font.valign)))
-end
-
-pointsize(font) = font.pointsize * 2
-
-function draw_ticks(
- axis, ticks, isx, isorigin, lims, m, text = "",
- positions = Point2f0[], offsets=Vec2f0[]
- )
- sz = pointsize(tickfont(axis))
- atlas = GLVisualize.get_texture_atlas()
- font = GLVisualize.defaultfont()
-
- flip = axis[:flip]; mirror = axis[:mirror]
-
- align = if isx
- mirror ? :bottom : :top
- else
- mirror ? :left : :right
- end
- axis_gap = Point2f0(isx ? 0 : sz / 2, isx ? sz / 2 : 0)
- for (cv, dv) in zip(ticks...)
-
- x, y = cv, lims[1]
- xy = if isorigin
- isx ? (x, 0) : (0, x)
- else
- isx ? (x, y) : (y, x)
- end
- _pos = m * GeometryTypes.Vec4f0(xy[1], xy[2], 0, 1)
- startpos = Point2f0(_pos[1], _pos[2]) - axis_gap
- str = string(dv)
- # need to tag a new UnicodeFun version for this... also the numbers become
- # so small that it looks terrible -.-
- # _str = split(string(dv), "^")
- # if length(_str) == 2
- # _str[2] = UnicodeFun.to_superscript(_str[2])
- # end
- # str = join(_str, "")
- position = GLVisualize.calc_position(str, startpos, sz, font, atlas)
- offset = GLVisualize.calc_offset(str, sz, font, atlas)
- alignoff = align_offset(startpos, last(position), atlas, sz, font, align)
- map!(position, position) do pos
- pos .+ alignoff
- end
- append!(positions, position)
- append!(offsets, offset)
- text *= str
-
- end
- text, positions, offsets
-end
-
-function glvisualize_text(position, text, kw_args)
- text_align = alignment2num(text.font)
- startpos = Vec2f0(position)
- atlas = GLVisualize.get_texture_atlas()
- font = GLVisualize.defaultfont()
- rscale = kw_args[:relative_scale]
-
- position = GLVisualize.calc_position(text.str, startpos, rscale, font, atlas)
- offset = GLVisualize.calc_offset(text.str, rscale, font, atlas)
- alignoff = align_offset(startpos, last(position), atlas, rscale, font, text_align)
-
- map!(position, position) do pos
- pos .+ alignoff
- end
- kw_args[:position] = position
- kw_args[:offset] = offset
- kw_args[:scale_primitive] = true
- visualize(text.str, Style(:default), kw_args)
-end
-
-function text_model(font, pivot)
- pv = GeometryTypes.Vec3f0(pivot[1], pivot[2], 0)
- if font.rotation != 0.0
- rot = Float32(deg2rad(font.rotation))
- rotm = GLAbstraction.rotationmatrix_z(rot)
- return GLAbstraction.translationmatrix(pv)*rotm*GLAbstraction.translationmatrix(-pv)
- else
- eye(GeometryTypes.Mat4f0)
- end
-end
-function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, area)
- xticks, yticks, xspine_segs, yspine_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs = Plots.axis_drawing_info(sp)
- xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
-
- xgc = Colors.color(Plots.gl_color(xaxis[:foreground_color_grid]))
- ygc = Colors.color(Plots.gl_color(yaxis[:foreground_color_grid]))
- axis_vis = []
- if xaxis[:grid]
- grid = draw_grid_lines(sp, xgrid_segs, xaxis[:gridlinewidth], xaxis[:gridstyle], model, RGBA(xgc, xaxis[:gridalpha]))
- push!(axis_vis, grid)
- end
- if yaxis[:grid]
- grid = draw_grid_lines(sp, ygrid_segs, yaxis[:gridlinewidth], yaxis[:gridstyle], model, RGBA(ygc, yaxis[:gridalpha]))
- push!(axis_vis, grid)
- end
- if xaxis[:minorgrid]
- minorgrid = draw_minorgrid_lines(sp, xminorgrid_segs, xaxis[:minorgridlinewidth], xaxis[:minorgridstyle], model, RGBA(xgc, xaxis[:minorgridalpha]))
- push!(axis_vis, minorgrid)
- end
- if yaxis[:minorgrid]
- minorgrid = draw_minorgrid_lines(sp, yminorgrid_segs, yaxis[:minorgridlinewidth], yaxis[:minorgridstyle], model, RGBA(ygc, yaxis[:minorgridalpha]))
- push!(axis_vis, minorgrid)
- end
-
- xac = Colors.color(Plots.gl_color(xaxis[:foreground_color_axis]))
- yac = Colors.color(Plots.gl_color(yaxis[:foreground_color_axis]))
- if alpha(xaxis[:foreground_color_axis]) > 0
- spine = draw_grid_lines(sp, xspine_segs, 1f0, :solid, model, RGBA(xac, 1.0f0))
- push!(axis_vis, spine)
- end
- if alpha(yaxis[:foreground_color_axis]) > 0
- spine = draw_grid_lines(sp, yspine_segs, 1f0, :solid, model, RGBA(yac, 1.0f0))
- push!(axis_vis, spine)
- end
- if sp[:framestyle] in (:zerolines, :grid)
- if alpha(xaxis[:foreground_color_grid]) > 0
- spine = draw_grid_lines(sp, xtick_segs, 1f0, :solid, model, RGBA(xgc, xaxis[:gridalpha]))
- push!(axis_vis, spine)
- end
- if alpha(yaxis[:foreground_color_grid]) > 0
- spine = draw_grid_lines(sp, ytick_segs, 1f0, :solid, model, RGBA(ygc, yaxis[:gridalpha]))
- push!(axis_vis, spine)
- end
- else
- if alpha(xaxis[:foreground_color_axis]) > 0
- spine = draw_grid_lines(sp, xtick_segs, 1f0, :solid, model, RGBA(xac, 1.0f0))
- push!(axis_vis, spine)
- end
- if alpha(yaxis[:foreground_color_axis]) > 0
- spine = draw_grid_lines(sp, ytick_segs, 1f0, :solid, model, RGBA(yac, 1.0f0))
- push!(axis_vis, spine)
- end
- end
- fcolor = Plots.gl_color(xaxis[:foreground_color_axis])
-
- xlim = Plots.axis_limits(xaxis)
- ylim = Plots.axis_limits(yaxis)
-
- if !(xaxis[:ticks] in (nothing, false, :none)) && !(sp[:framestyle] == :none) && xaxis[:showaxis]
- ticklabels = map(model) do m
- mirror = xaxis[:mirror]
- t, positions, offsets = draw_ticks(xaxis, xticks, true, sp[:framestyle] == :origin, ylim, m)
- end
- kw_args = Dict{Symbol, Any}(
- :position => map(x-> x[2], ticklabels),
- :offset => map(last, ticklabels),
- :color => fcolor,
- :relative_scale => pointsize(tickfont(xaxis)),
- :scale_primitive => false
- )
- push!(axis_vis, visualize(map(first, ticklabels), Style(:default), kw_args))
- end
-
- if !(yaxis[:ticks] in (nothing, false, :none)) && !(sp[:framestyle] == :none) && yaxis[:showaxis]
- ticklabels = map(model) do m
- mirror = yaxis[:mirror]
- t, positions, offsets = draw_ticks(yaxis, yticks, false, sp[:framestyle] == :origin, xlim, m)
- end
- kw_args = Dict{Symbol, Any}(
- :position => map(x-> x[2], ticklabels),
- :offset => map(last, ticklabels),
- :color => fcolor,
- :relative_scale => pointsize(tickfont(xaxis)),
- :scale_primitive => false
- )
- push!(axis_vis, visualize(map(first, ticklabels), Style(:default), kw_args))
- end
-
- xbc = Colors.color(Plots.gl_color(xaxis[:foreground_color_border]))
- ybc = Colors.color(Plots.gl_color(yaxis[:foreground_color_border]))
- intensity = sp[:framestyle] == :semi ? 0.5f0 : 1.0f0
- if sp[:framestyle] in (:box, :semi)
- xborder = draw_grid_lines(sp, xborder_segs, intensity, :solid, model, RGBA(xbc, intensity))
- yborder = draw_grid_lines(sp, yborder_segs, intensity, :solid, model, RGBA(ybc, intensity))
- push!(axis_vis, xborder, yborder)
- end
-
- area_w = GeometryTypes.widths(area)
- if sp[:title] != ""
- tf = titlefont(sp)
- font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, tf.rotation, tf.color)
- xy = Point2f0(area.w/2, area_w[2] + pointsize(tf)/2)
- kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
- extract_font(font, kw)
- t = PlotText(sp[:title], font)
- push!(axis_vis, glvisualize_text(xy, t, kw))
- end
- if xaxis[:guide] != ""
- tf = guidefont(xaxis)
- xy = Point2f0(area.w/2, - pointsize(tf)/2)
- font = Plots.Font(tf.family, tf.pointsize, :hcenter, :bottom, tf.rotation, tf.color)
- kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
- t = PlotText(xaxis[:guide], font)
- extract_font(font, kw)
- push!(axis_vis, glvisualize_text(xy, t, kw))
- end
-
- if yaxis[:guide] != ""
- tf = guidefont(yaxis)
- font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, 90f0, tf.color)
- xy = Point2f0(-pointsize(tf)/2, area.h/2)
- kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
- t = PlotText(yaxis[:guide], font)
- extract_font(font, kw)
- push!(axis_vis, glvisualize_text(xy, t, kw))
- end
-
- axis_vis
-end
-
-function gl_draw_axes_3d(sp, model)
- x = Plots.axis_limits(sp[:xaxis])
- y = Plots.axis_limits(sp[:yaxis])
- z = Plots.axis_limits(sp[:zaxis])
-
- min = Vec3f0(x[1], y[1], z[1])
- visualize(
- GeometryTypes.AABB{Float32}(min, Vec3f0(x[2], y[2], z[2])-min),
- :grid, model=model
- )
-end
-
-function gl_bar(plotattributes, kw_args)
- x, y = plotattributes[:x], plotattributes[:y]
- nx, ny = length(x), length(y)
- axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
- cv = [discrete_value!(axis, xi)[1] for xi=x]
- x = if nx == ny
- cv
- elseif nx == ny + 1
- 0.5diff(cv) + cv[1:end-1]
- else
- error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
- end
- if haskey(kw_args, :stroke_width) # stroke is inside for bars
- #kw_args[:stroke_width] = -kw_args[:stroke_width]
- end
- # compute half-width of bars
- bw = nothing
- hw = if bw == nothing
- ignorenan_mean(diff(x))
- else
- Float64[_cycle(bw,i)*0.5 for i=1:length(x)]
- end
-
- # make fillto a vector... default fills to 0
- fillto = plotattributes[:fillrange]
- if fillto == nothing
- fillto = 0
- end
- # create the bar shapes by adding x/y segments
- positions, scales = Array{Point2f0}(undef, ny), Array{Vec2f0}(undef, ny)
- m = Reactive.value(kw_args[:model])
- sx, sy = m[1,1], m[2,2]
- for i=1:ny
- center = x[i]
- hwi = abs(_cycle(hw,i)); yi = y[i]; fi = _cycle(fillto,i)
- if Plots.isvertical(plotattributes)
- sz = (hwi*sx, yi*sy)
- else
- sz = (yi*sx, hwi*2*sy)
- end
- positions[i] = (center-hwi*0.5, fi)
- scales[i] = sz
- end
-
- kw_args[:scale] = scales
- kw_args[:offset] = Vec2f0(0)
- visualize((GLVisualize.RECTANGLE, positions), Style(:default), kw_args)
- #[]
-end
-
-const _box_halfwidth = 0.4
-
-notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
-
-function gl_boxplot(plotattributes, kw_args)
- kwbox = copy(kw_args)
- range = 1.5; notch = false
- x, y = plotattributes[:x], plotattributes[:y]
- glabels = sort(collect(unique(x)))
- warning = false
- outliers_x, outliers_y = zeros(0), zeros(0)
-
- box_pos = Point2f0[]
- box_scale = Vec2f0[]
- outliers = Point2f0[]
- t_segments = Point2f0[]
- m = Reactive.value(kw_args[:model])
- sx, sy = m[1,1], m[2,2]
- for (i,glabel) in enumerate(glabels)
- # filter y
- values = y[filter(i -> _cycle(x,i) == glabel, 1:length(y))]
- # compute quantiles
- q1,q2,q3,q4,q5 = quantile(values, range(0, stop=1, length=5))
- # notch
- n = Plots.notch_width(q2, q4, length(values))
- # warn on inverted notches?
- if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
- @warn("Boxplot's notch went outside hinges. Set notch to false.")
- warning = true # Show the warning only one time
- end
-
- # make the shape
- center = Plots.discrete_value!(plotattributes[:subplot][:xaxis], glabel)[1]
- hw = plotattributes[:bar_width] == nothing ? Plots._box_halfwidth*2 : _cycle(plotattributes[:bar_width], i)
- l, m, r = center - hw/2, center, center + hw/2
-
- # internal nodes for notches
- L, R = center - 0.5 * hw, center + 0.5 * hw
- # outliers
- if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
- limit = range*(q4-q2)
- inside = Float64[]
- for value in values
- if (value < (q2 - limit)) || (value > (q4 + limit))
- push!(outliers, (center, value))
- else
- push!(inside, value)
- end
- end
- # change q1 and q5 to show outliers
- # using maximum and minimum values inside the limits
- q1, q5 = ignorenan_extrema(inside)
- end
- # Box
- if notch
- push!(t_segments, (m, q1), (l, q1), (r, q1), (m, q1), (m, q2))# lower T
- push!(box_pos, (l, q2));push!(box_scale, (hw*sx, n*sy)) # lower box
- push!(box_pos, (l, q4));push!(box_scale, (hw*sx, n*sy)) # upper box
- push!(t_segments, (m, q5), (l, q5), (r, q5), (m, q5), (m, q4))# upper T
-
- else
- push!(t_segments, (m, q2), (m, q1), (l, q1), (r, q1))# lower T
- push!(box_pos, (l, q2)); push!(box_scale, (hw*sx, (q3-q2)*sy)) # lower box
- push!(box_pos, (l, q4)); push!(box_scale, (hw*sx, (q3-q4)*sy)) # upper box
- push!(t_segments, (m, q4), (m, q5), (r, q5), (l, q5))# upper T
- end
- end
- kwbox = Dict{Symbol, Any}(
- :scale => box_scale,
- :model => kw_args[:model],
- :offset => Vec2f0(0),
- )
- extract_marker(plotattributes, kw_args)
- outlier_kw = Dict(
- :model => kw_args[:model],
- :color => scalar_color(plotattributes, :fill),
- :stroke_width => Float32(plotattributes[:markerstrokewidth]),
- :stroke_color => scalar_color(plotattributes, :markerstroke),
- )
- lines_kw = Dict(
- :model => kw_args[:model],
- :stroke_width => plotattributes[:linewidth],
- :stroke_color => scalar_color(plotattributes, :fill),
- )
- vis1 = GLVisualize.visualize((GLVisualize.RECTANGLE, box_pos), Style(:default), kwbox)
- vis2 = GLVisualize.visualize((GLVisualize.CIRCLE, outliers), Style(:default), outlier_kw)
- vis3 = GLVisualize.visualize(t_segments, Style(:linesegment), lines_kw)
- [vis1, vis2, vis3]
-end
-
-
-# ---------------------------------------------------------------------------
-function gl_viewport(bb, rect)
- l, b, bw, bh = bb
- rw, rh = rect.w, rect.h
- GLVisualize.SimpleRectangle(
- round(Int, rw * l),
- round(Int, rh * b),
- round(Int, rw * bw),
- round(Int, rh * bh)
- )
-end
-
-function to_modelmatrix(rect, subrect, rel_plotarea, sp)
- xmin, xmax = Plots.axis_limits(sp[:xaxis])
- ymin, ymax = Plots.axis_limits(sp[:yaxis])
- mini, maxi = Vec3f0(xmin, ymin, 0), Vec3f0(xmax, ymax, 1)
- if Plots.is3d(sp)
- zmin, zmax = Plots.axis_limits(sp[:zaxis])
- mini, maxi = Vec3f0(xmin, ymin, zmin), Vec3f0(xmax, ymax, zmax)
- s = Vec3f0(1) ./ (maxi-mini)
- return GLAbstraction.scalematrix(s)*GLAbstraction.translationmatrix(-mini)
- end
- l, b, bw, bh = rel_plotarea
- w, h = rect.w*bw, rect.h*bh
- x, y = rect.w*l - subrect.x, rect.h*b - subrect.y
- t = -mini
- s = Vec3f0(w, h, 1) ./ (maxi-mini)
- GLAbstraction.translationmatrix(Vec3f0(x,y,0))*GLAbstraction.scalematrix(s)*GLAbstraction.translationmatrix(t)
-end
-
-# ----------------------------------------------------------------
-
-
-function scale_for_annotations!(series::Series, scaletype::Symbol = :pixels)
- anns = series[:series_annotations]
- if anns != nothing && anns.baseshape != nothing
- # we use baseshape to overwrite the markershape attribute
- # with a list of custom shapes for each
- msw, msh = anns.scalefactor
- offsets = Array{Vec2f0}(undef, length(anns.strs))
- series[:markersize] = map(1:length(anns.strs)) do i
- str = _cycle(anns.strs, i)
- # get the width and height of the string (in mm)
- sw, sh = text_size(str, anns.font.pointsize)
-
- # how much to scale the base shape?
- # note: it's a rough assumption that the shape fills the unit box [-1,-1,1,1],
- # so we scale the length-2 shape by 1/2 the total length
- xscale = 0.5to_pixels(sw) * 1.8
- yscale = 0.5to_pixels(sh) * 1.8
-
- # we save the size of the larger direction to the markersize list,
- # and then re-scale a copy of baseshape to match the w/h ratio
- s = Vec2f0(xscale, yscale)
- offsets[i] = -s
- s
- end
- series[:offset] = offsets
- end
- return
-end
-
-
-
-
-function _display(plt::Plot{GLVisualizeBackend}, visible = true)
- screen = create_window(plt, visible)
- sw, sh = plt[:size]
- sw, sh = sw*px, sh*px
-
- for sp in plt.subplots
- _3d = Plots.is3d(sp)
- # camera = :perspective
- # initialize the sub-screen for this subplot
- rel_bbox = Plots.bbox_to_pcts(bbox(sp), sw, sh)
- sub_area = map(screen.area) do rect
- Plots.gl_viewport(rel_bbox, rect)
- end
- c = plt[:background_color_outside]
- sp_screen = GLVisualize.Screen(
- screen, color = c,
- area = sub_area
- )
- sp.o = sp_screen
- cam = get!(sp_screen.cameras, :perspective) do
- inside = sp_screen.inputs[:mouseinside]
- theta = _3d ? nothing : Signal(Vec3f0(0)) # surpress rotation for 2D (nothing will get usual rotation controle)
- GLAbstraction.PerspectiveCamera(
- sp_screen.inputs, Vec3f0(3), Vec3f0(0),
- keep = inside, theta = theta
- )
- end
-
- rel_plotarea = Plots.bbox_to_pcts(plotarea(sp), sw, sh)
- model_m = map(Plots.to_modelmatrix,
- screen.area, sub_area,
- Signal(rel_plotarea), Signal(sp)
- )
-
- # loop over the series and add them to the subplot
- if !_3d
- axis = gl_draw_axes_2d(sp, model_m, Reactive.value(sub_area))
- GLVisualize._view(axis, sp_screen, camera=:perspective)
- cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
- Reactive.run_till_now() # make sure Reactive.push! arrives
- GLAbstraction.center!(cam,
- GeometryTypes.AABB(
- Vec3f0(-20), Vec3f0((GeometryTypes.widths(sp_screen)+40f0)..., 1)
- )
- )
- else
- axis = gl_draw_axes_3d(sp, model_m)
- GLVisualize._view(axis, sp_screen, camera=:perspective)
- push!(cam.projectiontype, GLVisualize.PERSPECTIVE)
- end
- for series in Plots.series_list(sp)
-
- plotattributes = series.plotattributes
- st = plotattributes[:seriestype]; kw_args = KW() # exctract kw
-
- kw_args[:model] = model_m # add transformation
- if !_3d # 3D is treated differently, since we need boundingboxes for camera
- kw_args[:boundingbox] = nothing # don't calculate bb, we dont need it
- end
- scale_for_annotations!(series)
- if st in (:surface, :wireframe)
- x, y, z = extract_surface(plotattributes)
- extract_gradient(plotattributes, kw_args, :fill)
- z = Plots.transpose_z(plotattributes, z, false)
- if isa(x, AbstractMatrix) && isa(y, AbstractMatrix)
- x, y = Plots.transpose_z(plotattributes, x, false), Plots.transpose_z(plotattributes, y, false)
- end
- if st == :wireframe
- kw_args[:wireframe] = true
- kw_args[:stroke_color] = plotattributes[:linecolor]
- kw_args[:stroke_width] = Float32(plotattributes[:linewidth]/100f0)
- end
- vis = GL.gl_surface(x, y, z, kw_args)
- elseif (st in (:path, :path3d, :straightline)) && plotattributes[:linewidth] > 0
- kw = copy(kw_args)
- points = Plots.extract_points(plotattributes)
- extract_linestyle(plotattributes, kw)
- vis = GL.gl_lines(points, kw)
- if plotattributes[:markershape] != :none
- kw = copy(kw_args)
- extract_stroke(plotattributes, kw)
- extract_marker(plotattributes, kw)
- vis2 = GL.gl_scatter(copy(points), kw)
- vis = [vis; vis2]
- end
- if plotattributes[:fillrange] != nothing
- kw = copy(kw_args)
- fr = plotattributes[:fillrange]
- ps = if all(x-> x >= 0, diff(plotattributes[:x])) # if is monotonic
- vcat(points, Point2f0[(points[i][1], _cycle(fr, i)) for i=length(points):-1:1])
- else
- points
- end
- extract_c(plotattributes, kw, :fill)
- vis = [GL.gl_poly(ps, kw), vis]
- end
- elseif st in (:scatter, :scatter3d) #|| plotattributes[:markershape] != :none
- extract_marker(plotattributes, kw_args)
- points = extract_points(plotattributes)
- vis = GL.gl_scatter(points, kw_args)
- elseif st == :shape
- extract_c(plotattributes, kw_args, :fill)
- vis = GL.gl_shape(plotattributes, kw_args)
- elseif st == :contour
- x,y,z = extract_surface(plotattributes)
- z = transpose_z(plotattributes, z, false)
- extract_extrema(plotattributes, kw_args)
- extract_gradient(plotattributes, kw_args, :fill)
- kw_args[:fillrange] = plotattributes[:fillrange]
- kw_args[:levels] = plotattributes[:levels]
-
- vis = GL.gl_contour(x,y,z, kw_args)
- elseif st == :heatmap
- x,y,z = extract_surface(plotattributes)
- extract_gradient(plotattributes, kw_args, :fill)
- extract_extrema(plotattributes, kw_args)
- extract_limits(sp, plotattributes, kw_args)
- vis = GL.gl_heatmap(x,y,z, kw_args)
- elseif st == :bar
- extract_c(plotattributes, kw_args, :fill)
- extract_stroke(plotattributes, kw_args, :marker)
- vis = gl_bar(plotattributes, kw_args)
- elseif st == :image
- extract_extrema(plotattributes, kw_args)
- vis = GL.gl_image(plotattributes[:z].surf, kw_args)
- elseif st == :boxplot
- extract_c(plotattributes, kw_args, :fill)
- vis = gl_boxplot(plotattributes, kw_args)
- elseif st == :volume
- volume = plotattributes[:y]
- _plotattributes = copy(plotattributes)
- _plotattributes[:y] = 0:1
- _plotattributes[:x] = 0:1
- kw_args = KW()
- extract_gradient(_plotattributes, kw_args, :fill)
- vis = visualize(volume.v, Style(:default), kw_args)
- else
- error("failed to display plot type $st")
- end
-
- isa(vis, Array) && isempty(vis) && continue # nothing to see here
-
- GLVisualize._view(vis, sp_screen, camera=:perspective)
- if haskey(plotattributes, :hover) && !(plotattributes[:hover] in (false, :none, nothing))
- hover(vis, plotattributes[:hover], sp_screen)
- end
- if isdefined(:GLPlot) && isdefined(Main.GLPlot, :(register_plot!))
- del_signal = Main.GLPlot.register_plot!(vis, sp_screen, create_gizmo=false)
- append!(_glplot_deletes, del_signal)
- end
- anns = series[:series_annotations]
- for (x, y, str, font) in EachAnn(anns, plotattributes[:x], plotattributes[:y])
- txt_args = Dict{Symbol, Any}(:model => eye(GLAbstraction.Mat4f0))
- x, y = Reactive.value(model_m) * GeometryTypes.Vec{4, Float32}(x, y, 0, 1)
- extract_font(font, txt_args)
- t = glvisualize_text(Point2f0(x, y), PlotText(str, font), txt_args)
- GLVisualize._view(t, sp_screen, camera = :perspective)
- end
-
- end
- generate_legend(sp, sp_screen, model_m)
- if _3d
- GLAbstraction.center!(sp_screen)
- end
- GLAbstraction.post_empty()
- yield()
- end
-end
-
-function _show(io::IO, ::MIME"image/png", plt::Plot{GLVisualizeBackend})
- _display(plt, false)
- GLWindow.poll_glfw()
- if Base.n_avail(Reactive._messages) > 0
- Reactive.run_till_now()
- end
- yield()
- GLWindow.render_frame(GLWindow.rootscreen(plt.o))
- GLWindow.swapbuffers(plt.o)
- buff = GLWindow.screenbuffer(plt.o)
- png = map(RGB{U8}, buff)
- FileIO.save(FileIO.Stream(FileIO.DataFormat{:PNG}, io), png)
-end
-
-
-function gl_image(img, kw_args)
- rect = kw_args[:primitive]
- kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(rect.x, rect.y, rect.w, rect.h)
- visualize(img, Style(:default), kw_args)
-end
-
-function handle_segment(lines, line_segments, points::Vector{P}, segment) where P
- (isempty(segment) || length(segment) < 2) && return
- if length(segment) == 2
- append!(line_segments, view(points, segment))
- elseif length(segment) == 3
- p = view(points, segment)
- push!(line_segments, p[1], p[2], p[2], p[3])
- else
- append!(lines, view(points, segment))
- push!(lines, P(NaN))
- end
-end
-
-function gl_lines(points, kw_args)
- result = []
- isempty(points) && return result
- P = eltype(points)
- lines = P[]
- line_segments = P[]
- last = 1
- for (i,p) in enumerate(points)
- if isnan(p) || i==length(points)
- _i = isnan(p) ? i-1 : i
- handle_segment(lines, line_segments, points, last:_i)
- last = i+1
- end
- end
- if !isempty(lines)
- pop!(lines) # remove last NaN
- push!(result, visualize(lines, Style(:lines), kw_args))
- end
- if !isempty(line_segments)
- push!(result, visualize(line_segments, Style(:linesegment), kw_args))
- end
- return result
-end
-
-function gl_shape(plotattributes, kw_args)
- points = Plots.extract_points(plotattributes)
- result = []
- for rng in iter_segments(plotattributes[:x], plotattributes[:y])
- ps = points[rng]
- meshes = gl_poly(ps, kw_args)
- append!(result, meshes)
- end
- result
-end
-
-
-
-function gl_scatter(points, kw_args)
- prim = get(kw_args, :primitive, GeometryTypes.Circle)
- if isa(prim, GLNormalMesh)
- if haskey(kw_args, :model)
- p = get(kw_args, :perspective, eye(GeometryTypes.Mat4f0))
- kw_args[:scale] = GLAbstraction.const_lift(kw_args[:model], kw_args[:scale], p) do m, sc, p
- s = Vec3f0(m[1,1], m[2,2], m[3,3])
- ps = Vec3f0(p[1,1], p[2,2], p[3,3])
- r = sc ./ (s .* ps)
- r
- end
- end
- else # 2D prim
- kw_args[:scale] = to_vec(Vec2f0, kw_args[:scale])
- end
-
- if haskey(kw_args, :stroke_width)
- s = Reactive.value(kw_args[:scale])
- sw = kw_args[:stroke_width]
- if sw*5 > _cycle(Reactive.value(s), 1)[1] # restrict marker stroke to 1/10th of scale (and handle arrays of scales)
- kw_args[:stroke_width] = s[1] / 5f0
- end
- end
- kw_args[:scale_primitive] = false
- if isa(prim, String)
- kw_args[:position] = points
- if !isa(kw_args[:scale], Vector) # if not vector, we can assume it's relative scale
- kw_args[:relative_scale] = kw_args[:scale]
- delete!(kw_args, :scale)
- end
- return visualize(prim, Style(:default), kw_args)
- end
-
- visualize((prim, points), Style(:default), kw_args)
-end
-
-
-function gl_poly(points, kw_args)
- last(points) == first(points) && pop!(points)
- polys = GeometryTypes.split_intersections(points)
- result = []
- for poly in polys
- mesh = GLNormalMesh(poly) # make polygon
- if !isempty(GeometryTypes.faces(mesh)) # check if polygonation has any faces
- push!(result, GLVisualize.visualize(mesh, Style(:default), kw_args))
- else
- @warn("Couldn't draw the polygon: $points")
- end
- end
- result
-end
-
-
-
-
-function gl_surface(x,y,z, kw_args)
- if isa(x, AbstractRange) && isa(y, AbstractRange)
- main = z
- kw_args[:ranges] = (x, y)
- else
- if isa(x, AbstractMatrix) && isa(y, AbstractMatrix)
- main = map(s->map(Float32, s), (x, y, z))
- elseif isa(x, AbstractVector) || isa(y, AbstractVector)
- x = Float32[x[i] for i = 1:size(z,1), j = 1:size(z,2)]
- y = Float32[y[j] for i = 1:size(z,1), j = 1:size(z,2)]
- main = (x, y, map(Float32, z))
- else
- error("surface: combination of types not supported: $(typeof(x)) $(typeof(y)) $(typeof(z))")
- end
- if get(kw_args, :wireframe, false)
- points = map(Point3f0, zip(vec(x), vec(y), vec(z)))
- faces = Cuint[]
- idx = (i,j) -> CartesianIndices(size(z), i, j) - 1
- for i=1:size(z,1), j=1:size(z,2)
-
- i < size(z,1) && push!(faces, idx(i, j), idx(i+1, j))
- j < size(z,2) && push!(faces, idx(i, j), idx(i, j+1))
-
- end
- color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1))
- kw_args[:color] = color
- kw_args[:thickness] = Float32(get(kw_args, :stroke_width, 1f0))
- kw_args[:indices] = faces
- delete!(kw_args, :stroke_color)
- delete!(kw_args, :stroke_width)
-
- return visualize(points, Style(:linesegment), kw_args)
- end
- end
- return visualize(main, Style(:surface), kw_args)
-end
-
-
-function gl_contour(x, y, z, kw_args)
- if kw_args[:fillrange] != nothing
-
- delete!(kw_args, :intensity)
- I = GLVisualize.Intensity{Float32}
- main = [I(z[j,i]) for i=1:size(z, 2), j=1:size(z, 1)]
- return visualize(main, Style(:default), kw_args)
-
- else
- h = kw_args[:levels]
- T = eltype(z)
- levels = Contour.contours(map(T, x), map(T, y), z, h)
- result = Point2f0[]
- zmin, zmax = get(kw_args, :limits, Vec2f0(ignorenan_extrema(z)))
- cmap = get(kw_args, :color_map, get(kw_args, :color, RGBA{Float32}(0,0,0,1)))
- colors = RGBA{Float32}[]
- for c in levels.contours
- for elem in c.lines
- append!(result, elem.vertices)
- push!(result, Point2f0(NaN32))
- col = GLVisualize.color_lookup(cmap, c.level, zmin, zmax)
- append!(colors, fill(col, length(elem.vertices) + 1))
- end
- end
- kw_args[:color] = colors
- kw_args[:color_map] = nothing
- kw_args[:color_norm] = nothing
- kw_args[:intensity] = nothing
- return visualize(result, Style(:lines),kw_args)
- end
-end
-
-
-function gl_heatmap(x,y,z, kw_args)
- get!(kw_args, :color_norm, Vec2f0(ignorenan_extrema(z)))
- get!(kw_args, :color_map, Plots.make_gradient(cgrad()))
- delete!(kw_args, :intensity)
- I = GLVisualize.Intensity{Float32}
- heatmap = I[z[j,i] for i=1:size(z, 2), j=1:size(z, 1)]
- tex = GLAbstraction.Texture(heatmap, minfilter=:nearest)
- kw_args[:stroke_width] = 0f0
- kw_args[:levels] = 1f0
- visualize(tex, Style(:default), kw_args)
-end
-
-
-
-
-"""
-Ugh, so much special casing (╯°□°)╯︵ ┻━┻
-"""
-function label_scatter(plotattributes, w, ho)
- kw = KW()
- extract_stroke(plotattributes, kw)
- extract_marker(plotattributes, kw)
- kw[:scale] = Vec2f0(w/2)
- kw[:offset] = Vec2f0(-w/4)
- if haskey(kw, :intensity)
- cmap = kw[:color_map]
- norm = kw[:color_norm]
- kw[:color] = GLVisualize.color_lookup(cmap, kw[:intensity][1], norm)
- delete!(kw, :intensity)
- delete!(kw, :color_map)
- delete!(kw, :color_norm)
- else
- color = get(kw, :color, nothing)
- kw[:color] = isa(color, Array) ? first(color) : color
- end
- strcolor = get(kw, :stroke_color, RGBA{Float32}(0,0,0,0))
- kw[:stroke_color] = isa(strcolor, Array) ? first(strcolor) : strcolor
- p = get(kw, :primitive, GeometryTypes.Circle)
- if isa(p, GLNormalMesh)
- bb = GeometryTypes.AABB{Float32}(GeometryTypes.vertices(p))
- bbw = GeometryTypes.widths(bb)
- if isapprox(bbw[3], 0)
- bbw = Vec3f0(bbw[1], bbw[2], 1)
- end
- mini = minimum(bb)
- m = GLAbstraction.translationmatrix(-mini)
- m *= GLAbstraction.scalematrix(1 ./ bbw)
- kw[:primitive] = m * p
- kw[:scale] = Vec3f0(w/2)
- delete!(kw, :offset)
- end
- if isa(p, Array)
- kw[:primitive] = GeometryTypes.Circle
- end
- GL.gl_scatter(Point2f0[(w/2, ho)], kw)
-end
-
-
-function make_label(sp, series, i)
- GL = Plots
- w, gap, ho = 20f0, 5f0, 5
- result = []
- plotattributes = series.plotattributes
- st = plotattributes[:seriestype]
- kw_args = KW()
- if (st in (:path, :path3d, :straightline)) && plotattributes[:linewidth] > 0
- points = Point2f0[(0, ho), (w, ho)]
- kw = KW()
- extract_linestyle(plotattributes, kw)
- append!(result, GL.gl_lines(points, kw))
- if plotattributes[:markershape] != :none
- push!(result, label_scatter(plotattributes, w, ho))
- end
- elseif st in (:scatter, :scatter3d) #|| plotattributes[:markershape] != :none
- push!(result, label_scatter(plotattributes, w, ho))
- else
- extract_c(plotattributes, kw_args, :fill)
- if isa(kw_args[:color], AbstractVector)
- kw_args[:color] = first(kw_args[:color])
- end
- push!(result, visualize(
- GeometryTypes.SimpleRectangle(-w/2, ho-w/4, w/2, w/2),
- Style(:default), kw_args
- ))
- end
- labeltext = if isa(series[:label], Array)
- i += 1
- series[:label][i]
- else
- series[:label]
- end
- ft = legendfont(sp)
- font = Plots.Font(ft.family, ft.pointsize, :left, :bottom, 0.0, ft.color)
- xy = Point2f0(w+gap, 0.0)
- kw = Dict(:model => text_model(font, xy), :scale_primitive=>false)
- extract_font(font, kw)
- t = PlotText(labeltext, font)
- push!(result, glvisualize_text(xy, t, kw))
- GLAbstraction.Context(result...), i
-end
-
-
-function generate_legend(sp, screen, model_m)
- legend = GLAbstraction.Context[]
- if sp[:legend] != :none
- i = 0
- for series in series_list(sp)
- should_add_to_legend(series) || continue
- result, i = make_label(sp, series, i)
- push!(legend, result)
- end
- if isempty(legend)
- return
- end
- list = visualize(legend, gap=Vec3f0(0,5,0))
- bb = GLAbstraction._boundingbox(list)
- wx,wy,_ = GeometryTypes.widths(bb)
- xmin, _ = Plots.axis_limits(sp[:xaxis])
- _, ymax = Plots.axis_limits(sp[:yaxis])
- area = map(model_m) do m
- p = m * GeometryTypes.Vec4f0(xmin, ymax, 0, 1)
- h = round(Int, wy)+20
- w = round(Int, wx)+20
- x,y = round(Int, p[1])+30, round(Int, p[2]-h)-30
- GeometryTypes.SimpleRectangle(x, y, w, h)
- end
- sscren = GLWindow.Screen(
- screen, area = area,
- color = sp[:background_color_legend],
- stroke = (2f0, RGBA(0.3, 0.3, 0.3, 0.9))
- )
- GLAbstraction.translate!(list, Vec3f0(10,10,0))
- GLVisualize._view(list, sscren, camera=:fixed_pixel)
- end
- return
-end
diff --git a/src/backends/gr.jl b/src/backends/gr.jl
index 267875f9..0fa3d210 100644
--- a/src/backends/gr.jl
+++ b/src/backends/gr.jl
@@ -3,68 +3,10 @@
# significant contributions by @jheinen
-const _gr_attr = merge_with_base_supported([
- :annotations,
- :background_color_legend, :background_color_inside, :background_color_outside,
- :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
- :foreground_color_text, :foreground_color_border,
- :label,
- :seriescolor, :seriesalpha,
- :linecolor, :linestyle, :linewidth, :linealpha,
- :markershape, :markercolor, :markersize, :markeralpha,
- :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
- :fillrange, :fillcolor, :fillalpha,
- :bins,
- :layout,
- :title, :window_title,
- :guide, :guide_position, :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,
- :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,
- :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
-
-function add_backend_string(::GRBackend)
- """
- Pkg.add("GR")
- Pkg.build("GR")
- """
-end
-
import GR
export GR
+
# --------------------------------------------------------------------------------------
const gr_linetype = KW(
@@ -137,14 +79,28 @@ gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(_cycle(c,1)))
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(_cycle(c,1)))
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(_cycle(c,1)))
gr_set_transparency(α::Real) = GR.settransparency(clamp(α, 0, 1))
-function gr_set_transparency(::Nothing) end
+gr_set_transparency(::Nothing) = GR.settransparency(1)
+gr_set_transparency(c, α) = gr_set_transparency(α)
+gr_set_transparency(c::Colorant, ::Nothing) = gr_set_transparency(c)
+gr_set_transparency(c::Colorant) = GR.settransparency(alpha(c))
+
+const _gr_arrow_map = Dict(
+ :simple => 1,
+ :hollow => 3,
+ :filled => 4,
+ :triangle => 5,
+ :filledtriangle => 6,
+ :closed => 6,
+ :open => 5,
+)
+gr_set_arrowstyle(s::Symbol) = GR.setarrowstyle(get(_gr_arrow_map, s, 1))
# --------------------------------------------------------------------------------------
# draw line segments, splitting x/y into contiguous/finite segments
# note: this can be used for shapes by passing func `GR.fillarea`
-function gr_polyline(x, y, func = GR.polyline; arrowside=:none)
+function gr_polyline(x, y, func = GR.polyline; arrowside = :none, arrowstyle = :simple)
iend = 0
n = length(x)
while iend < n-1
@@ -173,9 +129,11 @@ function gr_polyline(x, y, func = GR.polyline; arrowside=:none)
if istart > 0 && iend > 0
func(x[istart:iend], y[istart:iend])
if arrowside in (:head,:both)
+ gr_set_arrowstyle(arrowstyle)
GR.drawarrow(x[iend-1], y[iend-1], x[iend], y[iend])
end
if arrowside in (:tail,:both)
+ gr_set_arrowstyle(arrowstyle)
GR.drawarrow(x[istart+1], y[istart+1], x[istart], y[istart])
end
else
@@ -217,7 +175,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
a = α .+ 90
sinf = sind.(a)
cosf = cosd.(a)
- rtick_values, rtick_labels = get_ticks(yaxis)
+ rtick_values, rtick_labels = get_ticks(sp, yaxis)
if yaxis[:formatter] in (:scientific, :auto) && yaxis[:ticks] in (:auto, :native)
rtick_labels = convert_sci_unicode.(rtick_labels)
end
@@ -225,7 +183,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
#draw angular grid
if xaxis[:grid]
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
- gr_set_transparency(xaxis[:gridalpha])
+ gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:gridalpha])
for i in 1:length(α)
GR.polyline([sinf[i], 0], [cosf[i], 0])
end
@@ -234,7 +192,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
#draw radial grid
if yaxis[:grid]
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
- gr_set_transparency(yaxis[:gridalpha])
+ gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:gridalpha])
for i in 1:length(rtick_values)
r = (rtick_values[i] - rmin) / (rmax - rmin)
if r <= 1.0 && r >= 0.0
@@ -273,16 +231,16 @@ end
# using the axis extrema and limit overrides, return the min/max value for this axis
-gr_x_axislims(sp::Subplot) = axis_limits(sp[:xaxis])
-gr_y_axislims(sp::Subplot) = axis_limits(sp[:yaxis])
-gr_z_axislims(sp::Subplot) = axis_limits(sp[:zaxis])
+gr_x_axislims(sp::Subplot) = axis_limits(sp, :x)
+gr_y_axislims(sp::Subplot) = axis_limits(sp, :y)
+gr_z_axislims(sp::Subplot) = axis_limits(sp, :z)
gr_xy_axislims(sp::Subplot) = gr_x_axislims(sp)..., gr_y_axislims(sp)...
-function gr_lims(axis::Axis, adjust::Bool, expand = nothing)
+function gr_lims(sp::Subplot, axis::Axis, adjust::Bool, expand = nothing)
if expand != nothing
expand_extrema!(axis, expand)
end
- lims = axis_limits(axis)
+ lims = axis_limits(sp, axis[:letter])
if adjust
GR.adjustrange(lims...)
else
@@ -294,6 +252,7 @@ end
function gr_fill_viewport(vp::AVec{Float64}, c)
GR.savestate()
GR.selntran(0)
+ GR.setscale(0)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
gr_set_fillcolor(c)
GR.fillrect(vp...)
@@ -353,15 +312,17 @@ function gr_draw_markers(series::Series, x, y, clims, msize = series[:markersize
# draw a filled in shape, slightly bigger, to estimate a stroke
if series[:markerstrokewidth] > 0
- cfunc(get_markerstrokecolor(series, i))
- gr_set_transparency(get_markerstrokealpha(series, i))
+ c = get_markerstrokecolor(series, i)
+ cfunc(c)
+ gr_set_transparency(c, get_markerstrokealpha(series, i))
gr_draw_marker(x[i], y[i], msi + series[:markerstrokewidth], shape)
end
# draw the shape - don't draw filled area if marker shape is 1D
if !(shape in (:hline, :vline, :+, :x))
- cfunc(get_markercolor(series, clims, i))
- gr_set_transparency(get_markeralpha(series, i))
+ c = get_markercolor(series, clims, i)
+ cfunc(c)
+ gr_set_transparency(c, get_markeralpha(series, i))
gr_draw_marker(x[i], y[i], msi, shape)
end
end
@@ -472,17 +433,112 @@ function gr_set_viewport_polar()
r
end
+struct GRColorbar
+ gradients
+ fills
+ lines
+ GRColorbar() = new([],[],[])
+end
+
+function gr_update_colorbar!(cbar::GRColorbar, series::Series)
+ style = colorbar_style(series)
+ style === nothing && return
+ list = style == cbar_gradient ? cbar.gradients :
+ style == cbar_fill ? cbar.fills :
+ style == cbar_lines ? cbar.lines :
+ error("Unknown colorbar style: $style.")
+ push!(list, series)
+end
+
+function gr_contour_levels(series::Series, clims)
+ levels = contour_levels(series, clims)
+ if isfilledcontour(series)
+ # GR implicitly uses the maximal z value as the highest level
+ levels = levels[1:end-1]
+ end
+ levels
+end
+
+function gr_colorbar_colors(series::Series, clims)
+ if iscontour(series)
+ levels = gr_contour_levels(series, clims)
+ if isfilledcontour(series)
+ # GR.contourf uses a color range according to supplied levels
+ zrange = ignorenan_extrema(levels)
+ else
+ # GR.contour uses a color range according to data range
+ zrange = clims
+ end
+ colors = 1000 .+ 255 .* (levels .- zrange[1]) ./ (zrange[2] - zrange[1])
+ else
+ colors = 1000:1255
+ end
+ round.(Int,colors)
+end
+
+
+function _cbar_unique(values, propname)
+ out = last(values)
+ if any(x != out for x in values)
+ @warn "Multiple series with different $propname share a colorbar. " *
+ "Colorbar may not refelct all series correctly."
+ end
+ out
+end
+
# add the colorbar
-function gr_colorbar(sp::Subplot, clims)
+function gr_draw_colorbar(cbar::GRColorbar, sp::Subplot, clims)
+ GR.savestate()
xmin, xmax = gr_xy_axislims(sp)[1:2]
+ zmin, zmax = clims[1:2]
gr_set_viewport_cmap(sp)
- l = zeros(Int32, 1, 256)
- l[1,:] = Int[round(Int, _i) for _i in range(1000, stop=1255, length=256)]
GR.setscale(0)
- GR.setwindow(xmin, xmax, clims[1], clims[2])
- GR.cellarray(xmin, xmax, clims[2], clims[1], 1, length(l), l)
- ztick = 0.5 * GR.tick(clims[1], clims[2])
- GR.axes(0, ztick, xmax, clims[1], 0, 1, 0.005)
+ GR.setwindow(xmin, xmax, zmin, zmax)
+ if !isempty(cbar.gradients)
+ series = cbar.gradients
+ gr_set_gradient(_cbar_unique(gr_get_color.(series),"color"))
+ gr_set_transparency(_cbar_unique(get_fillalpha.(series), "fill alpha"))
+ GR.cellarray(xmin, xmax, zmax, zmin, 1, 256, 1000:1255)
+ end
+
+ if !isempty(cbar.fills)
+ series = cbar.fills
+ GR.setfillintstyle(GR.INTSTYLE_SOLID)
+ gr_set_gradient(_cbar_unique(gr_get_color.(series), "color"))
+ gr_set_transparency(_cbar_unique(get_fillalpha.(series), "fill alpha"))
+ levels = _cbar_unique(contour_levels.(series, Ref(clims)), "levels")
+ # GR implicitly uses the maximal z value as the highest level
+ if levels[end] < clims[2]
+ @warn("GR: highest contour level less than maximal z value is not supported.")
+ # replace levels, rather than assign to levels[end], to ensure type
+ # promotion in case levels is an integer array
+ levels = [levels[1:end-1]; clims[2]]
+ end
+ colors = gr_colorbar_colors(last(series), clims)
+ for (from, to, color) in zip(levels[1:end-1], levels[2:end], colors)
+ GR.setfillcolorind(color)
+ GR.fillrect( xmin, xmax, from, to )
+ end
+ end
+
+ if !isempty(cbar.lines)
+ series = cbar.lines
+ gr_set_gradient(_cbar_unique(gr_get_color.(series),"color"))
+ gr_set_line(_cbar_unique(get_linewidth.(series), "line width"),
+ _cbar_unique(get_linestyle.(series), "line style"),
+ _cbar_unique(get_linecolor.(series, Ref(clims)), "line color"))
+ gr_set_transparency(_cbar_unique(get_linealpha.(series), "line alpha"))
+ levels = _cbar_unique(contour_levels.(series, Ref(clims)), "levels")
+ colors = gr_colorbar_colors(last(series), clims)
+ for (line, color) in zip(levels, colors)
+ GR.setlinecolorind(color)
+ GR.polyline([xmin,xmax], [line,line] )
+ end
+ end
+
+ ztick = 0.5 * GR.tick(zmin, zmax)
+ gr_set_line(1, :solid, plot_color(:black))
+ GR.axes(0, ztick, xmax, zmin, 0, 1, 0.005)
gr_set_font(guidefont(sp[:yaxis]))
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
@@ -490,28 +546,52 @@ function gr_colorbar(sp::Subplot, clims)
gr_text(viewport_plotarea[2] + gr_colorbar_ratio,
gr_view_ycenter(), sp[:colorbar_title])
- gr_set_viewport_plotarea()
+ GR.restorestate()
end
gr_view_xcenter() = 0.5 * (viewport_plotarea[1] + viewport_plotarea[2])
gr_view_ycenter() = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
-function gr_legend_pos(s::Symbol,w,h)
+function gr_legend_pos(sp::Subplot, w, h)
+ s = sp[:legend]
+ typeof(s) <: Symbol || return gr_legend_pos(s, w, h)
str = string(s)
if str == "best"
str = "topright"
end
+ if occursin("outer", str)
+ xaxis, yaxis = sp[:xaxis], sp[:yaxis]
+ xmirror = xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
+ ymirror = yaxis[:guide_position] == :right || (yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
+ end
if occursin("right", str)
- xpos = viewport_plotarea[2] - 0.05 - w
+ if occursin("outer", str)
+ # As per https://github.com/jheinen/GR.jl/blob/master/src/jlgr.jl#L525
+ xpos = viewport_plotarea[2] + 0.11 + ymirror * gr_yaxis_width(sp)
+ else
+ xpos = viewport_plotarea[2] - 0.05 - w
+ end
elseif occursin("left", str)
- xpos = viewport_plotarea[1] + 0.11
+ if occursin("outer", str)
+ xpos = viewport_plotarea[1] - 0.05 - w - !ymirror * gr_yaxis_width(sp)
+ else
+ xpos = viewport_plotarea[1] + 0.11
+ end
else
xpos = (viewport_plotarea[2]-viewport_plotarea[1])/2 - w/2 +.04
end
if occursin("top", str)
- ypos = viewport_plotarea[4] - 0.06
+ if s == :outertop
+ ypos = viewport_plotarea[4] + 0.02 + h + xmirror * gr_xaxis_height(sp)
+ else
+ ypos = viewport_plotarea[4] - 0.06
+ end
elseif occursin("bottom", str)
- ypos = viewport_plotarea[3] + h + 0.06
+ if s == :outerbottom
+ ypos = viewport_plotarea[3] - 0.05 - !xmirror * gr_xaxis_height(sp)
+ else
+ ypos = viewport_plotarea[3] + h + 0.06
+ end
else
ypos = (viewport_plotarea[4]-viewport_plotarea[3])/2 + h/2
end
@@ -529,7 +609,7 @@ end
const _gr_gradient_alpha = ones(256)
function gr_set_gradient(c)
- grad = isa(c, ColorGradient) ? c : cgrad()
+ grad = _as_gradient(c)
for (i,z) in enumerate(range(0, stop=1, length=256))
c = grad[z]
GR.setcolorrep(999+i, red(c), green(c), blue(c))
@@ -538,6 +618,26 @@ function gr_set_gradient(c)
grad
end
+function gr_set_gradient(series::Series)
+ color = gr_get_color(series)
+ color !== nothing && gr_set_gradient(color)
+end
+
+function gr_get_color(series::Series)
+ st = series[:seriestype]
+ if st in (:surface, :heatmap) || isfilledcontour(series)
+ series[:fillcolor]
+ elseif st in (:contour, :wireframe)
+ series[:linecolor]
+ elseif series[:marker_z] != nothing
+ series[:markercolor]
+ elseif series[:line_z] != nothing
+ series[:linecolor]
+ elseif series[:fill_z] != nothing
+ series[:fillcolor]
+ end
+end
+
# this is our new display func... set up the viewport_canvas, compute bounding boxes, and display each subplot
function gr_display(plt::Plot, fmt="")
GR.clearws()
@@ -610,17 +710,65 @@ function gr_set_yticks_font(sp)
return flip, mirror
end
-function gr_get_ticks_size(ticks, i)
+function gr_text_size(str)
GR.savestate()
GR.selntran(0)
- l = 0.0
- for (cv, dv) in zip(ticks...)
- tb = gr_inqtext(0, 0, string(dv))[i]
- tb_min, tb_max = extrema(tb)
- l = max(l, tb_max - tb_min)
- end
+ xs, ys = gr_inqtext(0, 0, string(str))
+ l, r = extrema(xs)
+ b, t = extrema(ys)
+ w = r - l
+ h = t - b
GR.restorestate()
- return l
+ return w, h
+end
+
+function gr_text_size(str, rot)
+ GR.savestate()
+ GR.selntran(0)
+ xs, ys = gr_inqtext(0, 0, string(str))
+ l, r = extrema(xs)
+ b, t = extrema(ys)
+ w = text_box_width(r - l, t - b, rot)
+ h = text_box_height(r - l, t - b, rot)
+ GR.restorestate()
+ return w, h
+end
+
+text_box_width(w, h, rot) = abs(cosd(rot)) * w + abs(cosd(rot + 90)) * h
+text_box_height(w, h, rot) = abs(sind(rot)) * w + abs(sind(rot + 90)) * h
+
+function gr_get_ticks_size(ticks, rot)
+ w, h = 0.0, 0.0
+ for (cv, dv) in zip(ticks...)
+ wi, hi = gr_text_size(dv, rot)
+ w = max(w, wi)
+ h = max(h, hi)
+ end
+ return w, h
+end
+
+function gr_xaxis_height(sp)
+ xaxis = sp[:xaxis]
+ xticks, yticks = axis_drawing_info(sp)[1:2]
+ gr_set_font(tickfont(xaxis))
+ h = (xticks in (nothing, false, :none) ? 0 : last(gr_get_ticks_size(xticks, xaxis[:rotation])))
+ if xaxis[:guide] != ""
+ gr_set_font(guidefont(xaxis))
+ h += last(gr_text_size(xaxis[:guide]))
+ end
+ return h
+end
+
+function gr_yaxis_width(sp)
+ yaxis = sp[:yaxis]
+ xticks, yticks = axis_drawing_info(sp)[1:2]
+ gr_set_font(tickfont(yaxis))
+ w = (xticks in (nothing, false, :none) ? 0 : first(gr_get_ticks_size(yticks, yaxis[:rotation])))
+ if yaxis[:guide] != ""
+ gr_set_font(guidefont(yaxis))
+ w += last(gr_text_size(yaxis[:guide]))
+ end
+ return w
end
function _update_min_padding!(sp::Subplot{GRBackend})
@@ -631,41 +779,60 @@ function _update_min_padding!(sp::Subplot{GRBackend})
end
end
# Add margin given by the user
- leftpad = 4mm + sp[:left_margin]
+ leftpad = 2mm + sp[:left_margin]
toppad = 2mm + sp[:top_margin]
- rightpad = 4mm + sp[:right_margin]
+ rightpad = 2mm + sp[:right_margin]
bottompad = 2mm + sp[:bottom_margin]
# Add margin for title
if sp[:title] != ""
- toppad += 5mm
+ gr_set_font(titlefont(sp))
+ l = last(last(gr_text_size(sp[:title])))
+ h = 1mm + gr_plot_size[2] * l * px
+ toppad += h
end
# Add margin for x and y ticks
xticks, yticks = axis_drawing_info(sp)[1:2]
if !(xticks in (nothing, false, :none))
flip, mirror = gr_set_xticks_font(sp)
- l = gr_get_ticks_size(xticks, 2)
+ l = 0.01 + last(gr_get_ticks_size(xticks, sp[:xaxis][:rotation]))
+ h = 1mm + gr_plot_size[2] * l * px
if mirror
- toppad += 1mm + gr_plot_size[2] * l * px
+ toppad += h
else
- bottompad += 1mm + gr_plot_size[2] * l * px
+ bottompad += h
end
end
if !(yticks in (nothing, false, :none))
flip, mirror = gr_set_yticks_font(sp)
- l = gr_get_ticks_size(yticks, 1)
+ l = 0.01 + first(gr_get_ticks_size(yticks, sp[:yaxis][:rotation]))
+ w = 1mm + gr_plot_size[1] * l * px
if mirror
- rightpad += 1mm + gr_plot_size[1] * l * px
+ rightpad += w
else
- leftpad += 1mm + gr_plot_size[1] * l * px
+ leftpad += w
end
end
# Add margin for x label
if sp[:xaxis][:guide] != ""
- bottompad += 4mm
+ gr_set_font(guidefont(sp[:xaxis]))
+ l = last(gr_text_size(sp[:xaxis][:guide]))
+ h = 1mm + gr_plot_size[2] * l * px
+ if sp[:xaxis][:guide_position] == :top || (sp[:xaxis][:guide_position] == :auto && sp[:xaxis][:mirror] == true)
+ toppad += h
+ else
+ bottompad += h
+ end
end
# Add margin for y label
if sp[:yaxis][:guide] != ""
- leftpad += 4mm
+ gr_set_font(guidefont(sp[:yaxis]))
+ l = last(gr_text_size(sp[:yaxis][:guide]))
+ w = 1mm + gr_plot_size[2] * l * px
+ if sp[:yaxis][:guide_position] == :right || (sp[:yaxis][:guide_position] == :auto && sp[:yaxis][:mirror] == true)
+ rightpad += w
+ else
+ leftpad += w
+ end
end
if sp[:colorbar_title] != ""
rightpad += 4mm
@@ -703,6 +870,54 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
end
+ # calculate legend size
+ # has to be done now due to a potential adjustment to the plotarea given an outer legend.
+ legendn = 0
+ legendw = 0
+ legendi = 0
+ if sp[:legend] != :none
+ GR.savestate()
+ GR.selntran(0)
+ GR.setscale(0)
+ gr_set_font(legendfont(sp))
+ if sp[:legendtitle] != nothing
+ tbx, tby = gr_inqtext(0, 0, string(sp[:legendtitle]))
+ legendw = tbx[3] - tbx[1]
+ legendn += 1
+ end
+ for series in series_list(sp)
+ should_add_to_legend(series) || continue
+ legendn += 1
+ if typeof(series[:label]) <: Array
+ legendi += 1
+ lab = series[:label][legendi]
+ else
+ lab = series[:label]
+ end
+ tbx, tby = gr_inqtext(0, 0, string(lab))
+ legendw = max(legendw, tbx[3] - tbx[1])
+ end
+
+ GR.setscale(1)
+ GR.selntran(1)
+ GR.restorestate()
+ end
+
+ dy = _gr_point_mult[1] * sp[:legendfontsize] * 1.75
+ legendh = dy * legendn
+ leg_str = string(sp[:legend])
+ if occursin("outer", leg_str)
+ if occursin("right", leg_str)
+ viewport_plotarea[2] -= legendw + 0.11
+ elseif occursin("left", leg_str)
+ viewport_plotarea[1] += legendw + 0.11
+ elseif occursin("top", leg_str)
+ viewport_plotarea[4] -= legendh + 0.03
+ elseif occursin("bottom", leg_str)
+ viewport_plotarea[3] += legendh + 0.04
+ end
+ end
+
# fill in the plot area background
bg = plot_color(sp[:background_color_inside])
gr_fill_viewport(viewport_plotarea, bg)
@@ -710,7 +925,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# reduced from before... set some flags based on the series in this subplot
# TODO: can these be generic flags?
outside_ticks = false
- cmap = hascolorbar(sp)
+ # calculate the colorbar limits once for a subplot
+ clims = get_clims(sp)
+ cbar = GRColorbar()
+
draw_axes = sp[:framestyle] != :none
# axes_2d = true
for series in series_list(sp)
@@ -722,7 +940,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
outside_ticks = true
for ax in (sp[:xaxis], sp[:yaxis])
v = series[ax[:letter]]
- if diff(collect(extrema(diff(v))))[1] > 1e-6*std(v)
+ if length(v) > 1 && diff(collect(extrema(diff(v))))[1] > 1e-6*std(v)
@warn("GR: heatmap only supported with equally spaced data.")
end
end
@@ -732,6 +950,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
expand_extrema!(sp[:yaxis], y)
data_lims = gr_xy_axislims(sp)
end
+
+ gr_update_colorbar!(cbar,series)
end
# set our plot area view
@@ -780,11 +1000,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.setlinewidth(sp.plt[:thickness_scaling])
if is3d(sp)
- zmin, zmax = gr_lims(zaxis, true)
- clims = sp[:clims]
- if is_2tuple(clims)
- isfinite(clims[1]) && (zmin = clims[1])
- isfinite(clims[2]) && (zmax = clims[2])
+ # TODO do we really need a different clims computation here from the one
+ # computed above using get_clims(sp)?
+ zmin, zmax = gr_lims(sp, zaxis, true)
+ clims3d = sp[:clims]
+ if is_2tuple(clims3d)
+ isfinite(clims3d[1]) && (zmin = clims3d[1])
+ isfinite(clims3d[2]) && (zmax = clims3d[2])
end
GR.setspace(zmin, zmax, round.(Int, sp[:camera])...)
xtick = GR.tick(xmin, xmax) / 2
@@ -794,28 +1016,28 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if xaxis[:grid]
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
- gr_set_transparency(xaxis[:gridalpha])
+ gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:gridalpha])
GR.grid3d(xtick, 0, 0, xmin, ymax, zmin, 2, 0, 0)
end
if yaxis[:grid]
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
- gr_set_transparency(yaxis[:gridalpha])
+ gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:gridalpha])
GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0)
end
if zaxis[:grid]
gr_set_line(zaxis[:gridlinewidth], zaxis[:gridstyle], zaxis[:foreground_color_grid])
- gr_set_transparency(zaxis[:gridalpha])
+ gr_set_transparency(zaxis[:foreground_color_grid], zaxis[:gridalpha])
GR.grid3d(0, 0, ztick, xmin, ymax, zmin, 0, 0, 2)
end
gr_set_line(1, :solid, xaxis[:foreground_color_axis])
- gr_set_transparency(1)
+ gr_set_transparency(xaxis[:foreground_color_axis])
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
elseif ispolar(sp)
r = gr_set_viewport_polar()
#rmin, rmax = GR.adjustrange(ignorenan_minimum(r), ignorenan_maximum(r))
- rmin, rmax = axis_limits(sp[:yaxis])
+ rmin, rmax = axis_limits(sp, :y)
gr_polaraxes(rmin, rmax, sp)
elseif draw_axes
@@ -831,36 +1053,36 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# gr_set_linecolor(sp[:foreground_color_grid])
# GR.grid(xtick, ytick, 0, 0, majorx, majory)
gr_set_line(xaxis[:gridlinewidth], xaxis[:gridstyle], xaxis[:foreground_color_grid])
- gr_set_transparency(xaxis[:gridalpha])
+ gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:gridalpha])
gr_polyline(coords(xgrid_segs)...)
end
if yaxis[:grid]
gr_set_line(yaxis[:gridlinewidth], yaxis[:gridstyle], yaxis[:foreground_color_grid])
- gr_set_transparency(yaxis[:gridalpha])
+ gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:gridalpha])
gr_polyline(coords(ygrid_segs)...)
end
if xaxis[:minorgrid]
# gr_set_linecolor(sp[:foreground_color_grid])
# GR.grid(xtick, ytick, 0, 0, majorx, majory)
gr_set_line(xaxis[:minorgridlinewidth], xaxis[:minorgridstyle], xaxis[:foreground_color_minor_grid])
- gr_set_transparency(xaxis[:minorgridalpha])
+ gr_set_transparency(xaxis[:foreground_color_minor_grid], xaxis[:minorgridalpha])
gr_polyline(coords(xminorgrid_segs)...)
end
if yaxis[:minorgrid]
gr_set_line(yaxis[:minorgridlinewidth], yaxis[:minorgridstyle], yaxis[:foreground_color_minor_grid])
- gr_set_transparency(yaxis[:minorgridalpha])
+ gr_set_transparency(yaxis[:foreground_color_minor_grid], yaxis[:minorgridalpha])
gr_polyline(coords(yminorgrid_segs)...)
end
gr_set_transparency(1.0)
# axis lines
if xaxis[:showaxis]
- gr_set_line(1, :solid, xaxis[:foreground_color_axis])
+ gr_set_line(1, :solid, xaxis[:foreground_color_border])
GR.setclip(0)
gr_polyline(coords(xspine_segs)...)
end
if yaxis[:showaxis]
- gr_set_line(1, :solid, yaxis[:foreground_color_axis])
+ gr_set_line(1, :solid, yaxis[:foreground_color_border])
GR.setclip(0)
gr_polyline(coords(yspine_segs)...)
end
@@ -870,7 +1092,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if xaxis[:showaxis]
if sp[:framestyle] in (:zerolines, :grid)
gr_set_line(1, :solid, xaxis[:foreground_color_grid])
- gr_set_transparency(xaxis[:gridalpha])
+ gr_set_transparency(xaxis[:foreground_color_grid], xaxis[:tick_direction] == :out ? xaxis[:gridalpha] : 0)
else
gr_set_line(1, :solid, xaxis[:foreground_color_axis])
end
@@ -880,7 +1102,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if yaxis[:showaxis]
if sp[:framestyle] in (:zerolines, :grid)
gr_set_line(1, :solid, yaxis[:foreground_color_grid])
- gr_set_transparency(yaxis[:gridalpha])
+ gr_set_transparency(yaxis[:foreground_color_grid], yaxis[:tick_direction] == :out ? yaxis[:gridalpha] : 0)
else
gr_set_line(1, :solid, yaxis[:foreground_color_axis])
end
@@ -932,10 +1154,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
intensity = sp[:framestyle] == :semi ? 0.5 : 1.0
if sp[:framestyle] in (:box, :semi)
gr_set_line(intensity, :solid, xaxis[:foreground_color_border])
- gr_set_transparency(intensity)
+ gr_set_transparency(xaxis[:foreground_color_border], intensity)
gr_polyline(coords(xborder_segs)...)
gr_set_line(intensity, :solid, yaxis[:foreground_color_border])
- gr_set_transparency(intensity)
+ gr_set_transparency(yaxis[:foreground_color_border], intensity)
gr_polyline(coords(yborder_segs)...)
end
end
@@ -959,27 +1181,34 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.settextalign(halign, GR.TEXT_VALIGN_TOP)
gr_text(xpos, viewport_subplot[4], sp[:title])
end
-
- if xaxis[:guide] != ""
+ if is3d(sp)
gr_set_font(guidefont(xaxis))
- if xaxis[:guide_position] == :top
- GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
- gr_text(gr_view_xcenter(), viewport_subplot[4], xaxis[:guide])
- else
- GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM)
- gr_text(gr_view_xcenter(), viewport_subplot[3], xaxis[:guide])
+ GR.titles3d(xaxis[:guide], yaxis[:guide], zaxis[:guide])
+ else
+ xticks, yticks = axis_drawing_info(sp)[1:2]
+ if xaxis[:guide] != ""
+ h = 0.01 + gr_xaxis_height(sp)
+ gr_set_font(guidefont(xaxis))
+ if xaxis[:guide_position] == :top || (xaxis[:guide_position] == :auto && xaxis[:mirror] == true)
+ GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
+ gr_text(gr_view_xcenter(), viewport_plotarea[4] + h, xaxis[:guide])
+ else
+ GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM)
+ gr_text(gr_view_xcenter(), viewport_plotarea[3] - h, xaxis[:guide])
+ end
end
- end
- if yaxis[:guide] != ""
- gr_set_font(guidefont(yaxis))
- GR.setcharup(-1, 0)
- if yaxis[:guide_position] == :left
- GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM)
- gr_text(viewport_subplot[2], gr_view_ycenter(), yaxis[:guide])
- else
- GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
- gr_text(viewport_subplot[1], gr_view_ycenter(), yaxis[:guide])
+ if yaxis[:guide] != ""
+ w = 0.02 + gr_yaxis_width(sp)
+ gr_set_font(guidefont(yaxis))
+ GR.setcharup(-1, 0)
+ if yaxis[:guide_position] == :right || (yaxis[:guide_position] == :auto && yaxis[:mirror] == true)
+ GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM)
+ gr_text(viewport_plotarea[2] + w, gr_view_ycenter(), yaxis[:guide])
+ else
+ GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
+ gr_text(viewport_plotarea[1] - w, gr_view_ycenter(), yaxis[:guide])
+ end
end
end
GR.restorestate()
@@ -989,26 +1218,14 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# this needs to be here to point the colormap to the right indices
GR.setcolormap(1000 + GR.COLORMAP_COOLWARM)
- # calculate the colorbar limits once for a subplot
- clims = get_clims(sp)
-
for (idx, series) in enumerate(series_list(sp))
st = series[:seriestype]
# update the current stored gradient
- if st in (:contour, :surface, :wireframe, :heatmap)
- gr_set_gradient(series[:fillcolor]) #, series[:fillalpha])
- elseif series[:marker_z] != nothing
- series[:markercolor] = gr_set_gradient(series[:markercolor])
- elseif series[:line_z] != nothing
- series[:linecolor] = gr_set_gradient(series[:linecolor])
- elseif series[:fill_z] != nothing
- series[:fillcolor] = gr_set_gradient(series[:fillcolor])
- end
+ gr_set_gradient(series)
GR.savestate()
-
# update the bounding window
if ispolar(sp)
gr_set_viewport_polar()
@@ -1041,7 +1258,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
if st in (:path, :scatter, :straightline)
- if length(x) > 1
+ if x != nothing && length(x) > 1
lz = series[:line_z]
segments = iter_segments(series)
# do area fill
@@ -1049,10 +1266,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
fr_from, fr_to = (is_2tuple(frng) ? frng : (y, frng))
for (i, rng) in enumerate(segments)
- gr_set_fillcolor(get_fillcolor(series, clims, i))
+ fc = get_fillcolor(series, clims, i)
+ gr_set_fillcolor(fc)
fx = _cycle(x, vcat(rng, reverse(rng)))
fy = vcat(_cycle(fr_from,rng), _cycle(fr_to,reverse(rng)))
- gr_set_transparency(get_fillalpha(series, i))
+ gr_set_transparency(fc, get_fillalpha(series, i))
GR.fillarea(fx, fy)
end
end
@@ -1060,10 +1278,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
# draw the line(s)
if st in (:path, :straightline)
for (i, rng) in enumerate(segments)
- gr_set_line(get_linewidth(series, i), get_linestyle(series, i), get_linecolor(series, clims, i)) #, series[:linealpha])
- gr_set_transparency(get_linealpha(series, i))
+ lc = get_linecolor(series, clims, i)
+ gr_set_line(get_linewidth(series, i), get_linestyle(series, i), lc) #, series[:linealpha])
arrowside = isa(series[:arrow], Arrow) ? series[:arrow].side : :none
- gr_polyline(x[rng], y[rng]; arrowside = arrowside)
+ arrowstyle = isa(series[:arrow], Arrow) ? series[:arrow].style : :simple
+ gr_set_fillcolor(lc)
+ gr_set_transparency(lc, get_linealpha(series, i))
+ gr_polyline(x[rng], y[rng]; arrowside = arrowside, arrowstyle = arrowstyle)
end
end
end
@@ -1073,35 +1294,21 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
elseif st == :contour
- zmin, zmax = clims
- GR.setspace(zmin, zmax, 0, 90)
- if typeof(series[:levels]) <: AbstractArray
- h = series[:levels]
- else
- h = series[:levels] > 1 ? range(zmin, stop=zmax, length=series[:levels]) : [(zmin + zmax) / 2]
+ GR.setspace(clims[1], clims[2], 0, 90)
+ GR.setlinetype(gr_linetype[get_linestyle(series)])
+ GR.setlinewidth(max(0, get_linewidth(series) / (sum(gr_plot_size) * 0.001)))
+ is_lc_black = let black=plot_color(:black)
+ plot_color(series[:linecolor]) in (black,[black])
end
+ h = gr_contour_levels(series, clims)
if series[:fillrange] != nothing
- GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
- else
- GR.setlinetype(gr_linetype[get_linestyle(series)])
- GR.setlinewidth(max(0, get_linewidth(series) / (sum(gr_plot_size) * 0.001)))
- if plot_color(series[:linecolor]) == [plot_color(:black)]
- GR.contour(x, y, h, z, 0 + (series[:contour_labels] == true ? 1 : 0))
- else
- GR.contour(x, y, h, z, 1000 + (series[:contour_labels] == true ? 1 : 0))
+ if series[:fillcolor] != series[:linecolor] && !is_lc_black
+ @warn("GR: filled contour only supported with black contour lines")
end
- end
-
- # create the colorbar of contour levels
- if cmap
- gr_set_line(1, :solid, yaxis[:foreground_color_axis])
- gr_set_viewport_cmap(sp)
- l = (length(h) > 1) ? round.(Int32, 1000 .+ (h .- ignorenan_minimum(h)) ./ (ignorenan_maximum(h) - ignorenan_minimum(h)) .* 255) : Int32[1000, 1255]
- GR.setwindow(xmin, xmax, zmin, zmax)
- GR.cellarray(xmin, xmax, zmax, zmin, 1, length(l), l)
- ztick = 0.5 * GR.tick(zmin, zmax)
- GR.axes(0, ztick, xmax, zmin, 0, 1, 0.005)
- gr_set_viewport_plotarea()
+ GR.contourf(x, y, h, z, series[:contour_labels] == true ? 1 : 0)
+ else
+ coff = is_lc_black ? 0 : 1000
+ GR.contour(x, y, h, z, coff + (series[:contour_labels] == true ? 1 : 0))
end
elseif st in [:surface, :wireframe]
@@ -1109,29 +1316,46 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if length(x) == length(y) == length(z)
GR.trisurface(x, y, z)
else
- GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
+ try
+ GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
+ catch
+ GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
+ end
end
else
GR.setfillcolorind(0)
GR.surface(x, y, z, GR.OPTION_FILLED_MESH)
end
+ elseif st == :volume
+ sp[:legend] = :none
+ GR.gr3.clear()
+ dmin, dmax = GR.gr3.volume(y.v, 0)
+
elseif st == :heatmap
- xmin, xmax, ymin, ymax = xy_lims
zmin, zmax = clims
- m, n = length(x), length(y)
- xinds = sort(1:m, rev = xaxis[:flip])
- yinds = sort(1:n, rev = yaxis[:flip])
- z = reshape(reshape(z, m, n)[xinds, yinds], m*n)
- GR.setspace(zmin, zmax, 0, 90)
- grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
- colors = [plot_color(grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)], series[:fillalpha]) for zi=z]
- rgba = map(c -> UInt32( round(UInt, alpha(c) * 255) << 24 +
- round(UInt, blue(c) * 255) << 16 +
- round(UInt, green(c) * 255) << 8 +
- round(UInt, red(c) * 255) ), colors)
- w, h = length(x), length(y)
- GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
+ if !ispolar(sp)
+ xmin, xmax, ymin, ymax = xy_lims
+ m, n = length(x), length(y)
+ xinds = sort(1:m, rev = xaxis[:flip])
+ yinds = sort(1:n, rev = yaxis[:flip])
+ z = reshape(reshape(z, m, n)[xinds, yinds], m*n)
+ GR.setspace(zmin, zmax, 0, 90)
+ grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
+ colors = [plot_color(grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)], series[:fillalpha]) for zi=z]
+ rgba = map(c -> UInt32( round(UInt, alpha(c) * 255) << 24 +
+ round(UInt, blue(c) * 255) << 16 +
+ round(UInt, green(c) * 255) << 8 +
+ round(UInt, red(c) * 255) ), colors)
+ w, h = length(x), length(y)
+ GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
+ else
+ h, w = length(x), length(y)
+ z = reshape(z, h, w)
+ colors = Int32[round(Int32, 1000 + _i * 255) for _i in z']
+ GR.setwindow(-1, 1, -1, 1)
+ GR.polarcellarray(0, 0, 0, 360, 0, 1, w, h, colors)
+ end
elseif st in (:path3d, :scatter3d)
# draw path
@@ -1140,8 +1364,9 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
lz = series[:line_z]
segments = iter_segments(series)
for (i, rng) in enumerate(segments)
- gr_set_line(get_linewidth(series, i), get_linestyle(series, i), get_linecolor(series, clims, i)) #, series[:linealpha])
- gr_set_transparency(get_linealpha(series, i))
+ lc = get_linecolor(series, clims, i)
+ gr_set_line(get_linewidth(series, i), get_linestyle(series, i), lc) #, series[:linealpha])
+ gr_set_transparency(lc, get_linealpha(series, i))
GR.polyline3d(x[rng], y[rng], z[rng])
end
end
@@ -1212,13 +1437,15 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
xseg, yseg = x[rng], y[rng]
# draw the interior
- gr_set_fill(get_fillcolor(series, clims, i))
- gr_set_transparency(get_fillalpha(series, i))
+ fc = get_fillcolor(series, clims, i)
+ gr_set_fill(fc)
+ gr_set_transparency(fc, get_fillalpha(series, i))
GR.fillarea(xseg, yseg)
# draw the shapes
- gr_set_line(get_linewidth(series, i), get_linestyle(series, i), get_linecolor(series, clims, i))
- gr_set_transparency(get_linealpha(series, i))
+ lc = get_linecolor(series, clims, i)
+ gr_set_line(get_linewidth(series, i), get_linestyle(series, i), lc)
+ gr_set_transparency(lc, get_linealpha(series, i))
GR.polyline(xseg, yseg)
end
end
@@ -1226,19 +1453,19 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
elseif st == :image
z = transpose_z(series, series[:z].surf, true)'
- w, h = length(x), length(y)
+ w, h = size(z)
xinds = sort(1:w, rev = xaxis[:flip])
yinds = sort(1:h, rev = yaxis[:flip])
z = z[xinds, yinds]
xmin, xmax = ignorenan_extrema(series[:x]); ymin, ymax = ignorenan_extrema(series[:y])
if eltype(z) <: Colors.AbstractGray
- grey = round.(UInt8, float(z) * 255)
+ grey = round.(UInt8, clamp.(float(z) * 255, 0, 255))
rgba = map(c -> UInt32( 0xff000000 + UInt(c)<<16 + UInt(c)<<8 + UInt(c) ), grey)
else
- rgba = map(c -> UInt32( round(UInt, alpha(c) * 255) << 24 +
- round(UInt, blue(c) * 255) << 16 +
- round(UInt, green(c) * 255) << 8 +
- round(UInt, red(c) * 255) ), z)
+ rgba = map(c -> UInt32( round(UInt, clamp(alpha(c) * 255, 0, 255)) << 24 +
+ round(UInt, clamp(blue(c) * 255, 0, 255)) << 16 +
+ round(UInt, clamp(green(c) * 255, 0, 255)) << 8 +
+ round(UInt, clamp(red(c) * 255, 0, 255)) ), z)
end
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
end
@@ -1254,14 +1481,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
# draw the colorbar
- GR.savestate()
- # special colorbar with steps is drawn for contours
- if cmap && any(series[:seriestype] != :contour for series in series_list(sp))
- gr_set_line(1, :solid, yaxis[:foreground_color_axis])
- gr_set_transparency(1)
- gr_colorbar(sp, clims)
- end
- GR.restorestate()
+ hascolorbar(sp) && gr_draw_colorbar(cbar, sp, clims)
# add the legend
if sp[:legend] != :none
@@ -1269,30 +1489,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
GR.selntran(0)
GR.setscale(0)
gr_set_font(legendfont(sp))
- w = 0
- i = 0
- n = 0
- if sp[:legendtitle] != nothing
- tbx, tby = gr_inqtext(0, 0, string(sp[:legendtitle]))
- w = tbx[3] - tbx[1]
- n += 1
- end
- for series in series_list(sp)
- should_add_to_legend(series) || continue
- n += 1
- if typeof(series[:label]) <: Array
- i += 1
- lab = series[:label][i]
- else
- lab = series[:label]
- end
- tbx, tby = gr_inqtext(0, 0, string(lab))
- w = max(w, tbx[3] - tbx[1])
- end
+ w = legendw
+ i = legendi
+ n = legendn
if w > 0
dy = _gr_point_mult[1] * sp[:legendfontsize] * 1.75
h = dy*n
- (xpos,ypos) = gr_legend_pos(sp[:legend],w,h)
+ xpos, ypos = gr_legend_pos(sp, w, h)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
gr_set_fillcolor(sp[:background_color_legend])
GR.fillrect(xpos - 0.08, xpos + w + 0.02, ypos + dy, ypos - dy * n)
@@ -1302,30 +1505,33 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if sp[:legendtitle] != nothing
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_HALF)
gr_set_textcolor(sp[:legendfontcolor])
- gr_set_transparency(1)
+ gr_set_transparency(sp[:legendfontcolor])
gr_text(xpos - 0.03 + 0.5*w, ypos, string(sp[:legendtitle]))
ypos -= dy
end
for series in series_list(sp)
should_add_to_legend(series) || continue
st = series[:seriestype]
- gr_set_line(get_linewidth(series), get_linestyle(series), get_linecolor(series, clims)) #, series[:linealpha])
+ lc = get_linecolor(series, clims)
+ gr_set_line(get_linewidth(series), get_linestyle(series), lc) #, series[:linealpha])
if (st == :shape || series[:fillrange] != nothing) && series[:ribbon] == nothing
- gr_set_fill(get_fillcolor(series, clims)) #, series[:fillalpha])
+ fc = get_fillcolor(series, clims)
+ gr_set_fill(fc) #, series[:fillalpha])
l, r = xpos-0.07, xpos-0.01
b, t = ypos-0.4dy, ypos+0.4dy
x = [l, r, r, l, l]
y = [b, b, t, t, b]
- gr_set_transparency(get_fillalpha(series))
+ gr_set_transparency(fc, get_fillalpha(series))
gr_polyline(x, y, GR.fillarea)
- gr_set_transparency(get_linealpha(series))
- gr_set_line(get_linewidth(series), get_linestyle(series), get_linecolor(series, clims))
+ lc = get_linecolor(series, clims)
+ gr_set_transparency(lc, get_linealpha(series))
+ gr_set_line(get_linewidth(series), get_linestyle(series), lc)
st == :shape && gr_polyline(x, y)
end
if st in (:path, :straightline)
- gr_set_transparency(get_linealpha(series))
+ gr_set_transparency(lc, get_linealpha(series))
if series[:fillrange] == nothing || series[:ribbon] != nothing
GR.polyline([xpos - 0.07, xpos - 0.01], [ypos, ypos])
else
@@ -1387,16 +1593,7 @@ const _gr_mimeformats = Dict(
"image/svg+xml" => "svg",
)
-const _gr_wstype_default = @static if Sys.islinux()
- "x11"
- # "cairox11"
-elseif Sys.isapple()
- "quartz"
-else
- "use_default"
-end
-
-const _gr_wstype = Ref(get(ENV, "GKSwstype", _gr_wstype_default))
+const _gr_wstype = Ref(get(ENV, "GKSwstype", ""))
gr_set_output(wstype::String) = (_gr_wstype[] = wstype)
for (mime, fmt) in _gr_mimeformats
@@ -1431,7 +1628,7 @@ function _display(plt::Plot{GRBackend})
rm(filepath)
else
ENV["GKS_DOUBLE_BUF"] = true
- if _gr_wstype[] != "use_default"
+ if _gr_wstype[] != ""
ENV["GKSwstype"] = _gr_wstype[]
end
gr_display(plt)
diff --git a/src/backends/hdf5.jl b/src/backends/hdf5.jl
index 28dd767d..9b357216 100644
--- a/src/backends/hdf5.jl
+++ b/src/backends/hdf5.jl
@@ -16,16 +16,19 @@ Read from .hdf5 file using:
#==TODO
===============================================================================
- 1. Support more features
- - SeriesAnnotations & GridLayout known to be missing.
- 3. Improve error handling.
+ 1. Support more features.
+ - GridLayout known not to be working.
+ 2. Improve error handling.
- 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.
- 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 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
@@ -56,53 +59,13 @@ const HDF5PLOT_PLOTREF = HDF5Plot_PlotRef(nothing)
#Simple sub-structures that can just be written out using _hdf5plot_gwritefields:
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
if length(HDF5PLOT_MAP_TELEM2STR) < 1
@@ -125,7 +88,8 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
"GRIDLAYOUT" => GridLayout,
"ROOTLAYOUT" => RootLayout,
"SERIESANNOTATIONS" => SeriesAnnotations,
-# "PLOTTEXT" => PlotText,
+ "PLOTTEXT" => PlotText,
+ "SHAPE" => Shape,
"COLORGRADIENT" => ColorGradient,
"AXIS" => Axis,
"SURFACE" => Surface,
@@ -284,6 +248,14 @@ end
# ----------------------------------------------------------------
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
_hdf5plot_writetype(grp, k, HDF5PlotNative)
end
@@ -338,10 +310,6 @@ function _hdf5plot_gwrite(grp, k::String, v::Colorant)
end
#Custom vector (when not using simple numeric type):
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)
_hdf5plot_writetype(vgrp, Array) #ANY
sz = size(v)
@@ -351,7 +319,7 @@ function _hdf5plot_gwritearray(grp, k::String, v::Array{T}) where T
coord = lidx[iter]
elem = v[iter]
idxstr = join(coord, "_")
- _hdf5plot_gwrite(vgrp, "v$idxstr", v[iter])
+ _hdf5plot_gwrite(vgrp, "v$idxstr", elem)
end
_hdf5plot_gwrite(vgrp, "dim", [sz...])
@@ -402,10 +370,6 @@ end
# return
# end
-function _hdf5plot_gwrite(grp, k::String, v::SeriesAnnotations)
- #Currently no support for SeriesAnnotations
- return
-end
function _hdf5plot_gwrite(grp, k::String, v::Subplot)
grp = HDF5.g_create(grp, k)
_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)
return tuple(v...)
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)
grp = HDF5.g_open(grp, k)
@@ -601,11 +588,6 @@ end
function _hdf5plot_read(sp::Subplot, subpath::String, f)
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"))
nseries = _hdf5plot_readcount(grp)
@@ -617,6 +599,12 @@ function _hdf5plot_read(sp::Subplot, subpath::String, f)
_hdf5_merge!(sp.series_list[end].plotattributes, kwlist)
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
end
diff --git a/src/backends/inspectdr.jl b/src/backends/inspectdr.jl
index 56fd7f32..c1e5c5b8 100644
--- a/src/backends/inspectdr.jl
+++ b/src/backends/inspectdr.jl
@@ -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
@@ -349,8 +294,8 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
- xmin, xmax = axis_limits(xaxis)
- ymin, ymax = axis_limits(yaxis)
+ xmin, xmax = axis_limits(sp, :x)
+ ymin, ymax = axis_limits(sp, :y)
if ispolar(sp)
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
rmax = NaNMath.max(abs(ymin), abs(ymax))
diff --git a/src/backends/pgfplots.jl b/src/backends/pgfplots.jl
index 26865bb9..8f849c07 100644
--- a/src/backends/pgfplots.jl
+++ b/src/backends/pgfplots.jl
@@ -2,47 +2,6 @@
# 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(
@@ -67,7 +26,8 @@ const _pgfplots_markers = KW(
:star6 => "asterisk",
:diamond => "diamond*",
:pentagon => "pentagon*",
- :hline => "-"
+ :hline => "-",
+ :vline => "|"
)
const _pgfplots_legend_pos = KW(
@@ -211,7 +171,7 @@ function pgf_series(sp::Subplot, series::Series)
elseif st == :shape
shape_data(series)
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
else
plotattributes[:x], plotattributes[:y]
@@ -401,13 +361,13 @@ function pgf_axis(sp::Subplot, letter)
# limits
# TODO: support zlims
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,:max)] = lims[2]
end
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
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
push!(style, string(letter, "tick = {", join(tick_values,","), "}"))
@@ -439,14 +399,18 @@ function pgf_axis(sp::Subplot, letter)
# framestyle
if framestyle in (:axes, :origin)
axispos = framestyle == :axes ? "left" : "middle"
- # the * after lines disables the arrows at the axes
- push!(style, string("axis lines* = ", axispos))
+ if axis[:draw_arrow]
+ 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
if framestyle == :zerolines
push!(style, string("extra ", letter, " ticks = 0"))
push!(style, string("extra ", letter, " tick labels = "))
- push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_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
if !axis[:showaxis]
@@ -455,7 +419,7 @@ function pgf_axis(sp::Subplot, letter)
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
else
- push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}"))
+ push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}"))
end
# return the style list and KW args
@@ -604,7 +568,7 @@ end
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
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))
end
diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl
index 6164af37..977d338f 100644
--- a/src/backends/plotly.jl
+++ b/src/backends/plotly.jl
@@ -1,60 +1,6 @@
# 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_string_supported(::PlotlyBackend) = true
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 = """
-
-"""
-
-# if we're in IJulia call setupnotebook to load js and css
-if isijulia()
- display("text/html", _js_script)
-end
-
# if isatom()
# import Atom
# Atom.@msg evaljs(_js_code)
@@ -102,8 +27,6 @@ end
using UUIDs
push!(_initialized_backends, :plotly)
-
-
# ----------------------------------------------------------------
const _plotly_legend_pos = KW(
@@ -115,7 +38,7 @@ const _plotly_legend_pos = KW(
:bottomright => [1., 0.],
:topright => [1., 1.],
:topleft => [0., 1.]
- )
+)
plotly_legend_pos(pos::Symbol) = get(_plotly_legend_pos, pos, [1.,1.])
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real, T<:Real} = v
@@ -191,8 +114,8 @@ function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
if aspect_ratio == :equal
aspect_ratio = 1.0
end
- xmin,xmax = axis_limits(sp[:xaxis])
- ymin,ymax = axis_limits(sp[:yaxis])
+ xmin,xmax = axis_limits(sp, :x)
+ ymin,ymax = axis_limits(sp, :y)
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
parea_ratio = width(plotarea) / height(plotarea)
if want_ratio > parea_ratio
@@ -251,7 +174,7 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
ax[:tickangle] = -axis[:rotation]
ax[:type] = plotly_scale(axis[:scale])
- lims = axis_limits(axis)
+ lims = axis_limits(sp, letter)
if axis[:ticks] != :native || axis[:lims] != :auto
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[:linecolor] = rgba_string(axis[:foreground_color_axis])
- # flip
- if axis[:flip]
- ax[:range] = reverse(ax[:range])
- end
-
# ticks
if axis[:ticks] != :native
- ticks = get_ticks(axis)
+ ticks = get_ticks(sp, axis)
ttype = ticksType(ticks)
if ttype == :ticks
ax[:tickmode] = "array"
@@ -285,20 +203,24 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
ax[:showgrid] = false
end
+ # flip
+ if axis[:flip]
+ ax[:range] = reverse(ax[:range])
+ end
ax
end
-function plotly_polaraxis(axis::Axis)
+function plotly_polaraxis(sp::Subplot, axis::Axis)
ax = KW(
:visible => axis[:showaxis],
:showline => axis[:grid],
)
if axis[:letter] == :x
- ax[:range] = rad2deg.(axis_limits(axis))
+ ax[:range] = rad2deg.(axis_limits(sp, :x))
else
- ax[:range] = axis_limits(axis)
+ ax[:range] = axis_limits(sp, :y)
ax[:orientation] = -90
end
@@ -360,8 +282,8 @@ function plotly_layout(plt::Plot)
),
)
elseif ispolar(sp)
- plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp[:xaxis])
- plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp[:yaxis])
+ plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp, sp[:xaxis])
+ plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp, sp[:yaxis])
else
plotattributes_out[Symbol("xaxis$(x_idx)")] = plotly_axis(plt, sp[:xaxis], sp)
# don't allow yaxis to be reupdated/reanchored in a linked subplot
@@ -431,7 +353,7 @@ end
function plotly_colorscale(grad::ColorGradient, α)
[[grad.values[i], rgba_string(plot_color(grad.colors[i], α))] for i in 1:length(grad.colors)]
end
-plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
+plotly_colorscale(c::Colorant,α) = plotly_colorscale(_as_gradient(c),α)
function plotly_colorscale(c::AbstractVector{<:RGBA}, α)
if length(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)
if formatter == datetimeformatter
- map(xi -> replace(formatter(xi), "T", " "), x)
+ map(xi -> replace(formatter(xi), "T" => " "), x)
elseif formatter == dateformatter
map(xi -> string(formatter(xi), " 00:00:00"), x)
elseif formatter == timeformatter
@@ -583,13 +505,13 @@ function plotly_series(plt::Plot, series::Series)
plotattributes_out[:showscale] = hascolorbar(sp)
elseif st == :contour
+ filled = isfilledcontour(series)
plotattributes_out[:type] = "contour"
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
- # plotattributes_out[:showscale] = series[:colorbar] != :none
- plotattributes_out[:ncontours] = series[:levels]
- plotattributes_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
+ plotattributes_out[:ncontours] = series[:levels] + 2
+ plotattributes_out[:contours] = KW(:coloring => filled ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
plotattributes_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
- plotattributes_out[:showscale] = hascolorbar(sp)
+ plotattributes_out[:showscale] = hascolorbar(sp) && hascolorbar(series)
elseif st in (:surface, :wireframe)
plotattributes_out[:type] = "surface"
@@ -851,7 +773,7 @@ end
function plotly_polar!(plotattributes_out::KW, series::Series)
# convert polar plots x/y to theta/radius
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[:r] = r
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})
- jsfilename = _use_remote[] ? _plotly_js_path_remote : ("file://" * _plotly_js_path)
- # ""
- ""
+ local_file = ("file://" * plotly_local_file_path)
+ plotly = use_local_dependencies[] ? local_file : plotly_remote_file_path
+ 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", """
+
+ """)
+ ijulia_initialized[] = true
+ end
+ # IJulia just needs one initialization
+ isijulia() && return ""
+ return ""
end
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
@@ -929,11 +866,16 @@ end
# ----------------------------------------------------------------
-
-function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
- write(io, html_head(plt) * html_body(plt))
+function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{PlotlyBackend})
+ data = []
+ for series in plot.series_list
+ append!(data, plotly_series(plot, series))
+ end
+ layout = plotly_layout(plot)
+ JSON.print(io, Dict(:data => data, :layout => layout))
end
+
function _display(plt::Plot{PlotlyBackend})
standalone_html_window(plt)
end
diff --git a/src/backends/plotlyjs.jl b/src/backends/plotlyjs.jl
index 98e626c1..83952c71 100644
--- a/src/backends/plotlyjs.jl
+++ b/src/backends/plotlyjs.jl
@@ -1,18 +1,11 @@
-
-# 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
+# https://github.com/sglyon/PlotlyJS.jl
# --------------------------------------------------------------------------------------
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
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
PlotlyJS.plot()
end
@@ -56,23 +49,16 @@ end
# ----------------------------------------------------------------
-function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
- if isijulia() && !_use_remote[]
- write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
- else
- show(io, MIME("text/html"), plt.o)
- end
+_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = show(io, MIME("text/html"), plt.o)
+_show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="svg")
+_show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="png")
+_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="pdf")
+_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plt.o, format="eps")
+
+function _show(io::IO, m::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend})
+ show(io, m, plt.o)
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})
filename = string(tempname(), ".html")
diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl
index 4524e967..eec1e6ca 100644
--- a/src/backends/pyplot.jl
+++ b/src/backends/pyplot.jl
@@ -1,55 +1,9 @@
+# Do "using PyPlot: PyCall, LaTeXStrings" without dependency warning:
+const PyCall = PyPlot.PyCall
+const LaTeXStrings = PyPlot.LaTeXStrings
# https://github.com/stevengj/PyPlot.jl
-const _pyplot_attr = merge_with_base_supported([
- :annotations,
- :background_color_legend, :background_color_inside, :background_color_outside,
- :foreground_color_grid, :foreground_color_legend, :foreground_color_title,
- :foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
- :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, :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,
- :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,
- :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]
is_marker_supported(::PyPlotBackend, shape::Shape) = true
@@ -67,7 +21,7 @@ pyfont = PyPlot.pyimport("matplotlib.font_manager")
pyticker = PyPlot.pyimport("matplotlib.ticker")
pycmap = PyPlot.pyimport("matplotlib.cm")
pynp = PyPlot.pyimport("numpy")
-pynp["seterr"](invalid="ignore")
+pynp."seterr"(invalid="ignore")
pytransforms = PyPlot.pyimport("matplotlib.transforms")
pycollections = PyPlot.pyimport("matplotlib.collections")
pyart3d = PyPlot.art3D
@@ -80,6 +34,11 @@ else
:set_facecolor
end
+# PyCall API changes in v1.90.0
+if !isdefined(PyCall, :_setproperty!)
+ @warn "Plots no longer supports PyCall < 1.90.0 and PyPlot < 2.8.0. Either update PyCall and PyPlot or pin Plots to a version <= 0.23.2."
+end
+
# # convert colorant to 4-tuple RGBA
# py_color(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
@@ -110,17 +69,17 @@ py_color(c::Colorant, α) = py_color(plot_color(c, α))
function py_colormap(grad::ColorGradient)
pyvals = [(z, py_color(grad[z])) for z in grad.values]
- cm = pycolors["LinearSegmentedColormap"][:from_list]("tmp", pyvals)
- cm[:set_bad](color=(0,0,0,0.0), alpha=0.0)
+ cm = pycolors."LinearSegmentedColormap"."from_list"("tmp", pyvals)
+ cm."set_bad"(color=(0,0,0,0.0), alpha=0.0)
cm
end
-py_colormap(c) = py_colormap(cgrad())
+py_colormap(c::Colorant) = py_colormap(_as_gradient(c))
function py_shading(c, z)
cmap = py_colormap(c)
- ls = pycolors["LightSource"](270,45)
- ls[:shade](z, cmap, vert_exag=0.1, blend_mode="soft")
+ ls = pycolors."LightSource"(270,45)
+ ls."shade"(z, cmap, vert_exag=0.1, blend_mode="soft")
end
# get the style (solid, dashed, etc)
@@ -143,7 +102,7 @@ function py_marker(marker::Shape)
mat[i,2] = y[i]
end
mat[n+1,:] = mat[1,:]
- pypath["Path"](mat)
+ pypath."Path"(mat)
end
const _path_MOVETO = UInt8(1)
@@ -228,7 +187,7 @@ end
# end
function get_locator_and_formatter(vals::AVec)
- pyticker["FixedLocator"](1:length(vals)), pyticker["FixedFormatter"](vals)
+ pyticker."FixedLocator"(1:length(vals)), pyticker."FixedFormatter"(vals)
end
function add_pyfixedformatter(cbar, vals::AVec)
@@ -236,53 +195,40 @@ function add_pyfixedformatter(cbar, vals::AVec)
cbar[:update_ticks]()
end
-@require LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" begin
- function labelfunc(scale::Symbol, backend::PyPlotBackend)
- if scale == :log10
- x -> LaTeXStrings.latexstring("10^{$x}")
- elseif scale == :log2
- x -> LaTeXStrings.latexstring("2^{$x}")
- elseif scale == :ln
- x -> LaTeXStrings.latexstring("e^{$x}")
- else
- string
- end
+function labelfunc(scale::Symbol, backend::PyPlotBackend)
+ if scale == :log10
+ x -> LaTeXStrings.latexstring("10^{$x}")
+ elseif scale == :log2
+ x -> LaTeXStrings.latexstring("2^{$x}")
+ elseif scale == :ln
+ x -> LaTeXStrings.latexstring("e^{$x}")
+ else
+ string
end
end
-@require PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" begin
- function py_mask_nans(z)
- # pynp["ma"][:masked_invalid](z)))
- PyCall.pycall(pynp["ma"][:masked_invalid], Any, z)
- # pynp["ma"][:masked_where](pynp["isnan"](z),z)
- end
+function py_mask_nans(z)
+ # pynp["ma"][:masked_invalid](z)))
+ PyCall.pycall(pynp."ma"."masked_invalid", Any, z)
+ # pynp["ma"][:masked_where](pynp["isnan"](z),z)
end
# ---------------------------------------------------------------------------
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
- x, y = series[:x], series[:y]
- nx, ny = length(x), length(y)
- if !isa(get(series.plotattributes, :z, nothing), Surface) && nx != ny
- if nx < ny
- series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
- else
- series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
+ if series[:x] != nothing
+ x, y = series[:x], series[:y]
+ nx, ny = length(x), length(y)
+ if !isa(get(series.plotattributes, :z, nothing), Surface) && nx != ny
+ if nx < ny
+ series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
+ else
+ series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
+ end
end
end
end
-# total hack due to PyPlot bug (see issue #145).
-# hack: duplicate the color vector when the total rgba fields is the same as the series length
-function py_color_fix(c, x)
- if (typeof(c) <: AbstractArray && length(c)*4 == length(x)) ||
- (typeof(c) <: Tuple && length(x) == 4)
- vcat(c, c)
- else
- c
- end
-end
-
py_linecolor(series::Series) = py_color(series[:linecolor])
py_markercolor(series::Series) = py_color(series[:markercolor])
py_markerstrokecolor(series::Series) = py_color(series[:markerstrokecolor])
@@ -309,26 +255,28 @@ py_fillcolormap(series::Series) = py_colormap(series[:fillcolor])
# Figure utils -- F*** matplotlib for making me work so hard to figure this crap out
# the drawing surface
-py_canvas(fig) = fig[:canvas]
+py_canvas(fig) = fig."canvas"
# the object controlling draw commands
-py_renderer(fig) = py_canvas(fig)[:get_renderer]()
+py_renderer(fig) = py_canvas(fig)."get_renderer"()
# draw commands... paint the screen (probably updating internals too)
-py_drawfig(fig) = fig[:draw](py_renderer(fig))
+py_drawfig(fig) = fig."draw"(py_renderer(fig))
# py_drawax(ax) = ax[:draw](py_renderer(ax[:get_figure]()))
# get a vector [left, right, bottom, top] in PyPlot coords (origin is bottom-left!)
-py_extents(obj) = obj[:get_window_extent]()[:get_points]()
+py_extents(obj) = obj."get_window_extent"()."get_points"()
# compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left
function py_bbox(obj)
- fl, fr, fb, ft = py_extents(obj[:get_figure]())
+ fl, fr, fb, ft = py_extents(obj."get_figure"())
l, r, b, t = py_extents(obj)
BoundingBox(l*px, (ft-t)*px, (r-l)*px, (t-b)*px)
end
+py_bbox(::Nothing) = BoundingBox(0mm, 0mm)
+
# get the bounding box of the union of the objects
function py_bbox(v::AVec)
bbox_union = defaultbox
@@ -340,13 +288,13 @@ end
# bounding box: union of axis tick labels
function py_bbox_ticks(ax, letter)
- labels = ax[Symbol("get_"*letter*"ticklabels")]()
+ labels = getproperty(ax, Symbol("get_"*letter*"ticklabels"))()
py_bbox(labels)
end
# bounding box: axis guide
function py_bbox_axislabel(ax, letter)
- pyaxis_label = ax[Symbol("get_"*letter*"axis")]()[:label]
+ pyaxis_label = getproperty(ax, Symbol("get_"*letter*"axis"))().label
py_bbox(pyaxis_label)
end
@@ -362,11 +310,14 @@ end
function py_bbox_title(ax)
bb = defaultbox
for s in (:title, :_left_title, :_right_title)
- bb = bb + py_bbox(ax[s])
+ bb = bb + py_bbox(getproperty(ax, s))
end
bb
end
+# bounding box: legend
+py_bbox_legend(ax) = py_bbox(ax."get_legend"())
+
function py_thickness_scale(plt::Plot{PyPlotBackend}, ptsz)
ptsz * plt[:thickness_scaling]
end
@@ -400,7 +351,7 @@ function py_init_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
proj = (proj in (nothing,:none) ? nothing : string(proj))
# add a new axis, and force it to create a new one by setting a distinct label
- ax = fig[:add_axes](
+ ax = fig."add_axes"(
[0,0,1,1],
label = string(gensym()),
projection = proj
@@ -432,6 +383,18 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
elseif st == :shape
x, y = shape_data(series)
end
+
+ if ispolar(series)
+ # make negative radii positive and flip the angle
+ # (PyPlot ignores negative radii)
+ for i in eachindex(y)
+ if y[i] < 0
+ y[i] = -y[i]
+ x[i] -= π
+ end
+ end
+ end
+
xyargs = (st in _3dTypes ? (x,y,z) : (x,y))
# handle zcolor and get c/cmap
@@ -439,7 +402,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
vmin, vmax = clims = get_clims(sp)
# Dict to store extra kwargs
- extrakw = KW(:vmin => vmin, :vmax => vmax)
+ if st == :wireframe
+ extrakw = KW() # vmin, vmax cause an error for wireframe plot
+ else
+ extrakw = KW(:vmin => vmin, :vmax => vmax)
+ end
# holds references to any python object representing the matplotlib series
handles = []
@@ -497,10 +464,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
# push!(handles, handle)
# else
for (i, rng) in enumerate(iter_segments(series))
- handle = ax[:plot]((arg[rng] for arg in xyargs)...;
+ handle = ax."plot"((arg[rng] for arg in xyargs)...;
label = i == 1 ? series[:label] : "",
zorder = series[:series_plotindex],
- color = py_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
+ color = py_color(single_color(get_linecolor(series, clims, i)), get_linealpha(series, i)),
linewidth = py_thickness_scale(plt, get_linewidth(series, i)),
linestyle = py_linestyle(st, get_linestyle(series, i)),
solid_capstyle = "round",
@@ -525,7 +492,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
:linestyle => py_linestyle(st, get_linestyle(series)),
)
add_arrows(x, y) do xyprev, xy
- ax[:annotate]("",
+ ax."annotate"("",
xytext = (0.001xyprev[1] + 0.999xy[1], 0.001xyprev[2] + 0.999xy[2]),
xy = xy,
arrowprops = arrowprops,
@@ -547,7 +514,13 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
else
py_color(plot_color(series[:markercolor], series[:markeralpha]))
end
- extrakw[:c] = py_color_fix(markercolor, x)
+ extrakw[:c] = if markercolor isa Array
+ permutedims(hcat([[m...] for m in markercolor]...),[2,1])
+ elseif markercolor isa Tuple
+ reshape([markercolor...], 1, length(markercolor))
+ else
+ error("This case is not handled. Please file an issue.")
+ end
xyargs = if st == :bar && !isvertical(series)
(y, x)
else
@@ -555,7 +528,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end
if isa(series[:markershape], AbstractVector{Shape})
- # this section will create one scatter per data point to accomodate the
+ # this section will create one scatter per data point to accommodate the
# vector of shapes
handle = []
x,y = xyargs
@@ -565,7 +538,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
for i=1:length(y)
extrakw[:c] = _cycle(markercolor, i)
- push!(handle, ax[:scatter](_cycle(x,i), _cycle(y,i);
+ push!(handle, ax."scatter"(_cycle(x,i), _cycle(y,i);
label = series[:label],
zorder = series[:series_plotindex] + 0.5,
marker = py_marker(_cycle(shapes,i)),
@@ -575,10 +548,72 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
extrakw...
))
end
+ push!(handles, handle)
+ elseif isa(series[:markershape], AbstractVector{Symbol})
+ handle = []
+ x,y = xyargs
+ shapes = series[:markershape]
+
+ prev_marker = py_marker(_cycle(shapes,1))
+
+ cur_x_list = []
+ cur_y_list = []
+
+ cur_color_list = []
+ cur_scale_list = []
+
+ delete!(extrakw, :c)
+
+ for i=1:length(y)
+ cur_marker = py_marker(_cycle(shapes,i))
+
+ if ( cur_marker == prev_marker )
+ push!(cur_x_list, _cycle(x,i))
+ push!(cur_y_list, _cycle(y,i))
+
+ push!(cur_color_list, _cycle(markercolor, i))
+ push!(cur_scale_list, py_thickness_scale(plt, _cycle(series[:markersize],i) .^ 2))
+
+ continue
+ end
+
+ push!(handle, ax."scatter"(cur_x_list, cur_y_list;
+ label = series[:label],
+ zorder = series[:series_plotindex] + 0.5,
+ marker = prev_marker,
+ s = cur_scale_list,
+ edgecolors = py_markerstrokecolor(series),
+ linewidths = py_thickness_scale(plt, series[:markerstrokewidth]),
+ facecolors = cur_color_list,
+ extrakw...
+ ))
+
+ cur_x_list = [_cycle(x,i)]
+ cur_y_list = [_cycle(y,i)]
+
+ cur_color_list = [_cycle(markercolor, i)]
+ cur_scale_list = [py_thickness_scale(plt, _cycle(series[:markersize],i) .^ 2)]
+
+ prev_marker = cur_marker
+ end
+
+ if !isempty(cur_color_list)
+ push!(handle, ax."scatter"(cur_x_list, cur_y_list;
+ label = series[:label],
+ zorder = series[:series_plotindex] + 0.5,
+ marker = prev_marker,
+ s = cur_scale_list,
+ edgecolors = py_markerstrokecolor(series),
+ linewidths = py_thickness_scale(plt, series[:markerstrokewidth]),
+ facecolors = cur_color_list,
+ extrakw...
+ ))
+ end
+
push!(handles, handle)
else
# do a normal scatter plot
- handle = ax[:scatter](xyargs...;
+ handle = ax."scatter"(xyargs...;
label = series[:label],
zorder = series[:series_plotindex] + 0.5,
marker = py_marker(series[:markershape]),
@@ -592,7 +627,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end
if st == :hexbin
- handle = ax[:hexbin](x, y;
+ handle = ax."hexbin"(x, y;
label = series[:label],
zorder = series[:series_plotindex],
gridsize = series[:bins],
@@ -624,7 +659,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end
# contour lines
- handle = ax[:contour](x, y, z, levelargs...;
+ handle = ax."contour"(x, y, z, levelargs...;
label = series[:label],
zorder = series[:series_plotindex],
linewidths = py_thickness_scale(plt, series[:linewidth]),
@@ -632,13 +667,13 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
extrakw...
)
if series[:contour_labels] == true
- PyPlot.clabel(handle, handle[:levels])
+ PyPlot."clabel"(handle, handle.levels)
end
push!(handles, handle)
# contour fills
if series[:fillrange] != nothing
- handle = ax[:contourf](x, y, z, levelargs...;
+ handle = ax."contourf"(x, y, z, levelargs...;
label = series[:label],
zorder = series[:series_plotindex] + 0.5,
extrakw...
@@ -664,7 +699,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
extrakw[:cmap] = py_fillcolormap(series)
end
end
- handle = ax[st == :surface ? :plot_surface : :plot_wireframe](x, y, z;
+ handle = getproperty(ax, st == :surface ? :plot_surface : :plot_wireframe)(x, y, z;
label = series[:label],
zorder = series[:series_plotindex],
rstride = series[:stride][1],
@@ -679,7 +714,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
if series[:contours]
for (zdir,mat) in (("x",x), ("y",y), ("z",z))
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat)
- handle = ax[:contourf](x, y, z, levelargs...;
+ handle = ax."contourf"(x, y, z, levelargs...;
zdir = zdir,
cmap = py_fillcolormap(series),
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat) # where to draw the contour plane
@@ -691,7 +726,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
elseif typeof(z) <: AbstractVector
# tri-surface plot (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots)
- handle = ax[:plot_trisurf](x, y, z;
+ handle = ax."plot_trisurf"(x, y, z;
label = series[:label],
zorder = series[:series_plotindex],
cmap = py_fillcolormap(series),
@@ -716,9 +751,9 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
else
z # hopefully it's in a data format that will "just work" with imshow
end
- handle = ax[:imshow](z;
+ handle = ax."imshow"(z;
zorder = series[:series_plotindex],
- cmap = py_colormap([:black, :white]),
+ cmap = py_colormap(cgrad([:black, :white])),
vmin = 0.0,
vmax = 1.0,
extent = (xmin, xmax, ymax, ymin)
@@ -726,7 +761,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
push!(handles, handle)
# expand extrema... handle is AxesImage object
- xmin, xmax, ymax, ymin = handle[:get_extent]()
+ xmin, xmax, ymax, ymin = handle."get_extent"()
expand_extrema!(sp, xmin, xmax, ymin, ymax)
# sp[:yaxis].series[:flip] = true
end
@@ -741,7 +776,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
discrete_colorbar_values = dvals
end
- handle = ax[:pcolormesh](x, y, py_mask_nans(z);
+ handle = ax."pcolormesh"(x, y, py_mask_nans(z);
label = series[:label],
zorder = series[:series_plotindex],
cmap = py_fillcolormap(series),
@@ -756,8 +791,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
handle = []
for (i, rng) in enumerate(iter_segments(series))
if length(rng) > 1
- path = pypath["Path"](hcat(x[rng], y[rng]))
- patches = pypatches["PathPatch"](
+ path = pypath."Path"(hcat(x[rng], y[rng]))
+ patches = pypatches."PathPatch"(
path;
label = series[:label],
zorder = series[:series_plotindex],
@@ -767,14 +802,14 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
linestyle = py_linestyle(st, get_linestyle(series, i)),
fill = true
)
- push!(handle, ax[:add_patch](patches))
+ push!(handle, ax."add_patch"(patches))
end
end
push!(handles, handle)
end
if st == :pie
- handle = ax[:pie](y;
+ handle = ax."pie"(y;
# colors = # a vector of colors?
labels = pie_labels(sp, series)
)[1]
@@ -809,7 +844,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng)
end
- handle = ax[f](args..., trues(n), false, py_fillstepstyle(st);
+ handle = getproperty(ax, f)(args..., trues(n), false, py_fillstepstyle(st);
zorder = series[:series_plotindex],
facecolor = py_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
linewidths = 0
@@ -827,36 +862,36 @@ end
# --------------------------------------------------------------------------
-function py_set_lims(ax, axis::Axis)
+function py_set_lims(ax, sp::Subplot, axis::Axis)
letter = axis[:letter]
- lfrom, lto = axis_limits(axis)
- ax[Symbol("set_", letter, "lim")](lfrom, lto)
+ lfrom, lto = axis_limits(sp, letter)
+ getproperty(ax, Symbol("set_", letter, "lim"))(lfrom, lto)
end
function py_set_ticks(ax, ticks, letter)
ticks == :auto && return
- axis = ax[Symbol(letter,"axis")]
+ axis = getproperty(ax, Symbol(letter,"axis"))
if ticks == :none || ticks == nothing || ticks == false
kw = KW()
for dir in (:top,:bottom,:left,:right)
- kw[dir] = kw[Symbol(:label,dir)] = "off"
+ kw[dir] = kw[Symbol(:label,dir)] = false
end
- axis[:set_tick_params](;which="both", kw...)
+ axis."set_tick_params"(;which="both", kw...)
return
end
ttype = ticksType(ticks)
if ttype == :ticks
- axis[:set_ticks](ticks)
+ axis."set_ticks"(ticks)
elseif ttype == :ticks_and_labels
- axis[:set_ticks](ticks[1])
- axis[:set_ticklabels](ticks[2])
+ axis."set_ticks"(ticks[1])
+ axis."set_ticklabels"(ticks[2])
else
error("Invalid input for $(letter)ticks: $ticks")
end
end
-function py_compute_axis_minval(axis::Axis)
+function py_compute_axis_minval(sp::Subplot, axis::Axis)
# compute the smallest absolute value for the log scale's linear threshold
minval = 1.0
sps = axis.sps
@@ -870,17 +905,17 @@ function py_compute_axis_minval(axis::Axis)
end
# now if the axis limits go to a smaller abs value, use that instead
- vmin, vmax = axis_limits(axis)
+ vmin, vmax = axis_limits(sp, axis[:letter])
minval = NaNMath.min(minval, abs(vmin), abs(vmax))
minval
end
-function py_set_scale(ax, axis::Axis)
+function py_set_scale(ax, sp::Subplot, axis::Axis)
scale = axis[:scale]
letter = axis[:letter]
scale in supported_scales() || return @warn("Unhandled scale value in pyplot: $scale")
- func = ax[Symbol("set_", letter, "scale")]
+ func = getproperty(ax, Symbol("set_", letter, "scale"))
kw = KW()
arg = if scale == :identity
"linear"
@@ -892,7 +927,7 @@ function py_set_scale(ax, axis::Axis)
elseif scale == :log10
10
end
- kw[Symbol(:linthresh,letter)] = NaNMath.min(1e-16, py_compute_axis_minval(axis))
+ kw[Symbol(:linthresh,letter)] = NaNMath.min(1e-16, py_compute_axis_minval(sp, axis))
"symlog"
end
func(arg; kw...)
@@ -900,16 +935,16 @@ end
function py_set_axis_colors(sp, ax, a::Axis)
- for (loc, spine) in ax[:spines]
- spine[:set_color](py_color(a[:foreground_color_border]))
+ for (loc, spine) in ax.spines
+ spine."set_color"(py_color(a[:foreground_color_border]))
end
axissym = Symbol(a[:letter], :axis)
- if haskey(ax, axissym)
+ if PyCall.hasproperty(ax, axissym)
tickcolor = sp[:framestyle] in (:zerolines, :grid) ? py_color(plot_color(a[:foreground_color_grid], a[:gridalpha])) : py_color(a[:foreground_color_axis])
- ax[:tick_params](axis=string(a[:letter]), which="both",
+ ax."tick_params"(axis=string(a[:letter]), which="both",
colors=tickcolor,
labelcolor=py_color(a[:tickfontcolor]))
- ax[axissym][:label][:set_color](py_color(a[:guidefontcolor]))
+ getproperty(ax, axissym).label.set_color(py_color(a[:guidefontcolor]))
end
end
@@ -921,14 +956,14 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# update the fig
w, h = plt[:size]
fig = plt.o
- fig[:clear]()
+ fig."clear"()
dpi = plt[:dpi]
- fig[:set_size_inches](w/DPI, h/DPI, forward = true)
- fig[set_facecolor_sym](py_color(plt[:background_color_outside]))
- fig[:set_dpi](plt[:dpi])
+ fig."set_size_inches"(w/DPI, h/DPI, forward = true)
+ getproperty(fig, set_facecolor_sym)(py_color(plt[:background_color_outside]))
+ fig."set_dpi"(plt[:dpi])
# resize the window
- PyPlot.plt[:get_current_fig_manager]()[:resize](w, h)
+ PyPlot.plt."get_current_fig_manager"().resize(w, h)
# initialize subplots
for sp in plt.subplots
@@ -962,10 +997,10 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
else
:title
end
- ax[func][:set_text](sp[:title])
- ax[func][:set_fontsize](py_thickness_scale(plt, sp[:titlefontsize]))
- ax[func][:set_family](sp[:titlefontfamily])
- ax[func][:set_color](py_color(sp[:titlefontcolor]))
+ getproperty(ax, func)."set_text"(sp[:title])
+ getproperty(ax, func)."set_fontsize"(py_thickness_scale(plt, sp[:titlefontsize]))
+ getproperty(ax, func)."set_family"(sp[:titlefontfamily])
+ getproperty(ax, func)."set_color"(py_color(sp[:titlefontcolor]))
# ax[:set_title](sp[:title], loc = loc)
end
@@ -985,7 +1020,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
kw[:boundaries] = vcat(0, kw[:values] + 0.5)
elseif any(colorbar_series[attr] != nothing for attr in (:line_z, :fill_z, :marker_z))
cmin, cmax = get_clims(sp)
- norm = pycolors[:Normalize](vmin = cmin, vmax = cmax)
+ norm = pycolors."Normalize"(vmin = cmin, vmax = cmax)
f = if colorbar_series[:line_z] != nothing
py_linecolormap
elseif colorbar_series[:fill_z] != nothing
@@ -993,21 +1028,22 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
else
py_markercolormap
end
- cmap = pycmap[:ScalarMappable](norm = norm, cmap = f(colorbar_series))
- cmap[:set_array]([])
+ cmap = pycmap."ScalarMappable"(norm = norm, cmap = f(colorbar_series))
+ cmap."set_array"([])
handle = cmap
end
+ kw[:spacing] = "proportional"
# create and store the colorbar object (handle) and the axis that it is drawn on.
# note: the colorbar axis is positioned independently from the subplot axis
fig = plt.o
- cbax = fig[:add_axes]([0.8,0.1,0.03,0.8], label = string(gensym()))
- cb = fig[:colorbar](handle; cax = cbax, kw...)
- cb[:set_label](sp[:colorbar_title],size=py_thickness_scale(plt, sp[:yaxis][:guidefontsize]),family=sp[:yaxis][:guidefontfamily], color = py_color(sp[:yaxis][:guidefontcolor]))
- for lab in cb[:ax][:yaxis][:get_ticklabels]()
- lab[:set_fontsize](py_thickness_scale(plt, sp[:yaxis][:tickfontsize]))
- lab[:set_family](sp[:yaxis][:tickfontfamily])
- lab[:set_color](py_color(sp[:yaxis][:tickfontcolor]))
+ cbax = fig."add_axes"([0.8,0.1,0.03,0.8], label = string(gensym()))
+ cb = fig."colorbar"(handle; cax = cbax, kw...)
+ cb."set_label"(sp[:colorbar_title],size=py_thickness_scale(plt, sp[:yaxis][:guidefontsize]),family=sp[:yaxis][:guidefontfamily], color = py_color(sp[:yaxis][:guidefontcolor]))
+ for lab in cb."ax"."yaxis"."get_ticklabels"()
+ lab."set_fontsize"(py_thickness_scale(plt, sp[:yaxis][:tickfontsize]))
+ lab."set_family"(sp[:yaxis][:tickfontfamily])
+ lab."set_color"(py_color(sp[:yaxis][:tickfontcolor]))
end
sp.attr[:cbar_handle] = cb
sp.attr[:cbar_ax] = cbax
@@ -1015,28 +1051,28 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# framestyle
if !ispolar(sp) && !is3d(sp)
- ax[:spines]["left"][:set_linewidth](py_thickness_scale(plt, 1))
- ax[:spines]["bottom"][:set_linewidth](py_thickness_scale(plt, 1))
+ ax.spines["left"]."set_linewidth"(py_thickness_scale(plt, 1))
+ ax.spines["bottom"]."set_linewidth"(py_thickness_scale(plt, 1))
if sp[:framestyle] == :semi
intensity = 0.5
- ax[:spines]["right"][:set_alpha](intensity)
- ax[:spines]["top"][:set_alpha](intensity)
- ax[:spines]["right"][:set_linewidth](py_thickness_scale(plt, intensity))
- ax[:spines]["top"][:set_linewidth](py_thickness_scale(plt, intensity))
+ ax.spines["right"]."set_alpha"(intensity)
+ ax.spines["top"]."set_alpha"(intensity)
+ ax.spines["right"]."set_linewidth"(py_thickness_scale(plt, intensity))
+ ax.spines["top"]."set_linewidth"(py_thickness_scale(plt, intensity))
elseif sp[:framestyle] in (:axes, :origin)
- ax[:spines]["right"][:set_visible](false)
- ax[:spines]["top"][:set_visible](false)
+ ax.spines["right"]."set_visible"(false)
+ ax.spines["top"]."set_visible"(false)
if sp[:framestyle] == :origin
- ax[:spines]["bottom"][:set_position]("zero")
- ax[:spines]["left"][:set_position]("zero")
+ ax.spines["bottom"]."set_position"("zero")
+ ax.spines["left"]."set_position"("zero")
end
elseif sp[:framestyle] in (:grid, :none, :zerolines)
- for (loc, spine) in ax[:spines]
- spine[:set_visible](false)
+ for (loc, spine) in ax.spines
+ spine."set_visible"(false)
end
if sp[:framestyle] == :zerolines
- ax[:axhline](y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
- ax[:axvline](x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
+ ax."axhline"(y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
+ ax."axvline"(x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
end
end
end
@@ -1044,51 +1080,51 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# axis attributes
for letter in (:x, :y, :z)
axissym = Symbol(letter, :axis)
- haskey(ax, axissym) || continue
+ PyCall.hasproperty(ax, axissym) || continue
axis = sp[axissym]
- pyaxis = ax[axissym]
+ pyaxis = getproperty(ax, axissym)
if axis[:mirror] && letter != :z
pos = letter == :x ? "top" : "right"
- pyaxis[:set_label_position](pos) # the guides
- pyaxis[:set_ticks_position]("both") # the hash marks
- pyaxis[Symbol(:tick_, pos)]() # the tick labels
+ pyaxis."set_label_position"(pos) # the guides
+ pyaxis."set_ticks_position"("both") # the hash marks
+ getproperty(pyaxis, Symbol(:tick_, pos))() # the tick labels
end
if axis[:guide_position] != :auto && letter != :z
- pyaxis[:set_label_position](axis[:guide_position])
+ pyaxis."set_label_position"(axis[:guide_position])
end
- py_set_scale(ax, axis)
- axis[:ticks] != :native ? py_set_lims(ax, axis) : nothing
+ py_set_scale(ax, sp, axis)
+ axis[:ticks] != :native ? py_set_lims(ax, sp, axis) : nothing
if ispolar(sp) && letter == :y
- ax[:set_rlabel_position](90)
+ ax."set_rlabel_position"(90)
end
- ticks = sp[:framestyle] == :none ? nothing : get_ticks(axis)
+ ticks = sp[:framestyle] == :none ? nothing : get_ticks(sp, axis)
# don't show the 0 tick label for the origin framestyle
if sp[:framestyle] == :origin && length(ticks) > 1
- ticks[2][ticks[1] .== 0] = ""
+ ticks[2][ticks[1] .== 0] .= ""
end
axis[:ticks] != :native ? py_set_ticks(ax, ticks, letter) : nothing
- pyaxis[:set_tick_params](direction = axis[:tick_direction] == :out ? "out" : "in")
- ax[Symbol("set_", letter, "label")](axis[:guide])
+ pyaxis."set_tick_params"(direction = axis[:tick_direction] == :out ? "out" : "in")
+ getproperty(ax, Symbol("set_", letter, "label"))(axis[:guide])
if get(axis.plotattributes, :flip, false)
- ax[Symbol("invert_", letter, "axis")]()
+ getproperty(ax, Symbol("invert_", letter, "axis"))()
end
- pyaxis[:label][:set_fontsize](py_thickness_scale(plt, axis[:guidefontsize]))
- pyaxis[:label][:set_family](axis[:guidefontfamily])
- for lab in ax[Symbol("get_", letter, "ticklabels")]()
- lab[:set_fontsize](py_thickness_scale(plt, axis[:tickfontsize]))
- lab[:set_family](axis[:tickfontfamily])
- lab[:set_rotation](axis[:rotation])
+ pyaxis."label"."set_fontsize"(py_thickness_scale(plt, axis[:guidefontsize]))
+ pyaxis."label"."set_family"(axis[:guidefontfamily])
+ for lab in getproperty(ax, Symbol("get_", letter, "ticklabels"))()
+ lab."set_fontsize"(py_thickness_scale(plt, axis[:tickfontsize]))
+ lab."set_family"(axis[:tickfontfamily])
+ lab."set_rotation"(axis[:rotation])
end
if axis[:grid] && !(ticks in (:none, nothing, false))
fgcolor = py_color(axis[:foreground_color_grid])
- pyaxis[:grid](true,
+ pyaxis."grid"(true,
color = fgcolor,
linestyle = py_linestyle(:line, axis[:gridstyle]),
linewidth = py_thickness_scale(plt, axis[:gridlinewidth]),
alpha = axis[:gridalpha])
- ax[:set_axisbelow](true)
+ ax."set_axisbelow"(true)
else
- pyaxis[:grid](false)
+ pyaxis."grid"(false)
end
py_set_axis_colors(sp, ax, axis)
end
@@ -1096,50 +1132,51 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# showaxis
if !sp[:xaxis][:showaxis]
kw = KW()
- for dir in (:top, :bottom)
- if ispolar(sp)
- ax[:spines]["polar"][:set_visible](false)
- else
- ax[:spines][string(dir)][:set_visible](false)
- end
- kw[dir] = kw[Symbol(:label,dir)] = "off"
+ if ispolar(sp)
+ ax.spines["polar"].set_visible(false)
end
- ax[:xaxis][:set_tick_params](; which="both", kw...)
+ for dir in (:top, :bottom)
+ if !ispolar(sp)
+ ax.spines[string(dir)].set_visible(false)
+ end
+ kw[dir] = kw[Symbol(:label,dir)] = false
+ end
+ ax."xaxis"."set_tick_params"(; which="both", kw...)
end
if !sp[:yaxis][:showaxis]
kw = KW()
for dir in (:left, :right)
if !ispolar(sp)
- ax[:spines][string(dir)][:set_visible](false)
+ ax.spines[string(dir)].set_visible(false)
end
- kw[dir] = kw[Symbol(:label,dir)] = "off"
+ kw[dir] = kw[Symbol(:label,dir)] = false
end
- ax[:yaxis][:set_tick_params](; which="both", kw...)
+ ax."yaxis"."set_tick_params"(; which="both", kw...)
end
# aspect ratio
aratio = sp[:aspect_ratio]
if aratio != :none
- ax[:set_aspect](isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
+ ax."set_aspect"(isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
end
#camera/view angle
if is3d(sp)
#convert azimuthal to match GR behaviour
#view_init(elevation, azimuthal) so reverse :camera args
- ax[:view_init]((sp[:camera].-(90,0))[end:-1:1]...)
+ ax."view_init"((sp[:camera].-(90,0))[end:-1:1]...)
end
# legend
py_add_legend(plt, sp, ax)
# this sets the bg color inside the grid
- ax[set_facecolor_sym](py_color(sp[:background_color_inside]))
+ getproperty(ax, set_facecolor_sym)(py_color(sp[:background_color_inside]))
# link axes
x_ax_link, y_ax_link = sp[:xaxis].sps[1].o, sp[:yaxis].sps[1].o
- ax != x_ax_link && ax[:get_shared_x_axes]()[:join](ax, sp[:xaxis].sps[1].o)
- ax != y_ax_link && ax[:get_shared_y_axes]()[:join](ax, sp[:yaxis].sps[1].o)
+ ax != x_ax_link && ax."get_shared_x_axes"()."join"(ax, sp[:xaxis].sps[1].o)
+ ax != y_ax_link && ax."get_shared_y_axes"()."join"(ax, sp[:yaxis].sps[1].o)
end
py_drawfig(fig)
end
@@ -1159,7 +1196,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
toppad = 0mm
rightpad = 0mm
bottompad = 0mm
- for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax))
+ for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax), py_bbox_legend(ax))
if ispositive(width(bb)) && ispositive(height(bb))
leftpad = max(leftpad, left(plotbb) - left(bb))
toppad = max(toppad, top(plotbb) - top(bb))
@@ -1170,7 +1207,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
# optionally add the width of colorbar labels and colorbar to rightpad
if haskey(sp.attr, :cbar_ax)
- bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]())
+ bb = py_bbox(sp.attr[:cbar_handle]."ax"."get_yticklabels"())
sp.attr[:cbar_width] = _cbar_width + width(bb) + 2.3mm + (sp[:colorbar_title] == "" ? 0px : 30px)
rightpad = rightpad + sp.attr[:cbar_width]
end
@@ -1191,19 +1228,19 @@ end
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val)
ax = sp.o
- ax[:annotate](val, xy = (x,y), zorder = 999)
+ ax."annotate"(val, xy = (x,y), zorder = 999)
end
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
ax = sp.o
- ax[:annotate](val.str,
+ ax."annotate"(val.str,
xy = (x,y),
family = val.font.family,
color = py_color(val.font.color),
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
- rotation = val.font.rotation * 180 / π,
+ rotation = val.font.rotation,
size = py_thickness_scale(sp.plt, val.font.pointsize),
zorder = 999
)
@@ -1222,6 +1259,34 @@ const _pyplot_legend_pos = KW(
:topleft => "upper left"
)
+const _pyplot_legend_pos_outer = KW(
+ :outerright => "center left",
+ :outerleft => "right",
+ :outertop => "lower center",
+ :outerbottom => "upper center",
+ :outerbottomleft => "lower right",
+ :outerbottomright => "lower left",
+ :outertopright => "upper left",
+ :outertopleft => "upper right"
+)
+
+py_legend_pos(pos::Symbol) = get(_pyplot_legend_pos, pos, get(_pyplot_legend_pos_outer, pos, "best"))
+py_legend_pos(pos) = "lower left"
+
+const _pyplot_legend_bbox_outer = KW(
+ :outerright => (1.0, 0.5, 0.0, 0.0),
+ :outerleft => (-0.15, 0.5, 0.0, 0.0),
+ :outertop => (0.5, 1.0, 0.0, 0.0),
+ :outerbottom => (0.5, -0.15, 0.0, 0.0),
+ :outerbottomleft => (-0.15, 0.0, 0.0, 0.0),
+ :outerbottomright => (1.0, 0.0, 0.0, 0.0),
+ :outertopright => (1.0, 1.0, 0.0, 0.0),
+ :outertopleft => (-0.15, 1.0, 0.0, 0.0)
+)
+
+py_legend_bbox(pos::Symbol) = get(_pyplot_legend_bbox_outer, pos, (0.0, 0.0, 1.0, 1.0))
+py_legend_bbox(pos) = pos
+
function py_add_legend(plt::Plot, sp::Subplot, ax)
leg = sp[:legend]
clims = get_clims(sp)
@@ -1233,20 +1298,20 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
if should_add_to_legend(series)
# add a line/marker and a label
push!(handles, if series[:seriestype] == :shape || series[:fillrange] != nothing
- pypatches[:Patch](
- edgecolor = py_color(get_linecolor(series, clims), get_linealpha(series)),
- facecolor = py_color(get_fillcolor(series, clims), get_fillalpha(series)),
+ pypatches."Patch"(
+ edgecolor = py_color(single_color(get_linecolor(series, clims)), get_linealpha(series)),
+ facecolor = py_color(single_color(get_fillcolor(series, clims)), get_fillalpha(series)),
linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)),
linestyle = py_linestyle(series[:seriestype], get_linestyle(series))
)
- elseif series[:seriestype] in (:path, :straightline)
- PyPlot.plt[:Line2D]((0,1),(0,0),
- color = py_color(get_linecolor(series, clims), get_linealpha(series)),
+ elseif series[:seriestype] in (:path, :straightline, :scatter)
+ PyPlot.plt."Line2D"((0,1),(0,0),
+ color = py_color(single_color(get_linecolor(series, clims)), get_linealpha(series)),
linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)),
linestyle = py_linestyle(:path, get_linestyle(series)),
- marker = py_marker(series[:markershape]),
- markeredgecolor = py_color(get_markerstrokecolor(series), get_markerstrokealpha(series)),
- markerfacecolor = series[:marker_z] == nothing ? py_color(get_markercolor(series, clims), get_markeralpha(series)) : py_color(series[:markercolor][0.5])
+ marker = py_marker(first(series[:markershape])),
+ markeredgecolor = py_color(single_color(get_markerstrokecolor(series)), get_markerstrokealpha(series)),
+ markerfacecolor = py_color(single_color(get_markercolor(series, clims)), get_markeralpha(series))
)
else
series[:serieshandle][1]
@@ -1257,22 +1322,23 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
# if anything was added, call ax.legend and set the colors
if !isempty(handles)
- leg = ax[:legend](handles,
+ leg = ax."legend"(handles,
labels,
- loc = get(_pyplot_legend_pos, leg, "best"),
+ loc = py_legend_pos(leg),
+ bbox_to_anchor = py_legend_bbox(leg),
scatterpoints = 1,
fontsize = py_thickness_scale(plt, sp[:legendfontsize]),
facecolor = py_color(sp[:background_color_legend]),
edgecolor = py_color(sp[:foreground_color_legend]),
framealpha = alpha(plot_color(sp[:background_color_legend])),
)
- frame = leg[:get_frame]()
- frame[:set_linewidth](py_thickness_scale(plt, 1))
- leg[:set_zorder](1000)
- sp[:legendtitle] != nothing && leg[:set_title](sp[:legendtitle])
+ frame = leg."get_frame"()
+ frame."set_linewidth"(py_thickness_scale(plt, 1))
+ leg."set_zorder"(1000)
+ sp[:legendtitle] != nothing && leg."set_title"(sp[:legendtitle])
- for txt in leg[:get_texts]()
- PyPlot.plt[:setp](txt, color = py_color(sp[:legendfontcolor]), family = sp[:legendfontfamily])
+ for txt in leg."get_texts"()
+ PyPlot.plt."setp"(txt, color = py_color(sp[:legendfontcolor]), family = sp[:legendfontfamily])
end
end
end
@@ -1290,7 +1356,7 @@ function _update_plot_object(plt::Plot{PyPlotBackend})
figw, figh = sp.plt[:size]
figw, figh = figw*px, figh*px
pcts = bbox_to_pcts(sp.plotarea, figw, figh)
- ax[:set_position](pcts)
+ ax."set_position"(pcts)
# set the cbar position if there is one
if haskey(sp.attr, :cbar_ax)
@@ -1300,7 +1366,7 @@ function _update_plot_object(plt::Plot{PyPlotBackend})
has_toplabel = !(1e-7 < max(abs(ex.emax), abs(ex.emin)) < 1e7)
cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox) + (has_toplabel ? 4mm : 2mm), _cbar_width-1mm, height(sp.bbox) - (has_toplabel ? 6mm : 4mm))
pcts = bbox_to_pcts(cb_bbox, figw, figh)
- sp.attr[:cbar_ax][:set_position](pcts)
+ sp.attr[:cbar_ax]."set_position"(pcts)
end
end
PyPlot.draw()
@@ -1309,9 +1375,7 @@ end
# -----------------------------------------------------------------
# display/output
-function _display(plt::Plot{PyPlotBackend})
- plt.o[:show]()
-end
+_display(plt::Plot{PyPlotBackend}) = plt.o."show"()
@@ -1321,23 +1385,24 @@ const _pyplot_mimeformats = Dict(
"application/pdf" => "pdf",
"image/png" => "png",
"application/postscript" => "ps",
- "image/svg+xml" => "svg"
+ "image/svg+xml" => "svg",
+ "application/x-tex" => "pgf"
)
for (mime, fmt) in _pyplot_mimeformats
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PyPlotBackend})
fig = plt.o
- fig[:canvas][:print_figure](
+ fig."canvas"."print_figure"(
io,
format=$fmt,
# bbox_inches = "tight",
# figsize = map(px2inch, plt[:size]),
- facecolor = fig[:get_facecolor](),
+ facecolor = fig."get_facecolor"(),
edgecolor = "none",
dpi = plt[:dpi]
)
end
end
-closeall(::PyPlotBackend) = PyPlot.plt[:close]("all")
+closeall(::PyPlotBackend) = PyPlot.plt."close"("all")
diff --git a/src/backends/unicodeplots.jl b/src/backends/unicodeplots.jl
index afdd08cc..cabb89bb 100644
--- a/src/backends/unicodeplots.jl
+++ b/src/backends/unicodeplots.jl
@@ -1,28 +1,6 @@
# 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!!
warnOnUnsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing
@@ -49,8 +27,8 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
for sp in plt.subplots
xaxis = sp[:xaxis]
yaxis = sp[:yaxis]
- xlim = axis_limits(xaxis)
- ylim = axis_limits(yaxis)
+ xlim = axis_limits(sp, :x)
+ ylim = axis_limits(sp, :y)
# make vectors
xlim = [xlim[1], xlim[2]]
@@ -138,7 +116,7 @@ function addUnicodeSeries!(o, plotattributes::KW, addlegend::Bool, xlim, ylim)
x, y = if st == :straightline
straightline_data(plotattributes)
elseif st == :shape
- shape_data(series)
+ shape_data(plotattributes)
else
[collect(float(plotattributes[s])) for s in (:x, :y)]
end
diff --git a/src/backends/web.jl b/src/backends/web.jl
index 55984167..b1663435 100644
--- a/src/backends/web.jl
+++ b/src/backends/web.jl
@@ -42,8 +42,14 @@ function write_temp_html(plt::AbstractPlot)
end
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)
open_browser_window(filename)
+ # restore for other backends
+ use_local_dependencies[] = old
end
# uses wkhtmltopdf/wkhtmltoimage: http://wkhtmltopdf.org/downloads.html
diff --git a/src/components.jl b/src/components.jl
index 8a35c95c..097aee19 100644
--- a/src/components.jl
+++ b/src/components.jl
@@ -1,7 +1,7 @@
-const P2 = FixedSizeArrays.Vec{2,Float64}
-const P3 = FixedSizeArrays.Vec{3,Float64}
+const P2 = GeometryTypes.Point2{Float64}
+const P3 = GeometryTypes.Point3{Float64}
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))
@@ -256,8 +256,24 @@ mutable struct Font
color::Colorant
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
family = "sans-serif"
@@ -301,6 +317,32 @@ function font(args...)
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)
end
@@ -344,15 +386,16 @@ end
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, font::Font) = PlotText(t.str, font)
text(str::AbstractString, f::Font) = PlotText(str, f)
-function text(str, args...)
- PlotText(string(str), font(args...))
+function text(str, args...;kw...)
+ PlotText(string(str), font(args...;kw...))
end
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
msw,msh = anns.scalefactor
msize = Float64[]
- shapes = Vector{Shape}(length(anns.strs))
+ shapes = Vector{Shape}(undef, length(anns.strs))
for i in eachindex(anns.strs)
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
maxscale = max(xscale, yscale)
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))
end
series[:markershape] = shapes
@@ -554,7 +597,7 @@ function process_annotation(sp::Subplot, xs, ys, labs, font = font())
alphabet = "abcdefghijklmnopqrstuvwxyz"
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
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
anns
@@ -569,7 +612,7 @@ function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol},
alphabet = "abcdefghijklmnopqrstuvwxyz"
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
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
anns
@@ -736,7 +779,7 @@ end
# -----------------------------------------------------------------------
"create a BezierCurve for plotting"
-mutable struct BezierCurve{T <: FixedSizeArrays.Vec}
+mutable struct BezierCurve{T <: GeometryTypes.Point}
control_points::Vector{T}
end
@@ -749,9 +792,6 @@ function (bc::BezierCurve)(t::Real)
p
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
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, range(range..., stop=n, length=50))
diff --git a/src/deprecated/backends/bokeh.jl b/src/deprecated/backends/bokeh.jl
deleted file mode 100644
index 9e906b75..00000000
--- a/src/deprecated/backends/bokeh.jl
+++ /dev/null
@@ -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
diff --git a/src/deprecated/backends/gadfly.jl b/src/deprecated/backends/gadfly.jl
deleted file mode 100644
index 37dac678..00000000
--- a/src/deprecated/backends/gadfly.jl
+++ /dev/null
@@ -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,
-# """
-#
-#
-#