Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23b191231c | ||
|
|
aa5d514d57 | ||
|
|
3fb45764b2 | ||
|
|
fcff639d0c | ||
|
|
62290a2a24 | ||
|
|
bfafe91842 | ||
|
|
cbaa8fca56 | ||
|
|
0d210843e7 | ||
|
|
08bed07632 | ||
|
|
c47ab69383 | ||
|
|
eb942352ce | ||
|
|
00379097cf | ||
|
|
16b568840d | ||
|
|
c1dae9e9d9 | ||
|
|
8d8921fd08 | ||
|
|
d04d651e55 | ||
|
|
c771739b1b | ||
|
|
a800f8d4d9 | ||
|
|
4b5ec3ad07 | ||
|
|
35c68c8890 | ||
|
|
5005120f5e | ||
|
|
7b68e7985f | ||
|
|
871c7d86c7 | ||
|
|
79614ffcec | ||
|
|
6821148e8e | ||
|
|
3255c5a122 |
@@ -8,3 +8,4 @@ deps/plotly-latest.min.js
|
||||
deps/build.log
|
||||
deps/deps.jl
|
||||
Manifest.toml
|
||||
dev/
|
||||
|
||||
+1
-1
@@ -35,4 +35,4 @@ notifications:
|
||||
script:
|
||||
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
||||
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
|
||||
- $TESTCMD -e 'using Pkg; pkg"add https://github.com/mkborregaard/RecipeUtils.jl"; Pkg.build(); Pkg.test(coverage=true)'
|
||||
- $TESTCMD -e 'using Pkg; Pkg.pin(PackageSpec(name="FixedPointNumbers", version="0.7")); Pkg.build(); Pkg.test(coverage=true)'
|
||||
|
||||
+4
-3
@@ -1,7 +1,7 @@
|
||||
name = "Plots"
|
||||
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
author = ["Tom Breloff (@tbreloff)"]
|
||||
version = "0.29.7"
|
||||
version = "0.29.8"
|
||||
|
||||
[deps]
|
||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
@@ -21,7 +21,6 @@ PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
|
||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
RecipePipeline = "01d81517-befc-4cb6-b9ec-a95719d0359c"
|
||||
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
|
||||
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
|
||||
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
|
||||
@@ -58,13 +57,15 @@ ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
|
||||
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
|
||||
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
|
||||
LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
||||
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
|
||||
PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925"
|
||||
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
||||
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
|
||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
|
||||
VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92"
|
||||
|
||||
[targets]
|
||||
test = ["FileIO", "GeometryTypes", "Gtk", "ImageMagick", "Images", "LaTeXStrings", "LibGit2", "PGFPlotsX", "Random", "RDatasets", "StatsPlots", "Test", "UnicodePlots", "VisualRegressionTests"]
|
||||
test = ["FileIO", "GeometryTypes", "Gtk", "ImageMagick", "Images", "LaTeXStrings", "LibGit2", "OffsetArrays", "PGFPlotsX", "Random", "RDatasets", "StaticArrays", "StatsPlots", "Test", "UnicodePlots", "VisualRegressionTests"]
|
||||
|
||||
+2
-1
@@ -33,9 +33,10 @@ install:
|
||||
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
|
||||
|
||||
build_script:
|
||||
- C:\julia\bin\julia --project=C:\projects\plots-jl -e "using Pkg; Pkg.pin(PackageSpec(name="""FixedPointNumbers""", version="""0.7"""))"
|
||||
- echo "%JL_BUILD_SCRIPT%"
|
||||
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
|
||||
|
||||
|
||||
test_script:
|
||||
- echo "%JL_TEST_SCRIPT%"
|
||||
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"
|
||||
|
||||
+2
-9
@@ -18,13 +18,6 @@ using Base.Meta
|
||||
import Showoff
|
||||
import StatsBase
|
||||
import JSON
|
||||
import RecipePipeline: _process_userrecipe, _process_plotrecipe,
|
||||
_process_seriesrecipe, _preprocess_args,
|
||||
preprocessArgs!, is_st_supported,
|
||||
finalize_subplot!, recipe_pipeline!,
|
||||
_recipe_init!, _recipe_after_user!,
|
||||
_recipe_after_plot!, _recipe_before_series!,
|
||||
_recipe_finish!
|
||||
|
||||
using Requires
|
||||
|
||||
@@ -229,7 +222,7 @@ end
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
# include("precompile.jl")
|
||||
# _precompile_()
|
||||
include("precompile.jl")
|
||||
_precompile_()
|
||||
|
||||
end # module
|
||||
|
||||
+109
-23
@@ -29,11 +29,20 @@ Base.@kwdef mutable struct PGFPlotsXPlot
|
||||
PGFPlotsX.push_preamble!(
|
||||
pgfx_plot.the_plot,
|
||||
raw"""
|
||||
\pgfplotsset{
|
||||
/pgfplots/layers/axis on top/.define layer set={
|
||||
background, axis background,pre main,main,axis grid,axis ticks,axis lines,axis tick labels,
|
||||
axis descriptions,axis foreground
|
||||
}{/pgfplots/layers/standard},
|
||||
\pgfplotsset{%
|
||||
layers/standard/.define layer set={%
|
||||
background,axis background,axis grid,axis ticks,axis lines,axis tick labels,pre main,main,axis descriptions,axis foreground%
|
||||
}{grid style= {/pgfplots/on layer=axis grid},%
|
||||
tick style= {/pgfplots/on layer=axis ticks},%
|
||||
axis line style= {/pgfplots/on layer=axis lines},%
|
||||
label style= {/pgfplots/on layer=axis descriptions},%
|
||||
legend style= {/pgfplots/on layer=axis descriptions},%
|
||||
title style= {/pgfplots/on layer=axis descriptions},%
|
||||
colorbar style= {/pgfplots/on layer=axis descriptions},%
|
||||
ticklabel style= {/pgfplots/on layer=axis tick labels},%
|
||||
axis background@ style={/pgfplots/on layer=axis background},%
|
||||
3d box foreground style={/pgfplots/on layer=axis foreground},%
|
||||
},
|
||||
}
|
||||
""",
|
||||
)
|
||||
@@ -76,6 +85,7 @@ end
|
||||
|
||||
function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
if !pgfx_plot.is_created || pgfx_plot.was_shown
|
||||
pgfx_sanitize_plot!(plt)
|
||||
the_plot = PGFPlotsX.TikzPicture(PGFPlotsX.Options())
|
||||
bgc = plt.attr[:background_color_outside] == :match ?
|
||||
plt.attr[:background_color] : plt.attr[:background_color_outside]
|
||||
@@ -107,14 +117,24 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
||||
title_cstr = plot_color(sp[:titlefontcolor])
|
||||
title_a = alpha(title_cstr)
|
||||
title_loc = sp[:title_location]
|
||||
bgc_inside = plot_color(sp[:background_color_inside])
|
||||
bgc_inside_a = alpha(bgc_inside)
|
||||
axis_opt = PGFPlotsX.Options(
|
||||
"title" => sp[:title],
|
||||
"title style" => PGFPlotsX.Options(
|
||||
"font" => pgfx_font(
|
||||
sp[:titlefontsize],
|
||||
pgfx_thickness_scaling(sp),
|
||||
"at" => if title_loc == :left
|
||||
"{(0,1)}"
|
||||
elseif title_loc == :right
|
||||
"{(1,1)}"
|
||||
elseif title_loc isa Tuple
|
||||
"{$(string(title_loc))}"
|
||||
else
|
||||
"{(0.5,1)}"
|
||||
end,
|
||||
"font" => pgfx_font(
|
||||
sp[:titlefontsize],
|
||||
pgfx_thickness_scaling(sp),
|
||||
),
|
||||
"color" => title_cstr,
|
||||
"draw opacity" => title_a,
|
||||
@@ -139,8 +159,6 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
"fill" => bgc_inside,
|
||||
"opacity" => bgc_inside_a,
|
||||
),
|
||||
"axis on top" => nothing,
|
||||
# "framed" => nothing,
|
||||
# These are for layouting
|
||||
"anchor" => "north west",
|
||||
"xshift" => string(dx),
|
||||
@@ -259,7 +277,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
if marker isa Shape
|
||||
x = marker.x
|
||||
y = marker.y
|
||||
scale_factor = 0.025
|
||||
scale_factor = 0.00125
|
||||
mark_size = opt[:markersize] * scale_factor
|
||||
path = join(
|
||||
[
|
||||
@@ -346,28 +364,28 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
)
|
||||
end
|
||||
# add to legend?
|
||||
if sp[:legend] != :none && pgfx_should_add_to_legend(series)
|
||||
if sp[:legend] != :none
|
||||
leg_entry = if opt[:label] isa AVec
|
||||
get(opt[:label], i, "")
|
||||
elseif opt[:label] isa AbstractString
|
||||
if i == 1
|
||||
opt[:label]
|
||||
get(opt, :label, "")
|
||||
else
|
||||
""
|
||||
end
|
||||
else
|
||||
throw(ArgumentError("Malformed label. label = $(opt[:label])"))
|
||||
end
|
||||
if leg_entry == ""
|
||||
push!(segment_plot.options, "forget plot" => nothing)
|
||||
continue
|
||||
if leg_entry == "" || !pgfx_should_add_to_legend(series)
|
||||
push!(axis.contents[end].options, "forget plot" => nothing)
|
||||
else
|
||||
leg_opt = PGFPlotsX.Options()
|
||||
if ribbon !== nothing
|
||||
pgfx_filllegend!(axis.contents[end - 3].options, opt)
|
||||
end
|
||||
legend = PGFPlotsX.LegendEntry(leg_opt, leg_entry, false)
|
||||
push!(axis, legend)
|
||||
end
|
||||
leg_opt = PGFPlotsX.Options()
|
||||
if ribbon !== nothing
|
||||
pgfx_filllegend!(axis.contents[end - 3].options, opt)
|
||||
end
|
||||
legend = PGFPlotsX.LegendEntry(leg_opt, leg_entry, false)
|
||||
push!(axis, legend)
|
||||
end
|
||||
end # for segments
|
||||
# add subplot annotations
|
||||
@@ -586,11 +604,22 @@ const _pgfplotsx_markers = KW(
|
||||
)
|
||||
|
||||
const _pgfplotsx_legend_pos = KW(
|
||||
:top => "north",
|
||||
:bottom => "south",
|
||||
:left => "west",
|
||||
:right => "east",
|
||||
:bottomleft => "south west",
|
||||
:bottomright => "south east",
|
||||
:topright => "north east",
|
||||
:topleft => "north west",
|
||||
:outertop => "north",
|
||||
:outerbottom => "outer south",
|
||||
:outerleft => "outer west",
|
||||
:outerright => "outer east",
|
||||
:outerbottomleft => "outer south west",
|
||||
:outerbottomright => "outer south east",
|
||||
:outertopright => "outer north east",
|
||||
:outertopleft => "outer north west",
|
||||
)
|
||||
|
||||
const _pgfx_framestyles = [:box, :axes, :origin, :zerolines, :grid, :none]
|
||||
@@ -691,7 +720,7 @@ function pgfx_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
|
||||
end
|
||||
|
||||
function pgfx_should_add_to_legend(series::Series)
|
||||
series.plotattributes[:primary] && series.plotattributes[:label] != "" &&
|
||||
series.plotattributes[:primary] &&
|
||||
!(
|
||||
series.plotattributes[:seriestype] in (
|
||||
:hexbin,
|
||||
@@ -862,6 +891,48 @@ function pgfx_fillrange_args(fillrange, x, y, z)
|
||||
z_fill = [z; _cycle(fillrange, n:-1:1); z[1]]
|
||||
return PGFPlotsX.Coordiantes(x_fill, y_fill, z_fill)
|
||||
end
|
||||
|
||||
function pgfx_sanitize_string(p::PlotText)
|
||||
PlotText(pgfx_sanitize_string(p.str), p.font)
|
||||
end
|
||||
function pgfx_sanitize_string(s::AbstractString)
|
||||
s = replace(s, r"\\?\#" => "\\#")
|
||||
s = replace(s, r"\\?\%" => "\\%")
|
||||
s = replace(s, r"\\?\_" => "\\_")
|
||||
s = replace(s, r"\\?\&" => "\\&")
|
||||
end
|
||||
function pgfx_sanitize_plot!(plt)
|
||||
for (key, value) in plt.attr
|
||||
if value isa Union{AbstractString, AbstractVector{<:AbstractString}}
|
||||
plt.attr[key] = pgfx_sanitize_string.(value)
|
||||
end
|
||||
end
|
||||
for subplot in plt.subplots
|
||||
for (key, value) in subplot.attr
|
||||
if key == :annotations && subplot.attr[:annotations] !== nothing
|
||||
old_ann = subplot.attr[key]
|
||||
for i in eachindex(old_ann)
|
||||
subplot.attr[key][i] = (old_ann[i][1], old_ann[i][2], pgfx_sanitize_string(old_ann[i][3]))
|
||||
end
|
||||
elseif value isa Union{AbstractString, AbstractVector{<:AbstractString}}
|
||||
subplot.attr[key] = pgfx_sanitize_string.(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
for series in plt.series_list
|
||||
for (key, value) in series.plotattributes
|
||||
if key == :series_annotations && series.plotattributes[:series_annotations] !== nothing
|
||||
old_ann = series.plotattributes[key].strs
|
||||
for i in eachindex(old_ann)
|
||||
series.plotattributes[key].strs[i] = pgfx_sanitize_string(old_ann[i])
|
||||
end
|
||||
elseif value isa Union{AbstractString, AbstractVector{<:AbstractString}}
|
||||
series.plotattributes[key] = pgfx_sanitize_string.(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
##
|
||||
end
|
||||
# --------------------------------------------------------------------------------------
|
||||
function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
axis = sp[Symbol(letter, :axis)]
|
||||
@@ -872,6 +943,21 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
"scaled $(letter) ticks" => "false",
|
||||
string(letter, :label) => axis[:guide],
|
||||
)
|
||||
tick_color = plot_color(axis[:foreground_color_axis])
|
||||
push!(opt,
|
||||
"$(letter) tick style" => PGFPlotsX.Options(
|
||||
"color" => color(tick_color),
|
||||
"opacity" => alpha(tick_color),
|
||||
),
|
||||
)
|
||||
tick_label_color = plot_color(axis[:tickfontcolor])
|
||||
push!(opt,
|
||||
"$(letter) tick label style" => PGFPlotsX.Options(
|
||||
"color" => color(tick_color),
|
||||
"opacity" => alpha(tick_color),
|
||||
"rotate" => axis[:rotation]
|
||||
),
|
||||
)
|
||||
|
||||
# set to supported framestyle
|
||||
framestyle = pgfx_framestyle(sp[:framestyle] == false ? :none : sp[:framestyle])
|
||||
|
||||
+14
-1
@@ -875,6 +875,20 @@ const _examples = PlotExample[
|
||||
end,
|
||||
],
|
||||
),
|
||||
PlotExample(
|
||||
"Array Types",
|
||||
"Plots supports different `Array` types that follow the `AbstractArray` interface, like `StaticArrays` and `OffsetArrays.`",
|
||||
[
|
||||
quote
|
||||
begin
|
||||
using StaticArrays, OffsetArrays
|
||||
sv = SVector{10}(rand(10))
|
||||
ov = OffsetVector(rand(10), -2)
|
||||
plot([sv, ov], label = ["StaticArray" "OffsetArray"])
|
||||
end
|
||||
end,
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
# Some constants for PlotDocs and PlotReferenceImages
|
||||
@@ -887,7 +901,6 @@ _backend_skips = Dict(
|
||||
:pgfplots => [2, 5, 6, 10, 16, 20, 22, 23, 25, 28, 30, 31, 34, 37, 38, 39],
|
||||
:pgfplotsx => [ 6, # images
|
||||
10, # histogram2d
|
||||
20, # annotations due to missing sanitation
|
||||
22, # contourf
|
||||
23, # pie
|
||||
32, # spy
|
||||
|
||||
+110
-8
@@ -1,13 +1,5 @@
|
||||
|
||||
function finalize_subplot!(plt::Plot, st, plotattributes::AKW)
|
||||
sp = _prepare_subplot(plt, plotattributes)
|
||||
_prepare_annotations(sp, plotattributes)
|
||||
_expand_subplot_extrema(sp, plotattributes, st)
|
||||
_update_series_attributes!(plotattributes, plt, sp)
|
||||
_add_the_series(plt, sp, plotattributes)
|
||||
end
|
||||
|
||||
is_st_supported(st::Symbol) = is_seriestype_supported(st)
|
||||
# ------------------------------------------------------------------
|
||||
# preprocessing
|
||||
|
||||
@@ -67,6 +59,39 @@ end
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(plotattributes, args, still_to_process)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a stack of series that still need to be processed.
|
||||
# each pass through the loop, we pop one off and apply the recipe.
|
||||
# the recipe will return a list a Series objects... the ones that are
|
||||
# finished (no more args) get added to the kw_list, the ones that are not
|
||||
# are placed on top of the stack and are then processed further.
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
# grab the first in line to be processed and either add it to the kw_list or
|
||||
# pass it through apply_recipe to generate a list of RecipeData objects (data + attributes)
|
||||
# for further processing.
|
||||
next_series = popfirst!(still_to_process)
|
||||
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
|
||||
if !(typeof(next_series) <: RecipeData)
|
||||
error("Inputs couldn't be processed... expected RecipeData but got: $next_series")
|
||||
end
|
||||
if isempty(next_series.args)
|
||||
_process_userrecipe(plt, kw_list, next_series)
|
||||
else
|
||||
rd_list = RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...)
|
||||
prepend!(still_to_process,rd_list)
|
||||
end
|
||||
end
|
||||
|
||||
# don't allow something else to handle it
|
||||
plotattributes[:smooth] = false
|
||||
kw_list
|
||||
end
|
||||
|
||||
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
@@ -143,6 +168,39 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# plot recipes
|
||||
|
||||
# Grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes).
|
||||
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
|
||||
# otherwise we just add the original KW.
|
||||
function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_process::Vector{KW})
|
||||
if !isa(get(kw, :seriestype, nothing), Symbol)
|
||||
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
|
||||
push!(kw_list, kw)
|
||||
return
|
||||
end
|
||||
try
|
||||
st = kw[:seriestype]
|
||||
st = kw[:seriestype] = get(_typeAliases, st, st)
|
||||
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
|
||||
for data in datalist
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.plotattributes)")
|
||||
end
|
||||
push!(still_to_process, data.plotattributes)
|
||||
end
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
push!(kw_list, kw)
|
||||
else
|
||||
rethrow()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
@@ -324,3 +382,47 @@ function _add_the_series(plt, sp, plotattributes)
|
||||
push!(sp.series_list, series)
|
||||
_series_added(plt, series)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
|
||||
#println("process $(typeof(plotattributes))")
|
||||
# replace seriestype aliases
|
||||
st = Symbol(plotattributes[:seriestype])
|
||||
st = plotattributes[:seriestype] = get(_typeAliases, st, st)
|
||||
|
||||
# shapes shouldn't have fillrange set
|
||||
if plotattributes[:seriestype] == :shape
|
||||
plotattributes[:fillrange] = nothing
|
||||
end
|
||||
|
||||
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
|
||||
if is_seriestype_supported(st)
|
||||
sp = _prepare_subplot(plt, plotattributes)
|
||||
_prepare_annotations(sp, plotattributes)
|
||||
_expand_subplot_extrema(sp, plotattributes, st)
|
||||
_update_series_attributes!(plotattributes, plt, sp)
|
||||
_add_the_series(plt, sp, plotattributes)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z])
|
||||
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
if isa(data, RecipeData)
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
|
||||
end
|
||||
_process_seriesrecipe(plt, data.plotattributes)
|
||||
else
|
||||
@warn("Unhandled recipe: $(data)")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
nothing
|
||||
end
|
||||
|
||||
+59
-16
@@ -54,7 +54,7 @@ function plot(args...; kw...)
|
||||
# create an empty Plot then process
|
||||
plt = Plot()
|
||||
# plt.user_attr = plotattributes
|
||||
recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
|
||||
_plot!(plt, plotattributes, args)
|
||||
end
|
||||
|
||||
# build a new plot from existing plots
|
||||
@@ -155,7 +155,7 @@ function plot!(plt::Plot, args...; kw...)
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
# merge!(plt.user_attr, plotattributes)
|
||||
recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
|
||||
_plot!(plt, plotattributes, args)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
@@ -163,36 +163,78 @@ end
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
|
||||
## here we implement the recipe interface
|
||||
function _recipe_init!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
function _plot!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
plotattributes[:plot_object] = plt
|
||||
|
||||
if !isempty(args) && !isdefined(Main, :StatsPlots) &&
|
||||
first(split(string(typeof(args[1])), ".")) == "DataFrames"
|
||||
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots")
|
||||
end
|
||||
end
|
||||
|
||||
function _recipe_after_plot!(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# --------------------------------
|
||||
# "USER RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
kw_list = _process_userrecipes(plt, plotattributes, args)
|
||||
|
||||
# @info(1)
|
||||
# map(DD, kw_list)
|
||||
|
||||
|
||||
# --------------------------------
|
||||
# "PLOT RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# "plot recipe", which acts like a series type, and is processed before
|
||||
# the plot layout is created, which allows for setting layouts and other plot-wide attributes.
|
||||
# we get inputs which have been fully processed by "user recipes" and "type recipes",
|
||||
# so we can expect standard vectors, surfaces, etc. No defaults have been set yet.
|
||||
still_to_process = kw_list
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
next_kw = popfirst!(still_to_process)
|
||||
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
|
||||
end
|
||||
|
||||
# @info(2)
|
||||
# map(DD, kw_list)
|
||||
|
||||
# --------------------------------
|
||||
# Plot/Subplot/Layout setup
|
||||
# --------------------------------
|
||||
_plot_setup(plt, plotattributes, kw_list)
|
||||
_subplot_setup(plt, plotattributes, kw_list)
|
||||
end
|
||||
|
||||
function _recipe_before_series!(plt::Plot, kw, kw_list)
|
||||
sp::Subplot = kw[:subplot]
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
|
||||
|
||||
# in series attributes given as vector with one element per series,
|
||||
# select the value for current series
|
||||
_slice_series_args!(kw, plt, sp, series_idx(kw_list,kw))
|
||||
# --------------------------------
|
||||
# "SERIES RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
series_attr = Attr(kw, _series_defaults)
|
||||
end
|
||||
# @info(3)
|
||||
# map(DD, kw_list)
|
||||
|
||||
for kw in kw_list
|
||||
sp::Subplot = kw[:subplot]
|
||||
|
||||
# in series attributes given as vector with one element per series,
|
||||
# select the value for current series
|
||||
_slice_series_args!(kw, plt, sp, series_idx(kw_list,kw))
|
||||
|
||||
|
||||
series_attr = Attr(kw, _series_defaults)
|
||||
# now we have a fully specified series, with colors chosen. we must recursively handle
|
||||
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
|
||||
# we check for a recipe that will convert that series type into one made up of lower-level components.
|
||||
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
|
||||
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
|
||||
# be able to support step, bar, and histogram plots (and any recipes that use those components).
|
||||
_process_seriesrecipe(plt, series_attr)
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
|
||||
function _recipe_finish!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
current(plt)
|
||||
|
||||
# do we want to force display?
|
||||
@@ -204,6 +246,7 @@ function _recipe_finish!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# we're getting ready to display/output. prep for layout calcs, then update
|
||||
# the plot object after
|
||||
function prepare_output(plt::Plot)
|
||||
|
||||
+5
-3
@@ -15,9 +15,11 @@ prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plo
|
||||
prepareSeriesData(::Nothing) = nothing
|
||||
prepareSeriesData(t::Tuple{T, T}) where {T<:Number} = t
|
||||
prepareSeriesData(f::Function) = f
|
||||
prepareSeriesData(a::AbstractArray{<:MaybeNumber}) = replace!(
|
||||
x -> ismissing(x) || isinf(x) ? NaN : x,
|
||||
map(float,a))
|
||||
prepareSeriesData(ar::AbstractRange{<:Number}) = ar
|
||||
function prepareSeriesData(a::AbstractArray{<:MaybeNumber})
|
||||
f = isimmutable(a) ? replace : replace!
|
||||
a = f(x -> ismissing(x) || isinf(x) ? NaN : x, map(float, a))
|
||||
end
|
||||
prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a)
|
||||
prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf))
|
||||
prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image
|
||||
|
||||
+2
-2
@@ -428,8 +428,8 @@ Add annotations to an existing plot.
|
||||
|
||||
# Arguments
|
||||
|
||||
- `anns`: An `AbstractVector` of tuples of the form (x,y,text). The text object
|
||||
can be an String or PlotText
|
||||
- `anns`: An `AbstractVector` of tuples of the form `(x,y,text)`. The `text` object
|
||||
can be a `String` or `PlotText`.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
|
||||
+284
-155
@@ -1,166 +1,295 @@
|
||||
using Plots, Test
|
||||
pgfplotsx()
|
||||
|
||||
function create_plot( args...; kwargs... )
|
||||
pgfx_plot = plot(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
function create_plot(args...; kwargs...)
|
||||
pgfx_plot = plot(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
end
|
||||
|
||||
function create_plot!( args...; kwargs... )
|
||||
pgfx_plot = plot!(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
function create_plot!(args...; kwargs...)
|
||||
pgfx_plot = plot!(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
end
|
||||
|
||||
@testset "PGFPlotsX" begin
|
||||
pgfx_plot = plot(1:5)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
|
||||
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
|
||||
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
|
||||
@test count( x-> x isa PGFPlotsX.Plot, axis.contents ) == 1
|
||||
@test !haskey(axis.contents[1].options.dict, "fill")
|
||||
pgfx_plot = plot(1:5)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
|
||||
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
|
||||
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
|
||||
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1
|
||||
@test !haskey(axis.contents[1].options.dict, "fill")
|
||||
|
||||
@testset "3D docs example" begin
|
||||
n = 100
|
||||
ts = range(0, stop=8π, length=n)
|
||||
x = ts .* map(cos, ts)
|
||||
y = (0.1ts) .* map(sin, ts)
|
||||
z = 1:n
|
||||
pl = plot(x, y, z, zcolor=reverse(z), m=(10, 0.8, :blues, Plots.stroke(0)), leg=false, cbar=true, w=5)
|
||||
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w=10)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
end
|
||||
end # testset
|
||||
@testset "Color docs example" begin
|
||||
y = rand(100)
|
||||
plot(0:10:100, rand(11, 4), lab="lines", w=3, palette=:grays, fill=0, α=0.6)
|
||||
pl = scatter!(y, zcolor=abs.(y .- 0.5), m=(:heat, 0.8, Plots.stroke(1, :green)), ms=10 * abs.(y .- 0.5) .+ 4, lab="grad")
|
||||
Plots._update_plot_object(pl)
|
||||
axis = Plots.pgfx_axes(pl.o)[1]
|
||||
@test count( x->x isa PGFPlotsX.LegendEntry, axis.contents ) == 5
|
||||
@test count( x->x isa PGFPlotsX.Plot, axis.contents ) == 108 # each marker is its own plot, fillranges create 2 plot-objects
|
||||
marker = axis.contents[15]
|
||||
@test marker isa PGFPlotsX.Plot
|
||||
@test marker.options["mark"] == "*"
|
||||
@test marker.options["mark options"]["color"] == RGBA{Float64}( colorant"green", 0.8)
|
||||
@test marker.options["mark options"]["line width"] == 1
|
||||
end # testset
|
||||
@testset "Plot in pieces" begin
|
||||
plot(rand(100) / 3, reg=true, fill=(0, :green))
|
||||
scatter!(rand(100), markersize=6, c=:orange)
|
||||
end # testset
|
||||
@testset "Marker types" begin
|
||||
markers = filter((m->begin
|
||||
m in Plots.supported_markers()
|
||||
end), Plots._shape_keys)
|
||||
markers = reshape(markers, 1, length(markers))
|
||||
n = length(markers)
|
||||
x = (range(0, stop=10, length=n + 2))[2:end - 1]
|
||||
y = repeat(reshape(reverse(x), 1, :), n, 1)
|
||||
scatter(x, y, m=(8, :auto), lab=map(string, markers), bg=:linen, xlim=(0, 10), ylim=(0, 10))
|
||||
end # testset
|
||||
@testset "Layout" begin
|
||||
plot(Plots.fakedata(100, 10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
|
||||
end # testset
|
||||
@testset "Polar plots" begin
|
||||
Θ = range(0, stop=1.5π, length=100)
|
||||
r = abs.(0.1 * randn(100) + sin.(3Θ))
|
||||
plot(Θ, r, proj=:polar, m=2)
|
||||
end # testset
|
||||
@testset "Drawing shapes" begin
|
||||
verts = [(-1.0, 1.0), (-1.28, 0.6), (-0.2, -1.4), (0.2, -1.4), (1.28, 0.6), (1.0, 1.0), (-1.0, 1.0), (-0.2, -0.6), (0.0, -0.2), (-0.4, 0.6), (1.28, 0.6), (0.2, -1.4), (-0.2, -1.4), (0.6, 0.2), (-0.2, 0.2), (0.0, -0.2), (0.2, 0.2), (-0.2, -0.6)]
|
||||
x = 0.1:0.2:0.9
|
||||
y = 0.7 * rand(5) .+ 0.15
|
||||
plot(x, y, line=(3, :dash, :lightblue), marker=(Shape(verts), 30, RGBA(0, 0, 0, 0.2)), bg=:pink, fg=:darkblue, xlim=(0, 1), ylim=(0, 1), leg=false)
|
||||
end # testset
|
||||
@testset "Histogram 2D" begin
|
||||
histogram2d(randn(10000), randn(10000), nbins=20)
|
||||
end # testset
|
||||
@testset "Heatmap-like" begin
|
||||
xs = [string("x", i) for i = 1:10]
|
||||
ys = [string("y", i) for i = 1:4]
|
||||
z = float((1:4) * reshape(1:10, 1, :))
|
||||
pgfx_plot = heatmap(xs, ys, z, aspect_ratio=1)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
|
||||
end
|
||||
@testset "Legends" begin
|
||||
legends_plot = plot( rand(5,2), lab = ["1" ""] )
|
||||
scatter!(legends_plot, rand(5) )
|
||||
Plots._update_plot_object(legends_plot)
|
||||
axis_contents = Plots.pgfx_axes(legends_plot.o)[1].contents
|
||||
leg_entries = filter( x -> x isa PGFPlotsX.LegendEntry, axis_contents )
|
||||
series = filter( x -> x isa PGFPlotsX.Plot, axis_contents )
|
||||
@test length(leg_entries) == 2
|
||||
@test !haskey(series[1].options.dict, "forget plot")
|
||||
@test haskey(series[2].options.dict, "forget plot")
|
||||
@test !haskey(series[3].options.dict, "forget plot")
|
||||
end # testset
|
||||
|
||||
pgfx_plot = wireframe(xs, ys, z, aspect_ratio=1)
|
||||
# TODO: clims are wrong
|
||||
end # testset
|
||||
@testset "Contours" begin
|
||||
x = 1:0.5:20
|
||||
y = 1:0.5:10
|
||||
f(x, y) = begin
|
||||
(3x + y ^ 2) * abs(sin(x) + cos(y))
|
||||
end
|
||||
X = repeat(reshape(x, 1, :), length(y), 1)
|
||||
Y = repeat(y, 1, length(x))
|
||||
Z = map(f, X, Y)
|
||||
p2 = contour(x, y, Z)
|
||||
p1 = contour(x, y, f, fill=true)
|
||||
plot(p1, p2)
|
||||
# TODO: colorbar for filled contours
|
||||
end # testset
|
||||
@testset "Varying colors" begin
|
||||
t = range(0, stop=1, length=100)
|
||||
θ = (6π) .* t
|
||||
x = t .* cos.(θ)
|
||||
y = t .* sin.(θ)
|
||||
p1 = plot(x, y, line_z=t, linewidth=3, legend=false)
|
||||
p2 = scatter(x, y, marker_z=((x, y)->begin
|
||||
x + y
|
||||
end), color=:bluesreds, legend=false)
|
||||
plot(p1, p2)
|
||||
end # testset
|
||||
@testset "Framestyles" begin
|
||||
scatter(fill(randn(10), 6), fill(randn(10), 6), framestyle=[:box :semi :origin :zerolines :grid :none], title=[":box" ":semi" ":origin" ":zerolines" ":grid" ":none"], color=permutedims(1:6), layout=6, label="", markerstrokewidth=0, ticks=-2:2)
|
||||
# TODO: support :semi
|
||||
end # testset
|
||||
@testset "Quiver" begin
|
||||
x = -2pi:0.2:2*pi
|
||||
y = sin.(x)
|
||||
@testset "3D docs example" begin
|
||||
n = 100
|
||||
ts = range(0, stop = 8π, length = n)
|
||||
x = ts .* map(cos, ts)
|
||||
y = (0.1ts) .* map(sin, ts)
|
||||
z = 1:n
|
||||
pl = plot(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
zcolor = reverse(z),
|
||||
m = (10, 0.8, :blues, Plots.stroke(0)),
|
||||
leg = false,
|
||||
cbar = true,
|
||||
w = 5,
|
||||
)
|
||||
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w = 10)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(
|
||||
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||
)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
end
|
||||
end # testset
|
||||
@testset "Color docs example" begin
|
||||
y = rand(100)
|
||||
plot(
|
||||
0:10:100,
|
||||
rand(11, 4),
|
||||
lab = "lines",
|
||||
w = 3,
|
||||
palette = :grays,
|
||||
fill = 0,
|
||||
α = 0.6,
|
||||
)
|
||||
pl = scatter!(
|
||||
y,
|
||||
zcolor = abs.(y .- 0.5),
|
||||
m = (:heat, 0.8, Plots.stroke(1, :green)),
|
||||
ms = 10 * abs.(y .- 0.5) .+ 4,
|
||||
lab = ["grad", "", "ient"],
|
||||
)
|
||||
Plots._update_plot_object(pl)
|
||||
axis = Plots.pgfx_axes(pl.o)[1]
|
||||
@test count(x -> x isa PGFPlotsX.LegendEntry, axis.contents) == 6
|
||||
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 108 # each marker is its own plot, fillranges create 2 plot-objects
|
||||
marker = axis.contents[15]
|
||||
@test marker isa PGFPlotsX.Plot
|
||||
@test marker.options["mark"] == "*"
|
||||
@test marker.options["mark options"]["color"] ==
|
||||
RGBA{Float64}(colorant"green", 0.8)
|
||||
@test marker.options["mark options"]["line width"] == 1
|
||||
end # testset
|
||||
@testset "Plot in pieces" begin
|
||||
pic = plot(rand(100) / 3, reg = true, fill = (0, :green))
|
||||
scatter!(pic, rand(100), markersize = 6, c = :orange)
|
||||
Plots._update_plot_object(pic)
|
||||
axis_contents = Plots.pgfx_axes(pic.o)[1].contents
|
||||
leg_entries = filter( x -> x isa PGFPlotsX.LegendEntry, axis_contents )
|
||||
series = filter( x -> x isa PGFPlotsX.Plot, axis_contents )
|
||||
@test length(leg_entries) == 2
|
||||
@test length(series) == 4
|
||||
@test haskey(series[1].options.dict, "forget plot")
|
||||
@test !haskey(series[2].options.dict, "forget plot")
|
||||
@test haskey(series[3].options.dict, "forget plot")
|
||||
@test !haskey(series[4].options.dict, "forget plot")
|
||||
end # testset
|
||||
@testset "Marker types" begin
|
||||
markers = filter((m -> begin
|
||||
m in Plots.supported_markers()
|
||||
end), Plots._shape_keys)
|
||||
markers = reshape(markers, 1, length(markers))
|
||||
n = length(markers)
|
||||
x = (range(0, stop = 10, length = n + 2))[2:(end - 1)]
|
||||
y = repeat(reshape(reverse(x), 1, :), n, 1)
|
||||
scatter(
|
||||
x,
|
||||
y,
|
||||
m = (8, :auto),
|
||||
lab = map(string, markers),
|
||||
bg = :linen,
|
||||
xlim = (0, 10),
|
||||
ylim = (0, 10),
|
||||
)
|
||||
end # testset
|
||||
@testset "Layout" begin
|
||||
plot(
|
||||
Plots.fakedata(100, 10),
|
||||
layout = 4,
|
||||
palette = [:grays :blues :heat :lightrainbow],
|
||||
bg_inside = [:orange :pink :darkblue :black],
|
||||
)
|
||||
end # testset
|
||||
@testset "Polar plots" begin
|
||||
Θ = range(0, stop = 1.5π, length = 100)
|
||||
r = abs.(0.1 * randn(100) + sin.(3Θ))
|
||||
plot(Θ, r, proj = :polar, m = 2)
|
||||
end # testset
|
||||
@testset "Drawing shapes" begin
|
||||
verts = [
|
||||
(-1.0, 1.0),
|
||||
(-1.28, 0.6),
|
||||
(-0.2, -1.4),
|
||||
(0.2, -1.4),
|
||||
(1.28, 0.6),
|
||||
(1.0, 1.0),
|
||||
(-1.0, 1.0),
|
||||
(-0.2, -0.6),
|
||||
(0.0, -0.2),
|
||||
(-0.4, 0.6),
|
||||
(1.28, 0.6),
|
||||
(0.2, -1.4),
|
||||
(-0.2, -1.4),
|
||||
(0.6, 0.2),
|
||||
(-0.2, 0.2),
|
||||
(0.0, -0.2),
|
||||
(0.2, 0.2),
|
||||
(-0.2, -0.6),
|
||||
]
|
||||
x = 0.1:0.2:0.9
|
||||
y = 0.7 * rand(5) .+ 0.15
|
||||
plot(
|
||||
x,
|
||||
y,
|
||||
line = (3, :dash, :lightblue),
|
||||
marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
|
||||
bg = :pink,
|
||||
fg = :darkblue,
|
||||
xlim = (0, 1),
|
||||
ylim = (0, 1),
|
||||
leg = false,
|
||||
)
|
||||
end # testset
|
||||
@testset "Histogram 2D" begin
|
||||
histogram2d(randn(10000), randn(10000), nbins = 20)
|
||||
end # testset
|
||||
@testset "Heatmap-like" begin
|
||||
xs = [string("x", i) for i = 1:10]
|
||||
ys = [string("y", i) for i = 1:4]
|
||||
z = float((1:4) * reshape(1:10, 1, :))
|
||||
pgfx_plot = heatmap(xs, ys, z, aspect_ratio = 1)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(
|
||||
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||
)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
|
||||
end
|
||||
|
||||
u = ones(length(x))
|
||||
v = cos.(x)
|
||||
arrow_plot = plot( x, y, quiver = (u, v), arrow = true )
|
||||
# TODO: could adjust limits to fit arrows if too long, but how?
|
||||
# TODO: get latex available on CI
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
|
||||
# end
|
||||
end # testset
|
||||
@testset "Annotations" begin
|
||||
y = rand(10)
|
||||
plot(y, annotations=(3, y[3], Plots.text("this is \\#3", :left)), leg=false)
|
||||
annotate!([(5, y[5], Plots.text("this is \\#5", 16, :red, :center)), (10, y[10], Plots.text("this is \\#10", :right, 20, "courier"))])
|
||||
annotation_plot = scatter!(range(2, stop=8, length=6), rand(6), marker=(50, 0.2, :orange), series_annotations=["series", "annotations", "map", "to", "series", Plots.text("data", :green)])
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
|
||||
# end
|
||||
end # testset
|
||||
@testset "Ribbon" begin
|
||||
aa = rand(10)
|
||||
bb = rand(10)
|
||||
cc = rand(10)
|
||||
conf = [aa-cc bb-cc]
|
||||
ribbon_plot = plot(collect(1:10),fill(1,10), ribbon=(conf[:,1],conf[:,2]))
|
||||
Plots._update_plot_object(ribbon_plot)
|
||||
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
|
||||
plots = filter(x->x isa PGFPlotsX.Plot, axis.contents)
|
||||
@test length(plots) == 4
|
||||
@test !haskey(plots[1].options.dict, "fill")
|
||||
@test !haskey(plots[2].options.dict, "fill")
|
||||
@test !haskey(plots[3].options.dict, "fill")
|
||||
@test haskey(plots[4].options.dict, "fill")
|
||||
@test ribbon_plot.o !== nothing
|
||||
@test ribbon_plot.o.the_plot !== nothing
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
|
||||
# end
|
||||
end # testset
|
||||
end # testset
|
||||
pgfx_plot = wireframe(xs, ys, z, aspect_ratio = 1)
|
||||
# TODO: clims are wrong
|
||||
end # testset
|
||||
@testset "Contours" begin
|
||||
x = 1:0.5:20
|
||||
y = 1:0.5:10
|
||||
f(x, y) = begin
|
||||
(3x + y^2) * abs(sin(x) + cos(y))
|
||||
end
|
||||
X = repeat(reshape(x, 1, :), length(y), 1)
|
||||
Y = repeat(y, 1, length(x))
|
||||
Z = map(f, X, Y)
|
||||
p2 = contour(x, y, Z)
|
||||
p1 = contour(x, y, f, fill = true)
|
||||
plot(p1, p2)
|
||||
# TODO: colorbar for filled contours
|
||||
end # testset
|
||||
@testset "Varying colors" begin
|
||||
t = range(0, stop = 1, length = 100)
|
||||
θ = (6π) .* t
|
||||
x = t .* cos.(θ)
|
||||
y = t .* sin.(θ)
|
||||
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
|
||||
p2 = scatter(
|
||||
x,
|
||||
y,
|
||||
marker_z = ((x, y) -> begin
|
||||
x + y
|
||||
end),
|
||||
color = :bluesreds,
|
||||
legend = false,
|
||||
)
|
||||
plot(p1, p2)
|
||||
end # testset
|
||||
@testset "Framestyles" begin
|
||||
scatter(
|
||||
fill(randn(10), 6),
|
||||
fill(randn(10), 6),
|
||||
framestyle = [:box :semi :origin :zerolines :grid :none],
|
||||
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
|
||||
color = permutedims(1:6),
|
||||
layout = 6,
|
||||
label = "",
|
||||
markerstrokewidth = 0,
|
||||
ticks = -2:2,
|
||||
)
|
||||
# TODO: support :semi
|
||||
end # testset
|
||||
@testset "Quiver" begin
|
||||
x = (-2pi):0.2:(2 * pi)
|
||||
y = sin.(x)
|
||||
|
||||
u = ones(length(x))
|
||||
v = cos.(x)
|
||||
arrow_plot = plot(x, y, quiver = (u, v), arrow = true)
|
||||
# TODO: could adjust limits to fit arrows if too long, but how?
|
||||
# TODO: get latex available on CI
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
|
||||
# end
|
||||
end # testset
|
||||
@testset "Annotations" begin
|
||||
y = rand(10)
|
||||
plot(
|
||||
y,
|
||||
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
|
||||
leg = false,
|
||||
)
|
||||
annotate!([
|
||||
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
|
||||
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
|
||||
])
|
||||
annotation_plot = scatter!(
|
||||
range(2, stop = 8, length = 6),
|
||||
rand(6),
|
||||
marker = (50, 0.2, :orange),
|
||||
series_annotations = [
|
||||
"series",
|
||||
"annotations",
|
||||
"map",
|
||||
"to",
|
||||
"series",
|
||||
Plots.text("data", :green),
|
||||
],
|
||||
)
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
|
||||
# end
|
||||
end # testset
|
||||
@testset "Ribbon" begin
|
||||
aa = rand(10)
|
||||
bb = rand(10)
|
||||
cc = rand(10)
|
||||
conf = [aa - cc bb - cc]
|
||||
ribbon_plot =
|
||||
plot(collect(1:10), fill(1, 10), ribbon = (conf[:, 1], conf[:, 2]))
|
||||
Plots._update_plot_object(ribbon_plot)
|
||||
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
|
||||
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
|
||||
@test length(plots) == 4
|
||||
@test !haskey(plots[1].options.dict, "fill")
|
||||
@test !haskey(plots[2].options.dict, "fill")
|
||||
@test !haskey(plots[3].options.dict, "fill")
|
||||
@test haskey(plots[4].options.dict, "fill")
|
||||
@test ribbon_plot.o !== nothing
|
||||
@test ribbon_plot.o.the_plot !== nothing
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
|
||||
# end
|
||||
end # testset
|
||||
end # testset
|
||||
|
||||
Reference in New Issue
Block a user