Compare commits

...

69 Commits

Author SHA1 Message Date
Simon Christ 23b191231c more positions (#2490)
* tick label styling

* add title_location

* add legend positions

* fix commata
2020-03-19 11:57:10 +01:00
Simon Christ aa5d514d57 Merge pull request #2489 from JuliaPlots/bbs/grid-lines
put gridlines in the back
2020-03-19 01:00:41 +01:00
Daniel Schwabeneder 3fb45764b2 Merge pull request #2471 from daschw/staticarrays
fix StaticArray plotting
2020-03-18 23:07:32 +01:00
Simon Christ fcff639d0c put gridlines in the back 2020-03-18 17:05:07 +01:00
Simon Christ 62290a2a24 Fix appveyor.yml (#2484)
* Update appveyor.yml

* change parantheses

* escape inner quotation marks

* another try

* Update appveyor.yml
2020-03-18 13:49:06 +01:00
Daniel Schwabeneder bfafe91842 Merge pull request #2485 from harryscholes/fix-annotate-docstring
Fix annotate docstring
2020-03-18 10:55:05 +01:00
harryscholes cbaa8fca56 Fix annotate docstring 2020-03-18 09:53:09 +00:00
Simon Christ 0d210843e7 add example 20 2020-03-17 21:30:48 +01:00
Simon Christ 08bed07632 Merge pull request #2476 from JuliaPlots/bbs/string-sani
sanitize strings
2020-03-17 17:38:17 +01:00
Daniel Schwabeneder c47ab69383 Merge pull request #2477 from JuliaPlots/bbs/fix-tests
pin FixedPointNumbers in tests
2020-03-17 17:29:27 +01:00
Simon Christ eb942352ce add pin to appveyor 2020-03-17 16:46:08 +01:00
Simon Christ 00379097cf change compat 2020-03-17 15:34:03 +01:00
Simon Christ 16b568840d pin FixedPointNumbers 2020-03-17 15:32:54 +01:00
Simon Christ c1dae9e9d9 sanitize strings 2020-03-17 15:26:35 +01:00
Simon Christ 8d8921fd08 bump version 2020-03-17 11:25:38 +01:00
Simon Christ d04d651e55 Merge pull request #2473 from BeastyBlacksmith/pgfplotsx
another legends fix
2020-03-17 10:17:07 +01:00
Simon Christ c771739b1b Merge pull request #2474 from JuliaPlots/bbs/markershape
reduce markershape
2020-03-17 10:01:12 +01:00
Daniel Schwabeneder a800f8d4d9 add test example 2020-03-16 22:53:33 +01:00
Daniel Schwabeneder 4b5ec3ad07 add prepareSeriesData method for AbstractRanges 2020-03-16 22:20:42 +01:00
Daniel Schwabeneder 35c68c8890 only copy for immutables 2020-03-16 16:33:25 +01:00
Simon Christ 5005120f5e change scaling factor 2020-03-16 14:46:26 +01:00
Simon Christ 7b68e7985f another legends fix 2020-03-16 14:29:49 +01:00
Simon Christ 871c7d86c7 add ticks color (#2472)
* add ticks color

* hotfix
2020-03-16 14:28:42 +01:00
Daniel Schwabeneder 79614ffcec fix StaticArray plotting 2020-03-16 09:17:15 +01:00
Michael Krabbe Borregaard 6821148e8e cap fixedpointnumbers to 0.7 2020-03-15 09:30:07 +01:00
Simon Christ 3255c5a122 Allow legend vectors (#2449)
* allow vector legends

* format file pgfplotsx.jl

* handle unmatching vectors

* add test

* format test_pgfplotsx.jl
2020-03-14 15:44:07 +01:00
Josef Heinen bb9d36eb7a Update Project.toml 2020-03-13 16:40:49 +01:00
Simon Christ 50984b3bd2 bump version 2020-03-13 14:41:56 +01:00
Simon Christ 7520c1dced Merge pull request #2454 from JuliaPlots/bbs/check-examples
examples to skip
2020-03-13 14:19:10 +01:00
Simon Christ bfd468fc3a Merge pull request #2453 from JuliaPlots/bbs/fix-rib-example
fix ribbon-example
2020-03-13 14:18:45 +01:00
Simon Christ 3ab0078818 add skipping examples 2020-03-13 13:53:12 +01:00
Simon Christ 1fc587559c fix ribbon-example 2020-03-13 13:50:26 +01:00
Simon Christ 4821e04145 Merge pull request #2450 from BeastyBlacksmith/pgfplotsx
recreate plot if it was shown
2020-03-12 13:13:01 +01:00
Simon Christ bb8e62769f recreate plot if it was shown 2020-03-11 16:30:51 +01:00
Yuval 15dc61feb5 Merge pull request #2444 from yha/tests-windows
Allow image comparison tests on Windows.
2020-03-09 01:39:23 +02:00
yha d0d72242cb Allow image comparison tests on Windows. 2020-03-08 18:24:39 +02:00
Daniel Schwabeneder bece5a2620 Merge pull request #2442 from JuliaPlots/release-0.29.6
Release a new version
2020-03-06 21:33:34 +01:00
Anshul Singhvi d972702d3e Update Project.toml 2020-03-06 15:01:42 -05:00
Simon Christ d08b33e696 Merge pull request #2438 from BeastyBlacksmith/pgfplotsx
Fix twinx()
2020-03-06 12:41:44 +01:00
Simon Christ da60b47af5 format backend code 2020-03-06 12:17:42 +01:00
Simon Christ 0ac63857c4 fix twinx 2020-03-06 12:17:42 +01:00
Simon Christ d08422811e format file 2020-03-06 12:17:42 +01:00
Simon Christ d5788fc1e7 Allow legend for surfaces (#2439)
* allow legends for surfaces

* remove unnecessary checks

* redo necessary checks
2020-03-06 12:12:01 +01:00
Daniel Schwabeneder 6da5bb8822 Merge pull request #2428 from JuliaPlots/compathelper/new_version/2020-03-02-11-05-30-705-1878394197
CompatHelper: bump compat for "GeometryTypes" to "0.8"
2020-03-05 14:00:17 +01:00
Daniel Schwabeneder a0a3b42872 Merge pull request #2437 from daschw/release
bump version
2020-03-05 13:59:48 +01:00
Daniel Schwabeneder caf0aa098a bump version 2020-03-05 09:31:11 +01:00
Simon Christ 55ea8b4a70 New recipe: lens (#2372)
* create recipe, add box and line

* add subplot

* improve linking

* add ghost subplot

* draw line only if inset is in subplot

* sp_bbox -> inset_bbox

* better linking

* add lens! shorthand

* apply keywords only to inset

* check bbox type

* add lens example

* fix typo and stray *

* move legend out of the plot

* move lens example down
2020-03-05 03:03:21 +01:00
Simon Christ 25c53f0e52 Merge pull request #2435 from BeastyBlacksmith/pgfplotsx
fix legends with skipped entries
2020-03-04 21:49:45 +01:00
Daniel Schwabeneder 7c3938f793 Merge pull request #2436 from daschw/gr-image
fix gr image lims
2020-03-04 20:27:29 +01:00
Daniel Schwabeneder 20a10fcf7d fix gr image lims 2020-03-04 19:28:43 +01:00
Simon Christ 6ee49f8430 fix legends with skipped entries 2020-03-04 18:18:06 +01:00
Daniel Schwabeneder 0768c8a658 Merge pull request #2433 from daschw/cache
cache artifacts in CI services
2020-03-04 17:26:39 +01:00
Michael Krabbe Borregaard 89b2be4ecb Merge pull request #2434 from BeastyBlacksmith/bbs/format
Format changes to examples.jl and recipes.jl
2020-03-04 16:23:28 +01:00
Simon Christ 369d510b3e format examples.jl 2020-03-04 15:55:45 +01:00
Simon Christ d1dbb1bf2a format file 2020-03-04 15:55:22 +01:00
Daniel Schwabeneder ccf726ddcb cache artifacts in CI services 2020-03-04 12:53:24 +01:00
github-actions[bot] 5b2cd1b8ee CompatHelper: bump compat for "GeometryTypes" to "0.8" 2020-03-02 11:05:31 +00:00
Daniel Schwabeneder 16b8d01b88 Merge pull request #2427 from daschw/ratio
use equal aspect ratio by default for images
2020-03-01 08:52:41 +01:00
Daniel Schwabeneder 8d7b07eab3 bump version 2020-03-01 01:57:22 +01:00
Daniel Schwabeneder 599d6a2ec8 add commented code to update refernce images 2020-03-01 01:56:46 +01:00
Daniel Schwabeneder 345f5654a3 use equal aspect ratio by default for images 2020-03-01 01:37:34 +01:00
Daniel Schwabeneder 77e04c45db Merge pull request #2426 from daschw/gr-scatter
Fix shape markers for GR
2020-02-29 21:38:49 +01:00
Daniel Schwabeneder ad4d7214e4 increase test accuracy 2020-02-29 21:00:51 +01:00
Daniel Schwabeneder 4051d016b9 Fix Shape markers for GR 2020-02-29 20:44:56 +01:00
Daniel Schwabeneder 8610a82a88 Merge pull request #2421 from daschw/release
bump version
2020-02-28 09:29:44 +01:00
Daniel Schwabeneder 4832d870dd bump version 2020-02-28 09:27:35 +01:00
Daniel Schwabeneder ace915fbfb Merge pull request #2420 from JuliaPlots/compathelper/new_version/2020-02-27-15-06-02-666-763792840
CompatHelper: bump compat for "GR" to "0.47"
2020-02-28 09:23:52 +01:00
Josef Heinen c6593423f5 Update Project.toml 2020-02-28 09:09:54 +01:00
github-actions[bot] f69daa8cfe CompatHelper: bump compat for "GR" to "0.47" 2020-02-27 15:06:03 +00:00
18 changed files with 1977 additions and 996 deletions
+1
View File
@@ -8,3 +8,4 @@ deps/plotly-latest.min.js
deps/build.log
deps/deps.jl
Manifest.toml
dev/
+5 -1
View File
@@ -20,6 +20,10 @@ addons:
- xauth
- xvfb
cache:
directories:
- $HOME/.julia/artifacts
sudo: required
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
@@ -31,4 +35,4 @@ notifications:
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
- $TESTCMD -e 'using Pkg; Pkg.build(); Pkg.test(coverage=true)'
- $TESTCMD -e 'using Pkg; Pkg.pin(PackageSpec(name="FixedPointNumbers", version="0.7")); Pkg.build(); Pkg.test(coverage=true)'
+6 -4
View File
@@ -1,7 +1,7 @@
name = "Plots"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
author = ["Tom Breloff (@tbreloff)"]
version = "0.29.2"
version = "0.29.8"
[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
@@ -34,8 +34,8 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
Contour = "0.5"
FFMPEG = "0.2, 0.3"
FixedPointNumbers = "0.6, 0.7, 0.8"
GR = "0.46"
GeometryTypes = "0.7"
GR = "0.46, 0.47, 0.48"
GeometryTypes = "0.7, 0.8"
JSON = "0.21"
Measures = "0.3"
NaNMath = "0.3"
@@ -57,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"]
+5 -1
View File
@@ -20,6 +20,9 @@ branches:
- master
- /release-.*/
cache:
- '%USERPROFILE%\.julia\artifacts'
notifications:
- provider: Email
on_build_success: false
@@ -30,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%"
+1 -1
View File
@@ -345,7 +345,7 @@ const _subplot_defaults = KW(
:legendtitlefontcolor => :match,
:annotations => [], # annotation tuples... list of (x,y,annotation)
:projection => :none, # can also be :polar or :3d
:aspect_ratio => :none, # choose from :none or :equal
:aspect_ratio => :auto, # choose from :none or :equal
:margin => 1mm,
:left_margin => :match,
:top_margin => :match,
+108 -30
View File
@@ -673,37 +673,86 @@ const _inspectdr_scale = [:identity, :ln, :log2, :log10]
const _pgfplotsx_attr = merge_with_base_supported([
:annotations,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
:foreground_color_text, :foreground_color_border,
:background_color_legend,
:background_color_inside,
:background_color_outside,
:foreground_color_legend,
:foreground_color_grid,
:foreground_color_axis,
:foreground_color_text,
:foreground_color_border,
:label,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:fillrange, :fillcolor, :fillalpha,
:seriescolor,
:seriesalpha,
:linecolor,
:linestyle,
:linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokealpha,
:fillrange,
:fillcolor,
:fillalpha,
:bins,
:layout,
:title, :window_title,
:guide, :lims, :ticks, :scale, :flip,
:title,
:window_title,
:guide,
:lims,
:ticks,
:scale,
:flip,
:match_dimensions,
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
:titlefontrotation, :titlefontcolor,
:legendfontfamily, :legendfontsize, :legendfonthalign, :legendfontvalign,
:legendfontrotation, :legendfontcolor,
:tickfontfamily, :tickfontsize, :tickfonthalign, :tickfontvalign,
:tickfontrotation, :tickfontcolor,
:guidefontfamily, :guidefontsize, :guidefonthalign, :guidefontvalign,
:guidefontrotation, :guidefontcolor,
:grid, :gridalpha, :gridstyle, :gridlinewidth,
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
:fill_z, :line_z, :marker_z, :levels,
:ribbon, :quiver,
:titlefontfamily,
:titlefontsize,
:titlefonthalign,
:titlefontvalign,
:titlefontrotation,
:titlefontcolor,
:legendfontfamily,
:legendfontsize,
:legendfonthalign,
:legendfontvalign,
:legendfontrotation,
:legendfontcolor,
:tickfontfamily,
:tickfontsize,
:tickfonthalign,
:tickfontvalign,
:tickfontrotation,
:tickfontcolor,
:guidefontfamily,
:guidefontsize,
:guidefonthalign,
:guidefontvalign,
:guidefontrotation,
:guidefontcolor,
:grid,
:gridalpha,
:gridstyle,
:gridlinewidth,
:legend,
:legendtitle,
:colorbar,
:colorbar_title,
:colorbar_entry,
:fill_z,
:line_z,
:marker_z,
:levels,
:ribbon,
:quiver,
:orientation,
:overwrite_figure,
:polar,
:aspect_ratio,
:normalize, :weights,
:normalize,
:weights,
:inset_subplots,
:bar_width,
:arrow,
@@ -712,13 +761,42 @@ const _pgfplotsx_attr = merge_with_base_supported([
:camera,
:contour_labels,
])
const _pgfplotsx_seriestype =
[:path, :scatter, :straightline,
:path3d, :scatter3d, :surface, :wireframe,
:heatmap, :contour, :contour3d,
:shape,
:steppre, :stepmid, :steppost, :ysticks, :xsticks]
const _pgfplotsx_seriestype = [
:path,
:scatter,
:straightline,
:path3d,
:scatter3d,
:surface,
:wireframe,
:heatmap,
:contour,
:contour3d,
:shape,
:steppre,
:stepmid,
:steppost,
:ysticks,
:xsticks,
]
const _pgfplotsx_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
const _pgfplotsx_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :ltriangle, :rtriangle, :cross, :xcross, :star5, :pentagon, :hline, :vline, Shape]
const _pgfplotsx_marker = [
:none,
:auto,
:circle,
:rect,
:diamond,
:utriangle,
:dtriangle,
:ltriangle,
:rtriangle,
:cross,
:xcross,
:star5,
:pentagon,
:hline,
:vline,
Shape,
]
const _pgfplotsx_scale = [:identity, :ln, :log2, :log10]
is_marker_supported(::PGFPlotsXBackend, shape::Shape) = true
+26 -17
View File
@@ -305,15 +305,27 @@ end
# ---------------------------------------------------------
# draw ONE Shape
function gr_draw_marker(xi, yi, msize, shape::Shape)
function gr_draw_marker(series, xi, yi, clims, i, msize, shape::Shape)
sx, sy = coords(shape)
# convert to ndc coords (percentages of window)
GR.selntran(0)
w, h = gr_plot_size
f = msize / (w + h)
xi, yi = GR.wctondc(xi, yi)
GR.fillarea(xi .+ sx .* f,
yi .+ sy .* f)
xs = xi .+ sx .* f
ys = yi .+ sy .* f
# draw the interior
mc = get_markercolor(series, clims, i)
gr_set_fill(mc)
gr_set_transparency(mc, get_markeralpha(series, i))
GR.fillarea(xs, ys)
# draw the shapes
msc = get_markerstrokecolor(series, i)
gr_set_line(get_markerstrokewidth(series, i), :solid, msc)
gr_set_transparency(msc, get_markerstrokealpha(series, i))
GR.polyline(xs, ys)
GR.selntran(1)
end
@@ -323,7 +335,11 @@ function nominal_size()
end
# draw ONE symbol marker
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
function gr_draw_marker(series, xi, yi, clims, i, msize::Number, shape::Symbol)
GR.setborderwidth(series[:markerstrokewidth]);
gr_set_bordercolor(get_markerstrokecolor(series, i));
gr_set_markercolor(get_markercolor(series, clims, i));
gr_set_transparency(get_markeralpha(series, i))
GR.setmarkertype(gr_markertype[shape])
GR.setmarkersize(0.3msize / nominal_size())
GR.polymarker([xi], [yi])
@@ -341,13 +357,7 @@ function gr_draw_markers(series::Series, x, y, clims, msize = series[:markersize
for i=eachindex(x)
msi = _cycle(msize, i)
shape = _cycle(shapes, i)
i
GR.setborderwidth(series[:markerstrokewidth]);
gr_set_bordercolor(get_markerstrokecolor(series, i));
gr_set_markercolor(get_markercolor(series, clims, i));
gr_set_transparency(get_markeralpha(series, i))
gr_draw_marker(x[i], y[i], msi, shape)
gr_draw_marker(series, x[i], y[i], clims, i, msi, shape)
end
end
end
@@ -971,7 +981,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
data_lims = gr_xy_axislims(sp)
xy_lims = data_lims
ratio = sp[:aspect_ratio]
ratio = get_aspect_ratio(sp)
if ratio != :none
if ratio == :equal
ratio = 1
@@ -1050,11 +1060,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
if st == :pie
draw_axes = false
end
if st == :heatmap
if st in (:heatmap, :image)
outside_ticks = true
for ax in (sp[:xaxis], sp[:yaxis])
v = series[ax[:letter]]
end
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(series[:z]))
xy_lims = x[1], x[end], y[1], y[end]
expand_extrema!(sp[:xaxis], x)
@@ -1776,8 +1783,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
elseif st == :image
z = transpose_z(series, series[:z].surf, true)'
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(z))
w, h = size(z)
xmin, xmax = ignorenan_extrema(series[:x]); ymin, ymax = ignorenan_extrema(series[:y])
xmin, xmax = ignorenan_extrema(x)
ymin, ymax = ignorenan_extrema(y)
rgba = gr_color.(z)
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
end
+1 -1
View File
@@ -470,7 +470,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
end
if sp[:aspect_ratio] in (1, :equal)
if get_aspect_ratio(sp) in (1, :equal)
kw[:axisEqual] = "true"
end
+246 -127
View File
@@ -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},%
},
}
""",
)
@@ -51,7 +60,7 @@ function pgfx_preamble(pgfx_plot::Plot{PGFPlotsXBackend})
old_flag = pgfx_plot.attr[:tex_output_standalone]
pgfx_plot.attr[:tex_output_standalone] = true
fulltext = String(repr("application/x-tex", pgfx_plot))
preamble = fulltext[1:first(findfirst("\\begin{document}", fulltext))-1]
preamble = fulltext[1:(first(findfirst("\\begin{document}", fulltext)) - 1)]
pgfx_plot.attr[:tex_output_standalone] = old_flag
preamble
end
@@ -75,10 +84,11 @@ function Base.push!(pgfx_plot::PGFPlotsXPlot, item)
end
function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
if !pgfx_plot.is_created
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]
plt.attr[:background_color] : plt.attr[:background_color_outside]
if bgc isa Colors.Colorant
cstr = plot_color(bgc)
a = alpha(cstr)
@@ -98,7 +108,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
sp_width = width(bb)
sp_height = height(bb)
dx, dy = bb.x0
# TODO: does this hold at every scale?
# TODO: does this hold at every scale?
if sp[:legend] in (:outertopright, nothing)
dx *= 1.2
end
@@ -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,
@@ -129,7 +149,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
) => nothing,
"fill" => cstr,
"fill opacity" => a,
"text opacity" => alpha(plot_color(sp[:legendfontcolor])),
"text opacity" => alpha(plot_color(sp[:legendfontcolor])),
"font" => pgfx_font(
sp[:legendfontsize],
pgfx_thickness_scaling(sp),
@@ -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),
@@ -157,11 +175,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
else
push!(
axis_opt,
"legend pos" => get(
_pgfplotsx_legend_pos,
sp[:legend],
"outer north east",
),
"legend pos" =>
get(_pgfplotsx_legend_pos, sp[:legend], "outer north east"),
)
end
for letter in (:x, :y, :z)
@@ -185,8 +200,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
PGFPlotsX.push_preamble!(
pgfx_plot.the_plot,
"""\\pgfplotsset{
colormap={plots$(sp.attr[:subplot_index])}{$(pgfx_colormap(series.plotattributes[col]))},
}""",
colormap={plots$(sp.attr[:subplot_index])}{$(pgfx_colormap(series.plotattributes[col]))},
}""",
)
push!(
axis_opt,
@@ -213,8 +228,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
push!(axis_opt, "view" => (azim, elev))
end
axisf = if sp[:projection] == :polar
# push!(axis_opt, "xmin" => 90)
# push!(axis_opt, "xmax" => 450)
# push!(axis_opt, "xmin" => 90)
# push!(axis_opt, "xmax" => 450)
PGFPlotsX.PolarAxis
else
PGFPlotsX.Axis
@@ -229,20 +244,21 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
sf = series[:fillrange]
series_opt = PGFPlotsX.Options(
"color" => single_color(opt[:linecolor]),
"name path" => string(series_id)
"name path" => string(series_id),
)
if is3d(series) || st == :heatmap
series_func = PGFPlotsX.Plot3
else
series_func = PGFPlotsX.Plot
end
if sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing
if sf !== nothing &&
!isfilledcontour(series) && series[:ribbon] === nothing
push!(series_opt, "area legend" => nothing)
end
if st == :heatmap
push!(axis.options, "view" => "{0}{90}")
end
# treat segments
# treat segments
segments =
if st in (:wireframe, :heatmap, :contour, :surface, :contour3d)
iter_segments(surface_to_vecs(
@@ -261,10 +277,13 @@ 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(
["($(x[i] * mark_size), $(y[i] * mark_size))" for i in eachindex(x)],
[
"($(x[i] * mark_size), $(y[i] * mark_size))"
for i in eachindex(x)
],
" -- ",
)
c = get_markercolor(series, i)
@@ -285,38 +304,44 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
segment_opt = merge(segment_opt, pgfx_fillstyle(opt, i))
end
# add fillrange
if sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing
if sf !== nothing &&
!isfilledcontour(series) && series[:ribbon] === nothing
if sf isa Number || sf isa AVec
pgfx_fillrange_series!( axis, series, series_func, i, _cycle(sf, rng), rng)
pgfx_fillrange_series!(
axis,
series,
series_func,
i,
_cycle(sf, rng),
rng,
)
end
if i == 1 && opt[:label] != "" && sp[:legend] != :none && should_add_to_legend(series)
if i == 1 &&
sp[:legend] != :none && pgfx_should_add_to_legend(series)
pgfx_filllegend!(series_opt, opt)
end
end
# series
#
coordinates = pgfx_series_coordinates!(
sp,
series,
segment_opt,
opt,
rng,
)
segment_plot = series_func(
merge(series_opt, segment_opt),
coordinates,
)
coordinates =
pgfx_series_coordinates!(sp, series, segment_opt, opt, rng)
segment_plot =
series_func(merge(series_opt, segment_opt), coordinates)
push!(axis, segment_plot)
# fill between functions
if sf isa Tuple && series[:ribbon] === nothing
sf1, sf2 = sf
@assert sf1 == series_index "First index of the tuple has to match the current series index."
push!(axis, series_func(
merge(pgfx_fillstyle(opt, series_index), PGFPlotsX.Options("forget plot" => nothing)),
"fill between [of=$series_id and $(_pgfplotsx_series_ids[Symbol(string(sf2))])]"
))
push!(
axis,
series_func(
merge(
pgfx_fillstyle(opt, series_index),
PGFPlotsX.Options("forget plot" => nothing),
),
"fill between [of=$series_id and $(_pgfplotsx_series_ids[Symbol(string(sf2))])]",
),
)
end
# add ribbons?
# add ribbons?
ribbon = series[:ribbon]
if ribbon !== nothing
pgfx_add_ribbons!(
@@ -327,18 +352,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
series_index,
)
end
# add to legend?
if i == 1 &&
opt[:label] != "" &&
sp[:legend] != :none && should_add_to_legend(series)
leg_opt = PGFPlotsX.Options()
if ribbon !== nothing
pgfx_filllegend!(axis.contents[end-3].options, opt)
end
legend = PGFPlotsX.LegendEntry(leg_opt, opt[:label], false)
push!(axis, legend)
end
# add series annotations
# add series annotations
anns = series[:series_annotations]
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
pgfx_add_annotation!(
@@ -349,10 +363,38 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
pgfx_thickness_scaling(series),
)
end
end
# add to legend?
if sp[:legend] != :none
leg_entry = if opt[:label] isa AVec
get(opt[:label], i, "")
elseif opt[:label] isa AbstractString
if i == 1
get(opt, :label, "")
else
""
end
else
throw(ArgumentError("Malformed label. label = $(opt[:label])"))
end
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
end
end # for segments
# add subplot annotations
for ann in sp[:annotations]
pgfx_add_annotation!(axis, locate_annotation(sp, ann...)..., pgfx_thickness_scaling(sp))
pgfx_add_annotation!(
axis,
locate_annotation(sp, ann...)...,
pgfx_thickness_scaling(sp),
)
end
end # for series
push!(the_plot, axis)
@@ -495,7 +537,7 @@ function pgfx_series_coordinates!(
)
push!(
segment_opt,
"contour prepared" => PGFPlotsX.Options("labels" => opt[:contour_labels],),
"contour prepared" => PGFPlotsX.Options("labels" => opt[:contour_labels]),
)
return PGFPlotsX.Table(Contour.contours(args..., opt[:levels]))
end
@@ -508,7 +550,7 @@ function pgfx_series_coordinates!(
xs, ys, zs = collect(args)
push!(
segment_opt,
"contour filled" => PGFPlotsX.Options("labels" => opt[:contour_labels],),
"contour filled" => PGFPlotsX.Options("labels" => opt[:contour_labels]),
"point meta" => "explicit",
"shader" => "flat",
)
@@ -519,10 +561,9 @@ function pgfx_series_coordinates!(
end
cs = join(
[join(["($x, $y) [$(zs[j, i])]" for (j, x) in enumerate(xs)], " ") for (
i,
y,
) in enumerate(ys)],
[
join(["($x, $y) [$(zs[j, i])]" for (j, x) in enumerate(xs)], " ") for (i, y) in enumerate(ys)
],
"\n\n",
)
"""
@@ -563,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]
@@ -575,11 +627,8 @@ const _pgfx_framestyle_defaults = Dict(:semi => :box)
# we use the anchors to define orientations for example to align left
# one needs to use the right edge as anchor
const _pgfx_annotation_halign = KW(
:center => "",
:left => "right",
:right => "left",
)
const _pgfx_annotation_halign =
KW(:center => "", :left => "right", :right => "left")
## --------------------------------------------------------------------------------------
# Generates a colormap for pgfplots based on a ColorGradient
pgfx_arrow(::Nothing) = "every arrow/.append style={-}"
@@ -607,12 +656,9 @@ function pgfx_filllegend!(series_opt, opt)
io = IOBuffer()
PGFPlotsX.print_tex(io, pgfx_fillstyle(opt))
style = strip(String(take!(io)), ['[', ']', ' '])
push!(
series_opt,
"legend image code/.code" => """{
\\draw[$style] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
}""",
)
push!(series_opt, "legend image code/.code" => """{
\\draw[$style] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
}""")
end
function pgfx_colormap(grad::ColorGradient)
@@ -631,9 +677,7 @@ function pgfx_framestyle(style::Symbol)
return style
else
default_style = get(_pgfx_framestyle_defaults, style, :axes)
@warn(
"Framestyle :$style is not (yet) supported by the PGFPlotsX backend. :$default_style was cosen instead.",
)
@warn( "Framestyle :$style is not (yet) supported by the PGFPlotsX backend. :$default_style was cosen instead.",)
default_style
end
end
@@ -675,6 +719,25 @@ function pgfx_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
return string("{\\fontsize{", fs, " pt}{", 1.3fs, " pt}", font, "}")
end
function pgfx_should_add_to_legend(series::Series)
series.plotattributes[:primary] &&
!(
series.plotattributes[:seriestype] in (
:hexbin,
:bins2d,
:histogram2d,
:hline,
:vline,
:contour,
:contourf,
:contour3d,
:heatmap,
:pie,
:image,
)
)
end
function pgfx_marker(plotattributes, i = 1)
shape = _cycle(plotattributes[:markershape], i)
cstr = plot_color(
@@ -687,19 +750,22 @@ function pgfx_marker(plotattributes, i = 1)
get_markerstrokealpha(plotattributes, i),
)
a_stroke = alpha(cstr_stroke)
mark_size = pgfx_thickness_scaling(plotattributes) * 0.5 *
_cycle(plotattributes[:markersize], i)
mark_size =
pgfx_thickness_scaling(plotattributes) *
0.5 *
_cycle(plotattributes[:markersize], i)
return PGFPlotsX.Options(
"mark" => shape isa Shape ? "PlotsShape$i" :
get(_pgfplotsx_markers, shape, "*"),
"mark" =>
shape isa Shape ? "PlotsShape$i" : get(_pgfplotsx_markers, shape, "*"),
"mark size" => "$mark_size pt",
"mark options" => PGFPlotsX.Options(
"color" => cstr_stroke,
"draw opacity" => a_stroke,
"fill" => cstr,
"fill opacity" => a,
"line width" => pgfx_thickness_scaling(plotattributes) *
_cycle(plotattributes[:markerstrokewidth], i),
"line width" =>
pgfx_thickness_scaling(plotattributes) *
_cycle(plotattributes[:markerstrokewidth], i),
"rotate" => if shape == :dtriangle
180
elseif shape == :rtriangle
@@ -725,15 +791,15 @@ function pgfx_add_annotation!(o, x, y, val, thickness_scaling = 1)
push!(
o,
[
"\\node",
PGFPlotsX.Options(
get(_pgfx_annotation_halign, val.font.halign, "") => nothing,
"color" => cstr,
"draw opacity" => convert(Float16, a),
"rotate" => val.font.rotation,
"font" => pgfx_font(val.font.pointsize, thickness_scaling),
),
" at (axis cs:$x, $y) {$(val.str)};",
"\\node",
PGFPlotsX.Options(
get(_pgfx_annotation_halign, val.font.halign, "") => nothing,
"color" => cstr,
"draw opacity" => convert(Float16, a),
"rotate" => val.font.rotation,
"font" => pgfx_font(val.font.pointsize, thickness_scaling),
),
" at (axis cs:$x, $y) {$(val.str)};",
],
)
end
@@ -803,7 +869,7 @@ function pgfx_fillrange_series!(axis, series, series_func, i, fillrange, rng)
push!(fillrange_opt, "forget plot" => nothing)
opt = series.plotattributes
args = is3d(series) ? (opt[:x][rng], opt[:y][rng], opt[:z][rng]) :
(opt[:x][rng], opt[:y][rng])
(opt[:x][rng], opt[:y][rng])
push!(
axis,
PGFPlotsX.PlotInc(fillrange_opt, pgfx_fillrange_args(fillrange, args...)),
@@ -825,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)]
@@ -835,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])
@@ -854,7 +977,7 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
opt,
string(letter, "label style") => PGFPlotsX.Options(
labelpos => nothing,
"font" => pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)),
"font" => pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)),
"color" => cstr,
"draw opacity" => α,
"rotate" => axis[:guidefontrotation],
@@ -868,10 +991,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
scale = axis[:scale]
if scale in (:log2, :ln, :log10)
push!(opt, string(letter, :mode) => "log")
scale == :ln || push!(
opt,
"log basis $letter" => "$(scale == :log2 ? 2 : 10)",
)
scale == :ln ||
push!(opt, "log basis $letter" => "$(scale == :log2 ? 2 : 10)")
end
# ticks on or off
@@ -888,14 +1009,15 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
# limits
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
axis_limits(sp, letter)
axis_limits(sp, letter)
push!(opt, string(letter, :min) => lims[1], string(letter, :max) => lims[2])
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
ticks = get_ticks(sp, axis)
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
tick_values = ispolar(sp) && letter == :x ?
[rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
tick_values =
ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] :
ticks[1]
push!(
opt,
string(letter, "tick") => string("{", join(tick_values, ","), "}"),
@@ -911,44 +1033,37 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
end
push!(
opt,
string(letter, "ticklabels") => string(
"{\$",
join(tick_labels, "\$,\$"),
"\$}",
),
string(letter, "ticklabels") =>
string("{\$", join(tick_labels, "\$,\$"), "\$}"),
)
elseif axis[:showaxis]
tick_labels = ispolar(sp) && letter == :x ?
[ticks[2][3:end]..., "0", "45"] : ticks[2]
tick_labels =
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] :
ticks[2]
if axis[:formatter] in (:scientific, :auto)
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
end
push!(
opt,
string(letter, "ticklabels") => string(
"{",
join(tick_labels, ","),
"}",
),
string(letter, "ticklabels") =>
string("{", join(tick_labels, ","), "}"),
)
else
push!(opt, string(letter, "ticklabels") => "{}")
end
push!(
opt,
string(letter, "tick align") => (axis[:tick_direction] == :out ?
"outside" : "inside"),
string(letter, "tick align") =>
(axis[:tick_direction] == :out ? "outside" : "inside"),
)
cstr = plot_color(axis[:tickfontcolor])
α = alpha(cstr)
push!(
opt,
string(letter, "ticklabel style") => PGFPlotsX.Options(
"font" => pgfx_font(
axis[:tickfontsize],
pgfx_thickness_scaling(sp),
),
"font" =>
pgfx_font(axis[:tickfontsize], pgfx_thickness_scaling(sp)),
"color" => cstr,
"draw opacity" => α,
"rotate" => axis[:tickfontrotation],
@@ -967,7 +1082,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
# framestyle
if framestyle in (:axes, :origin)
axispos = framestyle == :axes ? "left" : "middle"
axispos = axis[:mirror] ? "right" : framestyle == :axes ? "left" : "middle"
if axis[:draw_arrow]
push!(opt, string("axis ", letter, " line") => axispos)
else
@@ -1039,6 +1155,7 @@ for mime in ("application/pdf", "image/png", "image/svg+xml")
mime::MIME{Symbol($mime)},
plt::Plot{PGFPlotsXBackend},
)
plt.o.was_shown = true
show(io, mime, plt.o.the_plot)
end
end
@@ -1048,6 +1165,7 @@ function _show(
mime::MIME{Symbol("application/x-tex")},
plt::Plot{PGFPlotsXBackend},
)
plt.o.was_shown = true
PGFPlotsX.print_tex(
io,
plt.o.the_plot,
@@ -1056,5 +1174,6 @@ function _show(
end
function _display(plt::Plot{PGFPlotsXBackend})
plt.o.was_shown = true
display(PGFPlotsX.PGFPlotsXDisplay(), plt.o.the_plot)
end
+1 -1
View File
@@ -108,7 +108,7 @@ function shrink_by(lo, sz, ratio)
end
function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
aspect_ratio = sp[:aspect_ratio]
aspect_ratio = get_aspect_ratio(sp)
if aspect_ratio != :none
if aspect_ratio == :equal
aspect_ratio = 1.0
+1 -1
View File
@@ -1155,7 +1155,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
end
# aspect ratio
aratio = sp[:aspect_ratio]
aratio = get_aspect_ratio(sp)
if aratio != :none
ax."set_aspect"(isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
end
+922 -520
View File
File diff suppressed because it is too large Load Diff
+317 -117
View File
@@ -44,7 +44,7 @@ end
# ----------------------------------------------------------------------------------
num_series(x::AMat) = size(x,2)
num_series(x::AMat) = size(x, 2)
num_series(x) = 1
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
@@ -55,13 +55,26 @@ RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) wher
# for seriestype `line`, need to sort by x values
const POTENTIAL_VECTOR_ARGUMENTS = [
:seriescolor, :seriesalpha,
:linecolor, :linealpha, :linewidth, :linestyle, :line_z,
:fillcolor, :fillalpha, :fill_z,
:markercolor, :markeralpha, :markershape, :marker_z,
:markerstrokecolor, :markerstrokealpha,
:yerror, :yerror,
:series_annotations, :fillrange
:seriescolor,
:seriesalpha,
:linecolor,
:linealpha,
:linewidth,
:linestyle,
:line_z,
:fillcolor,
:fillalpha,
:fill_z,
:markercolor,
:markeralpha,
:markershape,
:marker_z,
:markerstrokecolor,
:markerstrokealpha,
:yerror,
:yerror,
:series_annotations,
:fillrange,
]
@recipe function f(::Type{Val{:line}}, x, y, z)
@@ -99,7 +112,7 @@ end
@recipe function f(::Type{Val{:hline}}, x, y, z)
n = length(y)
newx = repeat(Float64[-1, 1, NaN], n)
newy = vec(Float64[yi for i=1:3,yi=y])
newy = vec(Float64[yi for i = 1:3, yi in y])
x := newx
y := newy
seriestype := :straightline
@@ -109,7 +122,7 @@ end
@recipe function f(::Type{Val{:vline}}, x, y, z)
n = length(y)
newx = vec(Float64[yi for i=1:3,yi=y])
newx = vec(Float64[yi for i = 1:3, yi in y])
newy = repeat(Float64[-1, 1, NaN], n)
x := newx
y := newy
@@ -121,7 +134,7 @@ end
@recipe function f(::Type{Val{:hspan}}, x, y, z)
n = div(length(y), 2)
newx = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
newy = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
linewidth --> 0
x := newx
y := newy
@@ -132,7 +145,7 @@ end
@recipe function f(::Type{Val{:vspan}}, x, y, z)
n = div(length(y), 2)
newx = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
linewidth --> 0
x := newx
@@ -156,7 +169,7 @@ end
primary := false
()
end
()
()
end
@deps scatterpath path scatter
@@ -169,7 +182,7 @@ function make_steps(x::AbstractArray, st)
n = length(x)
n == 0 && return zeros(0)
newx = zeros(2n - 1)
for i in 1:n
for i = 1:n
idx = 2i - 1
newx[idx] = x[i]
if i > 1
@@ -249,10 +262,10 @@ end
end
end
newx, newy = zeros(3n), zeros(3n)
for i=1:n
rng = 3i-2:3i
for i = 1:n
rng = (3i - 2):(3i)
newx[rng] = [x[i], x[i], NaN]
newy[rng] = [_cycle(fr,i), y[i], NaN]
newy[rng] = [_cycle(fr, i), y[i], NaN]
end
x := newx
y := newy
@@ -282,16 +295,16 @@ end
# get the value of the curve point at position t
function bezier_value(pts::AVec, t::Real)
val = 0.0
n = length(pts)-1
for (i,p) in enumerate(pts)
val += p * binomial(n, i-1) * (1-t)^(n-i+1) * t^(i-1)
n = length(pts) - 1
for (i, p) in enumerate(pts)
val += p * binomial(n, i - 1) * (1 - t)^(n - i + 1) * t^(i - 1)
end
val
end
# create segmented bezier curves in place of line segments
@recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30)
args = z !== nothing ? (x,y,z) : (x,y)
args = z !== nothing ? (x, y, z) : (x, y)
newx, newy = zeros(0), zeros(0)
fr = plotattributes[:fillrange]
newfr = fr !== nothing ? zeros(0) : nothing
@@ -304,13 +317,13 @@ end
for rng in iter_segments(args...)
length(rng) < 2 && continue
ts = range(0, stop = 1, length = npoints)
nanappend!(newx, map(t -> bezier_value(_cycle(x,rng), t), ts))
nanappend!(newy, map(t -> bezier_value(_cycle(y,rng), t), ts))
nanappend!(newx, map(t -> bezier_value(_cycle(x, rng), t), ts))
nanappend!(newy, map(t -> bezier_value(_cycle(y, rng), t), ts))
if z !== nothing
nanappend!(newz, map(t -> bezier_value(_cycle(z,rng), t), ts))
nanappend!(newz, map(t -> bezier_value(_cycle(z, rng), t), ts))
end
if fr !== nothing
nanappend!(newfr, map(t -> bezier_value(_cycle(fr,rng), t), ts))
nanappend!(newfr, map(t -> bezier_value(_cycle(fr, rng), t), ts))
end
# if lz !== nothing
# lzrng = _cycle(lz, rng) # the line_z's for this segment
@@ -343,14 +356,15 @@ end
# create a bar plot as a filled step function
@recipe function f(::Type{Val{:bar}}, x, y, z)
procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y)
procx, procy, xscale, yscale, baseline =
_preprocess_barlike(plotattributes, x, y)
nx, ny = length(procx), length(procy)
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
cv = [discrete_value!(axis, xi)[1] for xi=procx]
cv = [discrete_value!(axis, xi)[1] for xi in procx]
procx = if nx == ny
cv
elseif nx == ny + 1
0.5diff(cv) + cv[1:end-1]
0.5 * diff(cv) + cv[1:(end - 1)]
else
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
end
@@ -359,12 +373,12 @@ end
bw = plotattributes[:bar_width]
hw = if bw === nothing
if nx > 1
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
0.5 * _bar_width * ignorenan_minimum(filter(x -> x > 0, diff(procx)))
else
0.5 * _bar_width
end
else
Float64[0.5_cycle(bw,i) for i=eachindex(procx)]
Float64[0.5 * _cycle(bw, i) for i in eachindex(procx)]
end
# make fillto a vector... default fills to 0
@@ -378,13 +392,20 @@ end
# create the bar shapes by adding x/y segments
xseg, yseg = Segments(), Segments()
for i=1:ny
for i = 1:ny
yi = procy[i]
if !isnan(yi)
center = procx[i]
hwi = _cycle(hw,i)
fi = _cycle(fillto,i)
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
hwi = _cycle(hw, i)
fi = _cycle(fillto, i)
push!(
xseg,
center - hwi,
center - hwi,
center + hwi,
center + hwi,
center - hwi,
)
push!(yseg, yi, fi, fi, yi, yi)
end
end
@@ -415,8 +436,8 @@ end
m, n = size(z.surf)
x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n)
fz = zeros(m * n)
for i in 1:m # y
for j in 1:n # x
for i = 1:m # y
for j = 1:n # x
k = (j - 1) * m + i
inds = (6 * (k - 1) + 1):(6 * k - 1)
x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]]
@@ -440,13 +461,17 @@ end
# ---------------------------------------------------------------------------
# Histograms
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
_bin_centers(v::AVec) = (v[1:(end - 1)] + v[2:end]) / 2
_is_positive(x) = (x > 0) && !(x 0)
_positive_else_nan(::Type{T}, x::Real) where {T} = _is_positive(x) ? T(x) : T(NaN)
function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) where T<:AbstractFloat
function _scale_adjusted_values(
::Type{T},
V::AbstractVector,
scale::Symbol,
) where {T<:AbstractFloat}
if scale in _logScales
[_positive_else_nan(T, x) for x in V]
else
@@ -455,7 +480,7 @@ function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) whe
end
function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
if (scale in _logScales)
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
else
@@ -464,7 +489,11 @@ function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
end
function _preprocess_binbarlike_weights(::Type{T}, w, wscale::Symbol) where T<:AbstractFloat
function _preprocess_binbarlike_weights(
::Type{T},
w,
wscale::Symbol,
) where {T<:AbstractFloat}
w_adj = _scale_adjusted_values(T, w, wscale)
w_min = ignorenan_minimum(w_adj)
w_max = ignorenan_maximum(w_adj)
@@ -490,7 +519,8 @@ end
@recipe function f(::Type{Val{:barbins}}, x, y, z)
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
edge, weights, xscale, yscale, baseline =
_preprocess_binlike(plotattributes, x, y)
if (plotattributes[:bar_width] === nothing)
bar_width := diff(edge)
end
@@ -503,8 +533,9 @@ end
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
xerror := diff(edge)/2
edge, weights, xscale, yscale, baseline =
_preprocess_binlike(plotattributes, x, y)
xerror := diff(edge) / 2
x := _bin_centers(edge)
y := weights
seriestype := :scatter
@@ -513,7 +544,13 @@ end
@deps scatterbins scatter
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
function _stepbins_path(
edge,
weights,
baseline::Real,
xscale::Symbol,
yscale::Symbol,
)
log_scale_x = xscale in _logScales
log_scale_y = yscale in _logScales
@@ -538,7 +575,7 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
w, it_state_w = it_tuple_w
if (log_scale_x && a 0)
a = oftype(a, b/_logScaleBases[xscale]^3)
a = oftype(a, b / _logScaleBases[xscale]^3)
end
if isnan(w)
@@ -575,9 +612,11 @@ end
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
axis = plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
axis =
plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
edge, weights, xscale, yscale, baseline =
_preprocess_binlike(plotattributes, x, y)
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
if !isvertical(plotattributes)
@@ -607,9 +646,19 @@ end
end
Plots.@deps stepbins path
wand_edges(x...) = (@warn("Load the StatsPlots package in order to use :wand bins. Defaulting to :auto", once = true); :auto)
wand_edges(x...) = (
@warn(
"Load the StatsPlots package in order to use :wand bins. Defaulting to :auto",
once = true
);
:auto
)
function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto) where N
function _auto_binning_nbins(
vs::NTuple{N,AbstractVector},
dim::Integer;
mode::Symbol = :auto,
) where {N}
max_bins = 10_000
_cl(x) = min(ceil(Int, max(x, one(x))), max_bins)
_iqr(v) = (q = quantile(v, 0.75) - quantile(v, 0.25); q > 0 ? q : oftype(q, 1))
@@ -618,8 +667,13 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
n_samples = length(LinearIndices(first(vs)))
# The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation
nd = n_samples^(1/(2+N))
nd = N == 2 ? min(n_samples^(1/(2+N)), nd / (1-cor(first(vs), last(vs))^2)^(3//8)) : nd # the >2-dimensional case does not have a nice solution to correlations
nd = n_samples^(1 / (2 + N))
nd = N == 2 ?
min(
n_samples^(1 / (2 + N)),
nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8),
) :
nd # the >2-dimensional case does not have a nice solution to correlations
v = vs[dim]
@@ -644,32 +698,52 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
end
end
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} = StatsBase.histrange(vs[dim], binning, :left)
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) where {N} = binning
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} =
StatsBase.histrange(vs[dim], binning, :left)
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} =
_hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
_hist_edge(
vs::NTuple{N,AbstractVector},
dim::Integer,
binning::AbstractVector,
) where {N} = binning
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N, Any}) where {N} =
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N,Any}) where {N} =
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...,))
_hist_edges(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) where {N} =
map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
_hist_edges(
vs::NTuple{N,AbstractVector},
binning::Union{Integer,Symbol,AbstractVector},
) where {N} = map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
_hist_norm_mode(mode::Symbol) = mode
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
_filternans(vs::NTuple{1,AbstractVector}) = filter!.(isfinite, vs)
function _filternans(vs::NTuple{N,AbstractVector}) where N
_invertedindex(v, not) = [j for (i,j) in enumerate(v) if !(i not)]
function _filternans(vs::NTuple{N,AbstractVector}) where {N}
_invertedindex(v, not) = [j for (i, j) in enumerate(v) if !(i not)]
nots = union(Set.(findall.(!isfinite, vs))...)
_invertedindex.(vs, Ref(nots))
end
function _make_hist(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing) where N
function _make_hist(
vs::NTuple{N,AbstractVector},
binning;
normed = false,
weights = nothing,
) where {N}
localvs = _filternans(vs)
edges = _hist_edges(localvs, binning)
h = float( weights === nothing ?
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
StatsBase.fit(StatsBase.Histogram, localvs, StatsBase.Weights(weights), edges, closed = :left)
h = float(
weights === nothing ?
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
StatsBase.fit(
StatsBase.Histogram,
localvs,
StatsBase.Weights(weights),
edges,
closed = :left,
),
)
normalize!(h, mode = _hist_norm_mode(normed))
end
@@ -682,7 +756,12 @@ end
@deps histogram barhist
@recipe function f(::Type{Val{:barhist}}, x, y, z)
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
h = _make_hist(
(y,),
plotattributes[:bins],
normed = plotattributes[:normalize],
weights = plotattributes[:weights],
)
x := h.edges[1]
y := h.weights
seriestype := :barbins
@@ -691,7 +770,12 @@ end
@deps barhist barbins
@recipe function f(::Type{Val{:stephist}}, x, y, z)
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
h = _make_hist(
(y,),
plotattributes[:bins],
normed = plotattributes[:normalize],
weights = plotattributes[:weights],
)
x := h.edges[1]
y := h.weights
seriestype := :stepbins
@@ -700,7 +784,12 @@ end
@deps stephist stepbins
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
h = _make_hist(
(y,),
plotattributes[:bins],
normed = plotattributes[:normalize],
weights = plotattributes[:weights],
)
x := h.edges[1]
y := h.weights
seriestype := :scatterbins
@@ -709,19 +798,23 @@ end
@deps scatterhist scatterbins
@recipe function f(h::StatsBase.Histogram{T, 1, E}) where {T, E}
@recipe function f(h::StatsBase.Histogram{T,1,E}) where {T,E}
seriestype --> :barbins
st_map = Dict(
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
:bar => :barbins,
:scatter => :scatterbins,
:step => :stepbins,
:steppost => :stepbins, # :step can be mapped to :steppost in pre-processing
)
seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
seriestype :=
get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
if plotattributes[:seriestype] == :scatterbins
# Workaround, error bars currently not set correctly by scatterbins
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, h.edges[1], h.weights)
xerror --> diff(h.edges[1])/2
edge, weights, xscale, yscale, baseline =
_preprocess_binlike(plotattributes, h.edges[1], h.weights)
xerror --> diff(h.edges[1]) / 2
seriestype := :scatter
(Plots._bin_centers(edge), weights)
else
@@ -730,7 +823,7 @@ end
end
@recipe function f(hv::AbstractVector{H}) where H <: StatsBase.Histogram
@recipe function f(hv::AbstractVector{H}) where {H<:StatsBase.Histogram}
for h in hv
@series begin
h
@@ -769,7 +862,12 @@ Plots.@deps bins2d heatmap
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
h = _make_hist((x, y), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
h = _make_hist(
(x, y),
plotattributes[:bins],
normed = plotattributes[:normalize],
weights = plotattributes[:weights],
)
x := h.edges[1]
y := h.edges[2]
z := Surface(h.weights)
@@ -779,7 +877,7 @@ end
@deps histogram2d bins2d
@recipe function f(h::StatsBase.Histogram{T, 2, E}) where {T, E}
@recipe function f(h::StatsBase.Histogram{T,2,E}) where {T,E}
seriestype --> :bins2d
(h.edges[1], h.edges[2], Surface(h.weights))
end
@@ -801,6 +899,91 @@ end
# note: don't add dependencies because this really isn't a drop-in replacement
# ---------------------------------------------------------------------------
# lens! - magnify a region of a plot
lens!(args...;kwargs...) = plot!(args...; seriestype=:lens, kwargs...)
export lens!
@recipe function f(::Type{Val{:lens}}, plt::AbstractPlot)
sp_index, inset_bbox = plotattributes[:inset_subplots]
if !(width(inset_bbox) isa Measures.Length{:w,<:Real})
throw(ArgumentError("Inset bounding box needs to in relative coordinates."))
end
sp = plt.subplots[sp_index]
xl1, xl2 = xlims(plt.subplots[sp_index])
bbx1 = xl1 + left(inset_bbox).value * (xl2 - xl1)
bbx2 = bbx1 + width(inset_bbox).value * (xl2 - xl1)
yl1, yl2 = ylims(plt.subplots[sp_index])
bby1 = yl1 + (1 - bottom(inset_bbox).value) * (yl2 - yl1)
bby2 = bby1 + height(inset_bbox).value * (yl2 - yl1)
bbx = bbx1 + width(inset_bbox).value * (xl2 - xl1) / 2
bby = bby1 + height(inset_bbox).value * (yl2 - yl1) / 2
lens_index = last(plt.subplots)[:subplot_index] + 1
x1, x2 = plotattributes[:x]
y1, y2 = plotattributes[:y]
seriestype := :path
label := ""
linecolor := :lightgray
bbx_mag = (x1 + x2) / 2
bby_mag = (y1 + y2) / 2
xi_lens, yi_lens = intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
xi_mag, yi_mag = intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
# add lines
if xl1 < xi_lens < xl2 &&
yl1 < yi_lens < yl2
@series begin
subplot := sp_index
x := [xi_mag, xi_lens]
y := [yi_mag, yi_lens]
()
end
end
# add magnification shape
@series begin
subplot := sp_index
x := [x1, x1, x2, x2, x1]
y := [y1, y2, y2, y1, y1]
()
end
# add subplot
for series in sp.series_list
@series begin
plotattributes = merge(plotattributes, copy(series.plotattributes))
subplot := lens_index
label := ""
xlims := (x1, x2)
ylims := (y1, y2)
()
end
end
backup = copy(plotattributes)
empty!(plotattributes)
seriestype := :path
series_plotindex := backup[:series_plotindex]
end
function intersection_point(xA, yA, xB, yB, h, w)
s = (yA - yB) / (xA - xB)
hh = h / 2
hw = w / 2
# left or right?
if -hh <= s * hw <= hh
if xA > xB
# right
return xB + hw, yB + s * hw
else # left
return xB - hw, yB - s * hw
end
# top or bot?
elseif -hw <= hh/s <= hw
if yA > yB
# top
return xB + hh/s, yB + hh
else
# bottom
return xB - hh/s, yB - hh
end
end
end
# ---------------------------------------------------------------------------
# contourf - filled contours
@@ -855,7 +1038,11 @@ end
@recipe function f(::Type{Val{:yerror}}, x, y, z)
error_style!(plotattributes)
markershape := :hline
plotattributes[:x], plotattributes[:y] = error_coords(plotattributes[:x], plotattributes[:y], error_zipit(plotattributes[:yerror]))
plotattributes[:x], plotattributes[:y] = error_coords(
plotattributes[:x],
plotattributes[:y],
error_zipit(plotattributes[:yerror]),
)
()
end
@deps yerror path
@@ -863,7 +1050,11 @@ end
@recipe function f(::Type{Val{:xerror}}, x, y, z)
error_style!(plotattributes)
markershape := :vline
plotattributes[:y], plotattributes[:x] = error_coords(plotattributes[:y], plotattributes[:x], error_zipit(plotattributes[:xerror]))
plotattributes[:y], plotattributes[:x] = error_coords(
plotattributes[:y],
plotattributes[:x],
error_zipit(plotattributes[:xerror]),
)
()
end
@deps xerror path
@@ -898,15 +1089,15 @@ function quiver_using_arrows(plotattributes::AKW)
first(vi), last(vi)
elseif isscalar(vi)
vi, vi
elseif isa(vi,Function)
elseif isa(vi, Function)
vi(xi, yi)
else
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
end
# add the points
nanappend!(x, [xi, xi+vx, NaN])
nanappend!(y, [yi, yi+vy, NaN])
nanappend!(x, [xi, xi + vx, NaN])
nanappend!(y, [yi, yi + vy, NaN])
end
plotattributes[:x], plotattributes[:y] = x, y
@@ -936,7 +1127,7 @@ function quiver_using_hack(plotattributes::AKW)
first(vi), last(vi)
elseif isscalar(vi)
vi, vi
elseif isa(vi,Function)
elseif isa(vi, Function)
vi(xi, yi)
else
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
@@ -951,8 +1142,11 @@ function quiver_using_hack(plotattributes::AKW)
U1 *= arrow_h
U2 *= arrow_w
ppv = p+v
nanappend!(pts, P2[p, ppv-U1, ppv-U1+U2, ppv, ppv-U1-U2, ppv-U1])
ppv = p + v
nanappend!(
pts,
P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1],
)
end
plotattributes[:x], plotattributes[:y] = Plots.unzip(pts[2:end])
@@ -977,32 +1171,28 @@ end
"Represent Open High Low Close data (used in finance)"
mutable struct OHLC{T<:Real}
open::T
high::T
low::T
close::T
open::T
high::T
low::T
close::T
end
Base.convert(::Type{OHLC}, tup::Tuple) = OHLC(tup...)
# Base.tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close)
# get one OHLC path
function get_xy(o::OHLC, x, xdiff)
xl, xm, xr = x-xdiff, x, x+xdiff
ox = [xl, xm, NaN,
xm, xm, NaN,
xm, xr]
oy = [o.open, o.open, NaN,
o.low, o.high, NaN,
o.close, o.close]
xl, xm, xr = x - xdiff, x, x + xdiff
ox = [xl, xm, NaN, xm, xm, NaN, xm, xr]
oy = [o.open, o.open, NaN, o.low, o.high, NaN, o.close, o.close]
ox, oy
end
# get the joined vector
function get_xy(v::AVec{OHLC}, x = eachindex(v))
xdiff = 0.3ignorenan_mean(abs.(diff(x)))
xdiff = 0.3 * ignorenan_mean(abs.(diff(x)))
x_out, y_out = zeros(0), zeros(0)
for (i,ohlc) in enumerate(v)
ox,oy = get_xy(ohlc, x[i], xdiff)
for (i, ohlc) in enumerate(v)
ox, oy = get_xy(ohlc, x[i], xdiff)
nanappend!(x_out, ox)
nanappend!(y_out, oy)
end
@@ -1015,10 +1205,17 @@ end
# to squash ambiguity warnings...
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
@recipe f(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
@recipe f(
x::AVec{Function},
v::AVec{Tuple{R1,R2,R3,R4}},
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
# this must be OHLC?
@recipe f(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = x, OHLC[OHLC(t...) for t in ohlc]
@recipe f(
x::AVec,
ohlc::AVec{Tuple{R1,R2,R3,R4}},
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} =
x, OHLC[OHLC(t...) for t in ohlc]
@recipe function f(x::AVec, v::AVec{OHLC})
seriestype := :path
@@ -1056,11 +1253,11 @@ end
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
seriestype := :spy
mat = g.args[1]
n,m = axes(mat)
n, m = axes(mat)
Plots.SliceIt, m, n, Surface(mat)
end
@recipe function f(::Type{Val{:spy}}, x,y,z)
@recipe function f(::Type{Val{:spy}}, x, y, z)
yflip := true
aspect_ratio := 1
rs, cs, zs = findnz(z.surf)
@@ -1086,7 +1283,8 @@ end
# -------------------------------------------------
"Adds ax+b... straight line over the current plot, without changing the axis limits"
abline!(plt::Plot, a, b; kw...) = plot!(plt, [0, 1], [b, b+a]; seriestype = :straightline, kw...)
abline!(plt::Plot, a, b; kw...) =
plot!(plt, [0, 1], [b, b + a]; seriestype = :straightline, kw...)
abline!(args...; kw...) = abline!(current(), args...; kw...)
@@ -1099,9 +1297,11 @@ datetimeformatter(dt) = string(DateTime(Dates.UTM(dt)))
timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
@recipe f(::Type{Date}, dt::Date) = (dt -> Dates.value(dt), dateformatter)
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> Dates.value(dt), datetimeformatter)
@recipe f(::Type{DateTime}, dt::DateTime) =
(dt -> Dates.value(dt), datetimeformatter)
@recipe f(::Type{Dates.Time}, t::Dates.Time) = (t -> Dates.value(t), timeformatter)
@recipe f(::Type{P}, t::P) where P <: Dates.Period = (t -> Dates.value(t), t -> string(P(t)))
@recipe f(::Type{P}, t::P) where {P<:Dates.Period} =
(t -> Dates.value(t), t -> string(P(t)))
# -------------------------------------------------
# Characters
@@ -1111,7 +1311,7 @@ timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
# -------------------------------------------------
# Complex Numbers
@recipe function f(A::Array{Complex{T}}) where T<:Number
@recipe function f(A::Array{Complex{T}}) where {T<:Number}
xguide --> "Re(x)"
yguide --> "Im(x)"
real.(A), imag.(A)
@@ -1120,10 +1320,10 @@ end
# Splits a complex matrix to its real and complex parts
# Reals defaults solid, imaginary defaults dashed
# Label defaults are changed to match the real-imaginary reference / indexing
@recipe function f(x::AbstractArray{T},y::Array{Complex{T2}}) where {T<:Real,T2}
ylabel --> "Re(y)"
zlabel --> "Im(y)"
x,real.(y),imag.(y)
@recipe function f(x::AbstractArray{T}, y::Array{Complex{T2}}) where {T<:Real,T2}
ylabel --> "Re(y)"
zlabel --> "Im(y)"
x, real.(y), imag.(y)
end
@@ -1137,7 +1337,7 @@ end
end
library = PlotUtils.color_libraries[cl.args[1]]
z = sqrt.((1:15)*reshape(1:20,1,:))
z = sqrt.((1:15) * reshape(1:20, 1, :))
seriestype := :heatmap
ticks := nothing
@@ -1161,7 +1361,7 @@ end
if !(length(grad.args) == 1 && isa(grad.args[1], Symbol))
error("showgradient takes the name of a color gradient as a Symbol")
end
z = sqrt.((1:15)*reshape(1:20,1,:))
z = sqrt.((1:15) * reshape(1:20, 1, :))
seriestype := :heatmap
ticks := nothing
legend := false
@@ -1184,9 +1384,9 @@ end
weights = cumsum(weights, dims = 2)
seriestype := :shape
# create a filled polygon for each item
for c=axes(weights,2)
sx = vcat(weights[:,c], c==1 ? zeros(n) : reverse(weights[:,c-1]))
# create a filled polygon for each item
for c in axes(weights, 2)
sx = vcat(weights[:, c], c == 1 ? zeros(n) : reverse(weights[:, c - 1]))
sy = vcat(returns, reverse(returns))
@series Plots.isvertical(plotattributes) ? (sx, sy) : (sy, sx)
end
@@ -1205,13 +1405,13 @@ julia> areaplot(1:3, [1 2 3; 7 8 9; 4 5 6], seriescolor = [:red :green :blue], f
@userplot AreaPlot
@recipe function f(a::AreaPlot)
data = cumsum(a.args[end], dims=2)
data = cumsum(a.args[end], dims = 2)
x = length(a.args) == 1 ? (axes(data, 1)) : a.args[1]
seriestype := :line
for i in axes(data, 2)
@series begin
fillrange := i > 1 ? data[:,i-1] : 0
x, data[:,i]
fillrange := i > 1 ? data[:, i - 1] : 0
x, data[:, i]
end
end
end
+6 -3
View File
@@ -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
@@ -347,6 +349,7 @@ end
seriestype := :heatmap
yflip --> true
cbar --> false
aspect_ratio --> :equal
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
SliceIt, m, n, Surface(z)
end
+2 -2
View File
@@ -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
+17 -13
View File
@@ -116,24 +116,11 @@ end
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
n, m = size(z)
# idx = 0
colors = ColorGradient(vec(z))
newz = reshape(range(0, stop=1, length=n*m), n, m)
newz, colors
# newz = zeros(n, m)
# for i=1:n, j=1:m
# push!(colors, T(z[i,j]...))
# newz[i,j] = idx / (n*m-1)
# idx += 1
# end
# newz, ColorGradient(colors)
end
function imageHack(plotattributes::AKW)
is_seriestype_supported(:heatmap) || error("Neither :image or :heatmap are supported!")
plotattributes[:seriestype] = :heatmap
plotattributes[:z], plotattributes[:fillcolor] = replace_image_with_heatmap(plotattributes[:z].surf)
end
# ---------------------------------------------------------------
"Build line segments for plotting"
@@ -693,6 +680,10 @@ function get_markerstrokealpha(series, i::Int = 1)
_cycle(series[:markerstrokealpha], i)
end
function get_markerstrokewidth(series, i::Int = 1)
_cycle(series[:markerstrokewidth], i)
end
function has_attribute_segments(series::Series)
# we want to check if a series needs to be split into segments just because
# of its attributes
@@ -706,6 +697,19 @@ function has_attribute_segments(series::Series)
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :linestyle, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z))
end
function get_aspect_ratio(sp)
aspect_ratio = sp[:aspect_ratio]
if aspect_ratio == :auto
aspect_ratio = :none
for series in series_list(sp)
if series[:seriestype] == :image
aspect_ratio = :equal
end
end
end
return aspect_ratio
end
# ---------------------------------------------------------------
makekw(; kw...) = KW(kw)
+28 -2
View File
@@ -42,7 +42,31 @@ include("imgcomp.jl")
Random.seed!(1234)
default(show=false, reuse=true)
is_ci() = get(ENV, "CI", "false") == "true"
img_tol = is_ci() ? 10e-2 : 10e-2
img_tol = is_ci() ? 1e-2 : Sys.islinux() ? 1e-3 : 0.1
## Uncomment the following lines to update reference images for different backends
#=
@testset "GR" begin
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
end
plotly()
@testset "Plotly" begin
image_comparison_facts(:plotly, tol=img_tol, skip = Plots._backend_skips[:plotlyjs])
end
pyplot()
@testset "PyPlot" begin
image_comparison_facts(:pyplot, tol=img_tol, skip = Plots._backend_skips[:pyplot])
end
pgfplots()
@testset "PGFPlots" begin
image_comparison_facts(:pgfplots, tol=img_tol, skip = Plots._backend_skips[:pgfplots])
end
=#
##
@testset "Backends" begin
@@ -52,7 +76,9 @@ img_tol = is_ci() ? 10e-2 : 10e-2
@test gr() == Plots.GRBackend()
@test backend() == Plots.GRBackend()
@static if Sys.islinux()
@static if haskey(ENV, "APPVEYOR")
@info "Skipping GR image comparison tests on AppVeyor"
else
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
end
end
+284 -155
View File
@@ -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