From 297a04567c7e9193ed77d9a0e89b7e721876ae78 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 14:29:57 +0200 Subject: [PATCH 01/58] rework extension `__init__` mechanism --- .github/workflows/ci.yml | 34 +++--------- PlotsBase/Project.toml | 2 +- PlotsBase/ext/GRExt.jl | 60 ++++----------------- PlotsBase/ext/GastonExt.jl | 47 ++--------------- PlotsBase/ext/HDF5Ext.jl | 87 +++++++++---------------------- PlotsBase/ext/IJuliaExt.jl | 23 +------- PlotsBase/ext/PGFPlotsXExt.jl | 46 ++-------------- PlotsBase/ext/PlotlyJSExt.jl | 54 +++---------------- PlotsBase/ext/PythonPlotExt.jl | 45 ++-------------- PlotsBase/ext/UnicodePlotsExt.jl | 39 +------------- PlotsBase/src/Axes.jl | 20 ++++--- PlotsBase/src/Commons/Commons.jl | 5 +- PlotsBase/src/abstract_backend.jl | 61 +++++++++++++++++++--- PlotsBase/src/axes_utils.jl | 14 ++--- PlotsBase/src/backends/plotly.jl | 6 +++ PlotsBase/src/output.jl | 17 +++++- PlotsBase/src/utils.jl | 32 ++++++------ PlotsBase/test/runtests.jl | 2 +- PlotsBase/test/test_axes.jl | 16 +++--- Project.toml | 2 +- 20 files changed, 189 insertions(+), 423 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dae99f4a..eb5495f9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,32 +28,17 @@ jobs: fail-fast: false matrix: version: - - '1.6' # LTS (minimal declared julia compat in `Project.toml`) + - '1.9' # minimal declared julia compat in `Project.toml` - '1' # latest stable experimental: - false os: [ubuntu-latest, windows-latest, macos-latest] arch: [x64] include: - - os: ubuntu-latest - experimental: false - prefix: xvfb-run # julia-actions/julia-runtest/blob/master/README.md - - os: ubuntu-latest - experimental: false - prefix: xvfb-run - version: '1.7' # only test intermediate release on `ubuntu` to spare resources - - os: ubuntu-latest - experimental: false - prefix: xvfb-run - version: '1.8' # only test intermediate release on `ubuntu` to spare resources - - os: ubuntu-latest - experimental: false - prefix: xvfb-run - version: '1.9' # only test intermediate release on `ubuntu` to spare resources - os: ubuntu-latest experimental: true prefix: xvfb-run - version: '~1.11.0-0' # upcoming julia version, next `rc` + version: '~1.11.0-0' # upcoming julia version (`alpha`, `beta` or `rc`) - os: ubuntu-latest experimental: true prefix: xvfb-run @@ -77,15 +62,14 @@ jobs: with: version: ${{ matrix.version }} - uses: julia-actions/cache@v1 - - uses: julia-actions/julia-buildpkg@latest - - name: Run upstream RecipesBase, RecipesPipeline tests + - name: Develop upstream RecipesBase, RecipesPipeline, PlotsBase shell: julia --project=@. --color=yes {0} run: | using Pkg - foreach(("RecipesBase", "RecipesPipeline")) do name - Pkg.develop(path=name); Pkg.test(name; coverage=true) - end + foreach(path -> Pkg.develop(; path), ("RecipesBase", "RecipesPipeline", "PlotsBase")) + + - uses: julia-actions/julia-buildpkg@latest - name: Install conda based matplotlib shell: julia --project=@. --color=yes {0} @@ -111,13 +95,11 @@ jobs: CondaPkg.PkgREPL.add([libgcc..., "matplotlib"]) CondaPkg.status() - - name: Run upstream PlotsBase tests + - name: Run upstream RecipesBase, RecipesPipeline, PlotsBase tests shell: julia --project=@. --color=yes {0} run: | using Pkg - foreach(("PlotsBase",)) do name - Pkg.develop(path=name); Pkg.test(name; coverage=true) - end + foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase")) - uses: julia-actions/julia-runtest@latest timeout-minutes: 60 diff --git a/PlotsBase/Project.toml b/PlotsBase/Project.toml index 192ef1cf0..3fbb625ce 100644 --- a/PlotsBase/Project.toml +++ b/PlotsBase/Project.toml @@ -105,7 +105,7 @@ UnicodePlots = "3" UnitfulLatexify = "1" Unzip = "0.1 - 0.2" UUIDs = "1" -julia = "1.6" +julia = "1.9" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index 5bc1e7f67..2e9dc60cd 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -5,8 +5,6 @@ import RecipesPipeline import NaNMath import GR -import PlotsBase.Colorbars: cbar_gradient, cbar_fill, cbar_lines - using PlotsBase.PlotMeasures using PlotsBase.Annotations using PlotsBase.PlotsSeries @@ -22,24 +20,8 @@ using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes -const package_str = "GR" -const str = lowercase(package_str) -const sym = Symbol(str) - struct GRBackend <: PlotsBase.AbstractBackend end - -get_concrete_backend() = GRBackend # opposite to abstract - -function __init__() - @debug "Initializing GR backend in PlotsBase; run `gr()` to activate it." - PlotsBase._backendType[sym] = get_concrete_backend() - PlotsBase._backendSymbol[GRBackend] = sym - - push!(PlotsBase._initialized_backends, sym) -end -# Make GR know to Plots -PlotsBase.backend_name(::GRBackend) = sym -PlotsBase.backend_package_name(::GRBackend) = PlotsBase.backend_package_name(sym) +@PlotsBase.extension_static GRBackend gr const _gr_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -196,28 +178,6 @@ const _gr_styles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] const _gr_markers = vcat(Commons._all_markers, :pixel) const _gr_scales = [:identity, :ln, :log2, :log10] -# ----------------------------------------------------------------------------- -# Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods -# defined in abstract_backend.jl - -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_$(str)_", s, "s") - quote - PlotsBase.$f1(::GRBackend, $s::Symbol) = $s in $v - PlotsBase.$f2(::GRBackend) = sort(collect($v)) - end |> eval -end - -## results in: -# PlotsBase.is_attr_supported(::GRbackend, attrname) -> Bool -# ... -# PlotsBase.supported_attrs(::GRbackend) -> ::Vector{Symbol} -# ... -# PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} -# ----------------------------------------------------------------------------- - PlotsBase.is_marker_supported(::GRBackend, shape::Shape) = true # https://github.com/jheinen/GR.jl - significant contributions by @jheinen @@ -716,9 +676,9 @@ end function gr_update_colorbar!(cbar::GRColorbar, series::Series) (style = colorbar_style(series)) === nothing && return list = - style == cbar_gradient ? cbar.gradients : - style == cbar_fill ? cbar.fills : - style == cbar_lines ? cbar.lines : error("Unknown colorbar style: $style.") + style == Colorbars.cbar_gradient ? cbar.gradients : + style == Colorbars.cbar_fill ? cbar.fills : + style == Colorbars.cbar_lines ? cbar.lines : error("Unknown colorbar style: $style.") push!(list, series) end @@ -973,12 +933,6 @@ function gr_get_ticks_size(ticks, rot) w, h end -function labelfunc(scale::Symbol, backend::GRBackend) - texfunc = PlotsBase.labelfunc_tex(scale) - # replace dash with \minus (U+2212) - label -> replace(texfunc(label), "-" => "−") -end - function gr_axis_height(sp, axis) GR.savestate() ticks = get_ticks(sp, axis, update = false) @@ -1011,6 +965,12 @@ function gr_axis_width(sp, axis) w end +function PlotsBase.labelfunc(scale::Symbol, backend::GRBackend) + texfunc = PlotsBase.labelfunc_tex(scale) + # replace dash with \minus (U+2212) + label -> replace(texfunc(label), "-" => "−") +end + function PlotsBase._update_min_padding!(sp::Subplot{GRBackend}) dpi = sp.plt[:thickness_scaling] width, height = sp_size = get_size(sp) diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index 27fb87a50..503796033 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -1,8 +1,8 @@ module GastonExt import RecipesPipeline -import PlotsBase: PlotsBase, ticks_type import PlotUtils +import PlotsBase import Gaston using PlotsBase.PlotMeasures @@ -15,25 +15,8 @@ using PlotsBase.Ticks using PlotsBase.Fonts using PlotsBase.Axes -const package_str = "Gaston" -const str = lowercase(package_str) -const sym = Symbol(str) - struct GastonBackend <: PlotsBase.AbstractBackend end -const T = GastonBackend - -get_concrete_backend() = T # opposite to abstract - -function __init__() - @debug "Initializing $package_str backend in PlotsBase; run `$str()` to activate it." - PlotsBase._backendType[sym] = get_concrete_backend() - PlotsBase._backendSymbol[T] = sym - - push!(PlotsBase._initialized_backends, sym) -end - -PlotsBase.backend_name(::T) = sym -PlotsBase.backend_package_name(::T) = PlotsBase.backend_package_name(sym) +@PlotsBase.extension_static GastonBackend gaston const _gaston_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -133,28 +116,6 @@ const _gaston_markers = [ const _gaston_scales = [:identity, :ln, :log2, :log10] -# ----------------------------------------------------------------------------- -# Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods -# defined in abstract_backend.jl - -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_$(str)_", s, "s") - quote - PlotsBase.$f1(::T, $s::Symbol) = $s in $v - PlotsBase.$f2(::T) = sort(collect($v)) - end |> eval -end - -## results in: -# PlotsBase.is_attr_supported(::GRbackend, attrname) -> Bool -# ... -# PlotsBase.supported_attrs(::GRbackend) -> ::Vector{Symbol} -# ... -# PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} -# ----------------------------------------------------------------------------- - # https://github.com/mbaz/Gaston. PlotsBase.should_warn_on_unsupported(::GastonBackend) = false @@ -714,7 +675,7 @@ function gaston_parse_axes_attrs( tmin, tmax = axis_limits(sp, :x, false, false) rmin, rmax = axis_limits(sp, :y, false, false) rticks = get_ticks(sp, :y) - gaston_ticks = if (ttype = ticks_type(rticks)) === :ticks + gaston_ticks = if (ttype = PlotsBase.ticks_type(rticks)) === :ticks string.(rticks) elseif ttype === :ticks_and_labels ["'$l' $t" for (t, l) in zip(rticks...)] @@ -751,7 +712,7 @@ function gaston_set_ticks!(axesconf, ticks, letter, I, maj_min, add) push!(axesconf, "unset $(maj_min)$(letter)tics") return end - gaston_ticks = if (ttype = ticks_type(ticks)) === :ticks + gaston_ticks = if (ttype = PlotsBase.ticks_type(ticks)) === :ticks tics = gaston_fix_ticks_overflow(ticks) if maj_min == "m" map(t -> "'' $t 1", tics) # see gnuplot manual 'Mxtics' diff --git a/PlotsBase/ext/HDF5Ext.jl b/PlotsBase/ext/HDF5Ext.jl index 1aeaf7783..bf4faac20 100644 --- a/PlotsBase/ext/HDF5Ext.jl +++ b/PlotsBase/ext/HDF5Ext.jl @@ -5,44 +5,25 @@ import HDF5: HDF5, Group, Dataset import PlotUtils: PlotUtils, Colors import PlotUtils.ColorSchemes: ColorScheme import PlotUtils.Colors: Colorant -import RecipesPipeline +import RecipesPipeline: RecipesPipeline, Surface, DefaultsDict, datetimeformatter -import RecipesPipeline.datetimeformatter import PlotUtils.ColorPalette, PlotUtils.CategoricalColorGradient, PlotUtils.ContinuousColorGradient -import PlotsBase: - PlotsBase, Surface, Arrow, GridLayout, RootLayout, Font, PlotText, SeriesAnnotations -import PlotsBase: BoundingBox, Length, Plot, DefaultsDict, plot, plot! +import PlotsBase: PlotsBase, GridLayout, RootLayout, BoundingBox, Length, Plot using PlotsBase.PlotsSeries using PlotsBase.Subplots using PlotsBase.Commons using PlotsBase.Shapes +using PlotsBase.Fonts using PlotsBase.Axes import Dates -const package_str = "HDF5" -const str = lowercase(package_str) -const sym = Symbol(str) - struct HDF5Backend <: PlotsBase.AbstractBackend end -const T = HDF5Backend - -get_concrete_backend() = T # opposite to abstract - -function __init__() - @debug "Initializing $package_str backend in PlotsBase; run `$str()` to activate it." - PlotsBase._backendType[sym] = get_concrete_backend() - PlotsBase._backendSymbol[T] = sym - - push!(PlotsBase._initialized_backends, sym) -end - -PlotsBase.backend_name(::T) = sym -PlotsBase.backend_package_name(::T) = PlotsBase.backend_package_name(sym) +@PlotsBase.extension_static HDF5Backend hdf5 -const _hdf5_attr = PlotsBase.merge_with_base_supported([ +const _hdf5_attrs = PlotsBase.merge_with_base_supported([ :annotations, :legend_background_color, :background_color_inside, @@ -109,7 +90,7 @@ const _hdf5_attr = PlotsBase.merge_with_base_supported([ :dpi, :colorbar_title, ]) -const _hdf5_seriestype = [ +const _hdf5_seriestypes = [ :path, :steppre, :stepmid, @@ -127,29 +108,9 @@ const _hdf5_seriestype = [ :surface, :wireframe, ] -const _hdf5_style = [:auto, :solid, :dash, :dot, :dashdot] -const _hdf5_marker = vcat(PlotsBase.Commons._all_markers, :pixel) -const _hdf5_scale = [:identity, :ln, :log2, :log10] - -#= -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_$(str)_", s, "s") - quote - PlotsBase.$f1(::HDF5Backend, $s::Symbol) = $s in $v - PlotsBase.$f2(::HDF5Backend) = sort(collect($v)) - end |> eval -end -=# - -## results in: -# PlotsBase.is_attr_supported(::HDF5Backend, attrname) -> Bool -# ... -# PlotsBase.supported_attrs(::HDF5Backend) -> ::Vector{Symbol} -# ... -# PlotsBase.supported_scales(::HDF5Backend) -> ::Vector{Symbol} -# ----------------------------------------------------------------------------- +const _hdf5_styles = [:auto, :solid, :dash, :dot, :dashdot] +const _hdf5_markers = vcat(PlotsBase.Commons._all_markers, :pixel) +const _hdf5_scales = [:identity, :ln, :log2, :log10] # Additional constants # Dict has problems using "Types" as keys. Initialize in "_initialize_backend": @@ -190,14 +151,14 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1 # Sub-structure types: "DEFAULTSDICT" => DefaultsDict, - "FONT" => Font, + "FONT" => PlotsBase.Font, "BOUNDINGBOX" => BoundingBox, "GRIDLAYOUT" => GridLayout, "ROOTLAYOUT" => RootLayout, - "SERIESANNOTATIONS" => SeriesAnnotations, + "SERIESANNOTATIONS" => PlotsBase.SeriesAnnotations, "PLOTTEXT" => PlotText, "SHAPE" => Shape, - "ARROW" => Arrow, + "ARROW" => PlotsBase.Arrow, "COLORSCHEME" => ColorScheme, "COLORPALETTE" => ColorPalette, "CONT_COLORGRADIENT" => ContinuousColorGradient, @@ -540,7 +501,7 @@ function _read(grp::Group, sp::Subplot) sgrp = HDF5.open_group(listgrp, "$i") seriesinfo = _read(KW, sgrp) - plot!(sp, seriesinfo[:x], seriesinfo[:y]) # Add data & create data structures + PlotsBase.plot!(sp, seriesinfo[:x], seriesinfo[:y]) # Add data & create data structures _hdf5_merge!(sp.series_list[end].plotattributes, seriesinfo) end @@ -556,7 +517,7 @@ function _read_plot(grp::Group) n = _read_length_attrs(Vector, listgrp) # Construct new plot, +allocate subplots: - plt = plot(layout = n) + plt = PlotsBase.plot(layout = n) HDF5PLOT_PLOTREF.ref = plt # Used when reading "layout" agrp = HDF5.open_group(grp, "attr") @@ -578,39 +539,39 @@ hdf5plot_read(path::AbstractString; name::String = "_unnamed") = # Implement PlotsBase.jl backend interface for HDF5Backend -is_marker_supported(::HDF5Backend, shape::Shape) = true +PlotsBase.is_marker_supported(::HDF5Backend, shape::Shape) = true # Create the window/figure for this backend. -function _create_backend_figure(plt::Plot{HDF5Backend}) end +function PlotsBase._create_backend_figure(plt::Plot{HDF5Backend}) end # Set up the subplot within the backend object. -function _initialize_subplot(plt::Plot{HDF5Backend}, sp::Subplot{HDF5Backend}) end +function PlotsBase._initialize_subplot(plt::Plot{HDF5Backend}, sp::Subplot{HDF5Backend}) end # Add one series to the underlying backend object. # Called once per series # NOTE: Seems to be called when user calls plot()... even if backend # plot, sp.o has not yet been constructed... -function _series_added(plt::Plot{HDF5Backend}, series::Series) end +function PlotsBase._series_added(plt::Plot{HDF5Backend}, series::Series) end # When series data is added/changed, this callback can do dynamic updates to the backend object. # note: if the backend rebuilds the plot from scratch on display, then you might not do anything here. -function _series_updated(plt::Plot{HDF5Backend}, series::Series) end +function PlotsBase._series_updated(plt::Plot{HDF5Backend}, series::Series) end # called just before updating layout bounding boxes... in case you need to prep # for the calcs -function _before_layout_calcs(plt::Plot{HDF5Backend}) end +function PlotsBase._before_layout_calcs(plt::Plot{HDF5Backend}) end # Set the (left, top, right, bottom) minimum padding around the plot area # to fit ticks, tick labels, guides, colorbars, etc. -function _update_min_padding!(sp::Subplot{HDF5Backend}) end +function PlotsBase._update_min_padding!(sp::Subplot{HDF5Backend}) end # Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations]) -function _update_plot_object(plt::Plot{HDF5Backend}) end +function PlotsBase._update_plot_object(plt::Plot{HDF5Backend}) end # ---------------------------------------------------------------- # Display/show the plot (open a GUI window, or browser page, for example). -function _display(plt::Plot{HDF5Backend}) +function PlotsBase._display(plt::Plot{HDF5Backend}) msg = "HDF5 interface does not support `display()` function." msg *= "\nUse `PlotsBase.hdf5plot_write(::String)` method to write to .HDF5 \"plot\" file instead." @warn msg @@ -619,6 +580,6 @@ end # Interface actually required to use HDF5Backend -hdf5plot_write(path::AbstractString) = hdf5plot_write(current(), path) +PlotsBase.hdf5plot_write(path::AbstractString) = hdf5plot_write(current(), path) end # module diff --git a/PlotsBase/ext/IJuliaExt.jl b/PlotsBase/ext/IJuliaExt.jl index b94020260..e889eac33 100644 --- a/PlotsBase/ext/IJuliaExt.jl +++ b/PlotsBase/ext/IJuliaExt.jl @@ -16,27 +16,6 @@ function _init_ijulia_plotting() ENV["MPLBACKEND"] = "Agg" end -""" -Add extra jupyter mimetypes to display_dict based on the plot backed. - -The default is nothing, except for plotly based backends, where it -adds data for `application/vnd.plotly.v1+json` that is used in -frontends like jupyterlab and nteract. -""" -_ijulia__extra_mime_info!(plt::Plot, out::Dict) = out - -function _ijulia__extra_mime_info!(plt::Plot{PlotsBase.PlotlyJSBackend}, out::Dict) - out["application/vnd.plotly.v1+json"] = - Dict(:data => PlotsBase.plotly_series(plt), :layout => PlotsBase.plotly_layout(plt)) - out -end - -function _ijulia__extra_mime_info!(plt::Plot{PlotsBase.PlotlyBackend}, out::Dict) - out["application/vnd.plotly.v1+json"] = - Dict(:data => PlotsBase.plotly_series(plt), :layout => PlotsBase.plotly_layout(plt)) - out -end - function _ijulia_display_dict(plt::Plot) output_type = Symbol(plt.attr[:html_output_format]) if output_type === :auto @@ -56,7 +35,7 @@ function _ijulia_display_dict(plt::Plot) elseif output_type === :html mime = "text/html" out[mime] = sprint(show, MIME(mime), plt) - _ijulia__extra_mime_info!(plt, out) + PlotsBase._ijulia__extra_mime_info!(plt, out) elseif output_type === :pdf mime = "application/pdf" out[mime] = base64encode(show, MIME(mime), plt) diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index e72abe26f..c907bf031 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -1,11 +1,12 @@ module PGFPlotsXExt import PlotsBase: PlotsBase, pgfx_sanitize_string -import PlotUtils: PlotUtils, ColorGradient import LaTeXStrings: LaTeXString import Printf: @sprintf import UUIDs: uuid4 + import RecipesPipeline +import PlotUtils import PGFPlotsX import Latexify import Contour @@ -25,25 +26,8 @@ using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes -const package_str = "PGFPlotsX" -const str = lowercase(package_str) -const sym = Symbol(str) - struct PGFPlotsXBackend <: PlotsBase.AbstractBackend end -const T = PGFPlotsXBackend - -get_concrete_backend() = T # opposite to abstract - -function __init__() - @debug "Initializing $package_str backend in PlotsBase; run `$str()` to activate it." - PlotsBase._backendType[sym] = get_concrete_backend() - PlotsBase._backendSymbol[T] = sym - - push!(PlotsBase._initialized_backends, sym) -end - -PlotsBase.backend_name(::T) = sym -PlotsBase.backend_package_name(::T) = PlotsBase.backend_package_name(sym) +@PlotsBase.extension_static PGFPlotsXBackend pgfplotsx const _pgfplotsx_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -216,28 +200,6 @@ PlotsBase.is_marker_supported(::PGFPlotsXBackend, shape::Shape) = true # additional constants const _pgfplotsx_series_ids = KW() -# ----------------------------------------------------------------------------- -# Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods -# defined in abstract_backend.jl - -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_$(str)_", s, "s") - quote - PlotsBase.$f1(::T, $s::Symbol) = $s in $v - PlotsBase.$f2(::T) = sort(collect($v)) - end |> eval -end - -## results in: -# PlotsBase.is_attr_supported(::GRbackend, attrname) -> Bool -# ... -# PlotsBase.supported_attrs(::GRbackend) -> ::Vector{Symbol} -# ... -# PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} -# ----------------------------------------------------------------------------- - const Options = PGFPlotsX.Options const Table = PGFPlotsX.Table @@ -1124,7 +1086,7 @@ end pgfx_colormap(cl::PlotUtils.AbstractColorList) = pgfx_colormap(PlotUtils.color_list(cl)) pgfx_colormap(v::Vector{<:Colorant}) = join(map(c -> @sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c), blue(c)), v), '\n') -pgfx_colormap(cg::ColorGradient) = join( +pgfx_colormap(cg::PlotUtils.ColorGradient) = join( map(1:length(cg)) do i @sprintf( "rgb(%.8f)=(%.8f,%.8f,%.8f)", diff --git a/PlotsBase/ext/PlotlyJSExt.jl b/PlotsBase/ext/PlotlyJSExt.jl index 27f06a4c1..e73de13ff 100644 --- a/PlotsBase/ext/PlotlyJSExt.jl +++ b/PlotsBase/ext/PlotlyJSExt.jl @@ -1,33 +1,14 @@ module PlotlyJSExt -import PlotsBase: PlotsBase, Plot, isijulia +import PlotsBase: PlotsBase, Plot using PlotsBase.PlotsPlots using PlotsBase.Commons using PlotsBase.Plotly import PlotlyJS: PlotlyJS, WebIO -# unrolling the old # init_backend macro by hand case by case -# this is not a macro for the backend maintainers and explicit control -const package_str = "PlotlyJS" -const str = lowercase(package_str) -const sym = Symbol(str) - struct PlotlyJSBackend <: PlotsBase.AbstractBackend end -const T = PlotlyJSBackend - -get_concrete_backend() = T # opposite to abstract - -function __init__() - @debug "Initializing $package_str backend in PlotsBase; run `$str()` to activate it." - PlotsBase._backendType[sym] = get_concrete_backend() - PlotsBase._backendSymbol[T] = sym - - push!(PlotsBase._initialized_backends, sym) -end - -PlotsBase.backend_name(::T) = sym -PlotsBase.backend_package_name(::T) = PlotsBase.backend_package_name(sym) +@PlotsBase.extension_static PlotlyJSBackend plotlyjs const _plotlyjs_attrs = PlotsBase.Plotly._plotly_attrs const _plotlyjs_seriestypes = PlotsBase.Plotly._plotly_seriestypes @@ -35,31 +16,6 @@ const _plotlyjs_styles = PlotsBase.Plotly._plotly_styles const _plotlyjs_markers = PlotsBase.Plotly._plotly_markers const _plotlyjs_scales = PlotsBase.Plotly._plotly_scales -# ----------------------------------------------------------------------------- -# Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods -# defined in abstract_backend.jl - -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_$(str)_", s, "s") - quote - PlotsBase.$f1(::T, $s::Symbol) = $s in $v - PlotsBase.$f2(::T) = sort(collect($v)) - end |> eval -end - -## results in: -# PlotsBase.is_attr_supported(::GRbackend, attrname) -> Bool -# ... -# PlotsBase.supported_attrs(::GRbackend) -> ::Vector{Symbol} -# ... -# PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} -# ----------------------------------------------------------------------------- -# https://github.com/JuliaPlots/PlotlyJS.jl - -# ------------------------------------------------------------------------------ - function plotlyjs_syncplot(plt::Plot{PlotlyJSBackend}) plt[:overwrite_figure] && PlotsBase.closeall() plt.o = PlotlyJS.plot() @@ -112,4 +68,10 @@ PlotsBase.closeall(::PlotlyJSBackend) = Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot{PlotlyJSBackend}) = true +function PlotsBase._ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict) + out["application/vnd.plotly.v1+json"] = + Dict(:data => plotly_series(plt), :layout => plotly_layout(plt)) + out +end + end # module diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index 5b37e0bd6..4fdc0cc29 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -22,39 +22,24 @@ using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes -const package_str = "PythonPlot" -const str = lowercase(package_str) -const sym = Symbol(str) - struct PythonPlotBackend <: PlotsBase.AbstractBackend end -const T = PythonPlotBackend - -get_concrete_backend() = T - -function __init__() - @debug "Initializing $package_str backend in PlotsBase; run `$str()` to activate it." - PlotsBase._backendType[sym] = get_concrete_backend() - PlotsBase._backendSymbol[T] = sym - - push!(PlotsBase._initialized_backends, sym) +function PlotsBase.extension_init(::PythonPlotBackend) if PythonPlot.version < v"3.4" @warn """You are using Matplotlib $(PythonPlot.version), which is no longer officially supported by the Plots community. To ensure smooth PlotsBase.jl integration update your Matplotlib library to a version ≥ 3.4.0 """ end - PythonCall.pycopy!(mpl, PythonCall.pyimport("matplotlib")) PythonCall.pycopy!(mpl_toolkits, PythonCall.pyimport("mpl_toolkits")) PythonCall.pycopy!(numpy, PythonCall.pyimport("numpy")) PythonCall.pyimport("mpl_toolkits.axes_grid1") numpy.seterr(invalid = "ignore") - PythonPlot.ioff() # we don't want every command to update the figure + PythonPlot.ioff() # we don't want every command to update the figure end -PlotsBase.backend_name(::T) = sym -PlotsBase.backend_package_name(::T) = PlotsBase.backend_package_name(sym) +@PlotsBase.extension_static PythonPlotBackend pythonplot const _pythonplot_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -190,28 +175,6 @@ const _pythonplot_styles = [:auto, :solid, :dash, :dot, :dashdot] const _pythonplot_markers = vcat(Commons._all_markers, :pixel) const _pythonplot_scales = [:identity, :ln, :log2, :log10] -# ----------------------------------------------------------------------------- -# Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods -# defined in abstract_backend.jl - -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_$(str)_", s, "s") - quote - PlotsBase.$f1(::T, $s::Symbol) = $s in $v - PlotsBase.$f2(::T) = sort(collect($v)) - end |> eval -end - -## results in: -# PlotsBase.is_attr_supported(::GRbackend, attrname) -> Bool -# ... -# PlotsBase.supported_attrs(::GRbackend) -> ::Vector{Symbol} -# ... -# PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} -# ----------------------------------------------------------------------------- - # github.com/stevengj/PythonPlot.jl const PythonCall = PythonPlot.PythonCall @@ -366,7 +329,7 @@ end get_locator_and_formatter(vals::AVec) = mpl.ticker.FixedLocator(eachindex(vals)), mpl.ticker.FixedFormatter(vals) -labelfunc(scale::Symbol, backend::PythonPlotBackend) = +PlotsBase.labelfunc(scale::Symbol, backend::PythonPlotBackend) = PythonPlot.LaTeXStrings.latexstring ∘ PlotsBase.labelfunc_tex(scale) _py_mask_nans(z) = PythonPlot.pycall(numpy.ma.masked_invalid, z) diff --git a/PlotsBase/ext/UnicodePlotsExt.jl b/PlotsBase/ext/UnicodePlotsExt.jl index b517e2176..b0a1d77e8 100644 --- a/PlotsBase/ext/UnicodePlotsExt.jl +++ b/PlotsBase/ext/UnicodePlotsExt.jl @@ -18,24 +18,8 @@ using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes -const package_str = "UnicodePlots" -const str = lowercase(package_str) -const sym = Symbol(str) - struct UnicodePlotsBackend <: PlotsBase.AbstractBackend end -const T = UnicodePlotsBackend - -get_concrete_backend() = UnicodePlotsBackend # opposite to abstract - -function __init__() - @debug "Initializing $package_str backend in PlotsBase; run `$str()` to activate it." - PlotsBase._backendType[sym] = get_concrete_backend() - PlotsBase._backendSymbol[T] = sym - - push!(PlotsBase._initialized_backends, sym) -end -PlotsBase.backend_name(::UnicodePlotsBackend) = sym -PlotsBase.backend_package_name(::UnicodePlotsBackend) = PlotsBase.backend_package_name(sym) +@PlotsBase.extension_static UnicodePlotsBackend unicodeplots const _unicodeplots_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -112,27 +96,6 @@ const _unicodeplots_markers = [ :x, ] const _unicodeplots_scales = [:identity, :ln, :log2, :log10] -# ----------------------------------------------------------------------------- -# Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods -# defined in abstract_backend.jl - -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_$(str)_", s, "s") - quote - PlotsBase.$f1(::UnicodePlotsBackend, $s::Symbol) = $s in $v - PlotsBase.$f2(::UnicodePlotsBackend) = sort(collect($v)) - end |> eval -end - -## results in: -# PlotsBase.is_attr_supported(::GRbackend, attrname) -> Bool -# ... -# PlotsBase.supported_attrs(::GRbackend) -> ::Vector{Symbol} -# ... -# PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} -# ----------------------------------------------------------------------------- # https://github.com/JuliaPlots/UnicodePlots.jl diff --git a/PlotsBase/src/Axes.jl b/PlotsBase/src/Axes.jl index 4e604fcba..b2d97cd76 100644 --- a/PlotsBase/src/Axes.jl +++ b/PlotsBase/src/Axes.jl @@ -2,9 +2,8 @@ module Axes export Axis, Extrema, tickfont, guidefont, widen_factor, scale_inverse_scale_func export sort_3d_axes, axes_letters, process_axis_arg!, has_ticks -import PlotsBase: get_ticks + using PlotsBase: PlotsBase, RecipesPipeline, Subplot, DefaultsDict, TimeType -using PlotsBase.Commons: _axis_defaults_byletter, _all_axis_attrs, dumpdict using PlotsBase.Commons using PlotsBase.Ticks using PlotsBase.Fonts @@ -49,7 +48,7 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...) :show => true, # show or hide the axis? (useful for linked subplots) ) - attr = DefaultsDict(explicit, _axis_defaults_byletter[letter]) + attr = DefaultsDict(explicit, Commons._axis_defaults_byletter[letter]) # update the defaults attr!(Axis([sp], attr), args...; kw...) @@ -303,7 +302,7 @@ function process_axis_arg!(plotattributes::AKW, arg, letter = "") end end -has_ticks(axis::Axis) = get(axis, :ticks, nothing) |> PlotsBase.Ticks._has_ticks +has_ticks(axis::Axis) = _has_ticks(get(axis, :ticks, nothing)) # update an Axis object with magic args and keywords function attr!(axis::Axis, args...; kw...) @@ -336,7 +335,7 @@ end # ------------------------------------------------------------------------- -Base.show(io::IO, axis::Axis) = dumpdict(io, axis.plotattributes, "Axis") +Base.show(io::IO, axis::Axis) = Commons.dumpdict(io, axis.plotattributes, "Axis") ignorenan_extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax)) tickfont(ax::Axis) = font(; @@ -365,7 +364,7 @@ function _update_axis( ) # build the KW of arguments from the letter version (i.e. xticks --> ticks) kw = KW() - for k in _all_axis_attrs + for k in Commons._all_axis_attrs # first get the args without the letter: `tickfont = font(10)` # note: we don't pop because we want this to apply to all axes! (delete after all have finished) if haskey(plotattributes_in, k) @@ -399,7 +398,12 @@ end """ returns (continuous_values, discrete_values) for the ticks on this axis """ -function get_ticks(sp::Subplot, axis::Axis; update = true, formatter = axis[:formatter]) +function PlotsBase.get_ticks( + sp::Subplot, + axis::Axis; + update = true, + formatter = axis[:formatter], +) if update || !haskey(axis.plotattributes, :optimized_ticks) dvals = axis[:discrete_values] ticks = _transform_ticks(axis[:ticks], axis) @@ -415,7 +419,7 @@ function get_ticks(sp::Subplot, axis::Axis; update = true, formatter = axis[:for else cvals = axis[:continuous_values] alims = axis_limits(sp, axis[:letter]) - get_ticks(ticks, cvals, dvals, alims, axis[:scale], formatter) + PlotsBase.get_ticks(ticks, cvals, dvals, alims, axis[:scale], formatter) end end axis.plotattributes[:optimized_ticks] diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index 5d995153d..83fa56b00 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -1,9 +1,8 @@ "Things that should be common to all backends and frontend modules" module Commons -export AVec, AMat, KW, AKW, TicksArgs -export PlotsBase, PLOTS_SEED -export _haligns, _valigns, _cbar_width +export AVec, + AMat, KW, AKW, TicksArgs, PlotsBase, PLOTS_SEED, _haligns, _valigns, _cbar_width # Functions export get_subplot, coords, diff --git a/PlotsBase/src/abstract_backend.jl b/PlotsBase/src/abstract_backend.jl index ba6530f33..0fe7d4c39 100644 --- a/PlotsBase/src/abstract_backend.jl +++ b/PlotsBase/src/abstract_backend.jl @@ -130,18 +130,67 @@ end # --------------------------------------------------------- # create the various `is_xxx_supported` and `supported_xxxs` methods # these methods should be overloaded (dispatched) by each backend in its init_code -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") +for sym in (:attr, :seriestype, :marker, :style, :scale) + f1 = Symbol("is_$(sym)_supported") + f2 = Symbol("supported_$(sym)s") @eval begin - $f1(::AbstractBackend, $s) = false - $f1(be::AbstractBackend, $s::AbstractVector) = all(v -> $f1(be, v), $s) - $f1($s) = $f1(backend(), $s) + $f1(::AbstractBackend, $sym) = false + $f1(be::AbstractBackend, $sym::AbstractVector) = all(v -> $f1(be, v), $sym) + $f1($sym) = $f1(backend(), $sym) $f2() = $f2(backend()) end end # ----------------------------------------------------------------------------- +extension_init(::AbstractBackend) = nothing + +""" +function __init__() + PlotsBase._backendType[sym] = GRBackend + PlotsBase._backendSymbol[GRBackend] = sym + push!(PlotsBase._initialized_backends, sym) + @debug "Initializing GR backend in PlotsBase; run `gr()` to activate it." +end +""" +macro extension_static(be_type, be) + be_sym = QuoteNode(be) + blk = Expr( + :block, + :(get_concrete_backend() = $be_type), + :(PlotsBase.backend_name(::$be_type)::Symbol = $be_sym), + :(PlotsBase.backend_package_name(::$be_type)::Symbol = PlotsBase.backend_package_name($be_sym)), + ) + #= + Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods, + results in: + PlotsBase.is_attr_supported(::GRbackend, attrname) -> Bool + ... + PlotsBase.supported_attrs(::GRbackend) -> ::Vector{Symbol} + ... + PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} + =# + for sym in (:attr, :seriestype, :marker, :style, :scale) + be_syms = Symbol("_$(be)_$(sym)s") + f1 = Symbol("is_$(sym)_supported") + f2 = Symbol("supported_$(sym)s") + push!( + blk.args, + :(PlotsBase.$f1(::$be_type, $sym::Symbol)::Bool = $sym in $be_syms), + :(PlotsBase.$f2(::$be_type)::Vector = sort!(collect($be_syms))), + ) + end + quote + $blk + function __init__() + PlotsBase._backendType[$be_sym] = $be_type + PlotsBase._backendSymbol[$be_type] = $be_sym + push!(PlotsBase._initialized_backends, $be_sym) + PlotsBase.extension_init($be_type()) + @debug "Initialized " * string($be_type) * " backend in PlotsBase; run `$be()` to activate it." + end + end |> esc +end + should_warn_on_unsupported(::AbstractBackend) = _plot_defaults[:warn_on_unsupported] const _already_warned = Dict{Symbol,Set{Symbol}}() diff --git a/PlotsBase/src/axes_utils.jl b/PlotsBase/src/axes_utils.jl index 46e0a461a..d36c16f6c 100644 --- a/PlotsBase/src/axes_utils.jl +++ b/PlotsBase/src/axes_utils.jl @@ -1,6 +1,6 @@ const _label_func = Dict{Symbol,Function}(:log10 => x -> "10^$x", :log2 => x -> "2^$x", :ln => x -> "e^$x") -labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string) +labelfunc(scale::Symbol, ::AbstractBackend) = get(_label_func, scale, string) const _label_func_tex = Dict{Symbol,Function}( :log10 => x -> "10^{$x}", @@ -101,18 +101,18 @@ Ticks.get_ticks(ticks::Int, dvals, cvals, args...) = cvals[rng], string.(dvals[rng]) end -function get_labels(formatter::Symbol, scaled_ticks, scale) +get_labels(formatter::Symbol, scaled_ticks, scale) = if formatter in (:auto, :plain, :scientific, :engineering) - return map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, formatter)) + map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, formatter)) elseif formatter === :latex - return map( + map( l -> string("\$", replace(convert_sci_unicode(l), '×' => "\\times"), "\$"), get_labels(:auto, scaled_ticks, scale), ) elseif formatter === :none - return String[] + String[] end -end + function get_labels(formatter::Function, scaled_ticks, scale) sf, invsf, _ = scale_inverse_scale_func(scale) fticks = map(formatter ∘ invsf, scaled_ticks) @@ -120,7 +120,7 @@ function get_labels(formatter::Function, scaled_ticks, scale) # CategoricalArrays's recipe gives "missing" label to those filter!(!ismissing, fticks) eltype(fticks) <: Number && return get_labels(:auto, map(sf, fticks), scale) - return fticks + fticks end # Ticks getter functions diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index 9a97182cf..e327c738d 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -1323,4 +1323,10 @@ PlotsBase._show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend}) = PlotsBase._display(plt::Plot{PlotlyBackend}) = standalone_html_window(plt) +function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict) + out["application/vnd.plotly.v1+json"] = + Dict(:data => plotly_series(plt), :layout => plotly_layout(plt)) + out +end + end # module diff --git a/PlotsBase/src/output.jl b/PlotsBase/src/output.jl index c9bab7865..4cf6595ee 100644 --- a/PlotsBase/src/output.jl +++ b/PlotsBase/src/output.jl @@ -163,8 +163,6 @@ Display a plot using the backends' gui window """ gui(plt::Plot = current()) = display(PlotsDisplay(), plt) -function inline end # for IJulia - function Base.display(::PlotsDisplay, plt::Plot) prepare_output(plt) _display(plt) @@ -246,6 +244,20 @@ closeall() = closeall(backend()) Base.show(io::IO, m::MIME"application/prs.juno.plotpane+html", plt::Plot) = showjuno(io, MIME("text/html"), plt) + +function inline end # for IJulia + +function hdf5plot_write end + +""" +Add extra jupyter mimetypes to display_dict based on the plot backed. + +The default is nothing, except for plotly based backends, where it +adds data for `application/vnd.plotly.v1+json` that is used in +frontends like jupyterlab and nteract. +""" +_ijulia__extra_mime_info!(::Plot, out::Dict) = out + # Atom PlotPane function showjuno(io::IO, m, plt) dpi = plt[:dpi] @@ -270,4 +282,5 @@ _showjuno(io::IO, m::MIME"image/svg+xml", plt) = Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot) = false _showjuno(io::IO, m, plt) = _show(io, m, plt) + # COV_EXCL_STOP diff --git a/PlotsBase/src/utils.jl b/PlotsBase/src/utils.jl index ef38e1baa..b7c4ecea9 100644 --- a/PlotsBase/src/utils.jl +++ b/PlotsBase/src/utils.jl @@ -659,27 +659,27 @@ function with(f::Function, args...; scalefonts = nothing, kw...) end # --------------------------------------------------------------- +const _convert_sci_unicode_dict = Dict( + '⁰' => "0", + '¹' => "1", + '²' => "2", + '³' => "3", + '⁴' => "4", + '⁵' => "5", + '⁶' => "6", + '⁷' => "7", + '⁸' => "8", + '⁹' => "9", + '⁻' => "-", + "×10" => "×10^{", +) # converts unicode scientific notation, as returned by Showoff, # to a tex-like format (supported by gr, pyplot, and pgfplots). function convert_sci_unicode(label::AbstractString) - unicode_dict = Dict( - '⁰' => "0", - '¹' => "1", - '²' => "2", - '³' => "3", - '⁴' => "4", - '⁵' => "5", - '⁶' => "6", - '⁷' => "7", - '⁸' => "8", - '⁹' => "9", - '⁻' => "-", - "×10" => "×10^{", - ) - for key in keys(unicode_dict) - label = replace(label, key => unicode_dict[key]) + for key in keys(_convert_sci_unicode_dict) + label = replace(label, key => _convert_sci_unicode_dict[key]) end if occursin("×10^{", label) label = string(label, "}") diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 2d8048ad1..029a8faf4 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -61,7 +61,7 @@ for name in ( "shorthands", "recipes", "unitful", - "hdf5plots", # broken ? + "hdf5plots", "pgfplotsx", "plotly", "animations", diff --git a/PlotsBase/test/test_axes.jl b/PlotsBase/test/test_axes.jl index abda149df..462a11c41 100644 --- a/PlotsBase/test/test_axes.jl +++ b/PlotsBase/test/test_axes.jl @@ -21,13 +21,15 @@ @test PlotsBase.labelfunc_tex(:log2)(1) == "2^{1}" @test PlotsBase.labelfunc_tex(:ln)(1) == "e^{1}" - @test PlotsBase.get_labels(:auto, 1:3, :identity) == ["1", "2", "3"] - @test PlotsBase.get_labels(:scientific, float.(500:500:1500), :identity) == - ["5.00×10^{2}", "1.00×10^{3}", "1.50×10^{3}"] - @test PlotsBase.get_labels(:engineering, float.(500:500:1500), :identity) == - ["500.×10^{0}", "1.00×10^{3}", "1.50×10^{3}"] - @test PlotsBase.get_labels(:latex, 1:3, :identity) == ["\$1\$", "\$2\$", "\$3\$"] - # GR is used during tests and it correctly overrides labelfunc(), but PGFPlotsX did not + with(:gr) do # NOTE: GR overrides `labelfunc` + @test PlotsBase.get_labels(:auto, 1:3, :identity) == ["1", "2", "3"] + @test PlotsBase.get_labels(:scientific, float.(500:500:1500), :identity) == + ["5.00×10^{2}", "1.00×10^{3}", "1.50×10^{3}"] + @test PlotsBase.get_labels(:engineering, float.(500:500:1500), :identity) == + ["500.×10^{0}", "1.00×10^{3}", "1.50×10^{3}"] + @test PlotsBase.get_labels(:latex, 1:3, :identity) == ["\$1\$", "\$2\$", "\$3\$"] + end + # GR is used during tests and it correctly overrides `labelfunc`, but PGFPlotsX did not with(:pgfplotsx) do @test PlotsBase.get_labels(:auto, 1:3, :log10) == ["10^{1}", "10^{2}", "10^{3}"] end diff --git a/Project.toml b/Project.toml index 430295e24..45c159c39 100644 --- a/Project.toml +++ b/Project.toml @@ -18,7 +18,7 @@ PlotsBase = "1.41" PrecompileTools = "1" Preferences = "1" Reexport = "0.2, 1" -julia = "1.6" +julia = "1.9" [extras] PythonPlot = "274fc56d-3b97-40fa-a1cd-1b4a50311bf9" From 278e7d4db949a89c4db9368901cebbc1a26e7e4e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 15:31:06 +0200 Subject: [PATCH 02/58] checkpoint plotly --- PlotsBase/ext/GRExt.jl | 7 +-- PlotsBase/ext/GastonExt.jl | 4 +- PlotsBase/ext/HDF5Ext.jl | 46 ++++++++----------- PlotsBase/ext/PGFPlotsXExt.jl | 4 +- PlotsBase/ext/PlotlyJSExt.jl | 4 +- PlotsBase/ext/PlotlyKaleidoExt.jl | 2 +- PlotsBase/ext/PythonPlotExt.jl | 8 ++-- PlotsBase/ext/UnicodePlotsExt.jl | 4 +- PlotsBase/ext/UnitfulExt.jl | 10 ++--- PlotsBase/src/abstract_backend.jl | 40 ++++++++++------- PlotsBase/src/backends/plotly.jl | 33 +++++--------- PlotsBase/src/output.jl | 2 +- Project.toml | 4 +- test/preferences.jl | 75 ------------------------------- test/runtests.jl | 8 +++- test/test_preferences.jl | 73 ++++++++++++++++++++++++++++++ 16 files changed, 160 insertions(+), 164 deletions(-) delete mode 100644 test/preferences.jl create mode 100644 test/test_preferences.jl diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index 2e9dc60cd..ba9c3bb8f 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -21,7 +21,7 @@ using PlotsBase.Ticks using PlotsBase.Axes struct GRBackend <: PlotsBase.AbstractBackend end -@PlotsBase.extension_static GRBackend gr +PlotsBase.@extension_static GRBackend gr const _gr_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -678,7 +678,8 @@ function gr_update_colorbar!(cbar::GRColorbar, series::Series) list = style == Colorbars.cbar_gradient ? cbar.gradients : style == Colorbars.cbar_fill ? cbar.fills : - style == Colorbars.cbar_lines ? cbar.lines : error("Unknown colorbar style: $style.") + style == Colorbars.cbar_lines ? cbar.lines : + error("Unknown colorbar style: $style.") push!(list, series) end @@ -2279,4 +2280,4 @@ end PlotsBase.closeall(::GRBackend) = GR.emergencyclosegks() -end # module +end # module diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index 503796033..61966f774 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -16,7 +16,7 @@ using PlotsBase.Fonts using PlotsBase.Axes struct GastonBackend <: PlotsBase.AbstractBackend end -@PlotsBase.extension_static GastonBackend gaston +PlotsBase.@extension_static GastonBackend gaston const _gaston_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -844,4 +844,4 @@ function gaston_enclose_tick_string(tick_string) "$base^{$power}" end -end # module +end # module diff --git a/PlotsBase/ext/HDF5Ext.jl b/PlotsBase/ext/HDF5Ext.jl index bf4faac20..b62b0b0a8 100644 --- a/PlotsBase/ext/HDF5Ext.jl +++ b/PlotsBase/ext/HDF5Ext.jl @@ -2,13 +2,11 @@ module HDF5Ext import HDF5: HDF5, Group, Dataset +import RecipesPipeline: RecipesPipeline, Surface, DefaultsDict, datetimeformatter import PlotUtils: PlotUtils, Colors import PlotUtils.ColorSchemes: ColorScheme import PlotUtils.Colors: Colorant -import RecipesPipeline: RecipesPipeline, Surface, DefaultsDict, datetimeformatter -import PlotUtils.ColorPalette, - PlotUtils.CategoricalColorGradient, PlotUtils.ContinuousColorGradient import PlotsBase: PlotsBase, GridLayout, RootLayout, BoundingBox, Length, Plot using PlotsBase.PlotsSeries @@ -21,7 +19,7 @@ using PlotsBase.Axes import Dates struct HDF5Backend <: PlotsBase.AbstractBackend end -@PlotsBase.extension_static HDF5Backend hdf5 +PlotsBase.@extension_static HDF5Backend hdf5 const _hdf5_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -160,9 +158,9 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1 "SHAPE" => Shape, "ARROW" => PlotsBase.Arrow, "COLORSCHEME" => ColorScheme, - "COLORPALETTE" => ColorPalette, - "CONT_COLORGRADIENT" => ContinuousColorGradient, - "CAT_COLORGRADIENT" => CategoricalColorGradient, + "COLORPALETTE" => PlotUtils.ColorPalette, + "CONT_COLORGRADIENT" => PlotUtils.ContinuousColorGradient, + "CAT_COLORGRADIENT" => PlotUtils.CategoricalColorGradient, "AXIS" => Axis, "SURFACE" => Surface, "SUBPLOT" => Subplot, @@ -176,7 +174,7 @@ end # Helper functions -h5plotpath(plotname::String) = "plots/$plotname" +h5plotpath(name::String) = "plots/$name" _hdf5_merge!(dest::AKW, src::AKW) = for (k, v) in src @@ -367,18 +365,6 @@ function _write(grp::Group, plt::Plot{HDF5Backend}) end end -function hdf5plot_write( - plt::Plot{HDF5Backend}, - path::AbstractString; - name::String = "_unnamed", -) - HDF5.h5open(path, "w") do file - HDF5.write_dataset(file, "VERSION_INFO", string(PlotsBase._current_plots_version)) - grp = HDF5.create_group(file, h5plotpath(name)) - _write(grp, plt) - end -end - # _read(): Read data, but not type information. # Types with built-in HDF5 support: @@ -531,12 +517,6 @@ function _read_plot(grp::Group) plt end -hdf5plot_read(path::AbstractString; name::String = "_unnamed") = - HDF5.h5open(path, "r") do file - grp = HDF5.open_group(file, h5plotpath("_unnamed")) - return _read_plot(grp) - end - # Implement PlotsBase.jl backend interface for HDF5Backend PlotsBase.is_marker_supported(::HDF5Backend, shape::Shape) = true @@ -579,7 +559,17 @@ function PlotsBase._display(plt::Plot{HDF5Backend}) end # Interface actually required to use HDF5Backend +PlotsBase.hdf5plot_write(path::AbstractString; kw...) = PlotsBase.hdf5plot_write(current(), path; kw...) + +PlotsBase.hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString; name::String = "_unnamed") = + HDF5.h5open(path, "w") do file + HDF5.write_dataset(file, "VERSION_INFO", string(PlotsBase._current_plots_version)) + _write(HDF5.create_group(file, h5plotpath(name)), plt) + end -PlotsBase.hdf5plot_write(path::AbstractString) = hdf5plot_write(current(), path) +PlotsBase.hdf5plot_read(path::AbstractString; name::String = "_unnamed") = + HDF5.h5open(path, "r") do file + return _read_plot(HDF5.open_group(file, h5plotpath("_unnamed"))) + end -end # module +end # module diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index c907bf031..ef62487dd 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -27,7 +27,7 @@ using PlotsBase.Ticks using PlotsBase.Axes struct PGFPlotsXBackend <: PlotsBase.AbstractBackend end -@PlotsBase.extension_static PGFPlotsXBackend pgfplotsx +PlotsBase.@extension_static PGFPlotsXBackend pgfplotsx const _pgfplotsx_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -1598,4 +1598,4 @@ function PlotsBase._display(plt::Plot{PGFPlotsXBackend}) display(PGFPlotsX.PGFPlotsXDisplay(), plt.o.the_plot) end -end # module +end # module diff --git a/PlotsBase/ext/PlotlyJSExt.jl b/PlotsBase/ext/PlotlyJSExt.jl index e73de13ff..e53d89db2 100644 --- a/PlotsBase/ext/PlotlyJSExt.jl +++ b/PlotsBase/ext/PlotlyJSExt.jl @@ -8,7 +8,7 @@ using PlotsBase.Plotly import PlotlyJS: PlotlyJS, WebIO struct PlotlyJSBackend <: PlotsBase.AbstractBackend end -@PlotsBase.extension_static PlotlyJSBackend plotlyjs +PlotsBase.@extension_static PlotlyJSBackend plotlyjs const _plotlyjs_attrs = PlotsBase.Plotly._plotly_attrs const _plotlyjs_seriestypes = PlotsBase.Plotly._plotly_seriestypes @@ -74,4 +74,4 @@ function PlotsBase._ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Di out end -end # module +end # module diff --git a/PlotsBase/ext/PlotlyKaleidoExt.jl b/PlotsBase/ext/PlotlyKaleidoExt.jl index bff882427..1a86add86 100644 --- a/PlotsBase/ext/PlotlyKaleidoExt.jl +++ b/PlotsBase/ext/PlotlyKaleidoExt.jl @@ -27,4 +27,4 @@ for (mime, fmt) in ( ) end -end # module +end # module diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index 4fdc0cc29..b2163ea48 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -39,7 +39,7 @@ function PlotsBase.extension_init(::PythonPlotBackend) PythonPlot.ioff() # we don't want every command to update the figure end -@PlotsBase.extension_static PythonPlotBackend pythonplot +PlotsBase.@extension_static PythonPlotBackend pythonplot const _pythonplot_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -605,7 +605,9 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) if series[:markershape] !== :none && st ∈ _py_marker_series for segment in series_segments(series, :scatter) i, rng = segment.attr_index, segment.range - args = if st === :bar + args = if st === :bar && !isvertical(series) + y[rng], x[rng] + else x[rng], y[rng] end RecipesPipeline.is3d(sp) && (args = (args..., z[rng])) @@ -1720,4 +1722,4 @@ end PlotsBase.closeall(::PythonPlotBackend) = PythonPlot.close("all") -end # module +end # module diff --git a/PlotsBase/ext/UnicodePlotsExt.jl b/PlotsBase/ext/UnicodePlotsExt.jl index b0a1d77e8..465a105f3 100644 --- a/PlotsBase/ext/UnicodePlotsExt.jl +++ b/PlotsBase/ext/UnicodePlotsExt.jl @@ -19,7 +19,7 @@ using PlotsBase.Ticks using PlotsBase.Axes struct UnicodePlotsBackend <: PlotsBase.AbstractBackend end -@PlotsBase.extension_static UnicodePlotsBackend unicodeplots +PlotsBase.@extension_static UnicodePlotsBackend unicodeplots const _unicodeplots_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -499,4 +499,4 @@ function PlotsBase._display(plt::PlotsBase.Plot{UnicodePlotsBackend}) println(stdout) end -end # module +end # module diff --git a/PlotsBase/ext/UnitfulExt.jl b/PlotsBase/ext/UnitfulExt.jl index ede2e5713..eeb7e4a86 100644 --- a/PlotsBase/ext/UnitfulExt.jl +++ b/PlotsBase/ext/UnitfulExt.jl @@ -20,7 +20,8 @@ import Unitful: import PlotsBase: PlotsBase, @recipe, PlotText, Subplot, AVec, AMat, Axis import RecipesBase import LaTeXStrings: LaTeXString -import Latexify: latexify +import Latexify + using UnitfulLatexify const MissingOrQuantity = Union{Missing,<:Quantity,<:LogScaled} @@ -232,7 +233,7 @@ append_unit_if_needed!(attr, key, label::ProtectedString, u) = nothing append_unit_if_needed!(attr, key, label::UnitfulString, u) = nothing function append_unit_if_needed!(attr, key, label::Nothing, u) attr[key] = if attr[:plot_object].backend == PlotsBase._backend_instance(:pgfplotsx) - UnitfulString(LaTeXString(latexify(u)), u) + UnitfulString(LaTeXString(Latexify.latexify(u)), u) else UnitfulString(string(u), u) end @@ -244,7 +245,7 @@ function append_unit_if_needed!(attr, key, label::S, u) where {S<:AbstractString LaTeXString( format_unit_label( label, - latexify(u), + Latexify.latexify(u), get(attr, Symbol(get(attr, :letter, ""), :unitformat), :round), ), ), @@ -351,8 +352,7 @@ function _unit(x) unit(x) end -function PlotsBase.pgfx_sanitize_string(s::UnitfulString) +PlotsBase.pgfx_sanitize_string(s::UnitfulString) = UnitfulString(PlotsBase.pgfx_sanitize_string(s.content), s.unit) -end end # module diff --git a/PlotsBase/src/abstract_backend.jl b/PlotsBase/src/abstract_backend.jl index 0fe7d4c39..02024eeb0 100644 --- a/PlotsBase/src/abstract_backend.jl +++ b/PlotsBase/src/abstract_backend.jl @@ -4,7 +4,7 @@ const _plots_compats = _plots_project.compat const _backendSymbol = Dict{DataType,Symbol}(NoBackend => :none) const _backendType = Dict{Symbol,DataType}(:none => NoBackend) -const _backend_packages = (gaston = :Gaston, gr = :GR, unicodeplots = :UnicodePlots, pgfplotsx = :PGFPlotsX, pythonplot = :PythonPlot, plotly = :Plotly, plotlyjs = :PlotlyJS, hdf5 = :HDF5) +const _backend_packages = (gaston = :Gaston, gr = :GR, unicodeplots = :UnicodePlots, pgfplotsx = :PGFPlotsX, pythonplot = :PythonPlot, plotly = nothing, plotlyjs = :PlotlyJS, hdf5 = :HDF5) const _initialized_backends = Set{Symbol}() const _backends = keys(_backend_packages) @@ -68,7 +68,7 @@ backends() = _backends backend_name() = CURRENT_BACKEND.sym _backend_instance(sym::Symbol)::AbstractBackend = _backendType[sym]() -backend_package_name(sym::Symbol = backend_name()) = get(_backend_packages, sym, :None) +backend_package_name(sym::Symbol = backend_name()) = get(_backend_packages, sym, nothing) # Traits to be implemented by the extensions backend_name(::AbstractBackend) = @info "`backend_name(::Backend) not implemented." @@ -142,23 +142,16 @@ for sym in (:attr, :seriestype, :marker, :style, :scale) end # ----------------------------------------------------------------------------- -extension_init(::AbstractBackend) = nothing - -""" -function __init__() - PlotsBase._backendType[sym] = GRBackend - PlotsBase._backendSymbol[GRBackend] = sym - push!(PlotsBase._initialized_backends, sym) - @debug "Initializing GR backend in PlotsBase; run `gr()` to activate it." -end -""" -macro extension_static(be_type, be) +function backend_defines(be_type::Symbol, be::Symbol) be_sym = QuoteNode(be) blk = Expr( :block, :(get_concrete_backend() = $be_type), :(PlotsBase.backend_name(::$be_type)::Symbol = $be_sym), - :(PlotsBase.backend_package_name(::$be_type)::Symbol = PlotsBase.backend_package_name($be_sym)), + :( + PlotsBase.backend_package_name(::$be_type)::Symbol = + PlotsBase.backend_package_name($be_sym) + ), ) #= Overload (dispatch) abstract `is_xxx_supported` and `supported_xxxs` methods, @@ -179,14 +172,29 @@ macro extension_static(be_type, be) :(PlotsBase.$f2(::$be_type)::Vector = sort!(collect($be_syms))), ) end + blk +end + +extension_init(::AbstractBackend) = nothing + +""" +function __init__() + PlotsBase._backendType[sym] = GRBackend + PlotsBase._backendSymbol[GRBackend] = sym + push!(PlotsBase._initialized_backends, sym) + @debug "Initializing GR backend in PlotsBase; run `gr()` to activate it." +end +""" +macro extension_static(be_type, be) + be_sym = QuoteNode(be) quote - $blk + $(PlotsBase.backend_defines(be_type, be)) function __init__() PlotsBase._backendType[$be_sym] = $be_type PlotsBase._backendSymbol[$be_type] = $be_sym push!(PlotsBase._initialized_backends, $be_sym) PlotsBase.extension_init($be_type()) - @debug "Initialized " * string($be_type) * " backend in PlotsBase; run `$be()` to activate it." + @debug "Initialized $be_type backend in PlotsBase; run `$be()` to activate it." end end |> esc end diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index e327c738d..a890c0f54 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -8,28 +8,28 @@ import Statistics import UUIDs import JSON -using PlotsBase.Annotations -using PlotsBase.Axes -using PlotsBase.Colorbars +using PlotUtils: PlotUtils, ColorGradient, rgba_string, rgb_string + using PlotsBase.Colors: Colorant -using PlotsBase.Commons -using PlotsBase.Fonts -using PlotsBase.Fonts: PlotText using PlotsBase.PlotMeasures -using PlotsBase.PlotsPlots +using PlotsBase.Annotations using PlotsBase.PlotsSeries -using PlotsBase.PlotUtils: PlotUtils, ColorGradient, rgba_string, rgb_string +using PlotsBase.PlotsPlots +using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Surfaces +using PlotsBase.Commons +using PlotsBase.Fonts using PlotsBase.Ticks +using PlotsBase.Axes struct PlotlyBackend <: PlotsBase.AbstractBackend end + PlotsBase._backendType[:plotly] = PlotlyBackend PlotsBase._backendSymbol[PlotlyBackend] = :plotly - push!(PlotsBase._initialized_backends, :plotly) -PlotsBase.backend_name(::PlotlyBackend) = :plotly -PlotsBase.backend_package_name(::PlotlyBackend) = PlotsBase.backend_package_name(:plotly) + +eval(PlotsBase.backend_defines(:PlotlyBackend, :plotly)) const _plotly_attrs = PlotsBase.merge_with_base_supported([ :annotations, @@ -181,15 +181,6 @@ const _plotly_scales = [:identity, :log10] PlotsBase.default_output_format(plt::Plot{PlotlyBackend}) = "html" -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") - v = Symbol("_plotly_", s, "s") - eval(quote - PlotsBase.$f1(::PlotlyBackend, $s::Symbol) = $s in $v - PlotsBase.$f2(::PlotlyBackend) = sort(collect($v)) - end) -end # ---------------------------------------------------------------- function labelfunc(scale::Symbol, backend::PlotlyBackend) @@ -1329,4 +1320,4 @@ function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict) out end -end # module +end # module diff --git a/PlotsBase/src/output.jl b/PlotsBase/src/output.jl index 4cf6595ee..6b9a886fa 100644 --- a/PlotsBase/src/output.jl +++ b/PlotsBase/src/output.jl @@ -244,10 +244,10 @@ closeall() = closeall(backend()) Base.show(io::IO, m::MIME"application/prs.juno.plotpane+html", plt::Plot) = showjuno(io, MIME("text/html"), plt) - function inline end # for IJulia function hdf5plot_write end +function hdf5plot_read end """ Add extra jupyter mimetypes to display_dict based on the plot backed. diff --git a/Project.toml b/Project.toml index 45c159c39..8186cee2d 100644 --- a/Project.toml +++ b/Project.toml @@ -12,12 +12,12 @@ Preferences = "21216c6a-2e73-6563-6e65-726566657250" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] -GR = "0.69.5 - 0.73" +GR = "0, 1" Pkg = "1" PlotsBase = "1.41" PrecompileTools = "1" Preferences = "1" -Reexport = "0.2, 1" +Reexport = "1" julia = "1.9" [extras] diff --git a/test/preferences.jl b/test/preferences.jl deleted file mode 100644 index 6f5101ffb..000000000 --- a/test/preferences.jl +++ /dev/null @@ -1,75 +0,0 @@ - -@testset "Preferences" begin - Plots.set_default_backend!() # start with empty preferences - - withenv("PLOTS_DEFAULT_BACKEND" => "invalid") do - @test_logs (:error, r"Unsupported backend.*") Plots.load_default_backend() - end - @test_logs (:error, r"Unsupported backend.*") backend(:invalid) - - @test Plots.load_default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() - - withenv("PLOTS_DEFAULT_BACKEND" => "unicodeplots") do - @test_logs (:info, r".*environment variable") Plots.diagnostics(devnull) - @test Plots.load_default_backend() == - Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() - end - - @test Plots.load_default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() - @test Plots.PlotsBase.backend_package_name() ≡ :GR - @test Plots.backend_name() ≡ :gr - - @test_logs (:info, r".*fallback") Plots.diagnostics(devnull) - - @test Plots.PlotsBase.merge_with_base_supported([:annotations, :guide]) isa Set - @test Plots.PlotsBase.CurrentBackend(:gr).sym ≡ :gr - - @test_logs (:warn, r".*is not compatible with") Plots.set_default_backend!(:invalid) - - @testset "persistent backend" begin - # this test mimics a restart, which is needed after a preferences change - Plots.set_default_backend!(:unicodeplots) - script = tempname() - write( - script, - """ - using Pkg, Test; io = (devnull, stdout)[1] # toggle for debugging - Pkg.activate(; temp = true, io) - Pkg.develop(; path = "$(escape_string(pkgdir(Plots)))", io) - Pkg.add("UnicodePlots"; io) # checked by Plots - import UnicodePlots - using Plots - res = @testset "Preferences UnicodePlots" begin - @test_logs (:info, r".*Preferences") Plots.diagnostics(io) - @test backend() == Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() - end - exit(res.n_passed == 2 ? 0 : 123) - """, - ) - @test success(run(```$(Base.julia_cmd()) $script```)) - end - - is_pkgeval() || for pkg in TEST_PACKAGES - be = Symbol(lowercase(pkg)) - (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs - (Sys.iswindows() && be ≡ :plotlyjs && is_ci()) && continue # FIXME: OutOfMemory - @test_logs Plots.set_default_backend!(be) # test the absence of warnings - rm.(Base.find_all_in_cache_path(Base.module_keys[Plots])) # make sure the compiled cache is removed - script = tempname() - write( - script, - """ - import $pkg - using Test, Plots - $be() - res = @testset "Persistent backend $pkg" begin - @test Plots.backend_name() ≡ :$be - end - exit(res.n_passed == 1 ? 0 : 123) - """, - ) - @test success(run(```$(Base.julia_cmd()) $script```)) # test default precompilation - end - - Plots.set_default_backend!() # clear `Preferences` key -end diff --git a/test/runtests.jl b/test/runtests.jl index 054a40117..09d3e7b97 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -20,7 +20,13 @@ is_ci() = Plots.PlotsBase.bool_env("CI") # get `Preferences` set backend, if any const PREVIOUS_DEFAULT_BACKEND = load_preference(Plots, "default_backend") -include("preferences.jl") +for name in ( + "preferences", +) + @testset "$name" begin + include("test_$name.jl") + end +end if PREVIOUS_DEFAULT_BACKEND === nothing delete_preferences!(Plots, "default_backend") # restore the absence of a preference diff --git a/test/test_preferences.jl b/test/test_preferences.jl new file mode 100644 index 000000000..a643de701 --- /dev/null +++ b/test/test_preferences.jl @@ -0,0 +1,73 @@ + +Plots.set_default_backend!() # start with empty preferences + +withenv("PLOTS_DEFAULT_BACKEND" => "invalid") do + @test_logs (:error, r"Unsupported backend.*") Plots.load_default_backend() +end +@test_logs (:error, r"Unsupported backend.*") backend(:invalid) + +@test Plots.load_default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() + +withenv("PLOTS_DEFAULT_BACKEND" => "unicodeplots") do + @test_logs (:info, r".*environment variable") Plots.diagnostics(devnull) + @test Plots.load_default_backend() == + Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() +end + +@test Plots.load_default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() +@test Plots.PlotsBase.backend_package_name() ≡ :GR +@test Plots.backend_name() ≡ :gr + +@test_logs (:info, r".*fallback") Plots.diagnostics(devnull) + +@test Plots.PlotsBase.merge_with_base_supported([:annotations, :guide]) isa Set +@test Plots.PlotsBase.CurrentBackend(:gr).sym ≡ :gr + +@test_logs (:warn, r".*is not compatible with") Plots.set_default_backend!(:invalid) + +@testset "persistent backend" begin + # this test mimics a restart, which is needed after a preferences change + Plots.set_default_backend!(:unicodeplots) + script = tempname() + write( + script, + """ + using Pkg, Test; io = (devnull, stdout)[1] # toggle for debugging + Pkg.activate(; temp = true, io) + Pkg.develop(; path = "$(escape_string(pkgdir(Plots)))", io) + Pkg.add("UnicodePlots"; io) # checked by Plots + import UnicodePlots + using Plots + res = @testset "Preferences UnicodePlots" begin + @test_logs (:info, r".*Preferences") Plots.diagnostics(io) + @test backend() == Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() + end + exit(res.n_passed == 2 ? 0 : 123) + """, + ) + @test success(run(```$(Base.julia_cmd()) $script```)) +end + +is_pkgeval() || for pkg in TEST_PACKAGES + be = Symbol(lowercase(pkg)) + (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs + (Sys.iswindows() && be ≡ :plotlyjs && is_ci()) && continue # FIXME: OutOfMemory + @test_logs Plots.set_default_backend!(be) # test the absence of warnings + rm.(Base.find_all_in_cache_path(Base.module_keys[Plots])) # make sure the compiled cache is removed + script = tempname() + write( + script, + """ + import $pkg + using Test, Plots + $be() + res = @testset "Persistent backend $pkg" begin + @test Plots.backend_name() ≡ :$be + end + exit(res.n_passed == 1 ? 0 : 123) + """, + ) + @test success(run(```$(Base.julia_cmd()) $script```)) # test default precompilation +end + +Plots.set_default_backend!() # clear `Preferences` key From 3d5ff498845c1a46f86076ff8e7e9c5b7393c5ba Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 16:08:49 +0200 Subject: [PATCH 03/58] fix `pythonplot` --- PlotsBase/ext/GRExt.jl | 2 +- PlotsBase/ext/HDF5Ext.jl | 11 ++- PlotsBase/ext/PGFPlotsXExt.jl | 2 +- PlotsBase/ext/PythonPlotExt.jl | 123 ++++++++++++++---------------- PlotsBase/src/abstract_backend.jl | 3 +- 5 files changed, 71 insertions(+), 70 deletions(-) diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index ba9c3bb8f..bd106c566 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -1377,7 +1377,7 @@ function gr_legend_pos(theta::Real, leg, vp; axisclearance = nothing) ymin = vp.ymin - leg.yoffset - leg.dy - axisclearance[3] ymax = vp.ymax + leg.yoffset + leg.h + axisclearance[4] end - legend_pos_from_angle(theta, xmin, xcenter(vp), xmax, ymin, ycenter(vp), ymax) + PlotsBase.legend_pos_from_angle(theta, xmin, xcenter(vp), xmax, ymin, ycenter(vp), ymax) end const gr_legend_marker_to_line_factor = Ref(2.0) diff --git a/PlotsBase/ext/HDF5Ext.jl b/PlotsBase/ext/HDF5Ext.jl index b62b0b0a8..0b09a63e8 100644 --- a/PlotsBase/ext/HDF5Ext.jl +++ b/PlotsBase/ext/HDF5Ext.jl @@ -559,9 +559,14 @@ function PlotsBase._display(plt::Plot{HDF5Backend}) end # Interface actually required to use HDF5Backend -PlotsBase.hdf5plot_write(path::AbstractString; kw...) = PlotsBase.hdf5plot_write(current(), path; kw...) - -PlotsBase.hdf5plot_write(plt::Plot{HDF5Backend}, path::AbstractString; name::String = "_unnamed") = +PlotsBase.hdf5plot_write(path::AbstractString; kw...) = + PlotsBase.hdf5plot_write(current(), path; kw...) + +PlotsBase.hdf5plot_write( + plt::Plot{HDF5Backend}, + path::AbstractString; + name::String = "_unnamed", +) = HDF5.h5open(path, "w") do file HDF5.write_dataset(file, "VERSION_INFO", string(PlotsBase._current_plots_version)) _write(HDF5.create_group(file, h5plotpath(name)), plt) diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index ef62487dd..5007e44d7 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -965,7 +965,7 @@ function pgfx_get_legend_pos(v::Tuple{<:Real,Symbol}) else (-0.15, 0.5, 1.05, -0.15, 0.52, 1.1), anchors[4 - I, 4 - I] end - return "at" => string(legend_pos_from_angle(v[1], rect...)), "anchor" => anchor + return "at" => string(PlotsBase.legend_pos_from_angle(v[1], rect...)), "anchor" => anchor end function pgfx_get_legend_style(sp) diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index b2163ea48..f88d68800 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -2,11 +2,22 @@ module PythonPlotExt import RecipesPipeline import PythonPlot + +const PythonCall = PythonPlot.PythonCall +const pyisnone = isdefined(PythonCall, :pyisnone) ? PythonCall.pyisnone : PythonCall.Core.pyisnone + +const mpl = PythonPlot.matplotlib +const mpl_toolkits = PythonCall.pynew() +const numpy = PythonCall.pynew() + import NaNMath import PlotsBase import PlotsBase.PlotUtils: PlotUtils, ColorGradient, plot_color, color_list, cgrad +import PlotsBase: bbox_to_pcts, right, left, bottom, top, width, height, ispositive +import PlotsBase: ticks_type import PlotsBase.Commons: Commons, single_color +import RecipesPipeline: Surface using PlotsBase.PlotMeasures using PlotsBase.Annotations @@ -177,17 +188,6 @@ const _pythonplot_scales = [:identity, :ln, :log2, :log10] # github.com/stevengj/PythonPlot.jl -const PythonCall = PythonPlot.PythonCall -const mpl = PythonCall.pynew() # PythonCall.pyimport("matplotlib") -const mpl_toolkits = PythonCall.pynew() # PythonCall.pyimport("mpl_toolkits") -const numpy = PythonCall.pynew() # PythonCall.pyimport("numpy") - -const pyisnone = if isdefined(PythonCall, :pyisnone) - PythonCall.pyisnone -else - PythonCall.Core.pyisnone -end - PlotsBase.is_marker_supported(::PythonPlotBackend, shape::Shape) = true # problem: github.com/tbreloff/Plots.jl/issues/308 @@ -205,7 +205,7 @@ for k in (:linthresh, :base, :label) end _py_handle_surface(v) = v -_py_handle_surface(z::PlotsBase.Surface) = z.surf +_py_handle_surface(z::Surface) = z.surf _py_color(s) = _py_color(parse(Colorant, string(s))) _py_color(c::Colorant) = [red(c), green(c), blue(c), alpha(c)] # NOTE: returning a tuple fails `PythonPlot` @@ -329,8 +329,8 @@ end get_locator_and_formatter(vals::AVec) = mpl.ticker.FixedLocator(eachindex(vals)), mpl.ticker.FixedFormatter(vals) -PlotsBase.labelfunc(scale::Symbol, backend::PythonPlotBackend) = - PythonPlot.LaTeXStrings.latexstring ∘ PlotsBase.labelfunc_tex(scale) +labelfunc(scale::Symbol, backend::PythonPlotBackend) = + PythonPlot.LaTeXStrings.latexstring ∘ labelfunc_tex(scale) _py_mask_nans(z) = PythonPlot.pycall(numpy.ma.masked_invalid, z) @@ -340,7 +340,7 @@ function fix_xy_lengths!(plt::Plot{PythonPlotBackend}, series::Series) if (x = series[:x]) !== nothing y = series[:y] nx, ny = length(x), length(y) - if !(get(series.plotattributes, :z, nothing) isa PlotsBase.Surface || nx == ny) + if !(get(series.plotattributes, :z, nothing) isa Surface || nx == ny) if nx < ny series[:x] = map(i -> Float64(x[mod1(i, nx)]), 1:ny) else @@ -389,11 +389,11 @@ _py_renderer(fig) = _py_canvas(fig).get_renderer() _py_drawfig(fig) = fig.draw(_py_renderer(fig)) # `get_points` returns a numpy array in the form [x0 y0; x1 y1] coords (origin is bottom-left (0, 0)!) -_py_extents(obj) = PythonCall.PyArray(obj.get_window_extent().get_points()) +_py_extents(obj) = PythonPlot.PyArray(obj.get_window_extent().get_points()) # see cjdoris.github.io/PythonCall.jl/stable/conversion-to-julia/#py2jl-conversion -to_vec(x) = PythonCall.pyconvert(Vector, x) -to_str(x) = PythonCall.pyconvert(String, x) +to_vec(x) = PythonPlot.pyconvert(Vector, x) +to_str(x) = PythonPlot.pyconvert(String, x) # compute a bounding box (with origin top-left), however PythonPlot gives coords with origin bottom-left function _py_bbox(obj) @@ -403,10 +403,10 @@ function _py_bbox(obj) # @show obj bb ex x0, y0, width, height = l * px, (ft - t) * px, (r - l) * px, (t - b) * px # @show width height - PlotsBase.BoundingBox(x0, y0, width, height) + BoundingBox(x0, y0, width, height) end -_py_bbox(::Nothing) = PlotsBase.BoundingBox(0mm, 0mm) +_py_bbox(::Nothing) = BoundingBox(0mm, 0mm) # get the bounding box of the union of the objects function _py_bbox(v::AVec) @@ -456,7 +456,7 @@ _py_thickness_scale(plt::Plot{PythonPlotBackend}, ptsz) = ptsz * plt[:thickness_ # Create the window/figure for this backend. function PlotsBase._create_backend_figure(plt::Plot{PythonPlotBackend}) - w, h = map(s -> PlotMeasures.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) + w, h = map(s -> PlotsBase.PlotMeasures.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) # reuse the current figure? plt[:overwrite_figure] ? PythonPlot.gcf() : PythonPlot.figure() end @@ -505,9 +505,9 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) # ax = getAxis(plt, series) x, y, z = (_py_handle_surface(series[letter]) for letter in (:x, :y, :z)) if st === :straightline - x, y = PlotsBase.straightline_data(series) + x, y = straightline_data(series) elseif st === :shape - x, y = PlotsBase.shape_data(series) + x, y = shape_data(series) end # make negative radii positive and flip the angle (PythonPlot ignores negative radii) @@ -614,8 +614,8 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) ax.scatter( args...; zorder = zorder + 0.5, - marker = _py_marker(PlotsBase._cycle(series[:markershape], i)), - s = _py_thickness_scale(plt, PlotsBase._cycle(series[:markersize], i)) .^ 2, + marker = _py_marker(_cycle(series[:markershape], i)), + s = _py_thickness_scale(plt, _cycle(series[:markersize], i)) .^ 2, facecolors = _py_color( get_markercolor(series, i, cbar_scale), get_markeralpha(series, i), @@ -709,7 +709,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) aspect, ) |> push_h elseif st === :heatmap - x, y = PlotsBase.heatmap_edges(x, xaxis[:scale], y, yaxis[:scale], size(z)) + x, y = heatmap_edges(x, xaxis[:scale], y, yaxis[:scale], size(z)) expand_extrema!(xaxis, x) expand_extrema!(yaxis, y) ax.pcolormesh( @@ -733,7 +733,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) map(inds -> map(i -> [x[i], y[i], z[i]], inds), cns) elseif cns isa NTuple{3,<:AbstractVector{<:Integer}} # Only triangles - connections have to be 0-based (indexing) - X, Y, Z = PlotsBase.mesh3d_triangles(x, y, z, cns) + X, Y, Z = mesh3d_triangles(x, y, z, cns) ntris = length(cns[1]) polys = sizehint!(Matrix{eltype(x)}[], ntris) for n in 1:ntris @@ -886,12 +886,16 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) if (fillrange = series[:fillrange]) !== nothing && st !== :contour for segment in series_segments(series) i, rng = segment.attr_index, segment.range - f, dim1, dim2 = :fill_between, x[rng], y[rng] + f, dim1, dim2 = if isvertical(series) + :fill_between, x[rng], y[rng] + else + :fill_betweenx, y[rng], x[rng] + end n = length(dim1) args = if typeof(fillrange) <: Union{Real,AVec} - dim1, PlotsBase._cycle(fillrange, rng), dim2 - elseif PlotsBase.is_2tuple(fillrange) - dim1, PlotsBase._cycle(fillrange[1], rng), PlotsBase._cycle(fillrange[2], rng) + dim1, _cycle(fillrange, rng), dim2 + elseif is_2tuple(fillrange) + dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng) end la = get_linealpha(series, i) @@ -941,7 +945,7 @@ function _py_set_ticks(sp, ax, ticks, letter) return end - tick_values, tick_labels = if (ttype = PlotsBase.ticks_type(ticks)) === :ticks + tick_values, tick_labels = if (ttype = ticks_type(ticks)) === :ticks ticks, [] elseif ttype === :ticks_and_labels ticks @@ -967,14 +971,13 @@ function _py_compute_axis_minval(sp::Subplot, axis::Axis) end function _py_set_scale(ax, sp::Subplot, scale::Symbol, letter::Symbol) - scale ∈ PlotsBase.supported_scales() || - return @warn "Unhandled scale value in PythonPlot: $scale" + scale ∈ PlotsBase.supported_scales() || return @warn "Unhandled scale value in PythonPlot: $scale" scl, kw = if scale === :identity "linear", KW() else "symlog", KW( - get_attr_symbol(:base, Symbol()) => _log_scale_bases[scale], + get_attr_symbol(:base, Symbol()) => _logScaleBases[scale], get_attr_symbol(:linthresh, Symbol()) => NaNMath.max( 1e-16, _py_compute_axis_minval(sp, sp[get_attr_symbol(letter, :axis)]), @@ -1019,7 +1022,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) w, h = plt[:size] fig = plt.o fig.clear() - fig.set_size_inches(w / PlotsBase.DPI, h / PlotsBase.DPI, forward = true) + fig.set_size_inches(w / DPI, h / PlotsBase.DPI, forward = true) fig.set_facecolor(_py_color(plt[:background_color_outside])) fig.set_dpi(plt[:dpi]) @@ -1090,7 +1093,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) (cmap = func(cbar_series)) === nothing || break end c_map = mpl.cm.ScalarMappable(; cmap, norm) - c_map.set_array(PythonCall.pylist([])) + c_map.set_array(PythonPlot.pylist([])) c_map else cbar_series[:serieshandle][end] @@ -1248,7 +1251,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) ticks = framestyle === :none ? nothing : get_ticks(sp, axis) has_major_ticks = ticks !== :none && ticks !== nothing && ticks !== false - has_major_ticks &= if (ttype = PlotsBase.ticks_type(ticks)) === :ticks + has_major_ticks &= if (ttype = ticks_type(ticks)) === :ticks length(ticks) > 0 elseif ttype === :ticks_and_labels tcs, labs = ticks @@ -1328,12 +1331,12 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) # minorticks if !no_minor_intervals(axis) && has_major_ticks ax.minorticks_on() - n_minor_intervals = num_minor_intervals(axis) + n_minor_intervals = PlotsBase.num_minor_intervals(axis) if (scale = axis[:scale]) === :identity mpl.ticker.AutoMinorLocator(n_minor_intervals) else mpl.ticker.LogLocator( - base = _log_scale_bases[scale], + base = _logScaleBases[scale], subs = 1:n_minor_intervals, ) end |> pyaxis.set_minor_locator @@ -1417,12 +1420,11 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) end expand_padding!(padding, bb, plotbb) = - if PlotsBase.ispositive(PlotsBase.width(bb)) && - PlotsBase.ispositive(PlotsBase.height(bb)) - padding[1] = max(padding[1], PlotsBase.left(plotbb) - PlotsBase.left(bb)) - padding[2] = max(padding[2], PlotsBase.top(plotbb) - PlotsBase.top(bb)) - padding[3] = max(padding[3], PlotsBase.right(bb) - PlotsBase.right(plotbb)) - padding[4] = max(padding[4], PlotsBase.bottom(bb) - PlotsBase.bottom(plotbb)) + if ispositive(width(bb)) && ispositive(height(bb)) + padding[1] = max(padding[1], left(plotbb) - left(bb)) + padding[2] = max(padding[2], top(plotbb) - top(bb)) + padding[3] = max(padding[3], right(bb) - right(plotbb)) + padding[4] = max(padding[4], bottom(bb) - bottom(plotbb)) end # Set the (left, top, right, bottom) minimum padding around the plot area @@ -1508,13 +1510,9 @@ function _py_legend_pos(pos::Tuple{<:Real,Symbol}) s, c = sincosd(pos[1]) .* (pos[2] === :outer ? -1 : 1) yanchors = "lower", "center", "upper" xanchors = "left", "center", "right" - join( - [ - yanchors[PlotsBase.legend_anchor_index(s)], - xanchors[PlotsBase.legend_anchor_index(c)], - ], - ' ', - ) + let lac = PlotsBase.legend_anchor_index + join([yanchors[lac(s)], xanchors[lac(c)]], ' ') + end end # legend_pos_from_angle(theta, xmin, xcenter, xmax, ymin, ycenter, ymax) @@ -1585,7 +1583,7 @@ function _py_add_legend(plt::Plot, sp::Subplot, ax) solid_joinstyle = "miter", dash_capstyle = "butt", dash_joinstyle = "miter", - marker = _py_marker(PlotsBase._cycle(series[:markershape], 1)), + marker = _py_marker(_cycle(series[:markershape], 1)), markersize = _py_thickness_scale(plt, 0.8sp[:legend_font_pointsize]), markeredgecolor = _py_color( single_color(get_markerstrokecolor(series)), @@ -1667,23 +1665,20 @@ function PlotsBase._update_plot_object(plt::Plot{PythonPlotBackend}) figw, figh = sp.plt[:size] .* px # ax.set_position signature: `[left, bottom, width, height]` - PlotsBase.bbox_to_pcts(sp.plotarea, figw, figh) |> ax.set_position + bbox_to_pcts(sp.plotarea, figw, figh) |> ax.set_position if haskey(sp.attr, :cbar_ax) && RecipesPipeline.is3d(sp) # 2D plots are completely handled by axis dividers bb = sp.attr[:cbar_bbox] # this is the bounding box of just the colors of the colorbar (not labels) pad = 2mm cb_bbox = PlotsBase.BoundingBox( - PlotsBase.right(sp.bbox) - 2PlotsBase.width(bb) - 2pad, # x0 - PlotsBase.top(sp.bbox) + pad, # y0 - PlotsBase.width(bb), # width - PlotsBase.height(sp.bbox) - 2pad, # height + right(sp.bbox) - 2width(bb) - 2pad, # x0 + top(sp.bbox) + pad, # y0 + width(bb), # width + height(sp.bbox) - 2pad, # height ) - get( - sp[:extra_kwargs], - "3d_colorbar_axis", - PlotsBase.bbox_to_pcts(cb_bbox, figw, figh), - ) |> sp.attr[:cbar_ax].set_position + get(sp[:extra_kwargs], "3d_colorbar_axis", bbox_to_pcts(cb_bbox, figw, figh)) |> + sp.attr[:cbar_ax].set_position end end PythonPlot.draw() diff --git a/PlotsBase/src/abstract_backend.jl b/PlotsBase/src/abstract_backend.jl index 02024eeb0..cc03b9511 100644 --- a/PlotsBase/src/abstract_backend.jl +++ b/PlotsBase/src/abstract_backend.jl @@ -190,10 +190,11 @@ macro extension_static(be_type, be) quote $(PlotsBase.backend_defines(be_type, be)) function __init__() + ccall(:jl_generating_output, Cint, ()) == 1 && return PlotsBase._backendType[$be_sym] = $be_type PlotsBase._backendSymbol[$be_type] = $be_sym push!(PlotsBase._initialized_backends, $be_sym) - PlotsBase.extension_init($be_type()) + Base.invokelatest(PlotsBase.extension_init, $be_type()) @debug "Initialized $be_type backend in PlotsBase; run `$be()` to activate it." end end |> esc From 462a172fa608cc852f33e3d986d3fde8a5afff89 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 16:09:09 +0200 Subject: [PATCH 04/58] format --- PlotsBase/ext/PGFPlotsXExt.jl | 3 ++- PlotsBase/ext/PythonPlotExt.jl | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index 5007e44d7..4f4ebc90f 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -965,7 +965,8 @@ function pgfx_get_legend_pos(v::Tuple{<:Real,Symbol}) else (-0.15, 0.5, 1.05, -0.15, 0.52, 1.1), anchors[4 - I, 4 - I] end - return "at" => string(PlotsBase.legend_pos_from_angle(v[1], rect...)), "anchor" => anchor + return "at" => string(PlotsBase.legend_pos_from_angle(v[1], rect...)), + "anchor" => anchor end function pgfx_get_legend_style(sp) diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index f88d68800..e497f0919 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -4,7 +4,8 @@ import RecipesPipeline import PythonPlot const PythonCall = PythonPlot.PythonCall -const pyisnone = isdefined(PythonCall, :pyisnone) ? PythonCall.pyisnone : PythonCall.Core.pyisnone +const pyisnone = + isdefined(PythonCall, :pyisnone) ? PythonCall.pyisnone : PythonCall.Core.pyisnone const mpl = PythonPlot.matplotlib const mpl_toolkits = PythonCall.pynew() @@ -456,7 +457,8 @@ _py_thickness_scale(plt::Plot{PythonPlotBackend}, ptsz) = ptsz * plt[:thickness_ # Create the window/figure for this backend. function PlotsBase._create_backend_figure(plt::Plot{PythonPlotBackend}) - w, h = map(s -> PlotsBase.PlotMeasures.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) + w, h = + map(s -> PlotsBase.PlotMeasures.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) # reuse the current figure? plt[:overwrite_figure] ? PythonPlot.gcf() : PythonPlot.figure() end @@ -971,7 +973,8 @@ function _py_compute_axis_minval(sp::Subplot, axis::Axis) end function _py_set_scale(ax, sp::Subplot, scale::Symbol, letter::Symbol) - scale ∈ PlotsBase.supported_scales() || return @warn "Unhandled scale value in PythonPlot: $scale" + scale ∈ PlotsBase.supported_scales() || + return @warn "Unhandled scale value in PythonPlot: $scale" scl, kw = if scale === :identity "linear", KW() else From c18876b3411ff1a7ce2b83cc4c950c834f98c3a2 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 17:26:28 +0200 Subject: [PATCH 05/58] more `pythonplot` fixes --- PlotsBase/ext/GRExt.jl | 163 ++++++------ PlotsBase/ext/GastonExt.jl | 126 +++++----- PlotsBase/ext/IJuliaExt.jl | 14 +- PlotsBase/ext/PGFPlotsXExt.jl | 123 +++++----- PlotsBase/ext/PythonPlotExt.jl | 272 ++++++++++----------- PlotsBase/ext/UnitfulExt.jl | 4 +- PlotsBase/src/Commons/postprocess_attrs.jl | 23 +- PlotsBase/src/Ticks.jl | 3 +- PlotsBase/src/abstract_backend.jl | 28 +-- PlotsBase/src/backends/plotly.jl | 104 ++++---- PlotsBase/src/backends/web.jl | 2 +- PlotsBase/src/recipes.jl | 2 +- PlotsBase/test/runtests.jl | 68 +++--- PlotsBase/test/test_backends.jl | 217 +--------------- PlotsBase/test/test_misc.jl | 2 +- PlotsBase/test/test_recipes.jl | 2 +- PlotsBase/test/test_reference.jl | 203 +++++++++++++++ src/Plots.jl | 20 +- test/runtests.jl | 8 +- test/test_preferences.jl | 6 +- 20 files changed, 691 insertions(+), 699 deletions(-) create mode 100644 PlotsBase/test/test_reference.jl diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index bd106c566..0f5a0696b 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -433,7 +433,7 @@ end gr_inqtext(x, y, s) = gr_inqtext(x, y, string(s)) gr_inqtext(x, y, s::AbstractString) = if (occursin('\\', s) || occursin("10^{", s)) && - match(r".*\$[^\$]+?\$.*", String(s)) === nothing + match(r".*\$[^\$]+?\$.*", String(s)) ≡ nothing GR.inqtextext(x, y, s) else GR.inqtext(x, y, s) @@ -442,7 +442,7 @@ gr_inqtext(x, y, s::AbstractString) = gr_text(x, y, s) = gr_text(x, y, string(s)) gr_text(x, y, s::AbstractString) = if (occursin('\\', s) || occursin("10^{", s)) && - match(r".*\$[^\$]+?\$.*", String(s)) === nothing + match(r".*\$[^\$]+?\$.*", String(s)) ≡ nothing GR.textext(x, y, s) else GR.text(x, y, s) @@ -543,7 +543,7 @@ gr_nominal_size(s) = minimum(get_size(s)) / 500 # draw ONE Shape function gr_draw_marker(series, xi, yi, zi, clims, i, msize, strokewidth, shape::Shape) # convert to ndc coords (percentages of window) ... - xi, yi = if zi === nothing + xi, yi = if zi ≡ nothing GR.wctondc(xi, yi) else gr_w3tondc(xi, yi, zi) @@ -577,7 +577,7 @@ function gr_draw_marker(series, xi, yi, zi, clims, i, msize, strokewidth, shape: gr_set_transparency(get_markeralpha(series, i)) GR.setmarkertype(gr_markertypes[shape]) GR.setmarkersize(0.3msize / gr_nominal_size(series)) - if zi === nothing + if zi ≡ nothing GR.polymarker([xi], [yi]) else GR.polymarker3d([xi], [yi], [zi]) @@ -674,7 +674,7 @@ struct GRColorbar end function gr_update_colorbar!(cbar::GRColorbar, series::Series) - (style = colorbar_style(series)) === nothing && return + (style = colorbar_style(series)) ≡ nothing && return list = style == Colorbars.cbar_gradient ? cbar.gradients : style == Colorbars.cbar_fill ? cbar.fills : @@ -805,18 +805,18 @@ function gr_draw_colorbar(cbar::GRColorbar, sp::Subplot, vp::GRViewport) end position(symb) = - if symb === :top || symb === :right + if symb ≡ :top || symb ≡ :right 0.95 - elseif symb === :left || symb === :bottom + elseif symb ≡ :left || symb ≡ :bottom 0.05 else 0.5 end alignment(symb) = - if symb === :top || symb === :right + if symb ≡ :top || symb ≡ :right :right - elseif symb === :left || symb === :bottom + elseif symb ≡ :left || symb ≡ :bottom :left else :center @@ -834,7 +834,7 @@ function gr_set_gradient(c) end gr_set_gradient(series::Series) = - (color = get_colorgradient(series)) !== nothing && gr_set_gradient(color) + (color = get_colorgradient(series)) ≢ nothing && gr_set_gradient(color) # this is our new display func... set up the viewport_canvas, compute bounding boxes, and display each subplot function gr_display(plt::Plot, dpi_factor = 1) @@ -1042,14 +1042,14 @@ function PlotsBase._update_min_padding!(sp::Subplot{GRBackend}) if (guide = zaxis[:guide]) != "" gr_set_font(guidefont(zaxis), sp) l = last(gr_text_size(guide)) - padding[mirrored(zaxis, :right) ? :right : :left][] += 1mm + height * l * px # NOTE: why `height` here ? + padding[mirrored(zaxis, :right) ? :right : :left][] += 1mm + height * l * px # NOTE: why `height` here ? end else # Add margin for x/y ticks & labels for (ax, tc, (a, b)) in ((xaxis, xticks, (:top, :bottom)), (yaxis, yticks, (:right, :left))) if !isempty(first(tc)) - isy = ax[:letter] === :y + isy = ax[:letter] ≡ :y gr_set_tickfont(sp, ax) ts = gr_get_ticks_size(tc, ax[:rotation]) l = 0.01 + (isy ? first(ts) : last(ts)) @@ -1084,7 +1084,7 @@ remap(x, lo, hi) = (x - lo) / (hi - lo) get_z_normalized(z, clims...) = isnan(z) ? 256 / 255 : remap(clamp(z, clims...), clims...) function gr_clims(sp, args...) - sp[:clims] === :auto || return get_clims(sp) + sp[:clims] ≡ :auto || return get_clims(sp) lo, hi = get_clims(sp, args...) if lo == hi if lo == 0 @@ -1202,7 +1202,7 @@ function gr_add_legend(sp, leg, viewport_area) GR.fillrect(xs..., ys...) # allocating white space for actual legend width here gr_set_line(1, :solid, sp[:legend_foreground_color], sp) GR.drawrect(xs..., ys...) # drawing actual legend width here - if (ttl = sp[:legend_title]) !== nothing + if (ttl = sp[:legend_title]) ≢ nothing shift = legend_rows > 1 ? 0.5(legend_cols - 1) * leg.dx : 0 # shifting title to center if multi-column gr_set_font(legendtitlefont(sp), sp) _debug[] && gr_legend_bbox(xpos, ypos, leg) @@ -1236,10 +1236,7 @@ function gr_add_legend(sp, leg, viewport_area) gr_set_line(clamped_lw, ls, lc, sp) # see github.com/JuliaPlots/Plots.jl/issues/3003 _debug[] && gr_legend_bbox(xpos, ypos, leg) - if ( - (st === :shape || series[:fillrange] !== nothing) && - series[:ribbon] === nothing - ) + if ((st ≡ :shape || series[:fillrange] ≢ nothing) && series[:ribbon] ≡ nothing) (fc = get_fillcolor(series, clims)) |> gr_set_fill gr_set_fillstyle(get_fillstyle(series, 0)) l, r = xpos + lft, xpos + rgt @@ -1255,18 +1252,18 @@ function gr_add_legend(sp, leg, viewport_area) gr_polyline(x, y, GR.fillarea) gr_set_transparency(lc, la) gr_set_line(clamped_lw, ls, lc, sp) - st === :shape && gr_polyline(x, y) + st ≡ :shape && gr_polyline(x, y) end max_markersize = Inf if st in (:path, :straightline, :path3d) max_markersize = leg.base_markersize gr_set_transparency(lc, la) - filled = series[:fillrange] !== nothing && series[:ribbon] === nothing + filled = series[:fillrange] ≢ nothing && series[:ribbon] ≡ nothing GR.polyline(xpos .+ [lft, rgt], ypos .+ (filled ? [top, top] : [0, 0])) end - if (msh = series[:markershape]) !== :none + if (msh = series[:markershape]) ≢ :none msz = max(first(series[:markersize]), 0) msw = max(first(series[:markerstrokewidth]), 0) mfac = 0.8 * lfps / (msz + 0.5 * msw + 1e-20) @@ -1301,7 +1298,7 @@ function gr_add_legend(sp, leg, viewport_area) end mirrored(ax::Axis, sym::Symbol) = - ax[:guide_position] === sym || (ax[:guide_position] === :auto && ax[:mirror]) + ax[:guide_position] ≡ sym || (ax[:guide_position] ≡ :auto && ax[:mirror]) function gr_legend_pos(sp::Subplot, leg, vp) xaxis, yaxis = sp[:xaxis], sp[:yaxis] @@ -1310,7 +1307,7 @@ function gr_legend_pos(sp::Subplot, leg, vp) if (lp = sp[:legend_position]) isa Real return gr_legend_pos(lp, leg, vp) elseif lp isa Tuple{<:Real,Symbol} - axisclearance = if lp[2] === :outer + axisclearance = if lp[2] ≡ :outer [ !ymirror * gr_axis_width(sp, yaxis), ymirror * gr_axis_width(sp, yaxis), @@ -1343,13 +1340,13 @@ function gr_legend_pos(sp::Subplot, leg, vp) vp.xmin + 0.5width(vp) - 0.5leg.w + leg.xoffset end ypos = if occursin("bottom", leg_str) - vp.ymin + if lp === :outerbottom + vp.ymin + if lp ≡ :outerbottom -leg.yoffset - leg.dy - !xmirror * gr_axis_height(sp, xaxis) else leg.yoffset + leg.h end elseif occursin("top", leg_str) # default / best - vp.ymax + if lp === :outertop + vp.ymax + if lp ≡ :outertop leg.yoffset + leg.h + xmirror * gr_axis_height(sp, xaxis) else -leg.yoffset - leg.dy @@ -1387,13 +1384,13 @@ function gr_get_legend_geometry(vp, sp) textw = texth = 0.0 has_title = false nseries = 0 - if sp[:legend_position] !== :none + if sp[:legend_position] ≢ :none GR.savestate() GR.selntran(0) GR.setcharup(0, 1) GR.setscale(0) ttl = sp[:legend_title] - if (has_title = ttl !== nothing) + if (has_title = ttl ≢ nothing) gr_set_font(legendtitlefont(sp), sp) (l, r), (b, t) = extrema.(gr_inqtext(0, 0, string(ttl))) texth = t - b @@ -1484,7 +1481,7 @@ function gr_update_viewport_legend!(vp, sp, leg) xaxis, yaxis = sp[:xaxis], sp[:yaxis] xmirror = mirrored(xaxis, :top) ymirror = mirrored(yaxis, :right) - leg_str = if (lp = sp[:legend_position]) isa Tuple{<:Real,Symbol} && lp[2] === :outer + leg_str = if (lp = sp[:legend_position]) isa Tuple{<:Real,Symbol} && lp[2] ≡ :outer x, y = gr_legend_pos(sp, leg, vp) # dry run, to figure out horz = x < vp.xmin ? "left" : (x > vp.xmax ? "right" : "") vert = y < vp.ymin ? "bot" : (y > vp.ymax ? "top" : "") @@ -1505,7 +1502,7 @@ function gr_update_viewport_legend!(vp, sp, leg) vp.ymin += yoff + !xmirror * gr_axis_height(sp, xaxis) end end - if lp === :inline + if lp ≡ :inline if yaxis[:mirror] vp.xmin += leg.textw else @@ -1516,8 +1513,8 @@ function gr_update_viewport_legend!(vp, sp, leg) end gr_update_viewport_ratio!(vp, sp) = - if (ratio = get_aspect_ratio(sp)) !== :none - ratio === :equal && (ratio = 1) + if (ratio = get_aspect_ratio(sp)) ≢ :none + ratio ≡ :equal && (ratio = 1) x_min, x_max, y_min, y_max = gr_xy_axislims(sp) viewport_ratio = width(vp) / height(vp) window_ratio = (x_max - x_min) / (y_max - y_min) / ratio @@ -1596,7 +1593,7 @@ function gr_draw_axes(sp, vp) # rmin, rmax = GR.adjustrange(ignorenan_minimum(r), ignorenan_maximum(r)) rmin, rmax = axis_limits(sp, :y) gr_polaraxes(rmin, rmax, sp) - elseif sp[:framestyle] !== :none + elseif sp[:framestyle] ≢ :none foreach(letter -> gr_draw_axis(sp, letter, vp), (:x, :y)) end GR.settransparency(1.0) @@ -1674,7 +1671,7 @@ gr_draw_spine(sp, axis, segments, func = gr_polyline) = gr_draw_border(sp, axis, segments, func = gr_polyline) = if sp[:framestyle] in (:box, :semi) - intensity = sp[:framestyle] === :semi ? 0.5 : 1 + intensity = sp[:framestyle] ≡ :semi ? 0.5 : 1 GR.setclip(0) gr_set_line(intensity, :solid, axis[:foreground_color_border], sp) gr_set_transparency(axis[:foreground_color_border], intensity) @@ -1688,7 +1685,7 @@ gr_draw_ticks(sp, axis, segments, func = gr_polyline) = gr_set_line(1, :solid, axis[:foreground_color_grid], sp) gr_set_transparency( axis[:foreground_color_grid], - axis[:tick_direction] === :out ? axis[:gridalpha] : 0, + axis[:tick_direction] ≡ :out ? axis[:gridalpha] : 0, ) else gr_set_line(1, :solid, axis[:foreground_color_axis], sp) @@ -1705,14 +1702,14 @@ function gr_label_ticks(sp, letter, ticks) _, (oamin, oamax) = map(l -> axis_limits(sp, l), letters) gr_set_tickfont(sp, letter) - out_factor = ifelse(ax[:tick_direction] === :out, 1.5, 1) + out_factor = ifelse(ax[:tick_direction] ≡ :out, 1.5, 1) - isy = letter === :y + isy = letter ≡ :y x_offset = isy ? -0.015out_factor : 0 y_offset = isy ? 0 : -0.008out_factor rot = ax[:rotation] % 360 - ov = sp[:framestyle] === :origin ? 0 : xor(oax[:flip], ax[:mirror]) ? oamax : oamin + ov = sp[:framestyle] ≡ :origin ? 0 : xor(oax[:flip], ax[:mirror]) ? oamax : oamin sgn = ax[:mirror] ? -1 : 1 sgn2 = iseven(Int(floor(rot / 90))) ? -1 : 1 sgn3 = if isy @@ -1747,12 +1744,12 @@ function gr_label_ticks_3d(sp, letter, ticks) ax = sp[get_attr_symbol(letter, :axis)] ax[:showaxis] || return - isy, isz = letter .=== (:y, :z) + isy, isz = letter .≡ (:y, :z) n0, n1 = isy ? (namax, namin) : (namin, namax) gr_set_tickfont(sp, letter) - nt = sp[:framestyle] === :origin ? 0 : ax[:mirror] ? n1 : n0 - ft = sp[:framestyle] === :origin ? 0 : ax[:mirror] ? famax : famin + nt = sp[:framestyle] ≡ :origin ? 0 : ax[:mirror] ? n1 : n0 + ft = sp[:framestyle] ≡ :origin ? 0 : ax[:mirror] ? famax : famin rot = mod(ax[:rotation], 360) sgn = ax[:mirror] ? -1 : 1 @@ -1762,7 +1759,7 @@ function gr_label_ticks_3d(sp, letter, ticks) axisθ = isz ? 270 : mod(gr_get_3d_axis_angle(cvs, nt, ft, letter), 360) # issue: doesn't work with 1 tick axisϕ = mod(axisθ - 90, 360) - out_factor = ifelse(ax[:tick_direction] === :out, 1.5, 1) + out_factor = ifelse(ax[:tick_direction] ≡ :out, 1.5, 1) axis_offset = 0.012out_factor y_offset, x_offset = axis_offset .* sincosd(axisϕ) @@ -1810,26 +1807,24 @@ gr_label_axis(sp, letter, vp) = GR.savestate() guide_position = ax[:guide_position] rotation = float(ax[:guidefontrotation]) # github.com/JuliaPlots/Plots.jl/issues/3089 - if letter === :x + if letter ≡ :x # default rotation = 0. should yield GR.setcharup(0, 1) i.e. 90° xpos = xposition(vp, position(ax[:guidefonthalign])) halign = alignment(ax[:guidefonthalign]) - ypos, valign = - if guide_position === :top || (guide_position === :auto && mirror) - vp.ymax + 0.015 + (mirror ? gr_axis_height(sp, ax) : 0.015), :top - else - vp.ymin - 0.015 - (mirror ? 0.015 : gr_axis_height(sp, ax)), :bottom - end + ypos, valign = if guide_position ≡ :top || (guide_position ≡ :auto && mirror) + vp.ymax + 0.015 + (mirror ? gr_axis_height(sp, ax) : 0.015), :top + else + vp.ymin - 0.015 - (mirror ? 0.015 : gr_axis_height(sp, ax)), :bottom + end else rotation += 90 # default rotation = 0. should yield GR.setcharup(-1, 0) i.e. 180° ypos = yposition(vp, position(ax[:guidefontvalign])) halign = alignment(ax[:guidefontvalign]) - xpos, valign = - if guide_position === :right || (guide_position === :auto && mirror) - vp.xmax + 0.03 + mirror * gr_axis_width(sp, ax), :bottom - else - vp.xmin - 0.03 - !mirror * gr_axis_width(sp, ax), :top - end + xpos, valign = if guide_position ≡ :right || (guide_position ≡ :auto && mirror) + vp.xmax + 0.03 + mirror * gr_axis_width(sp, ax), :bottom + else + vp.xmin - 0.03 - !mirror * gr_axis_width(sp, ax), :top + end end gr_set_font(guidefont(ax), sp; rotation, halign, valign) gr_text(xpos, ypos, ax[:guide]) @@ -1840,7 +1835,7 @@ gr_label_axis_3d(sp, letter) = if (ax = sp[get_attr_symbol(letter, :axis)])[:guide] != "" letters = axes_letters(sp, letter) (amin, amax), (namin, namax), (famin, famax) = map(l -> axis_limits(sp, l), letters) - n0, n1 = letter === :y ? (namax, namin) : (namin, namax) + n0, n1 = letter ≡ :y ? (namax, namin) : (namin, namax) GR.savestate() gr_set_font( @@ -1857,13 +1852,13 @@ gr_label_axis_3d(sp, letter) = x, y = gr_w3tondc(sort_3d_axes(ag, ng, fg, letter)...) if letter in (:x, :y) h = gr_axis_height(sp, ax) - x_offset = letter === :x ? -h : h + x_offset = letter ≡ :x ? -h : h y_offset = -h else x_offset = -0.03 - gr_axis_width(sp, ax) y_offset = 0 end - letter === :z && GR.setcharup(-1, 0) + letter ≡ :z && GR.setcharup(-1, 0) sgn = ax[:mirror] ? -1 : 1 gr_text(x + sgn * x_offset, y + sgn * y_offset, ax[:guide]) GR.restorestate() @@ -1872,11 +1867,11 @@ gr_label_axis_3d(sp, letter) = gr_add_title(sp, vp_plt, vp_sp) = if (title = sp[:title]) != "" GR.savestate() - xpos, ypos, halign, valign = if (loc = sp[:titlelocation]) === :left + xpos, ypos, halign, valign = if (loc = sp[:titlelocation]) ≡ :left vp_plt.xmin, vp_sp.ymax, :left, :top - elseif loc === :center + elseif loc ≡ :center xcenter(vp_plt), vp_sp.ymax, :center, :top - elseif loc === :right + elseif loc ≡ :right vp_plt.xmax, vp_sp.ymax, :right, :top else xposition(vp_plt, loc[1]), @@ -1902,9 +1897,9 @@ function gr_add_series(sp, series) frng = series[:fillrange] # recompute data - if ispolar(sp) && z === nothing + if ispolar(sp) && z ≡ nothing extrema_r = gr_y_axislims(sp) - if frng !== nothing + if frng ≢ nothing _, frng = PlotsBase.convert_to_polar(x, frng, extrema_r) end x, y = PlotsBase.convert_to_polar(x, y, extrema_r) @@ -1919,34 +1914,34 @@ function gr_add_series(sp, series) # draw the series clims = gr_clims(sp, series) if (st = series[:seriestype]) in (:path, :scatter, :straightline) - if st === :straightline + if st ≡ :straightline x, y = PlotsBase.straightline_data(series) end gr_draw_segments(series, x, y, nothing, frng, clims) - if series[:markershape] !== :none + if series[:markershape] ≢ :none gr_draw_markers(series, x, y, nothing, clims) end - elseif st === :shape + elseif st ≡ :shape gr_draw_shapes(series, clims) elseif st in (:path3d, :scatter3d) gr_draw_segments(series, x, y, z, nothing, clims) - if st === :scatter3d || series[:markershape] !== :none + if st ≡ :scatter3d || series[:markershape] ≢ :none gr_draw_markers(series, x, y, z, clims) end - elseif st === :contour + elseif st ≡ :contour gr_draw_contour(series, x, y, z, clims) elseif st in (:surface, :wireframe, :mesh3d) GR.setwindow(-1, 1, -1, 1) gr_draw_surface(series, x, y, z, clims) - elseif st === :volume + elseif st ≡ :volume sp[:legend_position] = :none GR.gr3.clear() - elseif st === :heatmap + elseif st ≡ :heatmap # `z` is already transposed, so we need to reverse before passing its size. x, y = PlotsBase.heatmap_edges(x, xscale, y, yscale, reverse(size(z)), ispolar(series)) gr_draw_heatmap(series, x, y, z, clims) - elseif st === :image + elseif st ≡ :image gr_draw_image(series, x, y, z, clims) end @@ -1956,7 +1951,7 @@ function gr_add_series(sp, series) gr_text(GR.wctondc(xi, yi)..., str) end - if sp[:legend_position] === :inline && should_add_to_legend(series) + if sp[:legend_position] ≡ :inline && should_add_to_legend(series) gr_set_textcolor(plot_color(sp[:legend_font_color])) offset, halign, valign = if sp[:yaxis][:mirror] _, i = sp[:xaxis][:flip] ? findmax(x) : findmin(x) @@ -1974,8 +1969,8 @@ function gr_add_series(sp, series) end function gr_draw_segments(series, x, y, z, fillrange, clims) - (x === nothing || length(x) ≤ 1) && return - if fillrange !== nothing # prepare fill-in + (x ≡ nothing || length(x) ≤ 1) && return + if fillrange ≢ nothing # prepare fill-in GR.setfillintstyle(GR.INTSTYLE_SOLID) fr_from, fr_to = PlotsBase.is_2tuple(fillrange) ? fillrange : (y, fillrange) end @@ -1985,9 +1980,9 @@ function gr_draw_segments(series, x, y, z, fillrange, clims) for segment in series_segments(series, st; check = true) i, rng = segment.attr_index, segment.range isempty(rng) && continue - is3d = st === :path3d && z !== nothing - is2d = st === :path || st === :straightline - if is2d && fillrange !== nothing + is3d = st ≡ :path3d && z ≢ nothing + is2d = st ≡ :path || st ≡ :straightline + if is2d && fillrange ≢ nothing (fc = get_fillcolor(series, clims, i)) |> gr_set_fillcolor gr_set_fillstyle(get_fillstyle(series, i)) fx = _cycle(x, vcat(rng, reverse(rng))) @@ -2022,7 +2017,7 @@ function gr_draw_markers( ) isempty(x) && return GR.setfillintstyle(GR.INTSTYLE_SOLID) - (shapes = series[:markershape]) === :none && return + (shapes = series[:markershape]) ≡ :none && return for segment in series_segments(series, :scatter) rng = intersect(eachindex(IndexLinear(), x), segment.range) isempty(rng) && continue @@ -2080,7 +2075,7 @@ function gr_draw_contour(series, x, y, z, clims) gr_set_line(get_linewidth(series), get_linestyle(series), get_linecolor(series), series) gr_set_transparency(get_fillalpha(series)) h = gr_contour_levels(series, clims) - if series[:fillrange] !== nothing + if series[:fillrange] ≢ nothing GR.contourf(x, y, h, z, Int(series[:contour_labels] == true)) else black = plot_color(:black) @@ -2092,7 +2087,7 @@ end function gr_draw_surface(series, x, y, z, clims) e_kwargs = series[:extra_kwargs] - if (st = series[:seriestype]) === :surface + if (st = series[:seriestype]) ≡ :surface if ndims(x) == ndims(y) == ndims(z) == 2 GR.gr3.surface(x', y', z, GR.OPTION_3D_MESH) else @@ -2111,10 +2106,10 @@ function gr_draw_surface(series, x, y, z, clims) GR.gr3.surface(x, y, z, d_opt) end end - elseif st === :wireframe + elseif st ≡ :wireframe GR.setfillcolorind(0) GR.surface(x, y, z, get(e_kwargs, :display_option, GR.OPTION_FILLED_MESH)) - elseif st === :mesh3d + elseif st ≡ :mesh3d if series[:connections] isa AbstractVector{<:AbstractVector{Int}} # Combination of any polygon types cns = map(cns -> [length(cns), cns...], series[:connections]) @@ -2182,7 +2177,7 @@ function gr_draw_heatmap(series, x, y, z, clims) # pdf output, and also supports alpha values. # Note that drawimage draws uniformly spaced data correctly # even on log scales, where it is visually non-uniform. - _z, colors = if (scale = sp[:colorbar_scale]) === :identity + _z, colors = if (scale = sp[:colorbar_scale]) ≡ :identity z, plot_color.(get(fillgrad, z, clims), series[:fillalpha]) elseif scale ∈ _log_scales z_log, z_normalized = gr_z_normalized_log_scaled(scale, z, clims) @@ -2196,7 +2191,7 @@ function gr_draw_heatmap(series, x, y, z, clims) if something(series[:fillalpha], 1) < 1 @warn "GR: transparency not supported in non-uniform heatmaps. Alpha values ignored." end - _z, z_normalized = if (scale = sp[:colorbar_scale]) === :identity + _z, z_normalized = if (scale = sp[:colorbar_scale]) ≡ :identity z, get_z_normalized.(z, clims...) elseif scale ∈ _log_scales gr_z_normalized_log_scaled(scale, z, clims) @@ -2254,7 +2249,7 @@ for (mime, fmt) in ( end function PlotsBase._display(plt::Plot{GRBackend}) - if plt[:display_type] === :inline + if plt[:display_type] ≡ :inline filepath = tempname() * ".pdf" GR.emergencyclosegks() withenv( diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index 61966f774..ddaf0c977 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -142,7 +142,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{GastonBackend}) foreach(series -> gaston_add_series(plt, series), plt.series_list) for sp in plt.subplots - sp === nothing && continue + sp ≡ nothing && continue for ann in sp[:annotations] x, y, val = locate_annotation(sp, ann...) sp.o.axesconf *= "; set label '$(val.str)' at $x,$y $(gaston_font(val.font))" @@ -179,14 +179,14 @@ for (mime, term) in ( @eval function PlotsBase._show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GastonBackend}) term = String($term) tmpfile = tempname() * ".$term" - if plt.o !== nothing + if plt.o ≢ nothing ret = Gaston.save(; saveopts = gaston_saveopts(plt), handle = plt.o.handle, output = tmpfile, term, ) - if ret === nothing || ret + if ret ≡ nothing || ret while !isfile(tmpfile) end # avoid race condition with read in next line write(io, read(tmpfile)) @@ -248,7 +248,7 @@ end function gaston_init_subplots(plt, sps) sz = nr, nc = size(sps) for c in 1:nc, r in 1:nr # NOTE: row major - if (sp = sps[r, c]) isa Subplot || sp === nothing + if (sp = sps[r, c]) isa Subplot || sp ≡ nothing gaston_init_subplot(plt, sp) else gaston_init_subplots(plt, sp) @@ -262,7 +262,7 @@ function gaston_init_subplot( plt::Plot{GastonBackend}, sp::Union{Nothing,Subplot{GastonBackend}}, ) - obj = if sp === nothing + obj = if sp ≡ nothing sp else dims = @@ -297,8 +297,8 @@ function gaston_multiplot_pos_size(layout, parent_xy_wh) prev_c = c > 1 ? dat[r, c - 1] : nothing prev_r isa Array && (prev_r = prev_r[end, end]) prev_c isa Array && (prev_c = prev_c[end, end]) - x = prev_c !== nothing ? prev_c[1] + prev_c[3] : parent_xy_wh[1] - y = prev_r !== nothing ? prev_r[2] + prev_r[4] : parent_xy_wh[2] + x = prev_c ≢ nothing ? prev_c[1] + prev_c[3] : parent_xy_wh[1] + y = prev_r ≢ nothing ? prev_r[2] + prev_r[4] : parent_xy_wh[2] dat[r, c] = if l isa PlotsBase.GridLayout sub = gaston_multiplot_pos_size(l, (x, y, w, h)) size(sub) == (1, 1) ? only(sub) : sub @@ -317,8 +317,8 @@ function gaston_multiplot_pos_size!(dat) gaston_multiplot_pos_size!(xy_wh_sp) elseif xy_wh_sp isa Tuple x, y, w, h, sp = xy_wh_sp - sp === nothing && continue - sp.o === nothing && continue + sp ≡ nothing && continue + sp.o ≡ nothing && continue # gnuplot screen coordinates: bottom left at 0,0 and top right at 1,1 gx, gy = x, 1 - y - h # @show gx, gy w, h @@ -330,11 +330,11 @@ end function gaston_add_series(plt::Plot{GastonBackend}, series::Series) sp = series[:subplot] - (gsp = sp.o) === nothing && return + (gsp = sp.o) ≡ nothing && return x, y, z = series[:x], series[:y], series[:z] st = series[:seriestype] curves = Gaston.Curve[] - if gsp.dims == 2 && z === nothing + if gsp.dims == 2 && z ≡ nothing for (n, seg) in enumerate(series_segments(series, st; check = true)) i, rng = seg.attr_index, seg.range fr = _cycle(series[:fillrange], 1:length(x[rng])) @@ -346,7 +346,7 @@ function gaston_add_series(plt::Plot{GastonBackend}, series::Series) supp = nothing # supplementary column if z isa Surface z = z.surf - if st === :image + if st ≡ :image z = reverse(Float32.(Gray.(z)), dims = 1) # flip y axis nr, nc = size(z) if (ly = length(y)) == 2 && ly != nr @@ -359,9 +359,9 @@ function gaston_add_series(plt::Plot{GastonBackend}, series::Series) length(x) == size(z, 2) + 1 && (x = (x[1:(end - 1)] + x[2:end]) / 2) length(y) == size(z, 1) + 1 && (y = (y[1:(end - 1)] + y[2:end]) / 2) end - if st === :mesh3d + if st ≡ :mesh3d x, y, z = PlotsBase.mesh3d_triangles(x, y, z, series[:connections]) - elseif st === :surface + elseif st ≡ :surface if ndims(x) == ndims(y) == ndims(z) == 1 # must reinterpret 1D data for `pm3d` (points are ordered) x, y = unique(x), unique(y) @@ -422,31 +422,31 @@ function gaston_seriesconf!( fc = gaston_color(get_fillcolor(series, i), get_fillalpha(series, i)) fs = gaston_fillstyle(get_fillstyle(series, i)) lc, dt, lw = gaston_lc_ls_lw(series, clims, i) - curveconf *= if fr !== nothing # filled curves, but not filled curves with markers + curveconf *= if fr ≢ nothing # filled curves, but not filled curves with markers "w filledcurves fc $fc fs $fs border lc $lc lw $lw dt $dt,'' w lines lc $lc lw $lw dt $dt" - elseif series[:markershape] === :none # simplepath + elseif series[:markershape] ≡ :none # simplepath "w lines lc $lc dt $dt lw $lw" else pt, ps, mc = gaston_mk_ms_mc(series, clims, i) "w lp lc $mc dt $dt lw $lw pt $pt ps $ps" end - elseif st === :shape + elseif st ≡ :shape fc = gaston_color(get_fillcolor(series, i), get_fillalpha(series, i)) fs = gaston_fillstyle(get_fillstyle(series, i)) lc, = gaston_lc_ls_lw(series, clims, i) curveconf *= "w filledcurves fc $fc fs $fs border lc $lc" elseif st ∈ (:steppre, :stepmid, :steppost) - step = if st === :steppre + step = if st ≡ :steppre "fsteps" - elseif st === :stepmid + elseif st ≡ :stepmid "histeps" - elseif st === :steppost + elseif st ≡ :steppost "steps" end curveconf *= "w $step" lc, dt, lw = gaston_lc_ls_lw(series, clims, i) push!(extra_curves, "w points lc $lc dt $dt lw $lw notitle") - elseif st === :image + elseif st ≡ :image gsp.axesconf *= gaston_palette_conf(series) curveconf *= "w image pixels" elseif st ∈ (:contour, :contour3d) @@ -457,7 +457,7 @@ function gaston_seriesconf!( push!(extra_curves, "w labels notitle") end levels = collect(contour_levels(series, clims)) - if st === :contour # 2D + if st ≡ :contour # 2D gsp.axesconf *= if filled "; set view map; set palette maxcolors $(length(levels))" else @@ -468,11 +468,11 @@ function gaston_seriesconf!( elseif st ∈ (:surface, :heatmap) curveconf *= "w pm3d" gsp.axesconf *= gaston_palette_conf(series) - st === :heatmap && (gsp.axesconf *= "; set view map") + st ≡ :heatmap && (gsp.axesconf *= "; set view map") elseif st ∈ (:wireframe, :mesh3d) lc, dt, lw = gaston_lc_ls_lw(series, clims, i) curveconf *= "w lines lc $lc dt $dt lw $lw" - elseif st === :quiver + elseif st ≡ :quiver curveconf *= "w vectors filled" else @warn "PlotsBase(Gaston): $st is not implemented yet" @@ -524,7 +524,7 @@ function gaston_parse_axes_attrs( fs = sp[:framestyle] for letter in (:x, :y, :z) - (letter === :z && dims == 2) && continue + (letter ≡ :z && dims == 2) && continue axis = sp[get_attr_symbol(letter, :axis)] # NOTE: there is no `z2tics` concept in gnuplot (only 2D) @@ -537,7 +537,7 @@ function gaston_parse_axes_attrs( # guide labels guide_font = guidefont(axis) - if letter === :y && dims == 2 + if letter ≡ :y && dims == 2 # vertical by default (consistency witht other backends) guide_font = font(guide_font; rotation = guide_font.rotation + 90) end @@ -546,19 +546,19 @@ function gaston_parse_axes_attrs( "set $(letter)$(I)label '$(axis[:guide])' $(gaston_font(guide_font))", ) - logscale, base = if (scale = axis[:scale]) === :identity + logscale, base = if (scale = axis[:scale]) ≡ :identity "nologscale", "" - elseif scale === :log10 + elseif scale ≡ :log10 "logscale", "10" - elseif scale === :log2 + elseif scale ≡ :log2 "logscale", "2" - elseif scale === :ln + elseif scale ≡ :ln "logscale", "e" end push!(axesconf, "set $logscale $letter $base") # handle ticks - if axis[:showaxis] && fs !== :none + if axis[:showaxis] && fs ≢ :none if polar push!(axesconf, "set size square; unset $(letter)tics") else @@ -568,7 +568,7 @@ function gaston_parse_axes_attrs( ) # major tick locations - if axis[:ticks] !== :native + if axis[:ticks] ≢ :native if axis[:flip] hi, lo = axis_limits(sp, letter) else @@ -587,7 +587,7 @@ function gaston_parse_axes_attrs( ticks = get_ticks(sp, axis) gaston_set_ticks!(axesconf, ticks, letter, I, "", "") - if axis[:minorticks] !== :native && !no_minor_intervals(axis) + if axis[:minorticks] ≢ :native && !no_minor_intervals(axis) minor_ticks = get_minor_ticks(sp, axis, ticks) gaston_set_ticks!(axesconf, minor_ticks, letter, I, "m", "add") end @@ -597,7 +597,7 @@ function gaston_parse_axes_attrs( if fs in (:zerolines, :origin) push!(axesconf, "set $(letter)zeroaxis") end - if !axis[:showaxis] || fs === :none + if !axis[:showaxis] || fs ≡ :none push!(axesconf, "set tics scale 0", "set format x \"\"", "set format y \"\"") end @@ -607,14 +607,14 @@ function gaston_parse_axes_attrs( push!(axesconf, "set grid " * (polar ? "polar" : "m$(letter)tics")) end - if (ratio = get_aspect_ratio(sp)) !== :none + if (ratio = get_aspect_ratio(sp)) ≢ :none if dims == 2 - ratio === :equal && (ratio = -1) + ratio ≡ :equal && (ratio = -1) push!(axesconf, "set size ratio $ratio") else # ratio and square have no effect on 3D plots, # but do affect 3D projections created using set view map - if ratio === :equal + if ratio ≡ :equal push!(axesconf, "set view equal xyz") end end @@ -634,11 +634,11 @@ function gaston_parse_axes_attrs( left = gp_borders[:bottom_left_back] top = gp_borders[:bottom_right_front] right = gp_borders[:bottom_right_back] - if fs === :box + if fs ≡ :box bottom + left + top + right - elseif fs === :semi + elseif fs ≡ :semi bottom + left - elseif fs === :axes + elseif fs ≡ :axes (sp[:xaxis][:mirror] ? top : bottom) + (sp[:yaxis][:mirror] ? right : left) else 0 @@ -675,9 +675,9 @@ function gaston_parse_axes_attrs( tmin, tmax = axis_limits(sp, :x, false, false) rmin, rmax = axis_limits(sp, :y, false, false) rticks = get_ticks(sp, :y) - gaston_ticks = if (ttype = PlotsBase.ticks_type(rticks)) === :ticks + gaston_ticks = if (ttype = PlotsBase.ticks_type(rticks)) ≡ :ticks string.(rticks) - elseif ttype === :ticks_and_labels + elseif ttype ≡ :ticks_and_labels ["'$l' $t" for (t, l) in zip(rticks...)] end push!( @@ -707,19 +707,19 @@ function gaston_fix_ticks_overflow(ticks::AbstractVector) end function gaston_set_ticks!(axesconf, ticks, letter, I, maj_min, add) - ticks === :auto && return + ticks ≡ :auto && return if ticks ∈ (:none, nothing, false) push!(axesconf, "unset $(maj_min)$(letter)tics") return end - gaston_ticks = if (ttype = PlotsBase.ticks_type(ticks)) === :ticks + gaston_ticks = if (ttype = PlotsBase.ticks_type(ticks)) ≡ :ticks tics = gaston_fix_ticks_overflow(ticks) if maj_min == "m" map(t -> "'' $t 1", tics) # see gnuplot manual 'Mxtics' else map(string, tics) end - elseif ttype === :ticks_and_labels + elseif ttype ≡ :ticks_and_labels tics = gaston_fix_ticks_overflow(first(ticks)) labs = last(ticks) map(i -> "'$(gaston_enclose_tick_string(labs[i]))' $(tics[i])", eachindex(tics)) @@ -727,7 +727,7 @@ function gaston_set_ticks!(axesconf, ticks, letter, I, maj_min, add) @error "Gaston: invalid input for $(maj_min)$(letter)ticks: $ticks ($ttype)" nothing end - if gaston_ticks !== nothing + if gaston_ticks ≢ nothing push!(axesconf, "set $(letter)$(I)tics $add (" * join(gaston_ticks, ", ") * ")") end nothing @@ -755,7 +755,7 @@ function gaston_set_legend!(axesconf, sp, any_label) pos *= sp[:legend_column] == 1 ? "vertical" : "horizontal" push!(axesconf, "set key $pos box lw 1 opaque noautotitle") push!(axesconf, "set key $(gaston_font(legendfont(sp), rot=false, align=false))") - if sp[:legend_title] !== nothing + if sp[:legend_title] ≢ nothing # NOTE: cannot use legendtitlefont(sp) as it will override legendfont push!(axesconf, "set key title '$(sp[:legend_title])'") end @@ -775,7 +775,7 @@ gaston_valign(k) = (top = :top, vcenter = :center, bottom = :bottom)[k] # from the gnuplot docs: # - an alpha value of 0 represents a fully opaque color; i.e., "#00RRGGBB" is the same as "#RRGGBB". # - an alpha value of 255 (FF) represents full transparency -gaston_alpha(alpha) = alpha === nothing ? 0 : alpha +gaston_alpha(alpha) = alpha ≡ nothing ? 0 : alpha gaston_lc_ls_lw(series::Series, clims, i::Int) = ( gaston_color(get_linecolor(series, clims, i), get_linealpha(series, i)), @@ -808,17 +808,17 @@ gaston_palette_conf(series) = function gaston_marker(marker, alpha) # NOTE: :rtriangle, :ltriangle, :hexagon, :heptagon, :octagon seems unsupported by gnuplot filled = gaston_alpha(alpha) != 1 - marker === :none && return -1 - marker === :pixel && return 0 + marker ≡ :none && return -1 + marker ≡ :pixel && return 0 marker ∈ (:+, :cross) && return 1 marker ∈ (:x, :xcross) && return 2 - marker === :star5 && return 3 - marker === :rect && return filled ? 5 : 4 - marker === :circle && return filled ? 7 : 6 - marker === :utriangle && return filled ? 9 : 8 - marker === :dtriangle && return filled ? 11 : 10 - marker === :diamond && return filled ? 13 : 12 - marker === :pentagon && return filled ? 15 : 14 + marker ≡ :star5 && return 3 + marker ≡ :rect && return filled ? 5 : 4 + marker ≡ :circle && return filled ? 7 : 6 + marker ≡ :utriangle && return filled ? 9 : 8 + marker ≡ :dtriangle && return filled ? 11 : 10 + marker ≡ :diamond && return filled ? 13 : 12 + marker ≡ :pentagon && return filled ? 15 : 14 # @debug "PlotsBase(Gaston): unsupported marker $marker" 1 end @@ -830,16 +830,16 @@ function gaston_color(col, alpha = 0) end function gaston_linestyle(style) - style === :solid && return 1 - style === :dash && return 2 - style === :dot && return 3 - style === :dashdot && return 4 - style === :dashdotdot && return 5 + style ≡ :solid && return 1 + style ≡ :dash && return 2 + style ≡ :dot && return 3 + style ≡ :dashdot && return 4 + style ≡ :dashdotdot && return 5 1 end function gaston_enclose_tick_string(tick_string) - findfirst('^', tick_string) === nothing && return tick_string + findfirst('^', tick_string) ≡ nothing && return tick_string base, power = split(tick_string, '^') "$base^{$power}" end diff --git a/PlotsBase/ext/IJuliaExt.jl b/PlotsBase/ext/IJuliaExt.jl index e889eac33..47f71b049 100644 --- a/PlotsBase/ext/IJuliaExt.jl +++ b/PlotsBase/ext/IJuliaExt.jl @@ -10,7 +10,7 @@ const IJulia = function _init_ijulia_plotting() # IJulia is more stable with local file PlotsBase._use_local_plotlyjs[] = - PlotsBase._plotly_local_file_path[] === nothing ? false : + PlotsBase._plotly_local_file_path[] ≡ nothing ? false : isfile(PlotsBase._plotly_local_file_path[]) ENV["MPLBACKEND"] = "Agg" @@ -18,25 +18,25 @@ end function _ijulia_display_dict(plt::Plot) output_type = Symbol(plt.attr[:html_output_format]) - if output_type === :auto + if output_type ≡ :auto output_type = get(PlotsBase._best_html_output_type, PlotsBase.backend_name(plt.backend), :svg) end out = Dict() - if output_type === :txt + if output_type ≡ :txt mime = "text/plain" out[mime] = sprint(show, MIME(mime), plt) - elseif output_type === :png + elseif output_type ≡ :png mime = "image/png" out[mime] = base64encode(show, MIME(mime), plt) - elseif output_type === :svg + elseif output_type ≡ :svg mime = "image/svg+xml" out[mime] = sprint(show, MIME(mime), plt) - elseif output_type === :html + elseif output_type ≡ :html mime = "text/html" out[mime] = sprint(show, MIME(mime), plt) PlotsBase._ijulia__extra_mime_info!(plt, out) - elseif output_type === :pdf + elseif output_type ≡ :pdf mime = "application/pdf" out[mime] = base64encode(show, MIME(mime), plt) else diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index 4f4ebc90f..57fb2ea85 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -275,7 +275,7 @@ surface_to_vecs(x::AVec, y::AVec, z::AVec) = x, y, z Base.push!(pgfx_plot::PGFPlotsXPlot, item) = push!(pgfx_plot.the_plot, item) pgfx_split_extra_kw(extra) = - (get(extra, :add, nothing), filter(x -> first(x) !== :add, extra)) + (get(extra, :add, nothing), filter(x -> first(x) ≢ :add, extra)) curly(obj) = "{$(string(obj))}" @@ -294,8 +294,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) # extract extra kwargs extra_plot, extra_plot_opt = pgfx_split_extra_kw(plt[:extra_plot_kwargs]) the_plot = PGFPlotsX.TikzPicture(Options(extra_plot_opt...)) - extra_plot !== nothing && push!(the_plot, wraptuple(extra_plot)...) - bgc = plt.attr[if plt.attr[:background_color_outside] === :match + extra_plot ≢ nothing && push!(the_plot, wraptuple(extra_plot)...) + bgc = plt.attr[if plt.attr[:background_color_outside] ≡ :match :background_color else :background_color_outside @@ -366,7 +366,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) sp_w > 0mm && push!(axis_opt, "width" => string(sp_w - (rpad + lpad))) sp_h > 0mm && push!(axis_opt, "height" => string(sp_h - (tpad + bpad))) for letter in (:x, :y, :z) - if letter !== :z || RecipesPipeline.is3d(sp) + if letter ≢ :z || RecipesPipeline.is3d(sp) pgfx_axis!(axis_opt, sp, letter) end end @@ -399,7 +399,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) if hascolorbar(sp) formatter = latex_formatter(sp[:colorbar_formatter]) cticks = curly(join(get_colorbar_ticks(sp; formatter = formatter)[1], ',')) - letter = sp[:colorbar] === :top ? :x : :y + letter = sp[:colorbar] ≡ :top ? :x : :y colorbar_style = push!( Options("$(letter)label" => sp[:colorbar_title]), @@ -408,7 +408,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) "$(letter)ticklabel style" => pgfx_get_colorbar_ticklabel_style(sp), ) - if sp[:colorbar] === :top + if sp[:colorbar] ≡ :top push!( colorbar_style, "at" => "(0.5, 1.05)", @@ -434,15 +434,12 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) push!(axis_opt, "colorbar" => "false") end if RecipesPipeline.is3d(sp) - if (ar = sp[:aspect_ratio]) !== :auto - push!( - axis_opt, - "unit vector ratio" => ar === :equal ? 1 : join(ar, ' '), - ) + if (ar = sp[:aspect_ratio]) ≢ :auto + push!(axis_opt, "unit vector ratio" => ar ≡ :equal ? 1 : join(ar, ' ')) end push!(axis_opt, "view" => tuple(sp[:camera])) end - axisf = if sp[:projection] === :polar + axisf = if sp[:projection] ≡ :polar # push!(axis_opt, "xmin" => 90) # push!(axis_opt, "xmax" => 450) PGFPlotsX.PolarAxis @@ -451,8 +448,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) end extra_sp, extra_sp_opt = pgfx_split_extra_kw(sp[:extra_kwargs]) axis = axisf(merge(axis_opt, Options(extra_sp_opt...))) - extra_sp !== nothing && push!(axis, wraptuple(extra_sp)...) - if sp[:legend_title] !== nothing + extra_sp ≢ nothing && push!(axis, wraptuple(extra_sp)...) + if sp[:legend_title] ≢ nothing legtfont = legendtitlefont(sp) leg_opt = Options( "font" => pgfx_font(legtfont.pointsize, pgfx_thickness_scaling(sp)), @@ -483,15 +480,15 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) if ( RecipesPipeline.is3d(series) || st in (:heatmap, :contour) || - (st === :quiver && opt[:z] !== nothing) + (st ≡ :quiver && opt[:z] ≢ nothing) ) PGFPlotsX.Plot3 else PGFPlotsX.Plot end if ( - series[:fillrange] !== nothing && - series[:ribbon] === nothing && + series[:fillrange] ≢ nothing && + series[:ribbon] ≡ nothing && !isfilledcontour(series) ) push!(series_opt, "area legend" => nothing) @@ -501,7 +498,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) axis.contents[end] isa PGFPlotsX.LegendEntry ? axis.contents[end - 1] : axis.contents[end] merge!(last_plot.options, Options(extra_series_opt...)) - if extra_series !== nothing + if extra_series ≢ nothing push!(axis.contents[end], wraptuple(extra_series)...) end # add series annotations @@ -551,7 +548,7 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o for (k, segment) in enumerate(segments) i, rng = segment.attr_index, segment.range segment_opt = pgfx_linestyle(opt, i) - if opt[:markershape] !== :none + if opt[:markershape] ≢ :none if (marker = _cycle(opt[:markershape], i)) isa Shape scale_factor = 0.00125 msize = opt[:markersize] * scale_factor @@ -572,10 +569,10 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o segment_opt = merge(segment_opt, pgfx_marker(opt, i)) end # add fillrange - if (sf = opt[:fillrange]) !== nothing && !isfilledcontour(series) + if (sf = opt[:fillrange]) ≢ nothing && !isfilledcontour(series) if sf isa Number || sf isa AVec pgfx_fillrange_series!(axis, series, series_func, i, _cycle(sf, rng), rng) - elseif sf isa Tuple && series[:ribbon] !== nothing + elseif sf isa Tuple && series[:ribbon] ≢ nothing for sfi in sf pgfx_fillrange_series!( axis, @@ -589,7 +586,7 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o end if ( i == 1 && - series[:subplot][:legend_position] !== :none && + series[:subplot][:legend_position] ≢ :none && pgfx_should_add_to_legend(series) ) pgfx_filllegend!(series_opt, opt) @@ -610,11 +607,11 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o isempty(opt[:label]) && push!(arrow_opt, "forget plot" => nothing) rx, ry = opt[:x][rng], opt[:y][rng] nx, ny = length(rx), length(ry) - x_arrow, y_arrow, x_path, y_path = if arrow.side === :head + x_arrow, y_arrow, x_path, y_path = if arrow.side ≡ :head rx[(nx - 1):nx], ry[(ny - 1):ny], rx[1:(nx - 1)], ry[1:(ny - 1)] - elseif arrow.side === :tail + elseif arrow.side ≡ :tail rx[2:-1:1], ry[2:-1:1], rx[2:nx], ry[2:ny] - elseif arrow.side === :both + elseif arrow.side ≡ :both rx[[2, 1, nx - 1, nx]], ry[[2, 1, ny - 1, ny]], rx[2:(nx - 1)], ry[2:(ny - 1)] end coords = Table([ @@ -632,7 +629,7 @@ function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, o end push!(axis, series_func(merge(series_opt, segment_opt), coordinates)) # fill between functions - if sf isa Tuple && series[:ribbon] === nothing + 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!( @@ -776,7 +773,7 @@ function pgfx_add_series!(::Val{:contour3d}, axis, series_opt, series, series_fu end function pgfx_add_series!(::Val{:quiver}, axis, series_opt, series, series_func, opt) - if (quiver = opt[:quiver]) !== nothing + if (quiver = opt[:quiver]) ≢ nothing push!( series_opt, "quiver" => Options( @@ -786,7 +783,7 @@ function pgfx_add_series!(::Val{:quiver}, axis, series_opt, series, series_func, ), ) x, y, z = opt[:x], opt[:y], opt[:z] - table = if z !== nothing + table = if z ≢ nothing push!(series_opt["quiver"], "w" => "\\thisrow{w}") pgfx_axis!(axis.options, series[:subplot], :z) [:x => x, :y => y, :z => z, :u => quiver[1], :v => quiver[2], :w => quiver[3]] @@ -829,7 +826,7 @@ function pgfx_add_series!(::Val{:xsticks}, axis, series_opt, args...) end function pgfx_add_legend!(axis, series, opt, i = 1) - if series[:subplot][:legend_position] !== :none + if series[:subplot][:legend_position] ≢ :none leg_entry = if (lab = opt[:label]) isa AVec get(lab, i, "") elseif lab isa AbstractString @@ -855,9 +852,9 @@ pgfx_series_arguments(series, opt) = surface_to_vecs(opt[:x], opt[:y], opt[:z]) elseif RecipesPipeline.is3d(st) opt[:x], opt[:y], opt[:z] - elseif st === :straightline + elseif st ≡ :straightline PlotsBase.straightline_data(series) - elseif st === :shape + elseif st ≡ :shape PlotsBase.shape_data(series) elseif ispolar(series) theta, r = opt[:x], opt[:y] @@ -960,7 +957,7 @@ function pgfx_get_legend_pos(v::Tuple{<:Real,Symbol}) "north west" "north" "north east" ] I = legend_anchor_index(s) - rect, anchor = if v[2] === :inner + rect, anchor = if v[2] ≡ :inner (0.07, 0.5, 1.0, 0.07, 0.52, 1.0), anchors[I, I] else (-0.15, 0.5, 1.05, -0.15, 0.52, 1.1), anchors[4 - I, 4 - I] @@ -1023,9 +1020,9 @@ function pgfx_get_ticklabel_style(sp, axis) ) # aligning rotated tick labels to ticks if RecipesPipeline.is3d(sp) - if axis === sp[:xaxis] + if axis ≡ sp[:xaxis] push!(opt, "anchor" => axis[:rotation] < 60 ? "north east" : "east") - elseif axis === sp[:yaxis] + elseif axis ≡ sp[:yaxis] push!(opt, "anchor" => axis[:rotation] < 45 ? "north west" : "north east") else push!( @@ -1037,7 +1034,7 @@ function pgfx_get_ticklabel_style(sp, axis) end else if mod(axis[:rotation], 90) > 0 # 0 and ±90 already look good with the default anchor - push!(opt, "anchor" => axis === sp[:xaxis] ? "north east" : "south east") + push!(opt, "anchor" => axis ≡ sp[:xaxis] ? "north east" : "south east") end end return opt @@ -1067,11 +1064,11 @@ pgfx_arrow(::Nothing) = "every arrow/.append style={-}" function pgfx_arrow(arr::Arrow, side = arr.side) components = "" arrow_head = "{Stealth[length = $(arr.headlength)pt, width = $(arr.headwidth)pt" - arr.style === :open && (arrow_head *= ", open") + arr.style ≡ :open && (arrow_head *= ", open") arrow_head *= "]}" - (side === :both || side === :tail) && (components *= arrow_head) + (side ≡ :both || side ≡ :tail) && (components *= arrow_head) components *= "-" - (side === :both || side === :head) && (components *= arrow_head) + (side ≡ :both || side ≡ :head) && (components *= arrow_head) return "every arrow/.append style={$components}" end @@ -1104,7 +1101,7 @@ pgfx_framestyle(style::Symbol) = if style in (:box, :axes, :origin, :zerolines, :grid, :none) style else - default_style = style === :semi ? :box : :axes + default_style = style ≡ :semi ? :box : :axes @warn "Framestyle :$style is not (yet) supported by the PGFPlotsX backend. :$default_style was chosen instead." default_style end @@ -1114,7 +1111,7 @@ pgfx_thickness_scaling(sp::Subplot) = pgfx_thickness_scaling(sp.plt) pgfx_thickness_scaling(series) = pgfx_thickness_scaling(series[:subplot]) function pgfx_fillstyle(plotattributes, i = 1) - if (a = get_fillalpha(plotattributes, i)) === nothing + if (a = get_fillalpha(plotattributes, i)) ≡ nothing a = alpha(single_color(get_fillcolor(plotattributes, i))) end Options("fill" => get_fillcolor(plotattributes, i), "fill opacity" => a) @@ -1130,7 +1127,7 @@ function pgfx_linestyle(linewidth::Real, color, α = 1, linestyle = :solid) ) end -pgfx_legend_col(s::Symbol) = s === :horizontal ? -1 : 1 +pgfx_legend_col(s::Symbol) = s ≡ :horizontal ? -1 : 1 pgfx_legend_col(n) = n function pgfx_linestyle(plotattributes, i = 1) @@ -1196,11 +1193,11 @@ function pgfx_marker(plotattributes, i = 1) pgfx_thickness_scaling(plotattributes) * 0.75 * _cycle(plotattributes[:markerstrokewidth], i), - "rotate" => if shape === :dtriangle + "rotate" => if shape ≡ :dtriangle 180 - elseif shape === :rtriangle + elseif shape ≡ :rtriangle 270 - elseif shape === :ltriangle + elseif shape ≡ :ltriangle 90 else 0 @@ -1253,7 +1250,7 @@ function pgfx_fillrange_series!(axis, series, series_func, i, fillrange, rng) opt[:x][rng], opt[:y][rng], opt[:z][rng] elseif ispolar(series) rad2deg.(opt[:x][rng]), opt[:y][rng] - elseif series[:seriestype] === :straightline + elseif series[:seriestype] ≡ :straightline PlotsBase.straightline_data(series) else opt[:x][rng], opt[:y][rng] @@ -1298,7 +1295,7 @@ function pgfx_sanitize_plot!(plt) end for subplot in plt.subplots for (key, value) in subplot.attr - if key === :annotations && subplot.attr[:annotations] !== nothing + if key ≡ :annotations && subplot.attr[:annotations] ≢ nothing old_ann = subplot.attr[key] for i in eachindex(old_ann) # [1:end-1] is a tuple of coordinates, [end] - text @@ -1318,8 +1315,8 @@ function pgfx_sanitize_plot!(plt) end for series in plt.series_list for (key, value) in series.plotattributes - if key === :series_annotations && - series.plotattributes[:series_annotations] !== nothing + 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]) @@ -1376,9 +1373,9 @@ function pgfx_axis!(opt::Options, sp::Subplot, letter) framestyle = pgfx_framestyle(sp[:framestyle] == false ? :none : sp[:framestyle]) # axis label position - labelpos = if letter === :x + labelpos = if letter ≡ :x pgfx_get_xguide_pos(axis[:guide_position]) - elseif letter === :y + elseif letter ≡ :y pgfx_get_yguide_pos(axis[:guide_position]) else "" @@ -1406,38 +1403,38 @@ function pgfx_axis!(opt::Options, sp::Subplot, letter) scale = axis[:scale] if (is_log_scale = scale in (:ln, :log2, :log10)) push!(opt, "$(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 - if axis[:ticks] in (nothing, false, :none) || framestyle === :none + if axis[:ticks] in (nothing, false, :none) || framestyle ≡ :none push!(opt, "$(letter)majorticks" => "false") elseif framestyle in (:grid, :zerolines) push!(opt, "$letter tick style" => Options("draw" => "none")) end # grid on or off - push!(opt, "$(letter)majorgrids" => string(axis[:grid] && framestyle !== :none)) + push!(opt, "$(letter)majorgrids" => string(axis[:grid] && framestyle ≢ :none)) # limits - lims = if ispolar(sp) && letter === :x + lims = if ispolar(sp) && letter ≡ :x rad2deg.(axis_limits(sp, :x)) else axis_limits(sp, letter) end push!(opt, "$(letter)min" => lims[1], "$(letter)max" => lims[2]) - if axis[:ticks] ∉ (nothing, false, :none, :native) && framestyle !== :none + if axis[:ticks] ∉ (nothing, false, :none, :native) && framestyle ≢ :none vals, labs = ticks = get_ticks(sp, axis, formatter = latex_formatter(axis[:formatter])) # pgfplot ignores ticks with angles below `90` when `xmin = 90`, so shift values - tick_values = if ispolar(sp) && letter === :x + tick_values = if ispolar(sp) && letter ≡ :x vcat(rad2deg.(vals[3:end]), 360, 405) else vals end tick_labels = if axis[:showaxis] - if is_log_scale && axis[:ticks] === :auto + if is_log_scale && axis[:ticks] ≡ :auto labels = wrap_power_labels(labs) if (lab = first(labels)) isa LaTeXString || pgfx_is_inline_math(lab) join(labels, ',') @@ -1445,7 +1442,7 @@ function pgfx_axis!(opt::Options, sp::Subplot, letter) "\\(" * join(labels, "\\),\\(") * "\\)" end else - labels = if ispolar(sp) && letter === :x + labels = if ispolar(sp) && letter ≡ :x vcat(labs[3:end], "0", "45") else labs @@ -1459,7 +1456,7 @@ function pgfx_axis!(opt::Options, sp::Subplot, letter) opt, "$(letter)ticklabels" => curly(tick_labels), "$(letter)tick" => curly(join(tick_values, ',')), - if (tick_dir = axis[:tick_direction]) === :none || axis[:showaxis] === false + if (tick_dir = axis[:tick_direction]) ≡ :none || axis[:showaxis] ≡ false "$(letter)tick style" => "draw=none" else "$(letter)tick align" => "$(tick_dir)side" @@ -1479,8 +1476,8 @@ function pgfx_axis!(opt::Options, sp::Subplot, letter) # Hence, we hack around with extra ticks. # Unfortunately this conflicts with `:zerolines` framestyle hack. # So minor ticks are not working with `:zerolines`. - if (minor_ticks = get_minor_ticks(sp, axis, ticks)) !== nothing - if ispolar(sp) && letter === :x + if (minor_ticks = get_minor_ticks(sp, axis, ticks)) ≢ nothing + if ispolar(sp) && letter ≡ :x minor_ticks = vcat(rad2deg.(minor_ticks[3:end]), 360, 405) end push!( @@ -1510,7 +1507,7 @@ function pgfx_axis!(opt::Options, sp::Subplot, letter) push!( opt, # the * after line disables the arrow at the axis "axis $letter line$(axis[:draw_arrow] ? "" : "*")" => - (axis[:mirror] ? "right" : framestyle === :axes ? "left" : "middle"), + (axis[:mirror] ? "right" : framestyle ≡ :axes ? "left" : "middle"), ) end @@ -1519,7 +1516,7 @@ function pgfx_axis!(opt::Options, sp::Subplot, letter) push!(opt, "$(letter)ticklabel pos" => (axis[:mirror] ? "right" : "left")) end - if framestyle === :zerolines + if framestyle ≡ :zerolines gs = pgfx_linestyle(pgfx_thickness_scaling(sp), axis[:foreground_color_border], 1) push!( opt, diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index e497f0919..6df929fa1 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -15,8 +15,8 @@ import NaNMath import PlotsBase import PlotsBase.PlotUtils: PlotUtils, ColorGradient, plot_color, color_list, cgrad -import PlotsBase: bbox_to_pcts, right, left, bottom, top, width, height, ispositive -import PlotsBase: ticks_type +import PlotsBase: bbox_to_pcts, right, left, bottom, top, width, height, ticks_type +import PlotsBase: ispositive import PlotsBase.Commons: Commons, single_color import RecipesPipeline: Surface @@ -49,6 +49,20 @@ function PlotsBase.extension_init(::PythonPlotBackend) PythonCall.pyimport("mpl_toolkits.axes_grid1") numpy.seterr(invalid = "ignore") PythonPlot.ioff() # we don't want every command to update the figure + + for letter in (:x, :y, :z, Symbol(), :top, :bottom, :left, :right) + Commons.add_attr_dict!(letter) + for keyword in (:linthresh, :base, :label) + Commons.add_attr!(letter, keyword) + end + end + + # problem: github.com/tbreloff/Plots.jl/issues/308 + # solution: hack from @stevengj: github.com/JuliaPy/PyPlot.jl/pull/223#issuecomment-229747768 + let otherdisplays = + splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays)) + append!(Base.Multimedia.displays, otherdisplays) + end end PlotsBase.@extension_static PythonPlotBackend pythonplot @@ -191,20 +205,6 @@ const _pythonplot_scales = [:identity, :ln, :log2, :log10] PlotsBase.is_marker_supported(::PythonPlotBackend, shape::Shape) = true -# problem: github.com/tbreloff/Plots.jl/issues/308 -# solution: hack from @stevengj: github.com/JuliaPy/PyPlot.jl/pull/223#issuecomment-229747768 -let otherdisplays = splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays)) - append!(Base.Multimedia.displays, otherdisplays) -end - -for k in (:linthresh, :base, :label) - # add PythonPlot specific symbols to cache - Commons._attrsymbolcache[k] = Dict{Symbol,Symbol}() - for letter in (:x, :y, :z, Symbol(), :top, :bottom, :left, :right) - Commons._attrsymbolcache[k][letter] = Symbol(k, letter) - end -end - _py_handle_surface(v) = v _py_handle_surface(z::Surface) = z.surf @@ -238,11 +238,11 @@ _py_shading(c, z) = mpl.colors.LightSource(270, 45).shade( # get the style (solid, dashed, etc) function _py_linestyle(seriestype::Symbol, linestyle::Symbol) - seriestype === :none && return " " - linestyle === :solid && return "-" - linestyle === :dash && return "--" - linestyle === :dot && return ":" - linestyle === :dashdot && return "-." + seriestype ≡ :none && return " " + linestyle ≡ :solid && return "-" + linestyle ≡ :dash && return "--" + linestyle ≡ :dot && return ":" + linestyle ≡ :dashdot && return "-." @warn "Unknown linestyle $linestyle" "-" end @@ -261,23 +261,24 @@ end # get the marker shape function _py_marker(marker::Symbol) - marker === :none && return " " - marker === :circle && return "o" - marker === :rect && return "s" - marker === :diamond && return "D" - marker === :utriangle && return "^" - marker === :dtriangle && return "v" - marker === :+ && return "+" - marker === :x && return "x" - marker === :star5 && return "*" - marker === :pentagon && return "p" - marker === :hexagon && return "h" - marker === :octagon && return "8" - marker === :pixel && return "," - marker === :hline && return "_" - marker === :vline && return "|" - haskey(_shapes, marker) && return _py_marker(_shapes[marker]) - + marker ≡ :none && return " " + marker ≡ :circle && return "o" + marker ≡ :rect && return "s" + marker ≡ :diamond && return "D" + marker ≡ :utriangle && return "^" + marker ≡ :dtriangle && return "v" + marker ≡ :+ && return "+" + marker ≡ :x && return "x" + marker ≡ :star5 && return "*" + marker ≡ :pentagon && return "p" + marker ≡ :hexagon && return "h" + marker ≡ :octagon && return "8" + marker ≡ :pixel && return "," + marker ≡ :hline && return "_" + marker ≡ :vline && return "|" + let _shapes = PlotsBase.Shapes._shapes + haskey(_shapes, marker) && return _py_marker(_shapes[marker]) + end @warn "Unknown marker $marker" "o" end @@ -295,16 +296,16 @@ function _py_marker(marker::AbstractString) end function _py_stepstyle(seriestype::Symbol) - seriestype === :steppost && return "steps-post" - seriestype === :stepmid && return "steps-mid" - seriestype === :steppre && return "steps-pre" + seriestype ≡ :steppost && return "steps-post" + seriestype ≡ :stepmid && return "steps-mid" + seriestype ≡ :steppre && return "steps-pre" "default" end function _py_fillstepstyle(seriestype::Symbol) - seriestype === :steppost && return "post" - seriestype === :stepmid && return "mid" - seriestype === :steppre && return "pre" + seriestype ≡ :steppost && return "post" + seriestype ≡ :stepmid && return "mid" + seriestype ≡ :steppre && return "pre" nothing end @@ -338,7 +339,7 @@ _py_mask_nans(z) = PythonPlot.pycall(numpy.ma.masked_invalid, z) # --------------------------------------------------------------------------- function fix_xy_lengths!(plt::Plot{PythonPlotBackend}, series::Series) - if (x = series[:x]) !== nothing + if (x = series[:x]) ≢ nothing y = series[:y] nx, ny = length(x), length(y) if !(get(series.plotattributes, :z, nothing) isa Surface || nx == ny) @@ -506,10 +507,10 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) # ax = getAxis(plt, series) x, y, z = (_py_handle_surface(series[letter]) for letter in (:x, :y, :z)) - if st === :straightline - x, y = straightline_data(series) - elseif st === :shape - x, y = shape_data(series) + if st ≡ :straightline + x, y = PlotsBase.straightline_data(series) + elseif st ≡ :shape + x, y = PlotsBase.shape_data(series) end # make negative radii positive and flip the angle (PythonPlot ignores negative radii) @@ -527,7 +528,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) vmin, vmax = clims = get_clims(sp, series) # Dict to store extra kwargs - extrakw = if st === :wireframe || st === :hexbin + extrakw = if st ≡ :wireframe || st ≡ :hexbin # vmin, vmax cause an error for wireframe plot # We are not supporting clims for hexbin as calculation of bins is not trivial KW() @@ -577,7 +578,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) ) |> push_h end - if (a = series[:arrow]) !== nothing && !RecipesPipeline.is3d(st) # TODO: handle 3d later + if (a = series[:arrow]) ≢ nothing && !RecipesPipeline.is3d(st) # TODO: handle 3d later if typeof(a) != Arrow @warn "Unexpected type for arrow: $(typeof(a))" else @@ -604,14 +605,10 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) end # add markers ? - if series[:markershape] !== :none && st ∈ _py_marker_series + if series[:markershape] ≢ :none && st ∈ _py_marker_series for segment in series_segments(series, :scatter) i, rng = segment.attr_index, segment.range - args = if st === :bar && !isvertical(series) - y[rng], x[rng] - else - x[rng], y[rng] - end + args = x[rng], y[rng] RecipesPipeline.is3d(sp) && (args = (args..., z[rng])) ax.scatter( args...; @@ -633,7 +630,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) end end - if st === :shape + if st ≡ :shape for segment in series_segments(series) i, rng = segment.attr_index, segment.range if length(rng) > 1 @@ -680,7 +677,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) end end end - elseif st === :image + elseif st ≡ :image x, y = series[:x], series[:y] xmin, xmax = ignorenan_extrema(x) ymin, ymax = ignorenan_extrema(y) @@ -696,7 +693,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) else z # hopefully it's in a data format that will "just work" with imshow end - aspect = if get_aspect_ratio(sp) === :equal + aspect = if get_aspect_ratio(sp) ≡ :equal "equal" else "auto" @@ -710,8 +707,8 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) zorder, aspect, ) |> push_h - elseif st === :heatmap - x, y = heatmap_edges(x, xaxis[:scale], y, yaxis[:scale], size(z)) + elseif st ≡ :heatmap + x, y = PlotsBase.heatmap_edges(x, xaxis[:scale], y, yaxis[:scale], size(z)) expand_extrema!(xaxis, x) expand_extrema!(yaxis, y) ax.pcolormesh( @@ -725,7 +722,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) label, extrakw..., ) |> push_h - elseif st === :mesh3d + elseif st ≡ :mesh3d cns = series[:connections] polygons = if cns isa AbstractVector{<:AbstractVector{<:Integer}} # Combination of any polygon types @@ -735,7 +732,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) map(inds -> map(i -> [x[i], y[i], z[i]], inds), cns) elseif cns isa NTuple{3,<:AbstractVector{<:Integer}} # Only triangles - connections have to be 0-based (indexing) - X, Y, Z = mesh3d_triangles(x, y, z, cns) + X, Y, Z = PlotsBase.mesh3d_triangles(x, y, z, cns) ntris = length(cns[1]) polys = sizehint!(Matrix{eltype(x)}[], ntris) for n in 1:ntris @@ -765,7 +762,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) ) |> ax.add_collection3d |> push_h - elseif st === :hexbin + elseif st ≡ :hexbin sekw = series[:extra_kwargs] extrakw[:mincnt] = get(sekw, :mincnt, nothing) extrakw[:edgecolors] = get(sekw, :edgecolors, edgecolor) @@ -773,7 +770,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) x, y; C = series[:weights], - gridsize = series[:bins] === :auto ? 100 : series[:bins], # 100 is the default value + gridsize = series[:bins] ≡ :auto ? 100 : series[:bins], # 100 is the default value cmap = _py_fillcolormap(series), # applies to the pcolorfast object linewidths, zorder, @@ -782,7 +779,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) extrakw..., ) |> push_h elseif st ∈ (:contour, :contour3d) - if st === :contour3d + if st ≡ :contour3d extrakw[:extend3d] = true if !ismatrix(x) || !ismatrix(y) x, y = repeat(x', length(y), 1), repeat(y, 1, length(x)) @@ -805,10 +802,10 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) extrakw..., ) ) |> push_h - series[:contour_labels] === true && ax.clabel(handle, handle.levels) + series[:contour_labels] ≡ true && ax.clabel(handle, handle.levels) # contour fills - series[:fillrange] !== nothing && + series[:fillrange] ≢ nothing && ax.contourf( x, y, @@ -824,8 +821,8 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) if !ismatrix(x) || !ismatrix(y) x, y = repeat(x', length(y), 1), repeat(y, 1, length(x)) end - if st === :surface - if series[:fill_z] !== nothing + if st ≡ :surface + if series[:fill_z] ≢ nothing # the surface colors are different than z-value extrakw[:facecolors] = _py_shading(series[:fillcolor], _py_handle_surface(series[:fill_z])) @@ -885,18 +882,14 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) # handleSmooth(plt, ax, series, series[:smooth]) # handle area filling - if (fillrange = series[:fillrange]) !== nothing && st !== :contour + if (fillrange = series[:fillrange]) ≢ nothing && st ≢ :contour for segment in series_segments(series) i, rng = segment.attr_index, segment.range - f, dim1, dim2 = if isvertical(series) - :fill_between, x[rng], y[rng] - else - :fill_betweenx, y[rng], x[rng] - end + f, dim1, dim2 = :fill_between, x[rng], y[rng] n = length(dim1) args = if typeof(fillrange) <: Union{Real,AVec} dim1, _cycle(fillrange, rng), dim2 - elseif is_2tuple(fillrange) + elseif PlotsBase.is_2tuple(fillrange) dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng) end @@ -936,20 +929,20 @@ _py_set_lims(ax, sp::Subplot, axis::Axis) = end function _py_set_ticks(sp, ax, ticks, letter) - ticks === :auto && return + ticks ≡ :auto && return axis = getproperty(ax, get_attr_symbol(letter, :axis)) - if ticks === :none || ticks === nothing || ticks == false + if ticks ≡ :none || ticks ≡ nothing || ticks == false kw = KW() for dir in (:top, :bottom, :left, :right) - kw[dir] = kw[get_attr_symbol(:label, dir)] = false + kw[dir] = kw[get_attr_symbol(dir, :label)] = false end axis.set_tick_params(; which = "both", kw...) return end - tick_values, tick_labels = if (ttype = ticks_type(ticks)) === :ticks + tick_values, tick_labels = if (ttype = ticks_type(ticks)) ≡ :ticks ticks, [] - elseif ttype === :ticks_and_labels + elseif ttype ≡ :ticks_and_labels ticks else error("Invalid input for $(letter)ticks: $ticks") @@ -975,13 +968,13 @@ end function _py_set_scale(ax, sp::Subplot, scale::Symbol, letter::Symbol) scale ∈ PlotsBase.supported_scales() || return @warn "Unhandled scale value in PythonPlot: $scale" - scl, kw = if scale === :identity + scl, kw = if scale ≡ :identity "linear", KW() else "symlog", KW( - get_attr_symbol(:base, Symbol()) => _logScaleBases[scale], - get_attr_symbol(:linthresh, Symbol()) => NaNMath.max( + get_attr_symbol(Symbol(), :base) => Commons._log_scale_bases[scale], + get_attr_symbol(Symbol(), :linthresh) => NaNMath.max( 1e-16, _py_compute_axis_minval(sp, sp[get_attr_symbol(letter, :axis)]), ), @@ -1040,7 +1033,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) # update subplots for sp in plt.subplots - (ax = sp.o) === nothing && continue + (ax = sp.o) ≡ nothing && continue xaxis, yaxis = sp[:xaxis], sp[:yaxis] # add the annotations @@ -1075,7 +1068,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) kw = KW() handle = if !isempty(sp[:zaxis][:discrete_values]) && - cbar_series[:seriestype] === :heatmap + cbar_series[:seriestype] ≡ :heatmap kw[:ticks], kw[:format] = get_locator_and_formatter(sp[:zaxis][:discrete_values]) # kw[:values] = eachindex(sp[:zaxis][:discrete_values]) @@ -1083,17 +1076,17 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) kw[:boundaries] = vcat(0, kw[:values] + 0.5) cbar_series[:serieshandle][end] elseif any( - cbar_series[attr] !== nothing for attr in (:line_z, :fill_z, :marker_z) + cbar_series[attr] ≢ nothing for attr in (:line_z, :fill_z, :marker_z) ) cmin, cmax = get_clims(sp) - norm = if cbar_scale === :identity + norm = if cbar_scale ≡ :identity mpl.colors.Normalize(vmin = cmin, vmax = cmax) else mpl.colors.LogNorm(vmin = cmin, vmax = cmax) end cmap = nothing for func in (_py_linecolormap, _py_fillcolormap, _py_markercolormap) - (cmap = func(cbar_series)) === nothing || break + (cmap = func(cbar_series)) ≡ nothing || break end c_map = mpl.cm.ScalarMappable(; cmap, norm) c_map.set_array(PythonPlot.pylist([])) @@ -1111,22 +1104,22 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) else # divider approach works only with 2d plots divider = mpl_toolkits.axes_grid1.make_axes_locatable(ax) - pos, pad, orientation = if cb_sym === :left + pos, pad, orientation = if cb_sym ≡ :left cb_sym, "5%", "vertical" - elseif cb_sym === :top + elseif cb_sym ≡ :top cb_sym, "2.5%", "horizontal" - elseif cb_sym === :bottom + elseif cb_sym ≡ :bottom cb_sym, "5%", "horizontal" else # :right or :best :right, "2.5%", "vertical" end # Reasonable value works most of the usecases cax = divider.append_axes(string(pos); size = "5%", label, pad) - if cb_sym === :left + if cb_sym ≡ :left cax.yaxis.set_ticks_position("left") - elseif cb_sym === :right + elseif cb_sym ≡ :right cax.yaxis.set_ticks_position("right") - elseif cb_sym === :top + elseif cb_sym ≡ :top cax.xaxis.set_ticks_position("top") else # :bottom or :best cax.xaxis.set_ticks_position("bottom") @@ -1143,7 +1136,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) ) # cbar.formatter.set_useOffset(false) # this for some reason does not work, must be a pyplot bug, instead this is a workaround: - cbar_scale === :identity && cbar.formatter.set_powerlimits((-Inf, Inf)) + cbar_scale ≡ :identity && cbar.formatter.set_powerlimits((-Inf, Inf)) cbar.update_ticks() ticks = get_colorbar_ticks(sp) @@ -1153,8 +1146,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) yaxis, cbar.ax.yaxis, :y # colorbar inherits from y axis end _py_set_scale(cbar.ax, sp, sp[:colorbar_scale], ticks_letter) - sp[:colorbar_ticks] === :native || - _py_set_ticks(sp, cbar.ax, ticks, ticks_letter) + sp[:colorbar_ticks] ≡ :native || _py_set_ticks(sp, cbar.ax, ticks, ticks_letter) for lab in cbar_axis.get_ticklabels() lab.set_fontsize(_py_thickness_scale(plt, sp[:colorbar_tickfontsize])) @@ -1168,9 +1160,9 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) # Adjust thickness of the cbar ticks intensity = 0.5 cbar_axis.set_tick_params( - direction = axis[:tick_direction] === :out ? "out" : "in", + direction = axis[:tick_direction] ≡ :out ? "out" : "in", width = _py_thickness_scale(plt, intensity), - length = axis[:tick_direction] === :none ? 0 : + length = axis[:tick_direction] ≡ :none ? 0 : 5_py_thickness_scale(plt, intensity), ) @@ -1188,7 +1180,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) end # Then set visible some of them - if framestyle === :semi + if framestyle ≡ :semi intensity = 0.5 pyspine = getproperty(ax.spines, yaxis[:mirror] ? "left" : "right") @@ -1198,7 +1190,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) pyspine = getproperty(ax.spines, xaxis[:mirror] ? "bottom" : "top") pyspine.set_linewidth(_py_thickness_scale(plt, intensity)) pyspine.set_alpha(intensity) - elseif framestyle === :box + elseif framestyle ≡ :box ax.tick_params(top = true) # Add ticks too ax.tick_params(right = true) # Add ticks too elseif framestyle ∈ (:axes, :origin) @@ -1206,13 +1198,13 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) (xaxis[:mirror] ? "bottom" : "top", yaxis[:mirror] ? "left" : "right") getproperty(ax.spines, loc).set_visible(false) end - if framestyle === :origin + if framestyle ≡ :origin ax.spines.bottom.set_position("zero") ax.spines.left.set_position("zero") end elseif framestyle ∈ (:grid, :none, :zerolines) _py_hide_spines(ax) - if framestyle === :zerolines + if framestyle ≡ :zerolines ax.axhline( y = 0, color = _py_color(xaxis[:foreground_color_axis]), @@ -1228,12 +1220,12 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) if xaxis[:mirror] ax.xaxis.set_label_position("top") # the guides - framestyle === :box || ax.xaxis.tick_top() + framestyle ≡ :box || ax.xaxis.tick_top() end if yaxis[:mirror] ax.yaxis.set_label_position("right") # the guides - framestyle === :box || ax.yaxis.tick_right() + framestyle ≡ :box || ax.yaxis.tick_right() end end @@ -1244,21 +1236,21 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) axis = sp[axissym] pyaxis = getproperty(ax, axissym) - if axis[:guide_position] !== :auto && letter !== :z + if axis[:guide_position] ≢ :auto && letter ≢ :z pyaxis.set_label_position(string(axis[:guide_position])) end _py_set_scale(ax, sp, axis) _py_set_lims(ax, sp, axis) - (ispolar(sp) && letter === :y) && ax.set_rlabel_position(90) - ticks = framestyle === :none ? nothing : get_ticks(sp, axis) + (ispolar(sp) && letter ≡ :y) && ax.set_rlabel_position(90) + ticks = framestyle ≡ :none ? nothing : get_ticks(sp, axis) - has_major_ticks = ticks !== :none && ticks !== nothing && ticks !== false - has_major_ticks &= if (ttype = ticks_type(ticks)) === :ticks + has_major_ticks = ticks ≢ :none && ticks ≢ nothing && ticks ≢ false + has_major_ticks &= if (ttype = ticks_type(ticks)) ≡ :ticks length(ticks) > 0 - elseif ttype === :ticks_and_labels + elseif ttype ≡ :ticks_and_labels tcs, labs = ticks - if framestyle === :origin + if framestyle ≡ :origin # don't show the 0 tick label for the origin framestyle labs[tcs .== 0] .= "" end @@ -1271,7 +1263,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) intensity = 0.5 # this value corresponds to scaling of other grid elements length_factor = 6 # arbitrary factor (closest to mpl examples) - if axis[:ticks] === :native # it is easier to reset than to account for this + if axis[:ticks] ≡ :native # it is easier to reset than to account for this _py_set_lims(ax, sp, axis) pyaxis.set_major_locator(mpl.ticker.AutoLocator()) pyaxis.set_major_formatter(mpl.ticker.ScalarFormatter()) @@ -1293,9 +1285,9 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) _py_set_ticks(sp, ax, ticks, letter) pyaxis.set_tick_params( - direction = axis[:tick_direction] === :out ? "out" : "in", + direction = axis[:tick_direction] ≡ :out ? "out" : "in", width = _py_thickness_scale(plt, intensity), - length = axis[:tick_direction] === :none ? 0 : + length = axis[:tick_direction] ≡ :none ? 0 : length_factor * _py_thickness_scale(plt, intensity), ) else @@ -1312,7 +1304,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) RecipesPipeline.is3d(sp) && pyaxis.set_rotate_label(false) axis[:flip] && getproperty(ax, Symbol(:invert_, letter, :axis))() - axis[:guidefontrotation] + if letter === :y && !RecipesPipeline.is3d(sp) + axis[:guidefontrotation] + if letter ≡ :y && !RecipesPipeline.is3d(sp) 90 else 0 @@ -1334,19 +1326,19 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) # minorticks if !no_minor_intervals(axis) && has_major_ticks ax.minorticks_on() - n_minor_intervals = PlotsBase.num_minor_intervals(axis) - if (scale = axis[:scale]) === :identity + n_minor_intervals = num_minor_intervals(axis) + if (scale = axis[:scale]) ≡ :identity mpl.ticker.AutoMinorLocator(n_minor_intervals) else mpl.ticker.LogLocator( - base = _logScaleBases[scale], + base = Commons._log_scale_bases[scale], subs = 1:n_minor_intervals, ) end |> pyaxis.set_minor_locator pyaxis.set_tick_params( which = "minor", - direction = axis[:tick_direction] === :out ? "out" : "in", - length = axis[:tick_direction] === :none ? 0 : + direction = axis[:tick_direction] ≡ :out ? "out" : "in", + length = axis[:tick_direction] ≡ :none ? 0 : 0.5length_factor * _py_thickness_scale(plt, intensity), ) end @@ -1369,7 +1361,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) ispolar(sp) && ax.spines.polar.set_visible(false) for dir in (:top, :bottom) ispolar(sp) || getproperty(ax.spines, string(dir)).set_visible(false) - kw[dir] = kw[get_attr_symbol(:label, dir)] = false + kw[dir] = kw[get_attr_symbol(dir, :label)] = false end ax.xaxis.set_tick_params(; which = "both", kw...) end @@ -1377,17 +1369,17 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) kw = KW() for dir in (:left, :right) ispolar(sp) || getproperty(ax.spines, string(dir)).set_visible(false) - kw[dir] = kw[get_attr_symbol(:label, dir)] = false + kw[dir] = kw[get_attr_symbol(dir, :label)] = false end ax.yaxis.set_tick_params(; which = "both", kw...) end # aspect ratio - if (ratio = get_aspect_ratio(sp)) !== :none + if (ratio = get_aspect_ratio(sp)) ≢ :none if RecipesPipeline.is3d(sp) - if ratio === :auto + if ratio ≡ :auto nothing - elseif ratio === :equal + elseif ratio ≡ :equal ax.set_box_aspect((1, 1, 1)) else ax.set_box_aspect(ratio) @@ -1430,10 +1422,10 @@ expand_padding!(padding, bb, plotbb) = padding[4] = max(padding[4], bottom(bb) - bottom(plotbb)) end -# Set the (left, top, right, bottom) minimum padding around the plot area +# set the (left, top, right, bottom) minimum padding around the plot area # to fit ticks, tick labels, guides, colorbars, etc. function PlotsBase._update_min_padding!(sp::Subplot{PythonPlotBackend}) - (ax = sp.o) === nothing && return sp.minpad + (ax = sp.o) ≡ nothing && return sp.minpad plotbb = _py_bbox(ax) # TODO: this should initialize to the margin from sp.attr @@ -1482,8 +1474,8 @@ _py_add_annotations(sp::Subplot{PythonPlotBackend}, x, y, val::PlotText) = sp.o. val.str, xy = (x, y), size = _py_thickness_scale(sp.plt, val.font.pointsize), - horizontalalignment = val.font.halign === :hcenter ? "center" : string(val.font.halign), - verticalalignment = val.font.valign === :vcenter ? "center" : string(val.font.valign), + horizontalalignment = val.font.halign ≡ :hcenter ? "center" : string(val.font.halign), + verticalalignment = val.font.valign ≡ :vcenter ? "center" : string(val.font.valign), color = _py_color(val.font.color), rotation = val.font.rotation, family = val.font.family, @@ -1497,8 +1489,8 @@ _py_add_annotations(sp::Subplot{PythonPlotBackend}, x, y, z, val::PlotText) = sp z, val.str; size = _py_thickness_scale(sp.plt, val.font.pointsize), - horizontalalignment = val.font.halign === :hcenter ? "center" : string(val.font.halign), - verticalalignment = val.font.valign === :vcenter ? "center" : string(val.font.valign), + horizontalalignment = val.font.halign ≡ :hcenter ? "center" : string(val.font.halign), + verticalalignment = val.font.valign ≡ :vcenter ? "center" : string(val.font.valign), color = _py_color(val.font.color), rotation = val.font.rotation, family = val.font.family, @@ -1510,7 +1502,7 @@ _py_add_annotations(sp::Subplot{PythonPlotBackend}, x, y, z, val::PlotText) = sp _py_legend_pos(pos::Tuple{S,T}) where {S<:Real,T<:Real} = "lower left" function _py_legend_pos(pos::Tuple{<:Real,Symbol}) - s, c = sincosd(pos[1]) .* (pos[2] === :outer ? -1 : 1) + s, c = sincosd(pos[1]) .* (pos[2] ≡ :outer ? -1 : 1) yanchors = "lower", "center", "upper" xanchors = "left", "center", "right" let lac = PlotsBase.legend_anchor_index @@ -1524,7 +1516,7 @@ _py_legend_bbox(pos::Tuple{<:Real,Symbol}) = _py_legend_bbox(pos) = pos function _py_add_legend(plt::Plot, sp::Subplot, ax) - (leg = sp[:legend_position]) === :none && return + (leg = sp[:legend_position]) ≡ :none && return # gotta do this to ensure both axes are included labels, handles = [], [] @@ -1536,7 +1528,7 @@ function _py_add_legend(plt::Plot, sp::Subplot, ax) clims = get_clims(sp, series) nseries += 1 # add a line/marker and a label - if series[:seriestype] === :shape || series[:fillrange] !== nothing + if series[:seriestype] ≡ :shape || series[:fillrange] ≢ nothing lc = get_linecolor(series, clims) fc = get_fillcolor(series, clims) la = get_linealpha(series) @@ -1637,7 +1629,7 @@ function _py_add_legend(plt::Plot, sp::Subplot, ax) ) leg.get_frame().set_linewidth(_py_thickness_scale(plt, 1)) leg.set_zorder(1_000) - if sp[:legend_title] !== nothing + if sp[:legend_title] ≢ nothing leg.set_title(string(sp[:legend_title])) PythonPlot.setp( leg.get_title(), @@ -1664,7 +1656,7 @@ end # position the subplot in the backend. function PlotsBase._update_plot_object(plt::Plot{PythonPlotBackend}) for sp in plt.subplots - (ax = sp.o) === nothing && return + (ax = sp.o) ≡ nothing && return figw, figh = sp.plt[:size] .* px # ax.set_position signature: `[left, bottom, width, height]` diff --git a/PlotsBase/ext/UnitfulExt.jl b/PlotsBase/ext/UnitfulExt.jl index eeb7e4a86..cbaace87f 100644 --- a/PlotsBase/ext/UnitfulExt.jl +++ b/PlotsBase/ext/UnitfulExt.jl @@ -232,7 +232,7 @@ append_unit_if_needed!(attr, key, u) = append_unit_if_needed!(attr, key, label::ProtectedString, u) = nothing append_unit_if_needed!(attr, key, label::UnitfulString, u) = nothing function append_unit_if_needed!(attr, key, label::Nothing, u) - attr[key] = if attr[:plot_object].backend == PlotsBase._backend_instance(:pgfplotsx) + attr[key] = if attr[:plot_object].backend == PlotsBase.backend_instance(:pgfplotsx) UnitfulString(LaTeXString(Latexify.latexify(u)), u) else UnitfulString(string(u), u) @@ -240,7 +240,7 @@ function append_unit_if_needed!(attr, key, label::Nothing, u) end function append_unit_if_needed!(attr, key, label::S, u) where {S<:AbstractString} isempty(label) && return attr[key] = UnitfulString(label, u) - if attr[:plot_object].backend == PlotsBase._backend_instance(:pgfplotsx) + if attr[:plot_object].backend == PlotsBase.backend_instance(:pgfplotsx) attr[key] = UnitfulString( LaTeXString( format_unit_label( diff --git a/PlotsBase/src/Commons/postprocess_attrs.jl b/PlotsBase/src/Commons/postprocess_attrs.jl index f6cfad022..b08561b92 100644 --- a/PlotsBase/src/Commons/postprocess_attrs.jl +++ b/PlotsBase/src/Commons/postprocess_attrs.jl @@ -1,21 +1,24 @@ # add all pluralized forms to the _keyAliases dict -for arg in _all_attrs - add_aliases(arg, makeplural(arg)) -end +foreach(arg -> add_aliases(arg, makeplural(arg)), _all_attrs) + +add_attr_dict!(letter::Symbol) = get!(_attrsymbolcache, letter, Dict{Symbol,Symbol}()) +add_attr!(letter::Symbol, keyword::Symbol) = + let letter_keyword = Symbol(letter, keyword) + _attrsymbolcache[letter][keyword] = letter_keyword + end # fill symbol cache for letter in (:x, :y, :z) - _attrsymbolcache[letter] = Dict{Symbol,Symbol}() - for k in _axis_attrs + add_attr_dict!(letter) + for keyword in _axis_attrs # populate attribute cache - lk = Symbol(letter, k) - _attrsymbolcache[letter][k] = lk + letter_keyword = add_attr!(letter, keyword) # allow the underscore version too: xguide or x_guide - add_aliases(lk, Symbol(letter, "_", k)) + add_aliases(letter_keyword, Symbol(letter, "_", keyword)) end - for k in (_magic_axis_attrs..., :(_discrete_indices)) - _attrsymbolcache[letter][k] = Symbol(letter, k) + for keyword in (_magic_axis_attrs..., :(_discrete_indices)) + _attrsymbolcache[letter][keyword] = Symbol(letter, keyword) end end diff --git a/PlotsBase/src/Ticks.jl b/PlotsBase/src/Ticks.jl index af37828f5..0f7aaf879 100644 --- a/PlotsBase/src/Ticks.jl +++ b/PlotsBase/src/Ticks.jl @@ -1,6 +1,7 @@ module Ticks -export get_ticks, _has_ticks, _transform_ticks, get_minor_ticks, no_minor_intervals +export get_ticks, + _has_ticks, _transform_ticks, get_minor_ticks, no_minor_intervals, num_minor_intervals using PlotsBase.Commons using PlotsBase.Dates diff --git a/PlotsBase/src/abstract_backend.jl b/PlotsBase/src/abstract_backend.jl index cc03b9511..987e8e303 100644 --- a/PlotsBase/src/abstract_backend.jl +++ b/PlotsBase/src/abstract_backend.jl @@ -6,7 +6,7 @@ const _backendSymbol = Dict{DataType,Symbol}(NoBackend => :none) const _backendType = Dict{Symbol,DataType}(:none => NoBackend) const _backend_packages = (gaston = :Gaston, gr = :GR, unicodeplots = :UnicodePlots, pgfplotsx = :PGFPlotsX, pythonplot = :PythonPlot, plotly = nothing, plotlyjs = :PlotlyJS, hdf5 = :HDF5) const _initialized_backends = Set{Symbol}() -const _backends = keys(_backend_packages) +const _supported_backends = keys(_backend_packages) const _plots_deps = let toml = Pkg.TOML.parsefile(normpath(@__DIR__, "..", "Project.toml")) merge(toml["deps"], toml["extras"]) @@ -28,12 +28,12 @@ function _check_installed(backend::Union{Module,AbstractString,Symbol}; warn = t end # check installed pkg_id = Base.identify_package(str) - version = if pkg_id === nothing + version = if pkg_id ≡ nothing nothing else get(Pkg.dependencies(), pkg_id.uuid, (; version = nothing)).version end - version === nothing && @warn "backend `$str` is not installed." + version ≡ nothing && @warn "backend `$str` is not installed." version end @@ -55,7 +55,7 @@ mutable struct CurrentBackend pkg::AbstractBackend end -CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym)) +CurrentBackend(sym::Symbol) = CurrentBackend(sym, backend_instance(sym)) """ Returns the current plotting package name. Initializes package on first call. @@ -63,10 +63,10 @@ Returns the current plotting package name. Initializes package on first call. backend() = CURRENT_BACKEND.pkg "Returns a list of supported backends" -backends() = _backends +backends() = _supported_backends backend_name() = CURRENT_BACKEND.sym -_backend_instance(sym::Symbol)::AbstractBackend = _backendType[sym]() +backend_instance(sym::Symbol) = _backendType[sym]() backend_package_name(sym::Symbol = backend_name()) = get(_backend_packages, sym, nothing) @@ -82,22 +82,18 @@ Set the plot backend. """ function backend(pkg::AbstractBackend) sym = backend_name(pkg) - if !initialized(sym) - _initialize_backend(pkg) - push!(_initialized_backends, sym) - end CURRENT_BACKEND.sym = sym CURRENT_BACKEND.pkg = pkg pkg end backend(sym::Symbol) = - if sym in _backends + if sym in _supported_backends if initialized(sym) - backend(_backend_instance(sym)) + backend(backend_instance(sym)) else name = backend_package_name(sym) - @warn "`:$sym` is not initialized, import it first to trigger the extension --- e.g. $(name === nothing ? '`' : string("`import ", name, ";")) $sym()`." + @warn "`:$sym` is not initialized, import it first to trigger the extension --- e.g. $(name ≡ nothing ? '`' : string("`import ", name, ";")) $sym()`." backend() end else @@ -117,7 +113,7 @@ end # -- Create backend init functions by hand as the corresponding structs do not # exist yet -for be in _backends +for be in _supported_backends @eval begin function $be(; kw...) default(; reset = false, kw...) @@ -190,11 +186,11 @@ macro extension_static(be_type, be) quote $(PlotsBase.backend_defines(be_type, be)) function __init__() - ccall(:jl_generating_output, Cint, ()) == 1 && return PlotsBase._backendType[$be_sym] = $be_type PlotsBase._backendSymbol[$be_type] = $be_sym push!(PlotsBase._initialized_backends, $be_sym) - Base.invokelatest(PlotsBase.extension_init, $be_type()) + # ccall(:jl_generating_output, Cint, ()) == 1 && return + PlotsBase.extension_init($be_type()) @debug "Initialized $be_type backend in PlotsBase; run `$be()` to activate it." end end |> esc diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index a890c0f54..68b777b0c 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -226,8 +226,8 @@ plotly_annotation_dict(x, y, ptxt::PlotText; xref = "paper", yref = "paper") = m plotly_annotation_dict(x, y, ptxt.str; xref = xref, yref = yref), KW( :font => plotly_font(ptxt.font), - :xanchor => ptxt.font.halign === :hcenter ? :center : ptxt.font.halign, - :yanchor => ptxt.font.valign === :vcenter ? :middle : ptxt.font.valign, + :xanchor => ptxt.font.halign ≡ :hcenter ? :center : ptxt.font.halign, + :yanchor => ptxt.font.valign ≡ :vcenter ? :middle : ptxt.font.valign, :rotation => -ptxt.font.rotation, ), ) @@ -244,13 +244,13 @@ plotly_annotation_dict( plotly_annotation_dict(x, y, z, ptxt.str; xref = xref, yref = yref, zref = zref), KW( :font => plotly_font(ptxt.font), - :xanchor => ptxt.font.halign === :hcenter ? :center : ptxt.font.halign, - :yanchor => ptxt.font.valign === :vcenter ? :middle : ptxt.font.valign, + :xanchor => ptxt.font.halign ≡ :hcenter ? :center : ptxt.font.halign, + :yanchor => ptxt.font.valign ≡ :vcenter ? :middle : ptxt.font.valign, :rotation => -ptxt.font.rotation, ), ) -plotly_scale(scale::Symbol) = scale === :log10 ? "log" : "-" +plotly_scale(scale::Symbol) = scale ≡ :log10 ? "log" : "-" function shrink_by(lo, sz, ratio) amt = 0.5(1 - ratio) * sz @@ -258,8 +258,8 @@ function shrink_by(lo, sz, ratio) end function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts) - if (aspect_ratio = get_aspect_ratio(sp)) !== :none - aspect_ratio === :equal && (aspect_ratio = 1.0) + if (aspect_ratio = get_aspect_ratio(sp)) ≢ :none + aspect_ratio ≡ :equal && (aspect_ratio = 1.0) xmin, xmax = axis_limits(sp, :x) ymin, ymax = axis_limits(sp, :y) want_ratio = ((xmax - xmin) / (ymax - ymin)) / aspect_ratio @@ -298,30 +298,30 @@ function plotly_axis(axis, sp, anchor = nothing, domain = nothing) letter = axis[:letter] framestyle = sp[:framestyle] ax = KW( - :visible => framestyle !== :none, + :visible => framestyle ≢ :none, :title => axis[:guide], :showgrid => axis[:grid], :gridcolor => rgba_string(plot_color(axis[:foreground_color_grid], axis[:gridalpha])), :gridwidth => axis[:gridlinewidth], - :zeroline => framestyle === :zerolines, + :zeroline => framestyle ≡ :zerolines, :zerolinecolor => rgba_string(axis[:foreground_color_axis]), :showline => framestyle in (:box, :axes) && axis[:showaxis], :linecolor => rgba_string(plot_color(axis[:foreground_color_axis])), :ticks => - axis[:tick_direction] === :out ? "outside" : - axis[:tick_direction] === :in ? "inside" : "", - :mirror => framestyle === :box, + axis[:tick_direction] ≡ :out ? "outside" : + axis[:tick_direction] ≡ :in ? "inside" : "", + :mirror => framestyle ≡ :box, :showticklabels => axis[:showaxis], ) - anchor === nothing || (ax[:anchor] = anchor) - domain === nothing || (ax[:domain] = domain) + anchor ≡ nothing || (ax[:anchor] = anchor) + domain ≡ nothing || (ax[:domain] = domain) ax[:tickangle] = -axis[:rotation] ax[:type] = plotly_scale(axis[:scale]) lims = axis_limits(sp, letter) - if axis[:ticks] !== :native || axis[:lims] !== :auto + if axis[:ticks] ≢ :native || axis[:lims] ≢ :auto ax[:range] = map(RecipesPipeline.scale_func(axis[:scale]), lims) end @@ -334,13 +334,13 @@ function plotly_axis(axis, sp, anchor = nothing, domain = nothing) ax[:linecolor] = rgba_string(axis[:foreground_color_axis]) # ticks - if axis[:ticks] !== :native + if axis[:ticks] ≢ :native ticks = PlotsBase.get_ticks(sp, axis) ttype = PlotsBase.ticks_type(ticks) - if ttype === :ticks + if ttype ≡ :ticks ax[:tickmode] = "array" ax[:tickvals] = ticks - elseif ttype === :ticks_and_labels + elseif ttype ≡ :ticks_and_labels ax[:tickmode] = "array" ax[:tickvals], ax[:ticktext] = ticks end @@ -359,7 +359,7 @@ end function plotly_polaraxis(sp::Subplot, axis::Axis) ax = KW(:visible => axis[:showaxis], :showline => axis[:grid]) - if axis[:letter] === :x + if axis[:letter] ≡ :x ax[:range] = rad2deg.(axis_limits(sp, :x)) else ax[:range] = axis_limits(sp, :y) @@ -388,13 +388,13 @@ function plotly_layout(plt::Plot) if sp[:title] != "" bb = plotarea(sp) tpos = sp[:titlelocation] - if tpos === :left + if tpos ≡ :left xmm, ymm = left(bb), top(bbox(sp)) halign, valign = :left, :top - elseif tpos === :center + elseif tpos ≡ :center xmm, ymm = 0.5(left(bb) + right(bb)), top(bbox(sp)) halign, valign = :hcenter, :top - elseif tpos === :right + elseif tpos ≡ :right xmm, ymm = right(bb), top(bbox(sp)) halign, valign = :right, :top else @@ -515,9 +515,9 @@ function plotly_layout(plt::Plot) end function plotly_add_legend!(plotattributes_out::KW, sp::Subplot) - plotattributes_out[:showlegend] = sp[:legend_position] !== :none + plotattributes_out[:showlegend] = sp[:legend_position] ≢ :none legend_position = plotly_legend_pos(sp[:legend_position]) - sp[:legend_position] === :none && return + sp[:legend_position] ≡ :none && return plotattributes_out[:legend] = KW( :bgcolor => rgba_string(sp[:legend_background_color]), :bordercolor => rgba_string(sp[:legend_foreground_color]), @@ -530,7 +530,7 @@ function plotly_add_legend!(plotattributes_out::KW, sp::Subplot) :x => legend_position.coords[1], :y => legend_position.coords[2], :title => KW( - :text => sp[:legend_title] === nothing ? "" : string(sp[:legend_title]), + :text => sp[:legend_title] ≡ nothing ? "" : string(sp[:legend_title]), :font => plotly_font(legendtitlefont(sp)), ), ) @@ -590,7 +590,7 @@ function plotly_legend_pos(v::Tuple{S,Symbol}) where {S<:Real} xanchors = ["left", "center", "right"] yanchors = ["bottom", "middle", "top"] - if v[2] === :inner + if v[2] ≡ :inner rect = 0.07, 0.5, 1.0, 0.07, 0.52, 1.0 xanchor = xanchors[legend_anchor_index(c)] yanchor = yanchors[legend_anchor_index(s)] @@ -669,7 +669,7 @@ end function plotly_data(series::Series, letter::Symbol, data) axis = series[:subplot][get_attr_symbol(letter, :axis)] - data = if axis[:ticks] === :native && data !== nothing + data = if axis[:ticks] ≡ :native && data ≢ nothing plotly_native_data(axis, data) else data @@ -681,7 +681,7 @@ function plotly_data(series::Series, letter::Symbol, data) plotly_data(data) end end -plotly_data(v) = v !== nothing ? collect(v) : v +plotly_data(v) = v ≢ nothing ? collect(v) : v plotly_data(v::AbstractArray) = v plotly_data(surf::Surface) = surf.surf plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v) @@ -721,7 +721,7 @@ function plotly_series(plt::Plot, series::Series) sp = series[:subplot] clims = get_clims(sp, series) - (st = series[:seriestype]) === :shape && return plotly_series_shapes(plt, series, clims) + (st = series[:seriestype]) ≡ :shape && return plotly_series_shapes(plt, series, clims) plotattributes_out = KW() @@ -739,7 +739,7 @@ function plotly_series(plt::Plot, series::Series) end plotattributes_out[:showlegend] = should_add_to_legend(series) - if st === :straightline + if st ≡ :straightline x, y = straightline_data(series, 100) z = series[:z] else @@ -754,7 +754,7 @@ function plotly_series(plt::Plot, series::Series) plotattributes_out[:name] = series[:label] isscatter = st in (:scatter, :scatter3d, :scattergl) - hasmarker = isscatter || series[:markershape] !== :none + hasmarker = isscatter || series[:markershape] ≢ :none hasline = st in (:path, :path3d, :straightline) hasfillrange = st in (:path, :scatter, :scattergl, :straightline) && @@ -770,7 +770,7 @@ function plotly_series(plt::Plot, series::Series) if st in (:path, :scatter, :scattergl, :straightline, :path3d, :scatter3d) return plotly_series_segments(series, plotattributes_out, x, y, z, clims) - elseif st === :heatmap + elseif st ≡ :heatmap x = heatmap_edges(x, sp[:xaxis][:scale]) y = heatmap_edges(y, sp[:yaxis][:scale]) plotattributes_out[:type] = "heatmap" @@ -779,7 +779,7 @@ function plotly_series(plt::Plot, series::Series) plotly_colorscale(series[:fillcolor], series[:fillalpha]) plotattributes_out[:showscale] = hascolorbar(sp) - elseif st === :contour + elseif st ≡ :contour filled = isfilledcontour(series) plotattributes_out[:type] = "contour" plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z @@ -818,7 +818,7 @@ function plotly_series(plt::Plot, series::Series) elseif st in (:surface, :wireframe) plotattributes_out[:type] = "surface" plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z - if st === :wireframe + if st ≡ :wireframe plotattributes_out[:hidesurface] = true wirelines = KW( :show => true, @@ -833,16 +833,16 @@ function plotly_series(plt::Plot, series::Series) plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha]) plotattributes_out[:opacity] = series[:fillalpha] - if series[:fill_z] !== nothing + if series[:fill_z] ≢ nothing plotattributes_out[:surfacecolor] = handle_surface(series[:fill_z]) end plotattributes_out[:showscale] = hascolorbar(sp) end - elseif st === :mesh3d + elseif st ≡ :mesh3d plotattributes_out[:type] = "mesh3d" plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z - if series[:connections] !== nothing + if series[:connections] ≢ nothing if typeof(series[:connections]) <: Tuple{Array,Array,Array} # 0-based indexing i, j, k = series[:connections] @@ -879,7 +879,7 @@ function plotly_series(plt::Plot, series::Series) plotattributes_out[:color] = rgba_string(plot_color(series[:fillcolor], series[:fillalpha])) plotattributes_out[:opacity] = series[:fillalpha] - if series[:fill_z] !== nothing + if series[:fill_z] ≢ nothing plotattributes_out[:surfacecolor] = handle_surface(series[:fill_z]) end plotattributes_out[:showscale] = hascolorbar(sp) @@ -986,11 +986,11 @@ function plotly_series_shapes(plt::Plot, series::Series, clims) plotly_adjust_hover_label!(plotattributes_out, _cycle(series[:hover], i)) plotattributes_outs[k] = merge(plotattributes_out, series[:extra_kwargs]) end - if series[:fill_z] !== nothing + if series[:fill_z] ≢ nothing push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill)) - elseif series[:line_z] !== nothing + elseif series[:line_z] ≢ nothing push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line)) - elseif series[:marker_z] !== nothing + elseif series[:marker_z] ≢ nothing push!( plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker), @@ -1003,7 +1003,7 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z st = series[:seriestype] sp = series[:subplot] isscatter = st in (:scatter, :scatter3d, :scattergl) - hasmarker = isscatter || series[:markershape] !== :none + hasmarker = isscatter || series[:markershape] ≢ :none hasline = st in (:path, :path3d, :straightline) hasfillrange = st in (:path, :scatter, :scattergl, :straightline) && @@ -1023,7 +1023,7 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z # set the type if st in (:path, :scatter, :scattergl, :straightline) - plotattributes_out[:type] = st === :scattergl ? "scattergl" : "scatter" + plotattributes_out[:type] = st ≡ :scattergl ? "scattergl" : "scatter" plotattributes_out[:mode] = if hasmarker hasline ? "lines+markers" : "markers" else @@ -1062,7 +1062,7 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z mcolor = rgba_string( plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i)), ) - mcolor_next = if (mz = series[:marker_z]) !== nothing && i < length(mz) + mcolor_next = if (mz = series[:marker_z]) ≢ nothing && i < length(mz) plot_color( get_markercolor(series, clims, i + 1), get_markeralpha(series, i + 1), @@ -1104,11 +1104,11 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z plot_color(get_linecolor(series, clims, i), get_linealpha(series, i)), ), :width => get_linewidth(series, i), - :shape => if st === :steppre + :shape => if st ≡ :steppre "vh" - elseif st === :stepmid + elseif st ≡ :stepmid "hvh" - elseif st === :steppost + elseif st ≡ :steppost "hv" else "linear" @@ -1159,11 +1159,11 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z plotattributes_outs[k] = merge(plotattributes_outs[k], series[:extra_kwargs]) end - if series[:line_z] !== nothing + if series[:line_z] ≢ nothing push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line)) - elseif series[:fill_z] !== nothing + elseif series[:fill_z] ≢ nothing push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill)) - elseif series[:marker_z] !== nothing + elseif series[:marker_z] ≢ nothing push!( plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker), @@ -1206,7 +1206,7 @@ plotly_polar!(plotattributes_out::KW, series::Series) = end function plotly_adjust_hover_label!(plotattributes_out::KW, hover) - if hover === nothing + if hover ≡ nothing return elseif all(in([:none, false]), hover) plotattributes_out[:hoverinfo] = "none" @@ -1263,7 +1263,7 @@ function plotly_html_head(plt::Plot) end function plotly_html_body(plt, style = nothing) - if style === nothing + if style ≡ nothing w, h = plt[:size] style = "width:$(w)px;height:$(h)px;" end diff --git a/PlotsBase/src/backends/web.jl b/PlotsBase/src/backends/web.jl index cfa7e454c..2cd82d1e0 100644 --- a/PlotsBase/src/backends/web.jl +++ b/PlotsBase/src/backends/web.jl @@ -46,7 +46,7 @@ function standalone_html_window(plt::AbstractPlot) # if we open a browser ourself, we can host local files, so # when we have a local plotly downloaded this is the way to go! _use_local_dependencies[] = - _plotly_local_file_path[] === nothing ? false : isfile(_plotly_local_file_path[]) + _plotly_local_file_path[] ≡ nothing ? false : isfile(_plotly_local_file_path[]) filename = write_temp_html(plt) open_browser_window(filename) # restore for other backends diff --git a/PlotsBase/src/recipes.jl b/PlotsBase/src/recipes.jl index 0431d2826..920b7a474 100644 --- a/PlotsBase/src/recipes.jl +++ b/PlotsBase/src/recipes.jl @@ -29,7 +29,7 @@ end function all_seriestypes() sts = Set{Symbol}(keys(_series_recipe_deps)) for bsym in _initialized_backends - be = _backend_instance(bsym) + be = backend_instance(bsym) sts = union(sts, Set{Symbol}(supported_seriestypes(be))) end sts |> collect |> sort diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 029a8faf4..2cae816f3 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -1,24 +1,23 @@ const TEST_PACKAGES = - strip.( - split( - get( - ENV, - "PLOTSBASE_TEST_PACKAGES", - "GR,UnicodePlots,PythonPlot,PGFPlotsX,PlotlyJS,Gaston", - ), - ",", + let val = get( + ENV, + "PLOTSBASE_TEST_PACKAGES", + "GR,UnicodePlots,PythonPlot,PGFPlotsX,PlotlyJS,Gaston", ) - ) + strip.(split(val, ",")) + end const TEST_BACKENDS = Symbol.(lowercase.(TEST_PACKAGES)) using PlotsBase +import GR +gr() + # initialize all backends for pkg in TEST_PACKAGES @eval import $(Symbol(pkg)) # trigger extension getproperty(PlotsBase, Symbol(lowercase(pkg)))() end -gr() import Unitful: m, s, cm, DimensionError import PlotsBase: PLOTS_SEED, Plot, with @@ -47,25 +46,38 @@ is_ci() = PlotsBase.bool_env("CI") is_ci() || @eval using Gtk # see JuliaPlots/VisualRegressionTests.jl/issues/30 +ref_name(i) = "ref" * lpad(i, 3, '0') + +const blacklist = if VERSION.major == 1 && VERSION.minor ≥ 9 + [ + 25, + 30, # FIXME: remove, when StatsPlots supports Plots v2 + 41, + ] # FIXME: github.com/JuliaLang/julia/issues/47261 +else + [] +end + for name in ( - "quality", - "misc", - "utils", - "args", - "defaults", - "dates", - "axes", - "layouts", - "contours", - "components", - "shorthands", - "recipes", - "unitful", - "hdf5plots", - "pgfplotsx", - "plotly", - "animations", - "output", + # "quality", + # "misc", + # "utils", + # "args", + # "defaults", + # "dates", + # "axes", + # "layouts", + # "contours", + # "components", + # "shorthands", + # "recipes", + # "unitful", + # "hdf5plots", + # "pgfplotsx", + # "plotly", + # "animations", + # "output", + # "reference", "backends", ) @testset "$name" begin diff --git a/PlotsBase/test/test_backends.jl b/PlotsBase/test/test_backends.jl index b30111944..cd737ba9a 100644 --- a/PlotsBase/test/test_backends.jl +++ b/PlotsBase/test/test_backends.jl @@ -1,223 +1,8 @@ -ci_tol() = - if Sys.islinux() - is_pkgeval() ? "1e-2" : "5e-4" - elseif Sys.isapple() - "1e-3" - else - "1e-1" - end - -const TESTS_MODULE = Module(:PlotsBaseTestModule) -const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? ci_tol() : "1e-5")) - -Base.eval(TESTS_MODULE, :(using Random, StableRNGs, PlotsBase)) - -reference_dir(args...) = - if (ref_dir = get(ENV, "PLOTS_REFERENCE_DIR", nothing)) !== nothing - ref_dir - else - joinpath(homedir(), ".julia", "dev", "PlotReferenceImages.jl", args...) - end -reference_path(backend, version) = reference_dir("Plots", string(backend), string(version)) - -function checkout_reference_dir(dn::AbstractString) - mkpath(dn) - local repo - for i in 1:6 - try - repo = LibGit2.clone( - "https://github.com/JuliaPlots/PlotReferenceImages.jl.git", - dn, - ) - break - catch err - @warn err - sleep(20i) - end - end - if (ver = PlotsBase._current_plots_version).prerelease |> isempty - try - tag = LibGit2.GitObject(repo, "v$ver") - hash = string(LibGit2.target(tag)) - LibGit2.checkout!(repo, hash) - catch err - @warn err - end - end - LibGit2.peel(LibGit2.head(repo)) |> println # print some information - nothing -end - -let dn = reference_dir() - isdir(dn) || checkout_reference_dir(dn) -end - -ref_name(i) = "ref" * lpad(i, 3, '0') - -function reference_file(backend, version, i) - # NOTE: keep ref[...].png naming consistent with `PlotDocs` - refdir = reference_dir("Plots", string(backend)) - fn = ref_name(i) * ".png" - reffn = joinpath(refdir, string(version), fn) - for ver in sort(VersionNumber.(readdir(refdir)), rev = true) - if (tmpfn = joinpath(refdir, string(ver), fn)) |> isfile - reffn = tmpfn - break - end - end - return reffn -end - -function image_comparison_tests( - pkg::Symbol, - idx::Int; - debug = false, - popup = !is_ci(), - sigma = [1, 1], - tol = 1e-2, -) - example = PlotsBase._examples[idx] - @info "Testing plot: $pkg:$idx:$(example.header)" - - ver = PlotsBase._current_plots_version - ver = VersionNumber(ver.major, ver.minor, ver.patch) - reffn = reference_file(pkg, ver, idx) - newfn = joinpath(reference_path(pkg, ver), ref_name(idx) * ".png") - - imports = something(example.imports, :()) - exprs = quote - PlotsBase.Commons.debug!($debug) - backend($(QuoteNode(pkg))) - theme(:default) - rng = StableRNG(PlotsBase.PLOTS_SEED) - $(PlotsBase.replace_rand(example.exprs)) - end - @debug imports exprs - - func = fn -> Base.eval.(Ref(TESTS_MODULE), (imports, exprs, :(png($fn)))) - test_images( - VisualTest(func, reffn), - newfn = newfn, - popup = popup, - sigma = sigma, - tol = tol, - ) -end - -function image_comparison_facts( - pkg::Symbol; - skip = [], # skip these examples (int index) - only = nothing, # limit to these examples (int index) - debug = false, # print debug information ? - sigma = [1, 1], # number of pixels to "blur" - tol = 1e-2, # acceptable error (percent) -) - for i in setdiff(1:length(PlotsBase._examples), skip) - if only === nothing || i in only - @test success(image_comparison_tests(pkg, i; debug, sigma, tol)) - end - end -end - -## Uncomment the following lines to update reference images for different backends -#= - -with(:gr) do - image_comparison_facts(:gr, tol = PLOTS_IMG_TOL, skip = PlotsBase._backend_skips[:gr]) -end - -with(:plotlyjs) do - image_comparison_facts(:plotlyjs, tol = PLOTS_IMG_TOL, skip = PlotsBase._backend_skips[:plotlyjs]) -end - -with(:pgfplotsx) do - image_comparison_facts(:pgfplotsx, tol = PLOTS_IMG_TOL, skip = PlotsBase._backend_skips[:pgfplotsx]) -end -=# - -@testset "UnicodePlots" begin - with(:unicodeplots) do - @test backend() == PlotsBase._backend_instance(:unicodeplots) - - io = IOContext(IOBuffer(), :color => true) - - # lets just make sure it runs without error - pl = plot(rand(10)) - @test show(io, pl) isa Nothing - - pl = bar(randn(10)) - @test show(io, pl) isa Nothing - - pl = plot([1, 2], [3, 4]) - annotate!(pl, [(1.5, 3.2, PlotsBase.text("Test", :red, :center))]) - hline!(pl, [3.1]) - @test show(io, pl) isa Nothing - - pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) - hline!(pl, [3.1]) - annotate!( - pl, - [(Dates.Date(2019, 1, 15), 3.2, PlotsBase.text("Test", :red, :center))], - ) - @test show(io, pl) isa Nothing - - pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) - annotate!(pl, [(Dates.Date(2019, 1, 15), 3.2, :auto)]) - hline!(pl, [3.1]) - @test show(io, pl) isa Nothing - - pl = plot(map(plot, 1:4)..., layout = (2, 2)) - @test show(io, pl) isa Nothing - - pl = plot(map(plot, 1:3)..., layout = (2, 2)) - @test show(io, pl) isa Nothing - - pl = plot(map(plot, 1:2)..., layout = @layout([° _; _ °])) - @test show(io, pl) isa Nothing - - redirect_stdout(devnull) do - show(plot(1:2)) - end - end -end - -const blacklist = if VERSION.major == 1 && VERSION.minor ∈ (9, 10) - [ - 25, - 30, # FIXME: remove, when StatsPlots supports Plots v2 - 41, - ] # FIXME: github.com/JuliaLang/julia/issues/47261 -else - [] -end - -@testset "GR - reference images" begin - with(:gr) do - # NOTE: use `ENV["VISUAL_REGRESSION_TESTS_AUTO"] = true;` to automatically replace reference images - @test backend() == PlotsBase._backend_instance(:gr) - @test backend_name() === :gr - image_comparison_facts( - :gr, - tol = PLOTS_IMG_TOL, - skip = vcat(PlotsBase._backend_skips[:gr], blacklist), - ) - end -end - -is_pkgeval() || @testset "PlotlyJS" begin - with(:plotlyjs) do - PlotlyJSExt = Base.get_extension(PlotsBase, :PlotlyJSExt) - @test backend() == PlotlyJSExt.PlotlyJSBackend() - pl = plot(rand(10)) - @test pl isa Plot - @test display(pl) isa Nothing - end -end is_pkgeval() || @testset "Examples" begin callback(m, pkgname, i) = begin - pl = m.PlotsBase.current() save_func = (; pgfplotsx = m.PlotsBase.pdf, unicodeplots = m.PlotsBase.txt) # fastest `savefig` for each backend + pl = m.PlotsBase.current() fn = Base.invokelatest( get(save_func, pkgname, m.PlotsBase.png), pl, diff --git a/PlotsBase/test/test_misc.jl b/PlotsBase/test/test_misc.jl index 6a53aad68..585d1b6cf 100644 --- a/PlotsBase/test/test_misc.jl +++ b/PlotsBase/test/test_misc.jl @@ -21,7 +21,7 @@ end @testset "NoFail" begin with(:unicodeplots) do - @test backend() == PlotsBase._backend_instance(:unicodeplots) + @test backend() == PlotsBase.backend_instance(:unicodeplots) dsp = TextDisplay(IOContext(IOBuffer(), :color => true)) diff --git a/PlotsBase/test/test_recipes.jl b/PlotsBase/test/test_recipes.jl index 86cd0e007..b0daef638 100644 --- a/PlotsBase/test/test_recipes.jl +++ b/PlotsBase/test/test_recipes.jl @@ -96,7 +96,7 @@ end # TODO: that should cover all seriestypes without the need to have the extension loaded # currently uses plotly seriestypes only @test :surface in PlotsBase.all_seriestypes() - unicode_instance = PlotsBase._backend_instance(:unicodeplots) + unicode_instance = PlotsBase.backend_instance(:unicodeplots) @test PlotsBase.seriestype_supported(unicode_instance, :surface) === :native @test PlotsBase.seriestype_supported(unicode_instance, :hspan) === :recipe @test PlotsBase.seriestype_supported(PlotsBase.NoBackend(), :line) === :native diff --git a/PlotsBase/test/test_reference.jl b/PlotsBase/test/test_reference.jl new file mode 100644 index 000000000..279177c23 --- /dev/null +++ b/PlotsBase/test/test_reference.jl @@ -0,0 +1,203 @@ +ci_tol() = + if Sys.islinux() + is_pkgeval() ? "1e-2" : "5e-4" + elseif Sys.isapple() + "1e-3" + else + "1e-1" + end + +const TESTS_MODULE = Module(:PlotsBaseTestModule) +const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? ci_tol() : "1e-5")) + +Base.eval(TESTS_MODULE, :(using Random, StableRNGs, PlotsBase)) + +reference_dir(args...) = + if (ref_dir = get(ENV, "PLOTS_REFERENCE_DIR", nothing)) ≢ nothing + ref_dir + else + joinpath(homedir(), ".julia", "dev", "PlotReferenceImages.jl", args...) + end +reference_path(backend, version) = reference_dir("Plots", string(backend), string(version)) + +function checkout_reference_dir(dn::AbstractString) + mkpath(dn) + local repo + for i in 1:6 + try + repo = LibGit2.clone( + "https://github.com/JuliaPlots/PlotReferenceImages.jl.git", + dn, + ) + break + catch err + @warn err + sleep(20i) + end + end + if (ver = PlotsBase._current_plots_version).prerelease |> isempty + try + tag = LibGit2.GitObject(repo, "v$ver") + hash = string(LibGit2.target(tag)) + LibGit2.checkout!(repo, hash) + catch err + @warn err + end + end + LibGit2.peel(LibGit2.head(repo)) |> println # print some information + nothing +end + +let dn = reference_dir() + isdir(dn) || checkout_reference_dir(dn) +end + +function reference_file(backend, version, i) + # NOTE: keep ref[...].png naming consistent with `PlotDocs` + refdir = reference_dir("Plots", string(backend)) + fn = ref_name(i) * ".png" + reffn = joinpath(refdir, string(version), fn) + for ver in sort(VersionNumber.(readdir(refdir)), rev = true) + if (tmpfn = joinpath(refdir, string(ver), fn)) |> isfile + reffn = tmpfn + break + end + end + return reffn +end + +function image_comparison_tests( + pkg::Symbol, + idx::Int; + debug = false, + popup = !is_ci(), + sigma = [1, 1], + tol = 1e-2, +) + example = PlotsBase._examples[idx] + @info "Testing plot: $pkg:$idx:$(example.header)" + + ver = PlotsBase._current_plots_version + ver = VersionNumber(ver.major, ver.minor, ver.patch) + reffn = reference_file(pkg, ver, idx) + newfn = joinpath(reference_path(pkg, ver), ref_name(idx) * ".png") + + imports = something(example.imports, :()) + exprs = quote + PlotsBase.Commons.debug!($debug) + backend($(QuoteNode(pkg))) + theme(:default) + rng = StableRNG(PlotsBase.PLOTS_SEED) + $(PlotsBase.replace_rand(example.exprs)) + end + @debug imports exprs + + func = fn -> Base.eval.(Ref(TESTS_MODULE), (imports, exprs, :(png($fn)))) + test_images( + VisualTest(func, reffn), + newfn = newfn, + popup = popup, + sigma = sigma, + tol = tol, + ) +end + +function image_comparison_facts( + pkg::Symbol; + skip = [], # skip these examples (int index) + only = nothing, # limit to these examples (int index) + debug = false, # print debug information ? + sigma = [1, 1], # number of pixels to "blur" + tol = 1e-2, # acceptable error (percent) +) + for i in setdiff(1:length(PlotsBase._examples), skip) + if only ≡ nothing || i in only + @test success(image_comparison_tests(pkg, i; debug, sigma, tol)) + end + end +end + +## Uncomment the following lines to update reference images for different backends +#= + +with(:gr) do + image_comparison_facts(:gr, tol = PLOTS_IMG_TOL, skip = PlotsBase._backend_skips[:gr]) +end + +with(:plotlyjs) do + image_comparison_facts(:plotlyjs, tol = PLOTS_IMG_TOL, skip = PlotsBase._backend_skips[:plotlyjs]) +end + +with(:pgfplotsx) do + image_comparison_facts(:pgfplotsx, tol = PLOTS_IMG_TOL, skip = PlotsBase._backend_skips[:pgfplotsx]) +end +=# + +@testset "UnicodePlots" begin + with(:unicodeplots) do + @test backend() == PlotsBase.backend_instance(:unicodeplots) + + io = IOContext(IOBuffer(), :color => true) + + # lets just make sure it runs without error + pl = plot(rand(10)) + @test show(io, pl) isa Nothing + + pl = bar(randn(10)) + @test show(io, pl) isa Nothing + + pl = plot([1, 2], [3, 4]) + annotate!(pl, [(1.5, 3.2, PlotsBase.text("Test", :red, :center))]) + hline!(pl, [3.1]) + @test show(io, pl) isa Nothing + + pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) + hline!(pl, [3.1]) + annotate!( + pl, + [(Dates.Date(2019, 1, 15), 3.2, PlotsBase.text("Test", :red, :center))], + ) + @test show(io, pl) isa Nothing + + pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) + annotate!(pl, [(Dates.Date(2019, 1, 15), 3.2, :auto)]) + hline!(pl, [3.1]) + @test show(io, pl) isa Nothing + + pl = plot(map(plot, 1:4)..., layout = (2, 2)) + @test show(io, pl) isa Nothing + + pl = plot(map(plot, 1:3)..., layout = (2, 2)) + @test show(io, pl) isa Nothing + + pl = plot(map(plot, 1:2)..., layout = @layout([° _; _ °])) + @test show(io, pl) isa Nothing + + redirect_stdout(devnull) do + show(plot(1:2)) + end + end +end + +@testset "GR - reference images" begin + with(:gr) do + # NOTE: use `ENV["VISUAL_REGRESSION_TESTS_AUTO"] = true;` to automatically replace reference images + @test backend() == PlotsBase.backend_instance(:gr) + @test backend_name() ≡ :gr + image_comparison_facts( + :gr, + tol = PLOTS_IMG_TOL, + skip = vcat(PlotsBase._backend_skips[:gr], blacklist), + ) + end +end + +is_pkgeval() || @testset "PlotlyJS" begin + with(:plotlyjs) do + PlotlyJSExt = Base.get_extension(PlotsBase, :PlotlyJSExt) + @test backend() == PlotlyJSExt.PlotlyJSBackend() + pl = plot(rand(10)) + @test pl isa Plot + display(pl) + end +end diff --git a/src/Plots.jl b/src/Plots.jl index 3d9c6e9f6..aabee092f 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -1,14 +1,11 @@ module Plots -using PrecompileTools -using Preferences + using Reexport -using Pkg @reexport using PlotsBase -function __init__() - ccall(:jl_generating_output, Cint, ()) == 1 && return - load_default_backend() -end +using PrecompileTools +using Preferences +using Pkg # from github.com/JuliaPackaging/Preferences.jl/blob/master/README.md: # "Preferences that are accessed during compilation are automatically marked as compile-time preferences" @@ -16,6 +13,11 @@ end # the cache will not invalidate when preferences change const PLOTS_DEFAULT_BACKEND = lowercase(load_preference(Plots, "default_backend", "gr")) +function __init__() + ccall(:jl_generating_output, Cint, ()) == 1 && return + load_default_backend() +end + function load_default_backend() # environment variable preempts the `Preferences` based mechanism PlotsBase.CURRENT_BACKEND.sym = @@ -23,7 +25,7 @@ function load_default_backend() if (pkg_name = PlotsBase.backend_package_name()) ≡ :GR @eval import GR end - Base.invokelatest(PlotsBase.backend, PlotsBase.CURRENT_BACKEND.sym) + PlotsBase.backend(PlotsBase.Plots.backend_instance(Base.CURRENT_BACKEND.sym)) end function set_default_backend!( @@ -118,4 +120,4 @@ end end # COV_EXCL_STOP -end +end # module diff --git a/test/runtests.jl b/test/runtests.jl index 09d3e7b97..b9fc63c86 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,11 @@ const TEST_PACKAGES = - strip.(split(get(ENV, "PLOTS_TEST_PACKAGES", "GR,UnicodePlots,PythonPlot"), ",")) + let val = get( + ENV, + "PLOTSBASE_TEST_PACKAGES", + "GR,UnicodePlots,PythonPlot", + ) + strip.(split(val, ",")) + end using PlotsBase # initialize all backends diff --git a/test/test_preferences.jl b/test/test_preferences.jl index a643de701..370ec5833 100644 --- a/test/test_preferences.jl +++ b/test/test_preferences.jl @@ -1,10 +1,10 @@ Plots.set_default_backend!() # start with empty preferences -withenv("PLOTS_DEFAULT_BACKEND" => "invalid") do +withenv("PLOTS_DEFAULT_BACKEND" => "test_invalid_backend") do @test_logs (:error, r"Unsupported backend.*") Plots.load_default_backend() end -@test_logs (:error, r"Unsupported backend.*") backend(:invalid) +@test_logs (:error, r"Unsupported backend.*") backend(:test_invalid_backend) @test Plots.load_default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() @@ -23,7 +23,7 @@ end @test Plots.PlotsBase.merge_with_base_supported([:annotations, :guide]) isa Set @test Plots.PlotsBase.CurrentBackend(:gr).sym ≡ :gr -@test_logs (:warn, r".*is not compatible with") Plots.set_default_backend!(:invalid) +@test_logs (:warn, r".*is not compatible with") Plots.set_default_backend!(:test_invalid_backend) @testset "persistent backend" begin # this test mimics a restart, which is needed after a preferences change From de44e802a51610b6ef3e2b40e7d16318f5eaf2c9 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 18:18:01 +0200 Subject: [PATCH 06/58] checkpoint replace --- PlotsBase/ext/PythonPlotExt.jl | 35 ++++---- PlotsBase/ext/UnitfulExt.jl | 10 +-- PlotsBase/src/Annotations.jl | 10 +-- PlotsBase/src/Axes.jl | 40 +++++----- PlotsBase/src/Commons/Commons.jl | 23 ++++-- PlotsBase/src/Commons/aliases.jl | 2 +- PlotsBase/src/Commons/attrs.jl | 92 +++++++++++----------- PlotsBase/src/Commons/postprocess_attrs.jl | 14 +--- PlotsBase/src/Fonts.jl | 18 ++--- PlotsBase/src/PlotsPlots.jl | 4 +- PlotsBase/src/Series.jl | 16 ++-- PlotsBase/src/Subplots.jl | 18 ++--- PlotsBase/src/Ticks.jl | 4 +- PlotsBase/src/alignment.jl | 4 +- PlotsBase/src/animation.jl | 4 +- PlotsBase/src/axes_utils.jl | 42 +++++----- PlotsBase/src/examples.jl | 22 +++--- PlotsBase/src/layouts.jl | 12 +-- PlotsBase/src/output.jl | 10 +-- PlotsBase/src/pipeline.jl | 24 +++--- PlotsBase/src/plot.jl | 4 +- PlotsBase/src/recipes.jl | 60 +++++++------- PlotsBase/src/utils.jl | 36 ++++----- PlotsBase/test/test_args.jl | 8 +- PlotsBase/test/test_axes.jl | 24 +++--- PlotsBase/test/test_components.jl | 6 +- PlotsBase/test/test_contours.jl | 8 +- PlotsBase/test/test_defaults.jl | 28 +++---- PlotsBase/test/test_layouts.jl | 20 ++--- PlotsBase/test/test_pgfplotsx.jl | 12 +-- PlotsBase/test/test_recipes.jl | 16 ++-- PlotsBase/test/test_shorthands.jl | 2 +- PlotsBase/test/test_utils.jl | 70 ++++++++-------- RecipesBase/src/RecipesBase.jl | 14 ++-- src/Plots.jl | 4 +- test/runtests.jl | 2 +- 36 files changed, 360 insertions(+), 358 deletions(-) diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index 6df929fa1..1e7a39b95 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -16,7 +16,7 @@ import NaNMath import PlotsBase import PlotsBase.PlotUtils: PlotUtils, ColorGradient, plot_color, color_list, cgrad import PlotsBase: bbox_to_pcts, right, left, bottom, top, width, height, ticks_type -import PlotsBase: ispositive +import PlotsBase: ispositive, ismatrix import PlotsBase.Commons: Commons, single_color import RecipesPipeline: Surface @@ -50,10 +50,11 @@ function PlotsBase.extension_init(::PythonPlotBackend) numpy.seterr(invalid = "ignore") PythonPlot.ioff() # we don't want every command to update the figure - for letter in (:x, :y, :z, Symbol(), :top, :bottom, :left, :right) - Commons.add_attr_dict!(letter) - for keyword in (:linthresh, :base, :label) - Commons.add_attr!(letter, keyword) + # WARNING: matplotlib uses a reverse convention: `labeltop` instead of `toplabel` + for keyword in (:linthresh, :base, :label) + Commons.new_attr_dict!(keyword) + for letter in (:x, :y, :z, Symbol(), :top, :bottom, :left, :right) + Commons.set_attr_symbol!(keyword, string(letter)) end end @@ -934,7 +935,7 @@ function _py_set_ticks(sp, ax, ticks, letter) if ticks ≡ :none || ticks ≡ nothing || ticks == false kw = KW() for dir in (:top, :bottom, :left, :right) - kw[dir] = kw[get_attr_symbol(dir, :label)] = false + kw[dir] = kw[get_attr_symbol(:label, dir)] = false end axis.set_tick_params(; which = "both", kw...) return @@ -973,8 +974,8 @@ function _py_set_scale(ax, sp::Subplot, scale::Symbol, letter::Symbol) else "symlog", KW( - get_attr_symbol(Symbol(), :base) => Commons._log_scale_bases[scale], - get_attr_symbol(Symbol(), :linthresh) => NaNMath.max( + get_attr_symbol(:base, Symbol()) => Commons._log_scale_bases[scale], + get_attr_symbol(:linthresh, Symbol()) => NaNMath.max( 1e-16, _py_compute_axis_minval(sp, sp[get_attr_symbol(letter, :axis)]), ), @@ -993,8 +994,8 @@ _py_set_spine_color(spines::Dict, color) = function _py_set_axis_colors(sp, ax, a::Axis) _py_set_spine_color(ax.spines, _py_color(a[:foreground_color_border])) - axissym = get_attr_symbol(a[:letter], :axis) - if hasproperty(ax, axissym) + axis_sym = get_attr_symbol(a[:letter], :axis) + if hasproperty(ax, axis_sym) tickcolor = sp[:framestyle] ∈ (:zerolines, :grid) ? _py_color(plot_color(a[:foreground_color_grid], a[:gridalpha])) : @@ -1005,7 +1006,7 @@ function _py_set_axis_colors(sp, ax, a::Axis) colors = tickcolor, labelcolor = _py_color(a[:tickfontcolor]), ) - getproperty(ax, axissym).label.set_color(_py_color(a[:guidefontcolor])) + getproperty(ax, axis_sym).label.set_color(_py_color(a[:guidefontcolor])) end end @@ -1231,10 +1232,10 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) # axis attributes for letter in (:x, :y, :z) - axissym = get_attr_symbol(letter, :axis) - hasproperty(ax, axissym) || continue - axis = sp[axissym] - pyaxis = getproperty(ax, axissym) + axis_sym = get_attr_symbol(letter, :axis) + hasproperty(ax, axis_sym) || continue + axis = sp[axis_sym] + pyaxis = getproperty(ax, axis_sym) if axis[:guide_position] ≢ :auto && letter ≢ :z pyaxis.set_label_position(string(axis[:guide_position])) @@ -1361,7 +1362,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) ispolar(sp) && ax.spines.polar.set_visible(false) for dir in (:top, :bottom) ispolar(sp) || getproperty(ax.spines, string(dir)).set_visible(false) - kw[dir] = kw[get_attr_symbol(dir, :label)] = false + kw[dir] = kw[get_attr_symbol(:label, dir)] = false end ax.xaxis.set_tick_params(; which = "both", kw...) end @@ -1369,7 +1370,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) kw = KW() for dir in (:left, :right) ispolar(sp) || getproperty(ax.spines, string(dir)).set_visible(false) - kw[dir] = kw[get_attr_symbol(dir, :label)] = false + kw[dir] = kw[get_attr_symbol(:label, dir)] = false end ax.yaxis.set_tick_params(; which = "both", kw...) end diff --git a/PlotsBase/ext/UnitfulExt.jl b/PlotsBase/ext/UnitfulExt.jl index cbaace87f..e4e42636e 100644 --- a/PlotsBase/ext/UnitfulExt.jl +++ b/PlotsBase/ext/UnitfulExt.jl @@ -33,7 +33,7 @@ Main recipe @recipe function f(::Type{T}, x::T) where {T<:AbstractArray{<:MissingOrQuantity}} # COV_EXCL_LINE axisletter = plotattributes[:letter] # x, y, or z clims_types = (:contour, :contourf, :heatmap, :surface) - if axisletter === :z && get(plotattributes, :seriestype, :nothing) ∈ clims_types + if axisletter ≡ :z && get(plotattributes, :seriestype, :nothing) ∈ clims_types u = get(plotattributes, :zunit, _unit(eltype(x))) ustripattribute!(plotattributes, :clims, u) append_unit_if_needed!(plotattributes, :colorbar_title, u) @@ -60,7 +60,7 @@ function fixaxis!(attr, x, axisletter) # fix the attributes: labels, lims, ticks, marker/line stuff, etc. append_unit_if_needed!(attr, axislabel, u) ustripattribute!(attr, err, u) - if axisletter === :y + if axisletter ≡ :y ustripattribute!(attr, :ribbon, u) ustripattribute!(attr, :fillrange, u) end @@ -146,7 +146,7 @@ function fixaspectratio!(attr, u, axisletter) # Keep the default behavior (let PlotsBase figure it out) return end - if aspect_ratio === :equal + if aspect_ratio ≡ :equal aspect_ratio = 1 end #======================================================================================= @@ -159,9 +159,9 @@ function fixaspectratio!(attr, u, axisletter) made, and the default aspect ratio fixing of PlotsBase throws a `DimensionError` as it tries to compare `0 < 1u"m"`. =======================================================================================# - if axisletter === :y + if axisletter ≡ :y attr[:aspect_ratio] = aspect_ratio * u - elseif axisletter === :x + elseif axisletter ≡ :x attr[:aspect_ratio] = aspect_ratio / u end nothing diff --git a/PlotsBase/src/Annotations.jl b/PlotsBase/src/Annotations.jl index 2c8c8a1c7..66b0de52e 100644 --- a/PlotsBase/src/Annotations.jl +++ b/PlotsBase/src/Annotations.jl @@ -127,7 +127,7 @@ mutable struct EachAnn end function Base.iterate(ea::EachAnn, i = 1) - (ea.anns === nothing || isempty(ea.anns.strs) || i > length(ea.y)) && return + (ea.anns ≡ nothing || isempty(ea.anns.strs) || i > length(ea.y)) && return tmp = _cycle(ea.anns.strs, i) str, fnt = if isa(tmp, PlotText) @@ -156,7 +156,7 @@ _annotationfont(sp::Subplot) = font(; _annotation(sp::Subplot, font, lab, pos...; alphabet = "abcdefghijklmnopqrstuvwxyz") = ( pos..., - lab === :auto ? text("($(alphabet[sp[:subplot_index]]))", font) : + lab ≡ :auto ? text("($(alphabet[sp[:subplot_index]]))", font) : _text_label(lab, font), ) @@ -205,11 +205,11 @@ process_annotation(sp::Subplot, ann) = function _relative_position(xmin, xmax, pos::Length{:pct}, scale::Symbol) # !TODO Add more scales in the future (asinh, sqrt) ? - if scale === :log || scale === :ln + if scale ≡ :log || scale ≡ :ln exp(log(xmin) + pos.value * log(xmax / xmin)) - elseif scale === :log10 + elseif scale ≡ :log10 exp10(log10(xmin) + pos.value * log10(xmax / xmin)) - elseif scale === :log2 + elseif scale ≡ :log2 exp2(log2(xmin) + pos.value * log2(xmax / xmin)) else # :identity (linear scale) xmin + pos.value * (xmax - xmin) diff --git a/PlotsBase/src/Axes.jl b/PlotsBase/src/Axes.jl index b2d97cd76..be7d171cc 100644 --- a/PlotsBase/src/Axes.jl +++ b/PlotsBase/src/Axes.jl @@ -56,7 +56,7 @@ end # properly retrieve from axis.attr, passing `:match` to the correct key Base.getindex(axis::Axis, k::Symbol) = - if (v = axis.plotattributes[k]) === :match + if (v = axis.plotattributes[k]) ≡ :match if haskey(Commons.Commons._match_map2, k) axis.sps[1][Commons.Commons._match_map2[k]] else @@ -76,9 +76,9 @@ end Extrema() = Extrema(Inf, -Inf) # ------------------------------------------------------------------------- sort_3d_axes(x, y, z, letter) = - if letter === :x + if letter ≡ :x x, y, z - elseif letter === :y + elseif letter ≡ :y y, x, z else z, y, x @@ -88,13 +88,13 @@ axes_letters(sp, letter) = if RecipesPipeline.is3d(sp) sort_3d_axes(:x, :y, :z, letter) else - letter === :x ? (:x, :y) : (:y, :x) + letter ≡ :x ? (:x, :y) : (:y, :x) end scale_inverse_scale_func(scale::Symbol) = ( RecipesPipeline.scale_func(scale), RecipesPipeline.inverse_scale_func(scale), - scale === :identity, + scale ≡ :identity, ) function get_axis(sp::Subplot, letter::Symbol) axissym = get_attr_symbol(letter, :axis) @@ -115,22 +115,22 @@ function Commons.axis_limits( ex = axis[:extrema] amin, amax = ex.emin, ex.emax lims = process_limits(axis[:lims], axis) - lims === nothing && warn_invalid_limits(axis[:lims], letter) + lims ≡ nothing && warn_invalid_limits(axis[:lims], letter) if (has_user_lims = lims isa Tuple) lmin, lmax = lims if lmin isa Number && isfinite(lmin) amin = lmin elseif lmin isa Symbol - lmin === :auto || @warn "Invalid min $(letter)limit" lmin + lmin ≡ :auto || @warn "Invalid min $(letter)limit" lmin end if lmax isa Number && isfinite(lmax) amax = lmax elseif lmax isa Symbol - lmax === :auto || @warn "Invalid max $(letter)limit" lmax + lmax ≡ :auto || @warn "Invalid max $(letter)limit" lmax end end - if lims === :symmetric + if lims ≡ :symmetric amax = max(abs(amin), abs(amax)) amin = -amax end @@ -141,15 +141,15 @@ function Commons.axis_limits( amin, amax = zero(amin), one(amax) end if ispolar(axis.sps[1]) - if axis[:letter] === :x + if axis[:letter] ≡ :x amin, amax = 0, 2π - elseif lims === :auto + elseif lims ≡ :auto # widen max radius so ticks dont overlap with theta axis amin, amax = 0, amax + 0.1abs(amax - amin) end elseif lims_factor !== nothing amin, amax = scale_lims(amin, amax, lims_factor, axis[:scale]) - elseif lims === :round + elseif lims ≡ :round amin, amax = round_limits(amin, amax, axis[:scale]) end @@ -158,14 +158,14 @@ function Commons.axis_limits( !has_user_lims && consider_aspect && letter in (:x, :y) && - !(aspect_ratio === :none || RecipesPipeline.is3d(:sp)) + !(aspect_ratio ≡ :none || RecipesPipeline.is3d(:sp)) ) aspect_ratio = aspect_ratio isa Number ? aspect_ratio : 1 area = PlotsBase.plotarea(sp) plot_ratio = PlotsBase.height(area) / PlotsBase.width(area) dist = amax - amin - factor = if letter === :x + factor = if letter ≡ :x ydist, = axis_limits(sp, :y, widen_factor(sp[:yaxis]), false) |> collect |> diff axis_ratio = aspect_ratio * ydist / dist axis_ratio / plot_ratio @@ -194,12 +194,12 @@ function widen_factor(axis::Axis; factor = default_widen_factor[]) elseif widen isa Number return widen else - widen === :auto || @warn "Invalid value specified for `widen`: $widen" + widen ≡ :auto || @warn "Invalid value specified for `widen`: $widen" end # automatic behavior: widen if limits aren't specified and series type is appropriate lims = process_limits(axis[:lims], axis) - (lims isa Tuple || lims === :round) && return + (lims isa Tuple || lims ≡ :round) && return for sp in axis.sps, series in series_list(sp) series.plotattributes[:seriestype] in _widen_seriestypes && return factor end @@ -281,7 +281,7 @@ function process_axis_arg!(plotattributes::AKW, arg, letter = "") elseif T <: AVec plotattributes[get_attr_symbol(letter, :ticks)] = arg - elseif arg === nothing + elseif arg ≡ nothing plotattributes[get_attr_symbol(letter, :ticks)] = [] elseif T <: Bool || arg in Commons._all_showaxis_attrs @@ -316,9 +316,9 @@ function attr!(axis::Axis, args...; kw...) # then override for any keywords... only those keywords that already exists in plotattributes for (k, v) in kw haskey(plotattributes, k) || continue - if k === :discrete_values + if k ≡ :discrete_values foreach(x -> discrete_value!(axis, x), v) # add these discrete values to the axis - elseif k === :lims && isa(v, NTuple{2,TimeType}) + elseif k ≡ :lims && isa(v, NTuple{2,TimeType}) plotattributes[k] = (v[1].instant.periods.value, v[2].instant.periods.value) else plotattributes[k] = v @@ -409,7 +409,7 @@ function PlotsBase.get_ticks( ticks = _transform_ticks(axis[:ticks], axis) axis.plotattributes[:optimized_ticks] = if ( - axis[:letter] === :x && + axis[:letter] ≡ :x && ticks isa Symbol && ticks !== :none && !isempty(dvals) && diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index 83fa56b00..d68ca2687 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -132,7 +132,7 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol) if !RecipesPipeline.is3d(st) && st ∉ (:contour, :contour3d, :quiver) if (z = plotattributes[:z]) !== nothing && size(plotattributes[:x]) == size(plotattributes[:y]) == size(z) - st = st === :scatter ? :scatter3d : :path3d + st = st ≡ :scatter ? :scatter3d : :path3d plotattributes[:seriestype] = st end end @@ -156,7 +156,7 @@ PlotsBase.@ScopeModule( function fg_color(plotattributes::AKW) fg = get(plotattributes, :foreground_color, :auto) - if fg === :auto + if fg ≡ :auto bg = plot_color(get(plotattributes, :background_color, :white)) fg = alpha(bg) > 0 && isdark(bg) ? colorant"white" : colorant"black" else @@ -164,15 +164,22 @@ function fg_color(plotattributes::AKW) end end function color_or_nothing!(plotattributes, k::Symbol) - plotattributes[k] = (v = plotattributes[k]) === :match ? v : plot_color(v) + plotattributes[k] = (v = plotattributes[k]) ≡ :match ? v : plot_color(v) nothing end # cache joined symbols so they can be looked up instead of constructed each time -const _attrsymbolcache = Dict{Symbol,Dict{Symbol,Symbol}}() +const _attrsymbolcache = Dict{Symbol,Dict{String,Symbol}}() + +get_attr_symbol(letter::Symbol, keyword::String) = _attrsymbolcache[letter][keyword] +get_attr_symbol(letter::Symbol, keyword::Symbol) = get_attr_symbol(letter, string(keyword)) + +new_attr_dict!(letter::Symbol) = get!(_attrsymbolcache, letter, Dict{String,Symbol}()) +set_attr_symbol!(letter::Symbol, keyword::String) = + let letter_keyword = Symbol(letter, keyword) + _attrsymbolcache[letter][keyword] = letter_keyword + end -get_attr_symbol(letter::Symbol, keyword::String) = get_attr_symbol(letter, Symbol(keyword)) -get_attr_symbol(letter::Symbol, keyword::Symbol) = _attrsymbolcache[letter][keyword] # ------------------------------------------------------------------------------------ _cycle(v::AVec, idx::Int) = v[mod(idx, axes(v, 1))] _cycle(v::AMat, idx::Int) = size(v, 1) == 1 ? v[end, mod(idx, axes(v, 2))] : v[:, mod(idx, axes(v, 2))] @@ -259,10 +266,10 @@ reverse_if(x, cond) = cond ? reverse(x) : x function get_aspect_ratio(sp) ar = sp[:aspect_ratio] check_aspect_ratio(ar) - if ar === :auto + if ar ≡ :auto ar = :none for series in series_list(sp) - if series[:seriestype] === :image + if series[:seriestype] ≡ :image ar = :equal end end diff --git a/PlotsBase/src/Commons/aliases.jl b/PlotsBase/src/Commons/aliases.jl index 1a8ed86a7..f7a887e75 100644 --- a/PlotsBase/src/Commons/aliases.jl +++ b/PlotsBase/src/Commons/aliases.jl @@ -9,7 +9,7 @@ function aliases_and_autopick( options::AVec, plotIndex::Int, ) - if plotattributes[sym] === :auto + if plotattributes[sym] ≡ :auto plotattributes[sym] = autopick_ignore_none_auto(options, plotIndex) elseif haskey(aliases, plotattributes[sym]) plotattributes[sym] = aliases[plotattributes[sym]] diff --git a/PlotsBase/src/Commons/attrs.jl b/PlotsBase/src/Commons/attrs.jl index 5f453a477..2d94c35f8 100644 --- a/PlotsBase/src/Commons/attrs.jl +++ b/PlotsBase/src/Commons/attrs.jl @@ -5,7 +5,7 @@ const _keyAliases = Dict{Symbol,Symbol}() function add_aliases(sym::Symbol, aliases::Symbol...) for alias in aliases - (haskey(_keyAliases, alias) || alias === sym) && return + (haskey(_keyAliases, alias) || alias ≡ sym) && return _keyAliases[alias] = sym end nothing @@ -699,7 +699,7 @@ function default(k::Symbol) letter, key = axis_k return _axis_defaults_byletter[letter][key] end - k === :letter && return k # for type recipe processing + k ≡ :letter && return k # for type recipe processing missing end @@ -746,7 +746,7 @@ end # if arg is a valid color value, then set plotattributes[csym] and return true function handle_colors!(plotattributes::AKW, arg, csym::Symbol) try - plotattributes[csym] = if arg === :auto + plotattributes[csym] = if arg ≡ :auto :auto else plot_color(arg) @@ -767,22 +767,22 @@ function process_line_attr(plotattributes::AKW, arg) plotattributes[:linestyle] = arg elseif typeof(arg) <: PlotsBase.Stroke - arg.width === nothing || (plotattributes[:linewidth] = arg.width) - arg.color === nothing || ( + arg.width ≡ nothing || (plotattributes[:linewidth] = arg.width) + arg.color ≡ nothing || ( plotattributes[:linecolor] = - arg.color === :auto ? :auto : plot_color(arg.color) + arg.color ≡ :auto ? :auto : plot_color(arg.color) ) - arg.alpha === nothing || (plotattributes[:linealpha] = arg.alpha) - arg.style === nothing || (plotattributes[:linestyle] = arg.style) + arg.alpha ≡ nothing || (plotattributes[:linealpha] = arg.alpha) + arg.style ≡ nothing || (plotattributes[:linestyle] = arg.style) elseif typeof(arg) <: PlotsBase.Brush - arg.size === nothing || (plotattributes[:fillrange] = arg.size) - arg.color === nothing || ( + arg.size ≡ nothing || (plotattributes[:fillrange] = arg.size) + arg.color ≡ nothing || ( plotattributes[:fillcolor] = - arg.color === :auto ? :auto : plot_color(arg.color) + arg.color ≡ :auto ? :auto : plot_color(arg.color) ) - arg.alpha === nothing || (plotattributes[:fillalpha] = arg.alpha) - arg.style === nothing || (plotattributes[:fillstyle] = arg.style) + arg.alpha ≡ nothing || (plotattributes[:fillalpha] = arg.alpha) + arg.style ≡ nothing || (plotattributes[:fillstyle] = arg.style) elseif typeof(arg) <: PlotsBase.Arrow || arg in (:arrow, :arrows) plotattributes[:arrow] = arg @@ -811,21 +811,21 @@ function process_marker_attr(plotattributes::AKW, arg) plotattributes[:markerstrokestyle] = arg elseif typeof(arg) <: PlotsBase.Stroke - arg.width === nothing || (plotattributes[:markerstrokewidth] = arg.width) - arg.color === nothing || ( + arg.width ≡ nothing || (plotattributes[:markerstrokewidth] = arg.width) + arg.color ≡ nothing || ( plotattributes[:markerstrokecolor] = - arg.color === :auto ? :auto : plot_color(arg.color) + arg.color ≡ :auto ? :auto : plot_color(arg.color) ) - arg.alpha === nothing || (plotattributes[:markerstrokealpha] = arg.alpha) - arg.style === nothing || (plotattributes[:markerstrokestyle] = arg.style) + arg.alpha ≡ nothing || (plotattributes[:markerstrokealpha] = arg.alpha) + arg.style ≡ nothing || (plotattributes[:markerstrokestyle] = arg.style) elseif typeof(arg) <: PlotsBase.Brush - arg.size === nothing || (plotattributes[:markersize] = arg.size) - arg.color === nothing || ( + arg.size ≡ nothing || (plotattributes[:markersize] = arg.size) + arg.color ≡ nothing || ( plotattributes[:markercolor] = - arg.color === :auto ? :auto : plot_color(arg.color) + arg.color ≡ :auto ? :auto : plot_color(arg.color) ) - arg.alpha === nothing || (plotattributes[:markeralpha] = arg.alpha) + arg.alpha ≡ nothing || (plotattributes[:markeralpha] = arg.alpha) # linealpha elseif all_alphas(arg) @@ -848,13 +848,13 @@ end function process_fill_attr(plotattributes::AKW, arg) # fr = get(plotattributes, :fillrange, 0) if typeof(arg) <: PlotsBase.Brush - arg.size === nothing || (plotattributes[:fillrange] = arg.size) - arg.color === nothing || ( + arg.size ≡ nothing || (plotattributes[:fillrange] = arg.size) + arg.color ≡ nothing || ( plotattributes[:fillcolor] = - arg.color === :auto ? :auto : plot_color(arg.color) + arg.color ≡ :auto ? :auto : plot_color(arg.color) ) - arg.alpha === nothing || (plotattributes[:fillalpha] = arg.alpha) - arg.style === nothing || (plotattributes[:fillstyle] = arg.style) + arg.alpha ≡ nothing || (plotattributes[:fillalpha] = arg.alpha) + arg.style ≡ nothing || (plotattributes[:fillstyle] = arg.style) elseif typeof(arg) <: Bool plotattributes[:fillrange] = arg ? 0 : nothing @@ -886,15 +886,15 @@ function process_grid_attr!(plotattributes::AKW, arg, letter) plotattributes[get_attr_symbol(letter, :gridstyle)] = arg elseif typeof(arg) <: PlotsBase.Stroke - arg.width === nothing || + arg.width ≡ nothing || (plotattributes[get_attr_symbol(letter, :gridlinewidth)] = arg.width) - arg.color === nothing || ( + arg.color ≡ nothing || ( plotattributes[get_attr_symbol(letter, :foreground_color_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color) ) - arg.alpha === nothing || + arg.alpha ≡ nothing || (plotattributes[get_attr_symbol(letter, :gridalpha)] = arg.alpha) - arg.style === nothing || + arg.style ≡ nothing || (plotattributes[get_attr_symbol(letter, :gridstyle)] = arg.style) # linealpha @@ -924,15 +924,15 @@ function process_minor_grid_attr!(plotattributes::AKW, arg, letter) plotattributes[get_attr_symbol(letter, :minorgrid)] = true elseif typeof(arg) <: PlotsBase.Stroke - arg.width === nothing || + arg.width ≡ nothing || (plotattributes[get_attr_symbol(letter, :minorgridlinewidth)] = arg.width) - arg.color === nothing || ( + arg.color ≡ nothing || ( plotattributes[get_attr_symbol(letter, :foreground_color_minor_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color) ) - arg.alpha === nothing || + arg.alpha ≡ nothing || (plotattributes[get_attr_symbol(letter, :minorgridalpha)] = arg.alpha) - arg.style === nothing || + arg.style ≡ nothing || (plotattributes[get_attr_symbol(letter, :minorgridstyle)] = arg.style) plotattributes[get_attr_symbol(letter, :minorgrid)] = true @@ -977,7 +977,7 @@ end Symbol(fontname, :valign) --> arg.valign Symbol(fontname, :rotation) --> arg.rotation Symbol(fontname, :color) --> arg.color - elseif arg === :center + elseif arg ≡ :center Symbol(fontname, :halign) --> :hcenter Symbol(fontname, :valign) --> :vcenter elseif arg ∈ _haligns @@ -1046,7 +1046,7 @@ function convert_legend_value(val::Symbol) :inline, ) val - elseif val === :horizontal + elseif val ≡ :horizontal -1 else error("Invalid symbol for legend: $val") @@ -1150,7 +1150,7 @@ ensure_gradient!(plotattributes::AKW, csym::Symbol, asym::Symbol) = # get a good default linewidth... 0 for surface and heatmaps _replace_linewidth(plotattributes::AKW) = - if plotattributes[:linewidth] === :auto + if plotattributes[:linewidth] ≡ :auto plotattributes[:linewidth] = (get(plotattributes, :seriestype, :path) ∉ (:surface, :heatmap, :image)) * DEFAULT_LINEWIDTH[] @@ -1161,9 +1161,9 @@ label_to_string(label::Bool, series_plotindex) = label_to_string(label::Nothing, series_plotindex) = "" label_to_string(label::Missing, series_plotindex) = "" label_to_string(label::Symbol, series_plotindex) = - if label === :auto + if label ≡ :auto string("y", series_plotindex) - elseif label === :none + elseif label ≡ :none "" else throw(ArgumentError("unsupported symbol $(label) passed to `label`")) @@ -1192,8 +1192,8 @@ Also creates pluralized and non-underscore aliases for these keywords. """ macro add_attributes(level, expr, match_table) expr = macroexpand(__module__, expr) # to expand @static - expr isa Expr && expr.head === :struct || error("Invalid usage of @add_attributes") - if (T = expr.args[2]) isa Expr && T.head === :<: + expr isa Expr && expr.head ≡ :struct || error("Invalid usage of @add_attributes") + if (T = expr.args[2]) isa Expr && T.head ≡ :<: T = T.args[1] end @@ -1239,12 +1239,12 @@ function _splitdef!(blk, key_dict) # var continue elseif ei isa Expr - if ei.head === :(=) + if ei.head ≡ :(=) lhs = ei.args[1] if lhs isa Symbol # var = defexpr var = lhs - elseif lhs isa Expr && lhs.head === :(::) && lhs.args[1] isa Symbol + elseif lhs isa Expr && lhs.head ≡ :(::) && lhs.args[1] isa Symbol # var::T = defexpr var = lhs.args[1] type = lhs.args[2] @@ -1262,11 +1262,11 @@ function _splitdef!(blk, key_dict) defexpr = ei.args[2] # defexpr key_dict[var] = defexpr blk.args[i] = lhs - elseif ei.head === :(::) && ei.args[1] isa Symbol + elseif ei.head ≡ :(::) && ei.args[1] isa Symbol # var::Typ var = ei.args[1] key_dict[var] = defexpr - elseif ei.head === :block + elseif ei.head ≡ :block # can arise with use of @static inside type decl _kwdef!(ei, value_attrs, key_attrs) end diff --git a/PlotsBase/src/Commons/postprocess_attrs.jl b/PlotsBase/src/Commons/postprocess_attrs.jl index b08561b92..18fdbd188 100644 --- a/PlotsBase/src/Commons/postprocess_attrs.jl +++ b/PlotsBase/src/Commons/postprocess_attrs.jl @@ -2,23 +2,17 @@ # add all pluralized forms to the _keyAliases dict foreach(arg -> add_aliases(arg, makeplural(arg)), _all_attrs) -add_attr_dict!(letter::Symbol) = get!(_attrsymbolcache, letter, Dict{Symbol,Symbol}()) -add_attr!(letter::Symbol, keyword::Symbol) = - let letter_keyword = Symbol(letter, keyword) - _attrsymbolcache[letter][keyword] = letter_keyword - end - # fill symbol cache for letter in (:x, :y, :z) - add_attr_dict!(letter) + new_attr_dict!(letter) for keyword in _axis_attrs # populate attribute cache - letter_keyword = add_attr!(letter, keyword) - # allow the underscore version too: xguide or x_guide + letter_keyword = set_attr_symbol!(letter, string(keyword)) + # allow the underscore version too: `xguide` or `x_guide` add_aliases(letter_keyword, Symbol(letter, "_", keyword)) end for keyword in (_magic_axis_attrs..., :(_discrete_indices)) - _attrsymbolcache[letter][keyword] = Symbol(letter, keyword) + _attrsymbolcache[letter][string(keyword)] = Symbol(letter, keyword) end end diff --git a/PlotsBase/src/Fonts.jl b/PlotsBase/src/Fonts.jl index 965c15611..29810dbac 100644 --- a/PlotsBase/src/Fonts.jl +++ b/PlotsBase/src/Fonts.jl @@ -53,7 +53,7 @@ function font(args...; kw...) valign = arg.valign rotation = arg.rotation color = arg.color - elseif arg === :center + elseif arg ≡ :center halign = :hcenter valign = :vcenter elseif arg ∈ _haligns @@ -78,21 +78,21 @@ function font(args...; kw...) end for sym in keys(kw) - if sym === :family + if sym ≡ :family family = string(kw[sym]) - elseif sym === :pointsize + elseif sym ≡ :pointsize pointsize = kw[sym] - elseif sym === :halign + elseif sym ≡ :halign halign = kw[sym] - halign === :center && (halign = :hcenter) + halign ≡ :center && (halign = :hcenter) @assert halign ∈ _haligns - elseif sym === :valign + elseif sym ≡ :valign valign = kw[sym] - valign === :center && (valign = :vcenter) + valign ≡ :center && (valign = :vcenter) @assert valign ∈ _valigns - elseif sym === :rotation + elseif sym ≡ :rotation rotation = kw[sym] - elseif sym === :color + elseif sym ≡ :color col = kw[sym] color = col isa Colorant ? col : parse(Colorant, col) else diff --git a/PlotsBase/src/PlotsPlots.jl b/PlotsBase/src/PlotsPlots.jl index 25a561201..b3231354e 100644 --- a/PlotsBase/src/PlotsPlots.jl +++ b/PlotsBase/src/PlotsPlots.jl @@ -155,7 +155,7 @@ ignorenan_extrema(plt::Plot) = (xmin(plt), xmax(plt)) # properly retrieve from plt.attr, passing `:match` to the correct key Base.getindex(plt::Plot, k::Symbol) = - if (v = plt.attr[k]) === :match + if (v = plt.attr[k]) ≡ :match plt[Commons._match_map[k]] else v @@ -182,7 +182,7 @@ PlotsBase.series_list(plt::Plot) = plt.series_list get_ticks(p::Plot, s::Symbol) = map(sp -> get_ticks(sp, s), p.subplots) -get_subplot_index(plt::Plot, sp::Subplot) = findfirst(x -> x === sp, plt.subplots) +get_subplot_index(plt::Plot, sp::Subplot) = findfirst(x -> x ≡ sp, plt.subplots) PlotsBase.RecipesPipeline.preprocess_attributes!(plt::Plot, plotattributes::AKW) = Commons.preprocess_attributes!(plotattributes) diff --git a/PlotsBase/src/Series.jl b/PlotsBase/src/Series.jl index 411e82549..9b05a6bec 100644 --- a/PlotsBase/src/Series.jl +++ b/PlotsBase/src/Series.jl @@ -103,7 +103,7 @@ end function copy_series!(series, letter) plt = series[:plot_object] for s in plt.series_list, l in (:x, :y, :z) - if (s !== series || l !== letter) && s[l] === series[letter] + if (s !== series || l !== letter) && s[l] ≡ series[letter] series[letter] = copy(series[letter]) end end @@ -137,11 +137,11 @@ for comp in (:line, :fill, :marker) ) c = series[$Symbol($compcolor)] # series[:linecolor], series[:fillcolor], series[:markercolor] z = series[$Symbol($comp_z)] # series[:line_z], series[:fill_z], series[:marker_z] - if z === nothing + if z ≡ nothing isa(c, ColorGradient) ? c : plot_color(_cycle(c, i)) else grad = get_gradient(c) - if s === :identity + if s ≡ :identity get(grad, z[i], (cmin, cmax)) else base = _log_scale_bases[s] @@ -151,7 +151,7 @@ for comp in (:line, :fill, :marker) end function $get_compcolor(series, i::Integer = 1, s::Symbol = :identity) - if series[$Symbol($comp_z)] === nothing + if series[$Symbol($comp_z)] ≡ nothing $get_compcolor(series, 0, 1, i, s) else $get_compcolor(series, get_clims(series[:subplot]), i, s) @@ -225,12 +225,12 @@ struct NaNSegmentsIterator end function Base.iterate(itr::NaNSegmentsIterator, nextidx::Int = itr.n1) - (i = findfirst(!PlotsBase.Commons.anynan(itr.args), nextidx:(itr.n2))) === nothing && + (i = findfirst(!PlotsBase.Commons.anynan(itr.args), nextidx:(itr.n2))) ≡ nothing && return nextval = nextidx + i - 1 j = findfirst(PlotsBase.Commons.anynan(itr.args), nextval:(itr.n2)) - nextnan = j === nothing ? itr.n2 + 1 : nextval + j - 1 + nextnan = j ≡ nothing ? itr.n2 + 1 : nextval + j - 1 nextval:(nextnan - 1), nextnan end @@ -258,7 +258,7 @@ has_attribute_segments(series::Series) = function series_segments(series::Series, seriestype::Symbol = :path; check = false) x, y, z = series[:x], series[:y], series[:z] - (x === nothing || isempty(x)) && return UnitRange{Int}[] + (x ≡ nothing || isempty(x)) && return UnitRange{Int}[] args = RecipesPipeline.is3d(series) ? (x, y, z) : (x, y) nan_segments = collect(iter_segments(args...)) @@ -280,7 +280,7 @@ function series_segments(series::Series, seriestype::Symbol = :path; check = fal segments = if has_attribute_segments(series) map(nan_segments) do r - if seriestype === :shape + if seriestype ≡ :shape warn_on_inconsistent_shape_attrs(series, x, y, z, r) (SeriesSegment(r, first(r)),) elseif seriestype in (:scatter, :scatter3d) diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index 3abbf6fc0..095fb9656 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -57,7 +57,7 @@ end # properly retrieve from sp.attr, passing `:match` to the correct key Base.getindex(sp::Subplot, k::Symbol) = - if (v = sp.attr[k]) === :match + if (v = sp.attr[k]) ≡ :match if haskey(Commons.Commons._match_map2, k) sp.plt[Commons.Commons._match_map2[k]] else @@ -107,7 +107,7 @@ function attr!(sp::Subplot; kw...) sp end -PlotsBase.series_list(sp::Subplot) = sp.series_list # filter(series -> series.plotattributes[:subplot] === sp, sp.plt.series_list) +PlotsBase.series_list(sp::Subplot) = sp.series_list # filter(series -> series.plotattributes[:subplot] ≡ sp, sp.plt.series_list) PlotsBase.RecipesPipeline.is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d" PlotsBase.ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar" @@ -116,7 +116,7 @@ get_ticks(sp::Subplot, s::Symbol) = get_ticks(sp, sp[get_attr_symbol(s, :axis)]) # converts a symbol or string into a Colorant or ColorGradient # and assigns a color automatically get_series_color(c, sp::Subplot, n::Int, seriestype) = - if c === :auto + if c ≡ :auto like_surface(seriestype) ? PlotsBase.cgrad() : _cycle(sp[:color_palette], n) elseif isa(c, Int) _cycle(sp[:color_palette], c) @@ -174,7 +174,7 @@ function _update_subplot_periphery(sp::Subplot, anns::AVec) # handle legend/colorbar sp.attr[:legend_position] = convert_legend_value(sp.attr[:legend_position]) sp.attr[:colorbar] = convert_legend_value(sp.attr[:colorbar]) - if sp.attr[:colorbar] === :legend + if sp.attr[:colorbar] ≡ :legend sp.attr[:colorbar] = sp.attr[:legend_position] end nothing @@ -215,7 +215,7 @@ function PlotsBase.expand_extrema!(sp::Subplot, plotattributes::AKW) data = plotattributes[letter] if ( letter !== :z && - plotattributes[:seriestype] === :straightline && + plotattributes[:seriestype] ≡ :straightline && any(series[:seriestype] !== :straightline for series in series_list(sp)) && length(data) > 1 && data[1] != data[2] @@ -248,7 +248,7 @@ function PlotsBase.expand_extrema!(sp::Subplot, plotattributes::AKW) # expand for fillrange fr = plotattributes[:fillrange] - if fr === nothing && plotattributes[:seriestype] === :bar + if fr ≡ nothing && plotattributes[:seriestype] ≡ :bar fr = 0.0 end if fr !== nothing && !RecipesPipeline.is3d(plotattributes) @@ -261,11 +261,11 @@ function PlotsBase.expand_extrema!(sp::Subplot, plotattributes::AKW) end # expand for bar_width - if plotattributes[:seriestype] === :bar + if plotattributes[:seriestype] ≡ :bar dsym = :x data = plotattributes[dsym] - if (bw = plotattributes[:bar_width]) === nothing + if (bw = plotattributes[:bar_width]) ≡ nothing pos = filter(>(0), diff(sort(data))) plotattributes[:bar_width] = bw = Commons._bar_width * ignorenan_minimum(pos) end @@ -275,7 +275,7 @@ function PlotsBase.expand_extrema!(sp::Subplot, plotattributes::AKW) end # expand for heatmaps - if plotattributes[:seriestype] === :heatmap + if plotattributes[:seriestype] ≡ :heatmap for letter in (:x, :y) data = plotattributes[letter] axis = sp[get_attr_symbol(letter, :axis)] diff --git a/PlotsBase/src/Ticks.jl b/PlotsBase/src/Ticks.jl index 0f7aaf879..37e19435e 100644 --- a/PlotsBase/src/Ticks.jl +++ b/PlotsBase/src/Ticks.jl @@ -45,11 +45,11 @@ function num_minor_intervals(axis) end no_minor_intervals(axis) = - if (n_intervals = axis[:minorticks]) === false + if (n_intervals = axis[:minorticks]) ≡ false true # must be tested with `===` since Bool <: Integer elseif n_intervals ∈ (:none, nothing) true - elseif (n_intervals === :auto && !axis[:minorgrid]) + elseif (n_intervals ≡ :auto && !axis[:minorgrid]) true else false diff --git a/PlotsBase/src/alignment.jl b/PlotsBase/src/alignment.jl index 1588a6dcd..0969a5837 100644 --- a/PlotsBase/src/alignment.jl +++ b/PlotsBase/src/alignment.jl @@ -17,7 +17,7 @@ text_size(lab::PlotText, sz::Number, rot::Number = 0) = text_size(length(lab.str # account for the size/length/rotation of tick labels function tick_padding(sp::Subplot, axis::Axis) - if (ticks = get_ticks(sp, axis)) === nothing + if (ticks = get_ticks(sp, axis)) ≡ nothing 0mm else vals, labs = ticks @@ -26,7 +26,7 @@ function tick_padding(sp::Subplot, axis::Axis) longest_label = maximum(length(lab) for lab in labs) # generalize by "rotating" y labels - rot = axis[:rotation] + (axis[:letter] === :y ? 90 : 0) + rot = axis[:rotation] + (axis[:letter] ≡ :y ? 90 : 0) # # we need to compute the size of the ticks generically # # this means computing the bounding box and then getting the width/height diff --git a/PlotsBase/src/animation.jl b/PlotsBase/src/animation.jl index 434c747b1..859caf35b 100644 --- a/PlotsBase/src/animation.jl +++ b/PlotsBase/src/animation.jl @@ -235,9 +235,9 @@ function _animate(forloop::Expr, args...; type::Symbol = :none) push!(block.args, :($countersym += 1)) # add a final call to `gif(anim)`? - retval = if type === :gif + retval = if type ≡ :gif :(PlotsBase.gif($animsym; $(animationsKwargs...))) - elseif type === :apng + elseif type ≡ :apng :(PlotsBase.apng($animsym; $(animationsKwargs...))) else animsym diff --git a/PlotsBase/src/axes_utils.jl b/PlotsBase/src/axes_utils.jl index d36c16f6c..c34201886 100644 --- a/PlotsBase/src/axes_utils.jl +++ b/PlotsBase/src/axes_utils.jl @@ -23,7 +23,7 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter) # rather than on the input format # TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime - if ticks === nothing && noop + if ticks ≡ nothing && noop if formatter == RecipesPipeline.dateformatter # optimize_datetime_ticks returns ticks and labels(!) based on # integers/floats corresponding to the DateTime type. Thus, the axes @@ -41,7 +41,7 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter) end # get a list of well-laid-out ticks - scaled_ticks = if ticks === nothing + scaled_ticks = if ticks ≡ nothing optimize_ticks( sf(amin), sf(amax); @@ -76,11 +76,11 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter) end Ticks.get_ticks(ticks::Symbol, cvals::T, dvals, args...) where {T} = - if ticks === :none + if ticks ≡ :none T[], String[] elseif !isempty(dvals) n = length(dvals) - if ticks === :all || n < 16 + if ticks ≡ :all || n < 16 cvals, string.(dvals) else Δ = ceil(Int, n / 10) @@ -104,12 +104,12 @@ Ticks.get_ticks(ticks::Int, dvals, cvals, args...) = get_labels(formatter::Symbol, scaled_ticks, scale) = if formatter in (:auto, :plain, :scientific, :engineering) map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, formatter)) - elseif formatter === :latex + elseif formatter ≡ :latex map( l -> string("\$", replace(convert_sci_unicode(l), '×' => "\\times"), "\$"), get_labels(:auto, scaled_ticks, scale), ) - elseif formatter === :none + elseif formatter ≡ :none String[] end @@ -256,21 +256,21 @@ function add_major_or_minor_segments_2d( factor, cond, ) - ticks === nothing && return + ticks ≡ nothing && return if cond f, invf = scale_inverse_scale_func(oax[:scale]) - tick_start, tick_stop = if sp[:framestyle] === :origin + tick_start, tick_stop = if sp[:framestyle] ≡ :origin oamin, oamax = oamM t = invf(f(0) + factor * (f(oamax) - f(oamin))) (-t, t) else - ticks_in = ax[:tick_direction] === :out ? -1 : 1 + ticks_in = ax[:tick_direction] ≡ :out ? -1 : 1 oa1, oa2 = oas t = invf(f(oa1) + factor * (f(oa2) - f(oa1)) * ticks_in) (oa1, t) end end - isy = ax[:letter] === :y + isy = ax[:letter] ≡ :y for tick in ticks (ax[:showaxis] && cond) && push!( tick_segments, @@ -300,7 +300,7 @@ function axis_drawing_info(sp, letter) map(_ -> Segments(2), 1:5) if sp[:framestyle] !== :none - isy = letter === :y + isy = letter ≡ :y oa1, oa2 = oas = if sp[:framestyle] in (:origin, :zerolines) 0, 0 else @@ -311,7 +311,7 @@ function axis_drawing_info(sp, letter) push!(segments, reverse_if((amin, oa1), isy), reverse_if((amax, oa1), isy)) # don't show the 0 tick label for the origin framestyle if ( - sp[:framestyle] === :origin && + sp[:framestyle] ≡ :origin && ticks ∉ (:none, nothing, false) && length(ticks) > 1 ) @@ -329,7 +329,7 @@ function axis_drawing_info(sp, letter) ) end if ax[:ticks] ∉ (:none, nothing, false) - ax_length = letter === :x ? height(sp.plotarea).value : width(sp.plotarea).value + ax_length = letter ≡ :x ? height(sp.plotarea).value : width(sp.plotarea).value # add major grid segments add_major_or_minor_segments_2d( @@ -345,7 +345,7 @@ function axis_drawing_info(sp, letter) grid_factor_2d[] / ax_length, ax[:tick_direction] !== :none, ) - if sp[:framestyle] === :box + if sp[:framestyle] ≡ :box add_major_or_minor_segments_2d( sp, ax, @@ -376,7 +376,7 @@ function axis_drawing_info(sp, letter) grid_factor_2d[] / 2ax_length, true, ) - if sp[:framestyle] === :box + if sp[:framestyle] ≡ :box add_major_or_minor_segments_2d( sp, ax, @@ -419,16 +419,16 @@ function add_major_or_minor_segments_3d( factor, cond, ) - ticks === nothing && return + ticks ≡ nothing && return if cond f, invf = scale_inverse_scale_func(nax[:scale]) - tick_start, tick_stop = if sp[:framestyle] === :origin + tick_start, tick_stop = if sp[:framestyle] ≡ :origin namin, namax = namM t = invf(f(0) + factor * (f(namax) - f(namin))) (-t, t) else na0, na1 = nas - ticks_in = ax[:tick_direction] === :out ? -1 : 1 + ticks_in = ax[:tick_direction] ≡ :out ? -1 : 1 t = invf(f(na0) + factor * (f(na1) - f(na0)) * ticks_in) (na0, t) end @@ -467,12 +467,12 @@ function axis_drawing_info_3d(sp, letter) segments, tick_segments, grid_segments, minorgrid_segments, border_segments = map(_ -> Segments(3), 1:5) - if sp[:framestyle] !== :none # && letter === :x + if sp[:framestyle] !== :none # && letter ≡ :x na0, na1 = nas = if sp[:framestyle] in (:origin, :zerolines) 0, 0 else - reverse_if(reverse_if(namM, letter === :y), xor(ax[:mirror], nax[:flip])) + reverse_if(reverse_if(namM, letter ≡ :y), xor(ax[:mirror], nax[:flip])) end fa0, fa1 = fas = if sp[:framestyle] in (:origin, :zerolines) 0, 0 @@ -488,7 +488,7 @@ function axis_drawing_info_3d(sp, letter) ) # don't show the 0 tick label for the origin framestyle if ( - sp[:framestyle] === :origin && + sp[:framestyle] ≡ :origin && ticks ∉ (:none, nothing, false) && length(ticks) > 1 ) diff --git a/PlotsBase/src/examples.jl b/PlotsBase/src/examples.jl index 96c40facc..424bf7e33 100644 --- a/PlotsBase/src/examples.jl +++ b/PlotsBase/src/examples.jl @@ -964,9 +964,9 @@ const _examples = PlotExample[ wireframe( args..., title = "wire-flip-$ax", - xflip = ax === :x, - yflip = ax === :y, - zflip = ax === :z; + xflip = ax ≡ :x, + yflip = ax ≡ :y, + zflip = ax ≡ :z; kw..., ), ) @@ -978,9 +978,9 @@ const _examples = PlotExample[ wireframe( args..., title = "wire-mirror-$ax", - xmirror = ax === :x, - ymirror = ax === :y, - zmirror = ax === :z; + xmirror = ax ≡ :x, + ymirror = ax ≡ :y, + zmirror = ax ≡ :z; kw..., ), ) @@ -1371,16 +1371,16 @@ function test_examples( PlotsBase.Commons.debug!($debug) backend($(QuoteNode(pkgname))) rng = $rng - rng === nothing || Random.seed!(rng, PlotsBase.PLOTS_SEED) + rng ≡ nothing || Random.seed!(rng, PlotsBase.PLOTS_SEED) theme(:default) end) - (imp = _examples[i].imports) === nothing || Base.eval(m, imp) + (imp = _examples[i].imports) ≡ nothing || Base.eval(m, imp) exprs = _examples[i].exprs - rng === nothing || (exprs = PlotsBase.replace_rand(exprs)) + rng ≡ nothing || (exprs = PlotsBase.replace_rand(exprs)) Base.eval(m, exprs) disp && Base.eval(m, :(gui(current()))) - callback === nothing || callback(m, pkgname, i) + callback ≡ nothing || callback(m, pkgname, i) m.PlotsBase.current() end @@ -1415,7 +1415,7 @@ function test_examples( end # COV_EXCL_STOP end - sleep === nothing || Base.sleep(sleep) + sleep ≡ nothing || Base.sleep(sleep) end plts end diff --git a/PlotsBase/src/layouts.jl b/PlotsBase/src/layouts.jl index 2f80680f0..92faee2fc 100644 --- a/PlotsBase/src/layouts.jl +++ b/PlotsBase/src/layouts.jl @@ -75,7 +75,7 @@ function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...) orighor = :left origver = :top for oarg in oargs - if oarg === :center + if oarg ≡ :center orighor = origver = oarg elseif oarg in (:left, :right, :hcenter) orighor = oarg @@ -94,14 +94,14 @@ function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top) y = make_measure_vert(y) width = make_measure_hor(width) height = make_measure_vert(height) - left = if h_anchor === :left + left = if h_anchor ≡ :left x elseif h_anchor in (:center, :hcenter) 0.5w - 0.5width + x else 1w - x - width end - top = if v_anchor === :top + top = if v_anchor ≡ :top y elseif v_anchor in (:center, :vcenter) 0.5h - 0.5height + y @@ -598,7 +598,7 @@ function link_axes!(layout::GridLayout, link::Symbol) link_axes!(layout.grid[r, :], :yaxis) end end - if link === :square + if link ≡ :square if (sps = filter(l -> isa(l, Subplot), layout.grid)) |> !isempty base_axis = sps[1][:xaxis] for sp in sps @@ -607,7 +607,7 @@ function link_axes!(layout::GridLayout, link::Symbol) end end end - if link === :all + if link ≡ :all link_axes!(layout.grid, :xaxis) link_axes!(layout.grid, :yaxis) end @@ -623,7 +623,7 @@ function twin(sp, letter) ax = orig_sp[get_attr_symbol(letter, :axis)] ax[:grid] = false # disable the grid (overlaps with twin axis) end - if orig_sp[:framestyle] === :box + if orig_sp[:framestyle] ≡ :box # incompatible with shared axes (see github.com/JuliaPlots/Plots.jl/issues/2894) orig_sp[:framestyle] = :axes end diff --git a/PlotsBase/src/output.jl b/PlotsBase/src/output.jl index 6b9a886fa..88822678c 100644 --- a/PlotsBase/src/output.jl +++ b/PlotsBase/src/output.jl @@ -170,7 +170,7 @@ end _do_plot_show(plt, showval::Bool) = showval && gui(plt) function _do_plot_show(plt, showval::Symbol) - showval === :gui && gui(plt) + showval ≡ :gui && gui(plt) showval in (:inline, :ijulia) && inline(plt) end @@ -182,10 +182,10 @@ const _best_html_output_type = # a backup for html... passes to svg or png depending on the html_output_format arg function _show(io::IO, ::MIME"text/html", plt::Plot) output_type = Symbol(plt.attr[:html_output_format]) - if output_type === :auto + if output_type ≡ :auto output_type = get(_best_html_output_type, backend_name(plt.backend), :svg) end - if output_type === :png + if output_type ≡ :png # @info "writing png to html output" print( io, @@ -193,10 +193,10 @@ function _show(io::IO, ::MIME"text/html", plt::Plot) base64encode(show, MIME("image/png"), plt), "\" />", ) - elseif output_type === :svg + elseif output_type ≡ :svg # @info "writing svg to html output" show(io, MIME("image/svg+xml"), plt) - elseif output_type === :txt + elseif output_type ≡ :txt show(io, MIME("text/plain"), plt) else error("only png or svg allowed. got: $(repr(output_type))") diff --git a/PlotsBase/src/pipeline.jl b/PlotsBase/src/pipeline.jl index 93764ebbb..04d73ed29 100644 --- a/PlotsBase/src/pipeline.jl +++ b/PlotsBase/src/pipeline.jl @@ -162,7 +162,7 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::PlotsBase.Plot, err_inds = findall(kw -> get(kw, :seriestype, :path) in (:xerror, :yerror, :zerror), kw_list) for ind in err_inds - if ind > 1 && get(kw_list[ind - 1], :seriestype, :path) === :scatter + if ind > 1 && get(kw_list[ind - 1], :seriestype, :path) ≡ :scatter tmp = copy(kw_list[ind]) kw_list[ind] = copy(kw_list[ind - 1]) kw_list[ind - 1] = tmp @@ -248,7 +248,7 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) sp = get_subplot( plt, _cycle( - sps === :auto ? plt.subplots : plt.subplots[sps], + sps ≡ :auto ? plt.subplots : plt.subplots[sps], series_idx(kw_list, kw), ), ) @@ -356,12 +356,12 @@ function RecipesPipeline.add_series!(plt::Plot, plotattributes) if (perm = plotattributes[:permute]) !== :none letter1, letter2 = perm ms = plotattributes[:markershape] - if ms === :hline && (perm == (:x, :y) || perm == (:y, :x)) + if ms ≡ :hline && (perm == (:x, :y) || perm == (:y, :x)) plotattributes[:markershape] = :vline - elseif ms === :vline && (perm == (:x, :y) || perm == (:y, :x)) + elseif ms ≡ :vline && (perm == (:x, :y) || perm == (:y, :x)) plotattributes[:markershape] = :hline end - if plotattributes[:seriestype] === :bar # bar calls expand_extrema! in its recipe... + if plotattributes[:seriestype] ≡ :bar # bar calls expand_extrema! in its recipe... sp = plotattributes[:subplot] sp[get_attr_symbol(letter1, :axis)][:lims], sp[get_attr_symbol(letter2, :axis)][:lims] = @@ -389,7 +389,7 @@ function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T} # change to a 3d projection for this subplot? if ( RecipesPipeline.needs_3d_axes(st) || - (st === :quiver && plotattributes[:z] !== nothing) + (st ≡ :quiver && plotattributes[:z] !== nothing) ) sp.attr[:projection] = "3d" end @@ -404,7 +404,7 @@ end function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol) # adjust extrema and discrete info - if st === :image + if st ≡ :image xmin, xmax = ignorenan_extrema(plotattributes[:x]) ymin, ymax = ignorenan_extrema(plotattributes[:y]) expand_extrema!(sp[:xaxis], (xmin, xmax)) @@ -426,11 +426,11 @@ function _add_the_series(plt, sp, plotattributes) plt[:extra_plot_kwargs] = get(kw, :plot, KW()) sp[:extra_kwargs] = get(kw, :subplot, KW()) plotattributes[:extra_kwargs] = get(kw, :series, KW()) - elseif kw === :plot + elseif kw ≡ :plot plt[:extra_plot_kwargs] = extra_kwargs - elseif kw === :subplot + elseif kw ≡ :subplot sp[:extra_kwargs] = extra_kwargs - elseif kw === :series + elseif kw ≡ :series plotattributes[:extra_kwargs] = extra_kwargs else throw(ArgumentError("Unsupported type for extra keyword arguments")) @@ -438,9 +438,9 @@ function _add_the_series(plt, sp, plotattributes) warn_on_unsupported(plt.backend, plotattributes) series = Series(plotattributes) push!(plt.series_list, series) - if (z_order = plotattributes[:z_order]) === :front + if (z_order = plotattributes[:z_order]) ≡ :front push!(sp.series_list, series) - elseif z_order === :back + elseif z_order ≡ :back pushfirst!(sp.series_list, series) elseif z_order isa Integer insert!(sp.series_list, z_order, series) diff --git a/PlotsBase/src/plot.jl b/PlotsBase/src/plot.jl index 963eea86c..774933f64 100644 --- a/PlotsBase/src/plot.jl +++ b/PlotsBase/src/plot.jl @@ -5,7 +5,7 @@ mutable struct CurrentPlot end const CURRENT_PLOT = CurrentPlot(nothing) -isplotnull() = CURRENT_PLOT.nullableplot === nothing +isplotnull() = CURRENT_PLOT.nullableplot ≡ nothing """ current() @@ -254,7 +254,7 @@ function prepare_output(plt::Plot) force_minpad = get(plt, :force_minpad, ()) isempty(force_minpad) || for i in eachindex(plt.layout.grid) plt.layout.grid[i].minpad = Tuple( - i === nothing ? j : i for + i ≡ nothing ? j : i for (i, j) in zip(force_minpad, plt.layout.grid[i].minpad) ) end diff --git a/PlotsBase/src/recipes.jl b/PlotsBase/src/recipes.jl index 920b7a474..f7e44a3f3 100644 --- a/PlotsBase/src/recipes.jl +++ b/PlotsBase/src/recipes.jl @@ -13,7 +13,7 @@ function seriestype_supported(pkg::AbstractBackend, st::Symbol) supported = true for dep in _series_recipe_deps[st] - if seriestype_supported(pkg, dep) === :no + if seriestype_supported(pkg, dep) ≡ :no supported = false break end @@ -199,11 +199,11 @@ function make_steps(x::AbstractArray, st, even) for i in 2:n xindex = xstartindex - 1 + i idx = 2i - 1 - if st === :mid + if st ≡ :mid newx[idx] = newx[idx - 1] = (x[xindex] + x[xindex - 1]) / 2 else newx[idx] = x[xindex] - newx[idx - 1] = x[st === :pre ? xindex : xindex - 1] + newx[idx - 1] = x[st ≡ :pre ? xindex : xindex - 1] end end even && (newx[end] = x[end]) @@ -294,9 +294,9 @@ end # create vertical line segments from fill @recipe function f(::Type{Val{:sticks}}, x, y, z) # COV_EXCL_LINE n = length(x) - if (fr = plotattributes[:fillrange]) === nothing + if (fr = plotattributes[:fillrange]) ≡ nothing sp = plotattributes[:subplot] - fr = if sp[:yaxis][:scale] === :identity + fr = if sp[:yaxis][:scale] ≡ :identity 0.0 else NaNMath.min(axis_limits(sp, :y)[1], ignorenan_minimum(y)) @@ -321,9 +321,9 @@ end fillrange := nothing seriestype := :path if ( - plotattributes[:linecolor] === :auto && + plotattributes[:linecolor] ≡ :auto && plotattributes[:marker_z] !== nothing && - plotattributes[:line_z] === nothing + plotattributes[:line_z] ≡ nothing ) line_z := plotattributes[:marker_z] end @@ -388,7 +388,7 @@ end x := newx y := newy - if z === nothing + if z ≡ nothing seriestype := :path else seriestype := :path3d @@ -422,7 +422,7 @@ end # compute half-width of bars bw = plotattributes[:bar_width] - hw = if bw === nothing + hw = if bw ≡ nothing 0.5Commons._bar_width * if nx > 1 ignorenan_minimum(filter(x -> x > 0, diff(sort(procx)))) else @@ -433,7 +433,7 @@ end end # make fillto a vector... default fills to 0 - if (fillto = plotattributes[:fillrange]) === nothing + if (fillto = plotattributes[:fillrange]) ≡ nothing fillto = 0 end if yscale in _log_scales && !all(_is_positive, fillto) @@ -587,7 +587,7 @@ end @recipe function f(::Type{Val{:barbins}}, x, y, z) # COV_EXCL_LINE edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y) - if plotattributes[:bar_width] === nothing + if plotattributes[:bar_width] ≡ nothing bar_width := diff(edge) end x := _bin_centers(edge) @@ -729,19 +729,19 @@ function _auto_binning_nbins( end v = vs[dim] - mode === :auto && (mode = :fd) + mode ≡ :auto && (mode = :fd) - if mode === :sqrt # Square-root choice + if mode ≡ :sqrt # Square-root choice _cl(sqrt(n_samples)) - elseif mode === :sturges # Sturges' formula + elseif mode ≡ :sturges # Sturges' formula _cl(log2(n_samples) + 1) - elseif mode === :rice # Rice Rule + elseif mode ≡ :rice # Rice Rule _cl(2 * nd) - elseif mode === :scott # Scott's normal reference rule + elseif mode ≡ :scott # Scott's normal reference rule _cl(_span(v) / (3.5 * std(v) / nd)) - elseif mode === :fd # Freedman–Diaconis rule + elseif mode ≡ :fd # Freedman–Diaconis rule _cl(_span(v) / (2 * _iqr(v) / nd)) - elseif mode === :wand + elseif mode ≡ :wand wand_edges(v) # this makes this function not type stable, but the type instability does not propagate else error("Unknown auto-binning mode $mode") @@ -782,7 +782,7 @@ function _make_hist( localvs = _filternans(vs) edges = _hist_edges(localvs, binning) h = float( - weights === nothing ? + weights ≡ nothing ? StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) : StatsBase.fit( StatsBase.Histogram, @@ -856,7 +856,7 @@ end ) seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype]) - if plotattributes[:seriestype] === :scatterbins + 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) @@ -883,7 +883,7 @@ end float_weights = float(weights) if !plotattributes[:show_empty_bins] - if float_weights === weights + if float_weights ≡ weights float_weights = deepcopy(float_weights) end for (i, c) in enumerate(float_weights) @@ -961,7 +961,7 @@ end @recipe function f(::Type{Val{:scatter3d}}, x, y, z) # COV_EXCL_LINE seriestype := :path3d - if plotattributes[:markershape] === :none + if plotattributes[:markershape] ≡ :none markershape := :circle end linewidth := 0 @@ -1084,9 +1084,9 @@ Commons.@attributes function error_style!(plotattributes::AKW) RecipesPipeline.reset_kw!(plotattributes, :marker_z) haskey(plotattributes, :line_z) && RecipesPipeline.reset_kw!(plotattributes, :line_z) - msc = if (msc = plotattributes[:markerstrokecolor]) === :match + msc = if (msc = plotattributes[:markerstrokecolor]) ≡ :match plotattributes[:subplot][:foreground_color_subplot] - elseif msc === :auto + elseif msc ≡ :auto get_series_color( plotattributes[:linecolor], plotattributes[:subplot], @@ -1137,13 +1137,13 @@ clamp_to_eps!(ary) = (replace!(x -> x <= 0.0 ? Base.eps(Float64) : x, ary); noth Commons.error_style!(plotattributes) markershape := :vline xerr = error_zipit(plotattributes[:xerror]) - if z === nothing + if z ≡ nothing plotattributes[:x], plotattributes[:y] = error_coords(xerr, x, y) else plotattributes[:x], plotattributes[:y], plotattributes[:z] = error_coords(xerr, x, y, z) end - if :xscale ∈ keys(plotattributes) && plotattributes[:xscale] === :log10 + if :xscale ∈ keys(plotattributes) && plotattributes[:xscale] ≡ :log10 clamp_to_eps!(plotattributes[:x]) end () @@ -1154,13 +1154,13 @@ end Commons.error_style!(plotattributes) markershape := :hline yerr = error_zipit(plotattributes[:yerror]) - if z === nothing + if z ≡ nothing plotattributes[:y], plotattributes[:x] = error_coords(yerr, y, x) else plotattributes[:y], plotattributes[:x], plotattributes[:z] = error_coords(yerr, y, x, z) end - if :yscale ∈ keys(plotattributes) && plotattributes[:yscale] === :log10 + if :yscale ∈ keys(plotattributes) && plotattributes[:yscale] ≡ :log10 clamp_to_eps!(plotattributes[:y]) end () @@ -1175,7 +1175,7 @@ end plotattributes[:z], plotattributes[:x], plotattributes[:y] = error_coords(zerr, z, x, y) end - if :zscale ∈ keys(plotattributes) && plotattributes[:zscale] === :log10 + if :zscale ∈ keys(plotattributes) && plotattributes[:zscale] ≡ :log10 clamp_to_eps!(plotattributes[:z]) end () @@ -1456,7 +1456,7 @@ end @recipe f(x::AVec, ohlc::AVec{NTuple{N,<:Number}}) where {N} = x, map(t -> OHLC(t...), ohlc) @recipe f(xyuv::AVec{NTuple}) = - get(plotattributes, :seriestype, :path) === :ohlc ? map(t -> OHLC(t...), xyuv) : + get(plotattributes, :seriestype, :path) ≡ :ohlc ? map(t -> OHLC(t...), xyuv) : RecipesPipeline.unzip(xyuv) @recipe function f(x::AVec, v::AVec{OHLC}) # COV_EXCL_LINE diff --git a/PlotsBase/src/utils.jl b/PlotsBase/src/utils.jl index b7c4ecea9..7201431c1 100644 --- a/PlotsBase/src/utils.jl +++ b/PlotsBase/src/utils.jl @@ -76,11 +76,11 @@ function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot) # update alphas for asym in (:linealpha, :markeralpha, :fillalpha) - if plotattributes[asym] === nothing + if plotattributes[asym] ≡ nothing plotattributes[asym] = plotattributes[:seriesalpha] end end - if plotattributes[:markerstrokealpha] === nothing + if plotattributes[:markerstrokealpha] ≡ nothing plotattributes[:markerstrokealpha] = plotattributes[:markeralpha] end @@ -92,13 +92,13 @@ function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot) # update other colors (`linecolor`, `markercolor`, `fillcolor`) <- for grep for s in (:line, :marker, :fill) csym, asym = Symbol(s, :color), Symbol(s, :alpha) - plotattributes[csym] = if plotattributes[csym] === :auto - plot_color(if Commons.has_black_border_for_default(stype) && s === :line + plotattributes[csym] = if plotattributes[csym] ≡ :auto + plot_color(if Commons.has_black_border_for_default(stype) && s ≡ :line sp[:foreground_color_subplot] else scolor end) - elseif plotattributes[csym] === :match + elseif plotattributes[csym] ≡ :match plot_color(scolor) else get_series_color(plotattributes[csym], sp, plotIndex, stype) @@ -106,9 +106,9 @@ function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot) end # update markerstrokecolor - plotattributes[:markerstrokecolor] = if plotattributes[:markerstrokecolor] === :match + plotattributes[:markerstrokecolor] = if plotattributes[:markerstrokecolor] ≡ :match plot_color(sp[:foreground_color_subplot]) - elseif plotattributes[:markerstrokecolor] === :auto + elseif plotattributes[:markerstrokecolor] ≡ :auto get_series_color(plotattributes[:markercolor], sp, plotIndex, stype) else get_series_color(plotattributes[:markerstrokecolor], sp, plotIndex, stype) @@ -128,7 +128,7 @@ function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot) # scatter plots don't have a line, but must have a shape if plotattributes[:seriestype] in (:scatter, :scatterbins, :scatterhist, :scatter3d) plotattributes[:linewidth] = 0 - if plotattributes[:markershape] === :none + if plotattributes[:markershape] ≡ :none plotattributes[:markershape] = :circle end end @@ -215,7 +215,7 @@ heatmap_edges( scale::Symbol = :identity, isedges::Bool = false, ispolar::Bool = false, -) = _heatmap_edges(Val(scale === :identity), v, scale, isedges, ispolar) +) = _heatmap_edges(Val(scale ≡ :identity), v, scale, isedges, ispolar) function heatmap_edges( x::AVec, @@ -239,8 +239,8 @@ function heatmap_edges( ArgumentError |> throw ( - _heatmap_edges(Val(xscale === :identity), x, xscale, isedges, false), - _heatmap_edges(Val(yscale === :identity), y, yscale, isedges, ispolar), # special handle for `r` in polar plots + _heatmap_edges(Val(xscale ≡ :identity), x, xscale, isedges, false), + _heatmap_edges(Val(yscale ≡ :identity), y, yscale, isedges, ispolar), # special handle for `r` in polar plots ) end @@ -287,10 +287,10 @@ ticks_type(ticks::Tuple{<:Union{AVec,Tuple},<:Union{AVec,Tuple}}) = :ticks_and_l ticks_type(ticks) = :invalid limsType(lims::Tuple{<:Real,<:Real}) = :limits -limsType(lims::Symbol) = lims === :auto ? :auto : :invalid +limsType(lims::Symbol) = lims ≡ :auto ? :auto : :invalid limsType(lims) = :invalid -isautop(sp::Subplot) = sp[:projection_type] === :auto +isautop(sp::Subplot) = sp[:projection_type] ≡ :auto isortho(sp::Subplot) = sp[:projection_type] ∈ (:ortho, :orthographic) ispersp(sp::Subplot) = sp[:projection_type] ∈ (:persp, :perspective) @@ -305,7 +305,7 @@ nanappend!(a::AbstractVector, b) = (push!(a, NaN); append!(a, b); nothing) function nansplit(v::AVec) vs = Vector{eltype(v)}[] while true - if (idx = findfirst(isnan, v)) === nothing + if (idx = findfirst(isnan, v)) ≡ nothing # no nans push!(vs, v) break @@ -339,7 +339,7 @@ function make_fillrange_from_ribbon(kw::AKW) rib1, rib2 = -first(rib), last(rib) # kw[:ribbon] = nothing kw[:fillrange] = make_fillrange_side(y, rib1), make_fillrange_side(y, rib2) - (get(kw, :fillalpha, nothing) === nothing) && (kw[:fillalpha] = 0.5) + (get(kw, :fillalpha, nothing) ≡ nothing) && (kw[:fillalpha] = 0.5) end #turn tuple of fillranges to one path @@ -407,7 +407,7 @@ function Commons.preprocess_attributes!(plotattributes::AKW) xformatter = get(plotattributes, :xformatter, :auto) yformatter = get(plotattributes, :yformatter, :auto) yformatter !== :auto && (plotattributes[:xformatter] = yformatter) - xformatter === :auto && + xformatter ≡ :auto && haskey(plotattributes, :yformatter) && pop!(plotattributes, :yformatter) end @@ -515,7 +515,7 @@ function Commons.preprocess_attributes!(plotattributes::AKW) if haskey(plotattributes, :markershape) plotattributes[:markershape] = Commons._replace_markershape(plotattributes[:markershape]) - if plotattributes[:markershape] === :none && + if plotattributes[:markershape] ≡ :none && get(plotattributes, :seriestype, :path) in (:scatter, :scatterbins, :scatterhist, :scatter3d) #the default should be :auto, not :none, so that :none can be set explicitly and would be respected plotattributes[:markershape] = :circle @@ -947,7 +947,7 @@ Computes the distances of the plot limits to a sample of points at the extremes the ranges, and places the legend at the corner where the maximum distance to the limits is found. """ function _guess_best_legend_position(lp::Symbol, plt) - lp === :best || return lp + lp ≡ :best || return lp _guess_best_legend_position(xlims(plt), ylims(plt), plt) end diff --git a/PlotsBase/test/test_args.jl b/PlotsBase/test/test_args.jl index 1e0da0f8e..a1c4207ee 100644 --- a/PlotsBase/test/test_args.jl +++ b/PlotsBase/test/test_args.jl @@ -16,14 +16,14 @@ x = collect(0.0:10.0) foo = Foo(x, sin.(x)) @testset "Magic attributes" begin - @test plot(foo)[1][1][:markershape] === :+ - @test plot(foo, markershape = :diamond)[1][1][:markershape] === :diamond - @test plot(foo, marker = :diamond)[1][1][:markershape] === :diamond + @test plot(foo)[1][1][:markershape] ≡ :+ + @test plot(foo, markershape = :diamond)[1][1][:markershape] ≡ :diamond + @test plot(foo, marker = :diamond)[1][1][:markershape] ≡ :diamond @test (@test_logs (:warn, "Skipped marker arg diamond.") plot( foo, marker = :diamond, markershape = :diamond, - )[1][1][:markershape]) === :diamond + )[1][1][:markershape]) ≡ :diamond end @testset "Subplot Attributes" begin diff --git a/PlotsBase/test/test_axes.jl b/PlotsBase/test/test_axes.jl index 462a11c41..5b9c82716 100644 --- a/PlotsBase/test/test_axes.jl +++ b/PlotsBase/test/test_axes.jl @@ -157,9 +157,9 @@ end @test haskey(PlotsBase.Commons._keyAliases, :x_guide_position) @test !haskey(PlotsBase.Commons._keyAliases, :xguide_position) pl = plot(1:2, xl = "x label") - @test pl[1][:xaxis][:guide] === "x label" + @test pl[1][:xaxis][:guide] ≡ "x label" pl = plot(1:2, xrange = (0, 3)) - @test xlims(pl) === (0, 3) + @test xlims(pl) ≡ (0, 3) pl = plot(1:2, xtick = [1.25, 1.5, 1.75]) @test pl[1][:xaxis][:ticks] == [1.25, 1.5, 1.75] pl = plot(1:2, xlabelfontsize = 4) @@ -167,17 +167,17 @@ end pl = plot(1:2, xgα = 0.07) @test pl[1][:xaxis][:gridalpha] ≈ 0.07 pl = plot(1:2, xgridls = :dashdot) - @test pl[1][:xaxis][:gridstyle] === :dashdot + @test pl[1][:xaxis][:gridstyle] ≡ :dashdot pl = plot(1:2, xgridcolor = :red) - @test pl[1][:xaxis][:foreground_color_grid] === RGBA{Float64}(1.0, 0.0, 0.0, 1.0) + @test pl[1][:xaxis][:foreground_color_grid] ≡ RGBA{Float64}(1.0, 0.0, 0.0, 1.0) pl = plot(1:2, xminorgridcolor = :red) - @test pl[1][:xaxis][:foreground_color_minor_grid] === RGBA{Float64}(1.0, 0.0, 0.0, 1.0) + @test pl[1][:xaxis][:foreground_color_minor_grid] ≡ RGBA{Float64}(1.0, 0.0, 0.0, 1.0) pl = plot(1:2, xgrid_lw = 0.01) @test pl[1][:xaxis][:gridlinewidth] ≈ 0.01 pl = plot(1:2, xminorgrid_lw = 0.01) @test pl[1][:xaxis][:minorgridlinewidth] ≈ 0.01 pl = plot(1:2, xtickor = :out) - @test pl[1][:xaxis][:tick_direction] === :out + @test pl[1][:xaxis][:tick_direction] ≡ :out end @testset "Aliases" begin @@ -188,7 +188,7 @@ end pl = plot(1:2, label = "test") @test compare(pl, :guide, "", ===) pl = plot(1:2, lim = (0, 3)) - @test xlims(pl) === ylims(pl) === zlims(pl) === (0, 3) + @test xlims(pl) ≡ ylims(pl) ≡ zlims(pl) ≡ (0, 3) pl = plot(1:2, tick = [1.25, 1.5, 1.75]) @test compare(pl, :ticks, [1.25, 1.5, 1.75], ==) pl = plot(1:2, labelfontsize = 4) @@ -239,7 +239,7 @@ end @testset "no labels" begin # github.com/JuliaPlots/Plots.jl/issues/4475 pl = plot(100:100:300, hcat([1, 2, 4], [-1, -2, -4]); yformatter = :none) - @test pl[1][:yaxis][:formatter] === :none + @test pl[1][:yaxis][:formatter] ≡ :none end @testset "minor ticks" begin @@ -247,9 +247,9 @@ end for minor_intervals in (:auto, :none, nothing, false, true, 0, 1, 2, 3, 4, 5) n_minor_ticks_per_major = if minor_intervals isa Bool minor_intervals ? PlotsBase.Ticks.DEFAULT_MINOR_INTERVALS[] - 1 : 0 - elseif minor_intervals === :auto + elseif minor_intervals ≡ :auto PlotsBase.Ticks.DEFAULT_MINOR_INTERVALS[] - 1 - elseif minor_intervals === :none || minor_intervals isa Nothing + elseif minor_intervals ≡ :none || minor_intervals isa Nothing 0 else max(0, minor_intervals - 1) @@ -267,9 +267,9 @@ end @test minor_ticks isa Nothing 0 end - elseif minor_intervals === :auto + elseif minor_intervals ≡ :auto length(minor_ticks) - elseif minor_intervals === :none || minor_intervals isa Nothing + elseif minor_intervals ≡ :none || minor_intervals isa Nothing @test minor_ticks isa Nothing 0 else diff --git a/PlotsBase/test/test_components.jl b/PlotsBase/test/test_components.jl index 501c43bab..0018d4850 100644 --- a/PlotsBase/test/test_components.jl +++ b/PlotsBase/test/test_components.jl @@ -67,7 +67,7 @@ @test coords(myshapes) isa Tuple{Vector{Vector{S}},Vector{Vector{T}}} where {T,S} local pl @test_nowarn pl = plot(myshapes) - @test pl[1][1][:seriestype] === :shape + @test pl[1][1][:seriestype] ≡ :shape end @testset "Misc" begin @@ -102,7 +102,7 @@ end end @testset "Alpha" begin @test brush(0.4).alpha == 0.4 - @test brush(20).alpha === nothing + @test brush(20).alpha ≡ nothing end @testset "Bad Argument" begin # using test_logs because test_warn seems to not work anymore @@ -138,7 +138,7 @@ end @test PlotsBase.series_annotations(["1" "2"; "3" "4"]) isa AbstractMatrix @test PlotsBase.series_annotations(10).strs[1].str == "10" - @test PlotsBase.series_annotations(nothing) === nothing + @test PlotsBase.series_annotations(nothing) ≡ nothing @test PlotsBase.series_annotations(ann) == ann @test PlotsBase.annotations(["1" "2"; "3" "4"]) isa AbstractMatrix diff --git a/PlotsBase/test/test_contours.jl b/PlotsBase/test/test_contours.jl index 5ec0072c8..04c656c00 100644 --- a/PlotsBase/test/test_contours.jl +++ b/PlotsBase/test/test_contours.jl @@ -1,8 +1,8 @@ @testset "check_contour_levels" begin let check_contour_levels = PlotsBase.Commons.check_contour_levels - @test check_contour_levels(2) === nothing - @test check_contour_levels(-1.0:0.2:10.0) === nothing - @test check_contour_levels([-100, -2, -1, 0, 1, 2, 100]) === nothing + @test check_contour_levels(2) ≡ nothing + @test check_contour_levels(-1.0:0.2:10.0) ≡ nothing + @test check_contour_levels([-100, -2, -1, 0, 1, 2, 100]) ≡ nothing @test_throws ArgumentError check_contour_levels(1.0) @test_throws ArgumentError check_contour_levels((1, 2, 3)) @test_throws ArgumentError check_contour_levels(-3) @@ -46,7 +46,7 @@ end @testset "$n contours" for n in (2, 5, 100) p = contour(x, y, z, levels = n) attr = p[1][1].plotattributes - @test attr[:seriestype] === :contour + @test attr[:seriestype] ≡ :contour @test attr[:levels] == n end end diff --git a/PlotsBase/test/test_defaults.jl b/PlotsBase/test/test_defaults.jl index f75fff7af..94cf69614 100644 --- a/PlotsBase/test/test_defaults.jl +++ b/PlotsBase/test/test_defaults.jl @@ -25,16 +25,16 @@ end pl = plot() @test pl[1][:legend_font_family] == "sans-serif" @test pl[1][:legend_font_pointsize] == 8 - @test pl[1][:legend_font_halign] === :hcenter - @test pl[1][:legend_font_valign] === :vcenter + @test pl[1][:legend_font_halign] ≡ :hcenter + @test pl[1][:legend_font_valign] ≡ :vcenter @test pl[1][:legend_font_rotation] == 0.0 @test pl[1][:legend_font_color] == RGB{Colors.N0f8}(0.0, 0.0, 0.0) - @test pl[1][:legend_position] === :best - @test pl[1][:legend_title] === nothing + @test pl[1][:legend_position] ≡ :best + @test pl[1][:legend_title] ≡ nothing @test pl[1][:legend_title_font_family] == "sans-serif" @test pl[1][:legend_title_font_pointsize] == 11 - @test pl[1][:legend_title_font_halign] === :hcenter - @test pl[1][:legend_title_font_valign] === :vcenter + @test pl[1][:legend_title_font_halign] ≡ :hcenter + @test pl[1][:legend_title_font_valign] ≡ :vcenter @test pl[1][:legend_title_font_rotation] == 0.0 @test pl[1][:legend_title_font_color] == RGB{Colors.N0f8}(0.0, 0.0, 0.0) @test pl[1][:legend_background_color] == RGBA{Float64}(1.0, 1.0, 1.0, 1.0) @@ -62,18 +62,18 @@ end ) @test pl[1][:legend_font_family] == "serif" @test pl[1][:legend_font_pointsize] == 12 - @test pl[1][:legend_font_halign] === :left - @test pl[1][:legend_font_valign] === :top + @test pl[1][:legend_font_halign] ≡ :left + @test pl[1][:legend_font_valign] ≡ :top @test pl[1][:legend_font_rotation] == 1.0 - @test pl[1][:legend_font_color] === :red - @test pl[1][:legend_position] === :outertopleft + @test pl[1][:legend_font_color] ≡ :red + @test pl[1][:legend_position] ≡ :outertopleft @test pl[1][:legend_title] == "The legend" @test pl[1][:legend_title_font_family] == "helvetica" @test pl[1][:legend_title_font_pointsize] == 3 - @test pl[1][:legend_title_font_halign] === :right - @test pl[1][:legend_title_font_valign] === :bottom + @test pl[1][:legend_title_font_halign] ≡ :right + @test pl[1][:legend_title_font_valign] ≡ :bottom @test pl[1][:legend_title_font_rotation] == -5.2 - @test pl[1][:legend_title_font_color] === :blue + @test pl[1][:legend_title_font_color] ≡ :blue @test pl[1][:legend_background_color] == RGBA{Float64}(0.0, 1.0, 1.0, 1.0) @test pl[1][:legend_foreground_color] == RGBA{Float64}(0.0, 0.5019607843137255, 0.0, 1.0) @@ -91,7 +91,7 @@ end foreground_color_subplot = :red, )[1] @test PlotsBase.legendfont(sp).pointsize == 12 - @test PlotsBase.legendfont(sp).halign === :left + @test PlotsBase.legendfont(sp).halign ≡ :left # match mechanism @test sp[:legend_font_color] == colorant"black" @test PlotsBase.legendfont(sp).color == colorant"black" diff --git a/PlotsBase/test/test_layouts.jl b/PlotsBase/test/test_layouts.jl index b98df9b4e..52cc7b0aa 100644 --- a/PlotsBase/test/test_layouts.jl +++ b/PlotsBase/test/test_layouts.jl @@ -11,10 +11,10 @@ end layout = 4, yscale = [:identity :identity :log10 :log10], ) - @test pl[1][:yaxis][:scale] === :identity - @test pl[2][:yaxis][:scale] === :identity - @test pl[3][:yaxis][:scale] === :log10 - @test pl[4][:yaxis][:scale] === :log10 + @test pl[1][:yaxis][:scale] ≡ :identity + @test pl[2][:yaxis][:scale] ≡ :identity + @test pl[3][:yaxis][:scale] ≡ :log10 + @test pl[4][:yaxis][:scale] ≡ :log10 end @testset "Plot title" begin @@ -41,9 +41,9 @@ end @testset "Plots.jl/issues/4083" begin pl = plot(plot(1:2), plot(1:2); border = :grid, plot_title = "abc") - @test pl[1][:framestyle] === :grid - @test pl[2][:framestyle] === :grid - @test pl[3][:framestyle] === :none + @test pl[1][:framestyle] ≡ :grid + @test pl[2][:framestyle] ≡ :grid + @test pl[3][:framestyle] ≡ :none end @testset "Allowed subplot counts" begin @@ -116,7 +116,7 @@ end rl = PlotsBase.RootLayout() show(io, rl) - @test parent(rl) === nothing + @test parent(rl) ≡ nothing @test PlotsBase.parent_bbox(rl) == PlotsBase.DEFAULT_BBOX[] @test PlotsBase.bbox(rl) == PlotsBase.DEFAULT_BBOX[] @test PlotsBase.origin(PlotsBase.DEFAULT_BBOX[]) == (0PlotsBase.mm, 0PlotsBase.mm) @@ -125,10 +125,10 @@ end end el = PlotsBase.EmptyLayout() - @test PlotsBase.update_position!(el) === nothing + @test PlotsBase.update_position!(el) ≡ nothing @test size(el) == (0, 0) @test length(el) == 0 - @test el[1, 1] === nothing + @test el[1, 1] ≡ nothing @test PlotsBase.left(el) == 0PlotsBase.mm @test PlotsBase.top(el) == 0PlotsBase.mm diff --git a/PlotsBase/test/test_pgfplotsx.jl b/PlotsBase/test/test_pgfplotsx.jl index 81dde6ea1..5f31cb7d1 100644 --- a/PlotsBase/test/test_pgfplotsx.jl +++ b/PlotsBase/test/test_pgfplotsx.jl @@ -20,7 +20,7 @@ with(:pgfplotsx) do pl = plot(1:5) axis = first(get_pgf_axes(pl)) @test pl.o.the_plot isa PGFPlotsX.TikzDocument - @test pl.series_list[1].plotattributes[:quiver] === nothing + @test pl.series_list[1].plotattributes[:quiver] ≡ nothing @test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1 @test !haskey(axis.contents[1].options.dict, "fill") @test occursin("documentclass", PlotsBase.pgfx_preamble(pl)) @@ -60,7 +60,7 @@ with(:pgfplotsx) do pl = plot!(pl, zeros(n), zeros(n), 1:n, w = 10) axis = first(get_pgf_axes(pl)) if @test_nowarn(haskey(axis.options.dict, "colorbar")) - @test axis["colorbar"] === nothing + @test axis["colorbar"] ≡ nothing end end @@ -190,7 +190,7 @@ with(:pgfplotsx) do pl = heatmap(xs, ys, z, aspect_ratio = 1) axis = first(get_pgf_axes(pl)) if @test_nowarn(haskey(axis.options.dict, "colorbar")) - @test axis["colorbar"] === nothing + @test axis["colorbar"] ≡ nothing @test axis["colormap name"] == "plots1" end @@ -414,14 +414,14 @@ with(:pgfplotsx) do pl = plot(1:5, title = "Test me", titlefont = (2, :left)) @test pl[1][:title] == "Test me" @test pl[1][:titlefontsize] == 2 - @test pl[1][:titlefonthalign] === :left + @test pl[1][:titlefonthalign] ≡ :left ax_opt = first(get_pgf_axes(pl)).options @test ax_opt["title"] == "Test me" @test(haskey(ax_opt.dict, "title style")) isa Test.Pass pl = plot(1:5, plot_title = "Test me", plot_titlefont = (2, :left)) @test pl[:plot_title] == "Test me" @test pl[:plot_titlefontsize] == 2 - @test pl[:plot_titlefonthalign] === :left + @test pl[:plot_titlefonthalign] ≡ :left pl = heatmap( rand(3, 3), colorbar_title = "Test me", @@ -429,7 +429,7 @@ with(:pgfplotsx) do ) @test pl[1][:colorbar_title] == "Test me" @test pl[1][:colorbar_titlefontsize] == 12 - @test pl[1][:colorbar_titlefonthalign] === :right + @test pl[1][:colorbar_titlefonthalign] ≡ :right end @testset "Latexify - LaTeXStrings" begin diff --git a/PlotsBase/test/test_recipes.jl b/PlotsBase/test/test_recipes.jl index b0daef638..9c4abddde 100644 --- a/PlotsBase/test/test_recipes.jl +++ b/PlotsBase/test/test_recipes.jl @@ -7,16 +7,16 @@ using OffsetArrays (1:3, 1:3) end let pl = pl = plot(LegendPlot(); legend = :right) - @test pl[1][:legend_position] === :right + @test pl[1][:legend_position] ≡ :right end let pl = pl = plot(LegendPlot()) - @test pl[1][:legend_position] === :topleft + @test pl[1][:legend_position] ≡ :topleft end let pl = plot(LegendPlot(); legend = :inline) - @test pl[1][:legend_position] === :inline + @test pl[1][:legend_position] ≡ :inline end let pl = plot(LegendPlot(); legend = :inline, ymirror = true) - @test pl[1][:legend_position] === :inline + @test pl[1][:legend_position] ≡ :inline end end @@ -24,7 +24,7 @@ end pl = plot(1:5) lens!(pl, [1, 2], [1, 2], inset = (1, bbox(0.0, 0.0, 0.2, 0.2)), colorbar = false) @test length(pl.series_list) == 4 - @test pl[2][:colorbar] === :none + @test pl[2][:colorbar] ≡ :none end @testset "vline, vspan" begin @@ -97,9 +97,9 @@ end # currently uses plotly seriestypes only @test :surface in PlotsBase.all_seriestypes() unicode_instance = PlotsBase.backend_instance(:unicodeplots) - @test PlotsBase.seriestype_supported(unicode_instance, :surface) === :native - @test PlotsBase.seriestype_supported(unicode_instance, :hspan) === :recipe - @test PlotsBase.seriestype_supported(PlotsBase.NoBackend(), :line) === :native + @test PlotsBase.seriestype_supported(unicode_instance, :surface) ≡ :native + @test PlotsBase.seriestype_supported(unicode_instance, :hspan) ≡ :recipe + @test PlotsBase.seriestype_supported(PlotsBase.NoBackend(), :line) ≡ :native end with(:gr) do diff --git a/PlotsBase/test/test_shorthands.jl b/PlotsBase/test/test_shorthands.jl index fb6a96703..69a9cdf28 100644 --- a/PlotsBase/test/test_shorthands.jl +++ b/PlotsBase/test/test_shorthands.jl @@ -97,7 +97,7 @@ end pl = plot3d([1, 2], [1, 2], [1, 2]) plot3d!(pl, [3, 4], [3, 4], [3, 4]) - @test PlotsBase.series_list(pl[1])[1][:seriestype] === :path3d + @test PlotsBase.series_list(pl[1])[1][:seriestype] ≡ :path3d end @testset "Set Ticks" begin diff --git a/PlotsBase/test/test_utils.jl b/PlotsBase/test/test_utils.jl index e3d9ab01b..5fffd5edf 100644 --- a/PlotsBase/test/test_utils.jl +++ b/PlotsBase/test/test_utils.jl @@ -106,21 +106,21 @@ @test PlotsBase.PlotsPlots.xmax(pl) == 3 @test PlotsBase.Commons.ignorenan_extrema(pl) == (1, 3) - @test PlotsBase.Commons.get_attr_symbol(:x, "lims") === :xlims - @test PlotsBase.Commons.get_attr_symbol(:x, :lims) === :xlims + @test PlotsBase.Commons.get_attr_symbol(:x, "lims") ≡ :xlims + @test PlotsBase.Commons.get_attr_symbol(:x, :lims) ≡ :xlims @test contains(PlotsBase._document_argument(:bar_position), "bar_position") - @test PlotsBase.limsType((1, 1)) === :limits - @test PlotsBase.limsType(:undefined) === :invalid - @test PlotsBase.limsType(:auto) === :auto - @test PlotsBase.limsType(NaN) === :invalid + @test PlotsBase.limsType((1, 1)) ≡ :limits + @test PlotsBase.limsType(:undefined) ≡ :invalid + @test PlotsBase.limsType(:auto) ≡ :auto + @test PlotsBase.limsType(NaN) ≡ :invalid - @test PlotsBase.ticks_type([1, 2]) === :ticks - @test PlotsBase.ticks_type(["1", "2"]) === :labels - @test PlotsBase.ticks_type(([1, 2], ["1", "2"])) === :ticks_and_labels - @test PlotsBase.ticks_type(((1, 2), ("1", "2"))) === :ticks_and_labels - @test PlotsBase.ticks_type(:undefined) === :invalid + @test PlotsBase.ticks_type([1, 2]) ≡ :ticks + @test PlotsBase.ticks_type(["1", "2"]) ≡ :labels + @test PlotsBase.ticks_type(([1, 2], ["1", "2"])) ≡ :ticks_and_labels + @test PlotsBase.ticks_type(((1, 2), ("1", "2"))) ≡ :ticks_and_labels + @test PlotsBase.ticks_type(:undefined) ≡ :invalid pl = plot(1:2, 1:2, 1:2, proj_type = :ortho) @test PlotsBase.isortho(first(pl.subplots)) @@ -195,45 +195,45 @@ end pl = plot(x, x, label = "linear") pl = plot!(x, x .^ 2, label = "quadratic") pl = plot!(x, x .^ 3, label = "cubic") - @test PlotsBase._guess_best_legend_position(:best, pl) === :topleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topleft x = OffsetArrays.OffsetArray(0:0.01:2, OffsetArrays.Origin(-3)) pl = plot(x, x, label = "linear") pl = plot!(x, x .^ 2, label = "quadratic") pl = plot!(x, x .^ 3, label = "cubic") - @test PlotsBase._guess_best_legend_position(:best, pl) === :topleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topleft x = OffsetArrays.OffsetArray(0:0.01:2, OffsetArrays.Origin(+3)) pl = plot(x, x, label = "linear") pl = plot!(x, x .^ 2, label = "quadratic") pl = plot!(x, x .^ 3, label = "cubic") - @test PlotsBase._guess_best_legend_position(:best, pl) === :topleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topleft x = 0:0.01:2 pl = plot(x, -x, label = "linear") pl = plot!(x, -x .^ 2, label = "quadratic") pl = plot!(x, -x .^ 3, label = "cubic") - @test PlotsBase._guess_best_legend_position(:best, pl) === :bottomleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :bottomleft x = OffsetArrays.OffsetArray(0:0.01:2, OffsetArrays.Origin(-3)) pl = plot(x, -x, label = "linear") pl = plot!(x, -x .^ 2, label = "quadratic") pl = plot!(x, -x .^ 3, label = "cubic") - @test PlotsBase._guess_best_legend_position(:best, pl) === :bottomleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :bottomleft x = [0, 1, 0, 1] y = [0, 0, 1, 1] pl = scatter(x, y, xlims = [0.0, 1.3], ylims = [0.0, 1.3], label = "test") - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright pl = scatter(x, y, xlims = [-0.3, 1.0], ylims = [-0.3, 1.0], label = "test") - @test PlotsBase._guess_best_legend_position(:best, pl) === :bottomleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :bottomleft pl = scatter(x, y, xlims = [0.0, 1.3], ylims = [-0.3, 1.0], label = "test") - @test PlotsBase._guess_best_legend_position(:best, pl) === :bottomright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :bottomright pl = scatter(x, y, xlims = [-0.3, 1.0], ylims = [0.0, 1.3], label = "test") - @test PlotsBase._guess_best_legend_position(:best, pl) === :topleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topleft y1 = [ 0.6640202072697099, @@ -250,48 +250,48 @@ end y2 = [0.40089741940615464, 0.6687326060649715, 0.6844117863127116] pl = plot(1:10, y1) pl = plot!(1:3, y2, xlims = (0, 10), ylims = (0, 1)) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright # test empty plot pl = plot([]) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright # test that we didn't overlap other placements - @test PlotsBase._guess_best_legend_position(:bottomleft, pl) === :bottomleft + @test PlotsBase._guess_best_legend_position(:bottomleft, pl) ≡ :bottomleft # test singleton pl = plot(1:1) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright # test cycling indexes x = 0.0:0.1:1 y = [1, 2, 3] pl = scatter(x, y) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright # Test step plot with variable limits x = 0:0.001:1 y = vcat([0.0 for _ in 1:100], [1.0 for _ in 101:200], [0.5 for _ in 201:1001]) pl = scatter(x, y) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright pl = scatter(x, y, xlims = [0, 0.25]) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topleft pl = scatter(x, y, xlims = [0.1, 0.25]) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright pl = scatter(x, y, xlims = [0.18, 0.25]) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright pl = scatter(x, y, ylims = [-1, 0.75]) - @test PlotsBase._guess_best_legend_position(:best, pl) === :bottomright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :bottomright pl = scatter(x, y, ylims = [0.25, 0.75]) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright pl = scatter(-x, y, ylims = [0.25, 0.75]) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright pl = scatter(-x, y) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topleft pl = scatter(-x, -y) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topleft + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topleft pl = scatter(x, -y) - @test PlotsBase._guess_best_legend_position(:best, pl) === :topright + @test PlotsBase._guess_best_legend_position(:best, pl) ≡ :topright end @testset "dispatch" begin diff --git a/RecipesBase/src/RecipesBase.jl b/RecipesBase/src/RecipesBase.jl index 5f3410aff..aa5103caa 100644 --- a/RecipesBase/src/RecipesBase.jl +++ b/RecipesBase/src/RecipesBase.jl @@ -73,10 +73,10 @@ _is_arrow_tuple(expr::Expr) = expr.head ≡ :tuple && !isempty(expr.args) && isa(expr.args[1], Expr) && - expr.args[1].head === :(-->) + expr.args[1].head ≡ :(-->) -_equals_symbol(x::Symbol, sym::Symbol) = x === sym -_equals_symbol(x::QuoteNode, sym::Symbol) = x.value === sym +_equals_symbol(x::Symbol, sym::Symbol) = x ≡ sym +_equals_symbol(x::QuoteNode, sym::Symbol) = x.value ≡ sym _equals_symbol(x, sym::Symbol) = false # build an apply_recipe function header from the recipe function header @@ -112,7 +112,7 @@ function create_kw_body(func_signature::Expr) if isa(arg1, Expr) && arg1.head ≡ :parameters for kwpair in arg1.args k, v = kwpair.args - if isa(k, Expr) && k.head === :(::) + if isa(k, Expr) && k.head ≡ :(::) k = k.args[1] @warn """ Type annotations on keyword arguments not currently supported in recipes. @@ -163,14 +163,14 @@ function process_recipe_body!(expr::Expr) # the unused operator `:=` will mean force: `x := 5` is equivalent to `x --> 5, force` # note: this means "x is defined as 5" - if e.head === :(:=) + if e.head ≡ :(:=) force = true e.head = :(-->) end # we are going to recursively swap out `a --> b, flags...` commands # note: this means "x may become 5" - if e.head === :(-->) + if e.head ≡ :(-->) k, v = e.args if isa(k, Symbol) k = QuoteNode(k) @@ -298,7 +298,7 @@ macro recipe(funcexpr::Expr) $cleanup_body series_list = $RecipesBase.RecipeData[] func_return = $func_body - func_return === nothing || push!( + func_return ≡ nothing || push!( series_list, $RecipesBase.RecipeData( plotattributes, diff --git a/src/Plots.jl b/src/Plots.jl index aabee092f..2850b1315 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -20,12 +20,12 @@ end function load_default_backend() # environment variable preempts the `Preferences` based mechanism - PlotsBase.CURRENT_BACKEND.sym = + sym = PlotsBase.CURRENT_BACKEND.sym = get(ENV, "PLOTS_DEFAULT_BACKEND", PLOTS_DEFAULT_BACKEND) |> lowercase |> Symbol if (pkg_name = PlotsBase.backend_package_name()) ≡ :GR @eval import GR end - PlotsBase.backend(PlotsBase.Plots.backend_instance(Base.CURRENT_BACKEND.sym)) + PlotsBase.backend(PlotsBase.backend_instance(sym)) end function set_default_backend!( diff --git a/test/runtests.jl b/test/runtests.jl index b9fc63c86..637548de8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -34,7 +34,7 @@ for name in ( end end -if PREVIOUS_DEFAULT_BACKEND === nothing +if PREVIOUS_DEFAULT_BACKEND ≡ nothing delete_preferences!(Plots, "default_backend") # restore the absence of a preference else Plots.set_default_backend!(PREVIOUS_DEFAULT_BACKEND) # reset to previous state From 28a293438f012e7a925348c7f5cf78d2852c00ea Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 18:19:12 +0200 Subject: [PATCH 07/58] syntax --- PlotsBase/src/Annotations.jl | 2 +- PlotsBase/src/Axes.jl | 4 +-- PlotsBase/src/Colorbars.jl | 22 ++++++++-------- PlotsBase/src/Commons/Commons.jl | 2 +- PlotsBase/src/Commons/attrs.jl | 6 ++--- PlotsBase/src/Fonts.jl | 2 +- PlotsBase/src/Series.jl | 10 ++++---- PlotsBase/src/Subplots.jl | 8 +++--- PlotsBase/src/Ticks.jl | 2 +- PlotsBase/src/axes_utils.jl | 20 +++++++-------- PlotsBase/src/layouts.jl | 8 +++--- PlotsBase/src/pipeline.jl | 16 ++++++------ PlotsBase/src/recipes.jl | 40 +++++++++++++++--------------- PlotsBase/src/utils.jl | 10 ++++---- PlotsBase/test/test_pgfplotsx.jl | 6 ++--- RecipesPipeline/src/user_recipe.jl | 16 ++++++------ 16 files changed, 87 insertions(+), 87 deletions(-) diff --git a/PlotsBase/src/Annotations.jl b/PlotsBase/src/Annotations.jl index 66b0de52e..791a752a0 100644 --- a/PlotsBase/src/Annotations.jl +++ b/PlotsBase/src/Annotations.jl @@ -87,7 +87,7 @@ end function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels) anns = series[:series_annotations] - if anns !== nothing && anns.baseshape !== nothing + if anns ≢ nothing && anns.baseshape ≢ nothing # we use baseshape to overwrite the markershape attribute # with a list of custom shapes for each msw, msh = anns.scalefactor diff --git a/PlotsBase/src/Axes.jl b/PlotsBase/src/Axes.jl index be7d171cc..a99929e74 100644 --- a/PlotsBase/src/Axes.jl +++ b/PlotsBase/src/Axes.jl @@ -147,7 +147,7 @@ function Commons.axis_limits( # widen max radius so ticks dont overlap with theta axis amin, amax = 0, amax + 0.1abs(amax - amin) end - elseif lims_factor !== nothing + elseif lims_factor ≢ nothing amin, amax = scale_lims(amin, amax, lims_factor, axis[:scale]) elseif lims ≡ :round amin, amax = round_limits(amin, amax, axis[:scale]) @@ -411,7 +411,7 @@ function PlotsBase.get_ticks( if ( axis[:letter] ≡ :x && ticks isa Symbol && - ticks !== :none && + ticks ≢ :none && !isempty(dvals) && ispolar(sp) ) diff --git a/PlotsBase/src/Colorbars.jl b/PlotsBase/src/Colorbars.jl index cfac48642..6aba98907 100644 --- a/PlotsBase/src/Colorbars.jl +++ b/PlotsBase/src/Colorbars.jl @@ -33,11 +33,11 @@ function update_clims(sp::Subplot, op = process_clims(sp[:clims]))::Tuple{Float6 # Avoid calling the inner `update_clims` if at all possible; dynamic dispatch hell if ( series[:seriestype] ∈ Commons._z_colored_series && - series[:z] !== nothing + series[:z] ≢ nothing ) || - series[:line_z] !== nothing || - series[:marker_z] !== nothing || - series[:fill_z] !== nothing + series[:line_z] ≢ nothing || + series[:marker_z] ≢ nothing || + series[:fill_z] ≢ nothing zmin, zmax = _update_clims(zmin, zmax, update_clims(series, op)...) else zmin, zmax = _update_clims(zmin, zmax, NaN, NaN) @@ -77,16 +77,16 @@ function update_clims(series::Series, op = ignorenan_extrema)::Tuple{Float64,Flo zmin, zmax = Inf, -Inf # keeping this unrolled has higher performance - if series[:seriestype] ∈ Commons._z_colored_series && series[:z] !== nothing + if series[:seriestype] ∈ Commons._z_colored_series && series[:z] ≢ nothing zmin, zmax = update_clims(zmin, zmax, series[:z], op) end - if series[:line_z] !== nothing + if series[:line_z] ≢ nothing zmin, zmax = update_clims(zmin, zmax, series[:line_z], op) end - if series[:marker_z] !== nothing + if series[:marker_z] ≢ nothing zmin, zmax = update_clims(zmin, zmax, series[:marker_z], op) end - if series[:fill_z] !== nothing + if series[:fill_z] ≢ nothing zmin, zmax = update_clims(zmin, zmax, series[:fill_z], op) end return series[:clims_calculated] = zmin <= zmax ? (zmin, zmax) : (NaN, NaN) @@ -116,16 +116,16 @@ function colorbar_style(series::Series) elseif iscontour(series) cbar_lines elseif series[:seriestype] ∈ (:heatmap, :surface) || - any(series[z] !== nothing for z in (:marker_z, :line_z, :fill_z)) + any(series[z] ≢ nothing for z in (:marker_z, :line_z, :fill_z)) cbar_gradient else nothing end end -hascolorbar(series::Series) = colorbar_style(series) !== nothing +hascolorbar(series::Series) = colorbar_style(series) ≢ nothing hascolorbar(sp::Subplot) = - sp[:colorbar] !== :none && any(hascolorbar(s) for s in series_list(sp)) + sp[:colorbar] ≢ :none && any(hascolorbar(s) for s in series_list(sp)) function get_colorbar_ticks(sp::Subplot; update = true, formatter = sp[:colorbar_formatter]) if update || !haskey(sp.attr, :colorbar_optimized_ticks) diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index d68ca2687..f8e013323 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -130,7 +130,7 @@ include("attrs.jl") function _override_seriestype_check(plotattributes::AKW, st::Symbol) # do we want to override the series type? if !RecipesPipeline.is3d(st) && st ∉ (:contour, :contour3d, :quiver) - if (z = plotattributes[:z]) !== nothing && + if (z = plotattributes[:z]) ≢ nothing && size(plotattributes[:x]) == size(plotattributes[:y]) == size(z) st = st ≡ :scatter ? :scatter3d : :path3d plotattributes[:seriestype] = st diff --git a/PlotsBase/src/Commons/attrs.jl b/PlotsBase/src/Commons/attrs.jl index 2d94c35f8..0fd2bfb39 100644 --- a/PlotsBase/src/Commons/attrs.jl +++ b/PlotsBase/src/Commons/attrs.jl @@ -695,7 +695,7 @@ function default(k::Symbol) haskey(defaults, k) && return defaults[k] end haskey(_axis_defaults, k) && return _axis_defaults[k] - if (axis_k = parse_axis_kw(k)) !== nothing + if (axis_k = parse_axis_kw(k)) ≢ nothing letter, key = axis_k return _axis_defaults_byletter[letter][key] end @@ -715,7 +715,7 @@ function default(k::Symbol, v) _axis_defaults[k] = v return v end - if (axis_k = parse_axis_kw(k)) !== nothing + if (axis_k = parse_axis_kw(k)) ≢ nothing letter, key = axis_k _axis_defaults_byletter[letter][key] = v return v @@ -1013,7 +1013,7 @@ function _add_markershape(plotattributes::AKW) # add the markershape if it needs to be added... hack to allow "m=10" to add a shape, # and still allow overriding in _apply_recipe ms = pop!(plotattributes, :markershape_to_add, :none) - if !haskey(plotattributes, :markershape) && ms !== :none + if !haskey(plotattributes, :markershape) && ms ≢ :none plotattributes[:markershape] = ms end end diff --git a/PlotsBase/src/Fonts.jl b/PlotsBase/src/Fonts.jl index 29810dbac..0d87c9553 100644 --- a/PlotsBase/src/Fonts.jl +++ b/PlotsBase/src/Fonts.jl @@ -44,7 +44,7 @@ function font(args...; kw...) for arg in args T = typeof(arg) - @assert arg !== :match + @assert arg ≢ :match if T == Font family = arg.family diff --git a/PlotsBase/src/Series.jl b/PlotsBase/src/Series.jl index 9b05a6bec..a784e5f40 100644 --- a/PlotsBase/src/Series.jl +++ b/PlotsBase/src/Series.jl @@ -103,7 +103,7 @@ end function copy_series!(series, letter) plt = series[:plot_object] for s in plt.series_list, l in (:x, :y, :z) - if (s !== series || l !== letter) && s[l] ≡ series[letter] + if (s ≢ series || l ≢ letter) && s[l] ≡ series[letter] series[letter] = copy(series[letter]) end end @@ -182,17 +182,17 @@ function get_colorgradient(series::Series) series[:fillcolor] elseif st in (:contour, :wireframe, :contour3d) series[:linecolor] - elseif series[:marker_z] !== nothing + elseif series[:marker_z] ≢ nothing series[:markercolor] - elseif series[:line_z] !== nothing + elseif series[:line_z] ≢ nothing series[:linecolor] - elseif series[:fill_z] !== nothing + elseif series[:fill_z] ≢ nothing series[:fillcolor] end end iscontour(series::Series) = series[:seriestype] in (:contour, :contour3d) -isfilledcontour(series::Series) = iscontour(series) && series[:fillrange] !== nothing +isfilledcontour(series::Series) = iscontour(series) && series[:fillrange] ≢ nothing function contour_levels(series::Series, clims) iscontour(series) || error("Not a contour series") diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index 095fb9656..0b76b2f64 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -214,9 +214,9 @@ function PlotsBase.expand_extrema!(sp::Subplot, plotattributes::AKW) for letter in (:x, :y, :z) data = plotattributes[letter] if ( - letter !== :z && + letter ≢ :z && plotattributes[:seriestype] ≡ :straightline && - any(series[:seriestype] !== :straightline for series in series_list(sp)) && + any(series[:seriestype] ≢ :straightline for series in series_list(sp)) && length(data) > 1 && data[1] != data[2] ) @@ -235,7 +235,7 @@ function PlotsBase.expand_extrema!(sp::Subplot, plotattributes::AKW) data = plotattributes[letter] = Surface(Matrix{Float64}(data.surf)) end expand_extrema!(axis, data) - elseif data !== nothing + elseif data ≢ nothing # TODO: need more here... gotta track the discrete reference value # as well as any coord offset (think of boxplot shape coords... they all # correspond to the same x-value) @@ -251,7 +251,7 @@ function PlotsBase.expand_extrema!(sp::Subplot, plotattributes::AKW) if fr ≡ nothing && plotattributes[:seriestype] ≡ :bar fr = 0.0 end - if fr !== nothing && !RecipesPipeline.is3d(plotattributes) + if fr ≢ nothing && !RecipesPipeline.is3d(plotattributes) axis = sp.attr[:yaxis] if typeof(fr) <: Tuple foreach(x -> expand_extrema!(axis, x), fr) diff --git a/PlotsBase/src/Ticks.jl b/PlotsBase/src/Ticks.jl index 37e19435e..5ef577836 100644 --- a/PlotsBase/src/Ticks.jl +++ b/PlotsBase/src/Ticks.jl @@ -19,7 +19,7 @@ get_ticks(::T, args...) where {T} = # do not specify array item type to also catch e.g. "xlabel=[]" and "xlabel=([],[])" _has_ticks(v::AVec) = !isempty(v) _has_ticks(t::Tuple{AVec,AVec}) = !isempty(t[1]) -_has_ticks(s::Symbol) = s !== :none +_has_ticks(s::Symbol) = s ≢ :none _has_ticks(b::Bool) = b _has_ticks(::Nothing) = false _has_ticks(::Any) = true diff --git a/PlotsBase/src/axes_utils.jl b/PlotsBase/src/axes_utils.jl index c34201886..e057eac4c 100644 --- a/PlotsBase/src/axes_utils.jl +++ b/PlotsBase/src/axes_utils.jl @@ -190,7 +190,7 @@ end # whenever we have discrete values, we automatically set the ticks to match. # we return (continuous_value, discrete_index) discrete_value!(plotattributes, letter::Symbol, dv) = - let l = if plotattributes[:permute] !== :none + let l = if plotattributes[:permute] ≢ :none filter(!=(letter), plotattributes[:permute]) |> only else letter @@ -299,7 +299,7 @@ function axis_drawing_info(sp, letter) segments, tick_segments, grid_segments, minorgrid_segments, border_segments = map(_ -> Segments(2), 1:5) - if sp[:framestyle] !== :none + if sp[:framestyle] ≢ :none isy = letter ≡ :y oa1, oa2 = oas = if sp[:framestyle] in (:origin, :zerolines) 0, 0 @@ -307,7 +307,7 @@ function axis_drawing_info(sp, letter) xor(ax[:mirror], oax[:flip]) ? reverse(oamM) : oamM end if ax[:showaxis] - if sp[:framestyle] !== :grid + if sp[:framestyle] ≢ :grid push!(segments, reverse_if((amin, oa1), isy), reverse_if((amax, oa1), isy)) # don't show the 0 tick label for the origin framestyle if ( @@ -315,7 +315,7 @@ function axis_drawing_info(sp, letter) ticks ∉ (:none, nothing, false) && length(ticks) > 1 ) - if (i = findfirst(==(0), ticks[1])) !== nothing + if (i = findfirst(==(0), ticks[1])) ≢ nothing deleteat!(ticks[1], i) deleteat!(ticks[2], i) end @@ -343,7 +343,7 @@ function axis_drawing_info(sp, letter) tick_segments, grid_segments, grid_factor_2d[] / ax_length, - ax[:tick_direction] !== :none, + ax[:tick_direction] ≢ :none, ) if sp[:framestyle] ≡ :box add_major_or_minor_segments_2d( @@ -357,7 +357,7 @@ function axis_drawing_info(sp, letter) tick_segments, grid_segments, grid_factor_2d[] / ax_length, - ax[:tick_direction] !== :none, + ax[:tick_direction] ≢ :none, ) end @@ -467,7 +467,7 @@ function axis_drawing_info_3d(sp, letter) segments, tick_segments, grid_segments, minorgrid_segments, border_segments = map(_ -> Segments(3), 1:5) - if sp[:framestyle] !== :none # && letter ≡ :x + if sp[:framestyle] ≢ :none # && letter ≡ :x na0, na1 = nas = if sp[:framestyle] in (:origin, :zerolines) 0, 0 @@ -480,7 +480,7 @@ function axis_drawing_info_3d(sp, letter) reverse_if(famM, xor(ax[:mirror], fax[:flip])) end if ax[:showaxis] - if sp[:framestyle] !== :grid + if sp[:framestyle] ≢ :grid push!( segments, sort_3d_axes(amin, na0, fa0, letter), @@ -492,7 +492,7 @@ function axis_drawing_info_3d(sp, letter) ticks ∉ (:none, nothing, false) && length(ticks) > 1 ) - if (i = findfirst(==(0), ticks[1])) !== nothing + if (i = findfirst(==(0), ticks[1])) ≢ nothing deleteat!(ticks[1], i) deleteat!(ticks[2], i) end @@ -519,7 +519,7 @@ function axis_drawing_info_3d(sp, letter) tick_segments, grid_segments, grid_factor_3d[], - ax[:tick_direction] !== :none, + ax[:tick_direction] ≢ :none, ) # add minor grid segments diff --git a/PlotsBase/src/layouts.jl b/PlotsBase/src/layouts.jl index 92faee2fc..01fcbea18 100644 --- a/PlotsBase/src/layouts.jl +++ b/PlotsBase/src/layouts.jl @@ -512,19 +512,19 @@ function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot}) merge!(spmap, plt.spmap) inc = length(plt.subplots) end - if get(l.attr, :width, :auto) !== :auto + if get(l.attr, :width, :auto) ≢ :auto layout.widths[c] = attr(l, :width) end - if get(l.attr, :height, :auto) !== :auto + if get(l.attr, :height, :auto) ≢ :auto layout.heights[r] = attr(l, :height) end i += inc elseif isa(l, GridLayout) # sub-grid - if get(l.attr, :width, :auto) !== :auto + if get(l.attr, :width, :auto) ≢ :auto layout.widths[c] = attr(l, :width) end - if get(l.attr, :height, :auto) !== :auto + if get(l.attr, :height, :auto) ≢ :auto layout.heights[r] = attr(l, :height) end l, sps, m = build_layout(l, n - i, plts) diff --git a/PlotsBase/src/pipeline.jl b/PlotsBase/src/pipeline.jl index 04d73ed29..736bfcbc4 100644 --- a/PlotsBase/src/pipeline.jl +++ b/PlotsBase/src/pipeline.jl @@ -10,7 +10,7 @@ function RecipesPipeline.warn_on_recipe_aliases!( ) pkeys = keys(plotattributes) for k in pkeys - if (dk = get(Commons._keyAliases, k, nothing)) !== nothing + if (dk = get(Commons._keyAliases, k, nothing)) ≢ nothing kv = RecipesPipeline.pop_kw!(plotattributes, k) dk ∈ pkeys || (plotattributes[dk] = kv) end @@ -63,7 +63,7 @@ end function _preprocess_userrecipe(kw::AKW) Commons._add_markershape(kw) - if get(kw, :permute, default(:permute)) !== :none + if get(kw, :permute, default(:permute)) ≢ :none l1, l2 = kw[:permute] for k in Commons._axis_attrs k1 = Commons._attrsymbolcache[l1][k] @@ -99,7 +99,7 @@ function _add_errorbar_kw(kw_list::Vector{KW}, kw::AKW) errors = (:xerror, :yerror, :zerror) if st ∉ errors for esym in errors - if get(kw, esym, nothing) !== nothing + if get(kw, esym, nothing) ≢ nothing # we make a copy of the KW and apply an errorbar recipe errkw = copy(kw) errkw[:seriestype] = esym @@ -181,10 +181,10 @@ function RecipesPipeline.process_sliced_series_attributes!(plt::PlotsBase.Plot, kw[:ribbon] = map(rib, kw[:x]) end # convert a ribbon into a fillrange - if rib !== nothing + if rib ≢ nothing make_fillrange_from_ribbon(kw) # map fillrange if it's a Function - elseif fr !== nothing && fr isa Function + elseif fr ≢ nothing && fr isa Function kw[:fillrange] = map(fr, kw[:x]) end end @@ -214,7 +214,7 @@ function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) end # handle inset subplots - if (insets = plt[:inset_subplots]) !== nothing + if (insets = plt[:inset_subplots]) ≢ nothing typeof(insets) <: AVec || (insets = [insets]) for inset in insets parent, bb = is_2tuple(inset) ? inset : (nothing, inset) @@ -353,7 +353,7 @@ RecipesPipeline.is_seriestype_supported(plt::Plot, st) = is_seriestype_supported function RecipesPipeline.add_series!(plt::Plot, plotattributes) sp = _prepare_subplot(plt, plotattributes) - if (perm = plotattributes[:permute]) !== :none + if (perm = plotattributes[:permute]) ≢ :none letter1, letter2 = perm ms = plotattributes[:markershape] if ms ≡ :hline && (perm == (:x, :y) || perm == (:y, :x)) @@ -389,7 +389,7 @@ function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T} # change to a 3d projection for this subplot? if ( RecipesPipeline.needs_3d_axes(st) || - (st ≡ :quiver && plotattributes[:z] !== nothing) + (st ≡ :quiver && plotattributes[:z] ≢ nothing) ) sp.attr[:projection] = "3d" end diff --git a/PlotsBase/src/recipes.jl b/PlotsBase/src/recipes.jl index f7e44a3f3..9c57db750 100644 --- a/PlotsBase/src/recipes.jl +++ b/PlotsBase/src/recipes.jl @@ -223,7 +223,7 @@ make_steps(t::Tuple, st, even) = Tuple(make_steps(ti, st, even) for ti in t) plotattributes[:fillrange] = make_steps(plotattributes[:fillrange], :pre, false) # create a secondary series for the markers - if plotattributes[:markershape] !== :none + if plotattributes[:markershape] ≢ :none @series begin seriestype := :scatter x := x @@ -248,7 +248,7 @@ end plotattributes[:fillrange] = make_steps(plotattributes[:fillrange], :post, true) # create a secondary series for the markers - if plotattributes[:markershape] !== :none + if plotattributes[:markershape] ≢ :none @series begin seriestype := :scatter x := x @@ -273,7 +273,7 @@ end plotattributes[:fillrange] = make_steps(plotattributes[:fillrange], :post, false) # create a secondary series for the markers - if plotattributes[:markershape] !== :none + if plotattributes[:markershape] ≢ :none @series begin seriestype := :scatter x := x @@ -302,11 +302,11 @@ end NaNMath.min(axis_limits(sp, :y)[1], ignorenan_minimum(y)) end end - newx, newy, newz = zeros(3n), zeros(3n), z !== nothing ? zeros(3n) : nothing - for (i, (xi, yi, zi)) in enumerate(zip(x, y, z !== nothing ? z : 1:n)) + newx, newy, newz = zeros(3n), zeros(3n), z ≢ nothing ? zeros(3n) : nothing + for (i, (xi, yi, zi)) in enumerate(zip(x, y, z ≢ nothing ? z : 1:n)) rng = (3i - 2):(3i) newx[rng] = [xi, xi, NaN] - if z !== nothing + if z ≢ nothing newy[rng] = [yi, yi, NaN] newz[rng] = [_cycle(fr, i), zi, NaN] else @@ -315,27 +315,27 @@ end end x := newx y := newy - if z !== nothing + if z ≢ nothing z := newz end fillrange := nothing seriestype := :path if ( plotattributes[:linecolor] ≡ :auto && - plotattributes[:marker_z] !== nothing && + plotattributes[:marker_z] ≢ nothing && plotattributes[:line_z] ≡ nothing ) line_z := plotattributes[:marker_z] end # create a primary series for the markers - if plotattributes[:markershape] !== :none + if plotattributes[:markershape] ≢ :none primary := false @series begin seriestype := :scatter x := x y := y - if z !== nothing + if z ≢ nothing z := z end primary := true @@ -366,10 +366,10 @@ end # create segmented bezier curves in place of line segments @recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30) # COV_EXCL_LINE - args = z !== nothing ? (x, y, z) : (x, y) + args = z ≢ nothing ? (x, y, z) : (x, y) newx, newy = zeros(0), zeros(0) - newfr = (fr = plotattributes[:fillrange]) !== nothing ? zeros(0) : nothing - newz = z !== nothing ? zeros(0) : nothing + newfr = (fr = plotattributes[:fillrange]) ≢ nothing ? zeros(0) : nothing + newz = z ≢ nothing ? zeros(0) : nothing # for each line segment (point series with no NaNs), convert it into a bezier curve # where the points are the control points of the curve @@ -378,10 +378,10 @@ end 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)) - if z !== nothing + if z ≢ nothing nanappend!(newz, map(t -> bezier_value(_cycle(z, rng), t), ts)) end - if fr !== nothing + if fr ≢ nothing nanappend!(newfr, map(t -> bezier_value(_cycle(fr, rng), t), ts)) end end @@ -394,7 +394,7 @@ end seriestype := :path3d z := newz end - if fr !== nothing + if fr ≢ nothing fillrange := newfr end () @@ -634,7 +634,7 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S last_w = eltype(weights)(NaN) - while it_tuple_e !== nothing && it_tuple_w !== nothing + while it_tuple_e ≢ nothing && it_tuple_w ≢ nothing b, it_state_e = it_tuple_e w, it_state_w = it_tuple_w @@ -678,7 +678,7 @@ end xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale) # create a secondary series for the markers - if plotattributes[:markershape] !== :none + if plotattributes[:markershape] ≢ :none @series begin seriestype := :scatter x := _bin_centers(edge) @@ -948,7 +948,7 @@ end @recipe function f(::Type{Val{:mesh3d}}, x, y, z) # COV_EXCL_LINE # As long as no i,j,k are supplied this should work with PyPlot and GR seriestype := :surface - if plotattributes[:connections] !== nothing + if plotattributes[:connections] ≢ nothing "Giving triangles using the connections argument is only supported on Plotly backend." |> ArgumentError |> throw @@ -1170,7 +1170,7 @@ end @recipe function f(::Type{Val{:zerror}}, x, y, z) # COV_EXCL_LINE Commons.error_style!(plotattributes) markershape := :hline - if z !== nothing + if z ≢ nothing zerr = error_zipit(plotattributes[:zerror]) plotattributes[:z], plotattributes[:x], plotattributes[:y] = error_coords(zerr, z, x, y) diff --git a/PlotsBase/src/utils.jl b/PlotsBase/src/utils.jl index 7201431c1..edce32255 100644 --- a/PlotsBase/src/utils.jl +++ b/PlotsBase/src/utils.jl @@ -115,13 +115,13 @@ function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot) end # if marker_z, fill_z or line_z are set, ensure we have a gradient - if plotattributes[:marker_z] !== nothing + if plotattributes[:marker_z] ≢ nothing Commons.ensure_gradient!(plotattributes, :markercolor, :markeralpha) end - if plotattributes[:line_z] !== nothing + if plotattributes[:line_z] ≢ nothing Commons.ensure_gradient!(plotattributes, :linecolor, :linealpha) end - if plotattributes[:fill_z] !== nothing + if plotattributes[:fill_z] ≢ nothing Commons.ensure_gradient!(plotattributes, :fillcolor, :fillalpha) end @@ -406,7 +406,7 @@ function Commons.preprocess_attributes!(plotattributes::AKW) if treats_y_as_x(get(plotattributes, :seriestype, :path)) xformatter = get(plotattributes, :xformatter, :auto) yformatter = get(plotattributes, :yformatter, :auto) - yformatter !== :auto && (plotattributes[:xformatter] = yformatter) + yformatter ≢ :auto && (plotattributes[:xformatter] = yformatter) xformatter ≡ :auto && haskey(plotattributes, :yformatter) && pop!(plotattributes, :yformatter) @@ -475,7 +475,7 @@ function Commons.preprocess_attributes!(plotattributes::AKW) end # handle axes args for k in Commons._axis_attrs - if haskey(plotattributes, k) && k !== :link + if haskey(plotattributes, k) && k ≢ :link v = plotattributes[k] for letter in (:x, :y, :z) lk = get_attr_symbol(letter, k) diff --git a/PlotsBase/test/test_pgfplotsx.jl b/PlotsBase/test/test_pgfplotsx.jl index 5f31cb7d1..07bacbedf 100644 --- a/PlotsBase/test/test_pgfplotsx.jl +++ b/PlotsBase/test/test_pgfplotsx.jl @@ -336,8 +336,8 @@ with(:pgfplotsx) do @test haskey(plots[1].options.dict, "fill") @test haskey(plots[2].options.dict, "fill") @test !haskey(plots[3].options.dict, "fill") - @test pl.o !== nothing - @test pl.o.the_plot !== nothing + @test pl.o ≢ nothing + @test pl.o.the_plot ≢ nothing end @testset "Markers and Paths" begin @@ -455,7 +455,7 @@ with(:pgfplotsx) do plt1 = plot(rand(10, 5)) plt2 = plot(rand(10)) - @test plot(plt1, plt2, layout = (1, 2), plot_titles = ["(a)" "(b)"]) !== nothing + @test plot(plt1, plt2, layout = (1, 2), plot_titles = ["(a)" "(b)"]) ≢ nothing end if Sys.islinux() && Sys.which("pdflatex") ≢ nothing diff --git a/RecipesPipeline/src/user_recipe.jl b/RecipesPipeline/src/user_recipe.jl index 4c0fcbaaa..00321d7ca 100644 --- a/RecipesPipeline/src/user_recipe.jl +++ b/RecipesPipeline/src/user_recipe.jl @@ -115,9 +115,9 @@ end @recipe function f(x, y, z) # COV_EXCL_LINE wrap_surfaces!(plotattributes, x, y, z) did_replace = false - did_replace |= x !== (newx = _apply_type_recipe(plotattributes, x, :x)) - did_replace |= y !== (newy = _apply_type_recipe(plotattributes, y, :y)) - did_replace |= z !== (newz = _apply_type_recipe(plotattributes, z, :z)) + did_replace |= x ≢ (newx = _apply_type_recipe(plotattributes, x, :x)) + did_replace |= y ≢ (newy = _apply_type_recipe(plotattributes, y, :y)) + did_replace |= z ≢ (newz = _apply_type_recipe(plotattributes, z, :z)) if did_replace newx, newy, newz else @@ -127,8 +127,8 @@ end @recipe function f(x, y) # COV_EXCL_LINE wrap_surfaces!(plotattributes, x, y) did_replace = false - did_replace |= x !== (newx = _apply_type_recipe(plotattributes, x, :x)) - did_replace |= y !== (newy = _apply_type_recipe(plotattributes, y, :y)) + did_replace |= x ≢ (newx = _apply_type_recipe(plotattributes, x, :x)) + did_replace |= y ≢ (newy = _apply_type_recipe(plotattributes, y, :y)) if did_replace newx, newy else @@ -137,7 +137,7 @@ end end @recipe function f(y) # COV_EXCL_LINE wrap_surfaces!(plotattributes, y) - if y !== (newy = _apply_type_recipe(plotattributes, y, :y)) + if y ≢ (newy = _apply_type_recipe(plotattributes, y, :y)) newy else SliceIt, nothing, y, nothing @@ -150,7 +150,7 @@ end did_replace = false newargs = map( v -> begin - did_replace |= v !== (newv = _apply_type_recipe(plotattributes, v, :unknown)) + did_replace |= v ≢ (newv = _apply_type_recipe(plotattributes, v, :unknown)) newv end, (v1, v2, v3, v4, vrest...), @@ -167,7 +167,7 @@ wrap_surfaces!(plotattributes, x::AVec, y::AVec, z::AMat) = wrap_surfaces!(plota wrap_surfaces!(plotattributes, x::AVec, y::AVec, z::Surface) = wrap_surfaces!(plotattributes) wrap_surfaces!(plotattributes) = - if (v = get(plotattributes, :fill_z, nothing)) !== nothing + if (v = get(plotattributes, :fill_z, nothing)) ≢ nothing v isa Surface || (plotattributes[:fill_z] = Surface(v)) end From 1f124c8041cfef958accff35ca2f887cc5fd336a Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 19:33:13 +0200 Subject: [PATCH 08/58] fix `plotlyjs` --- PlotsBase/src/Annotations.jl | 3 +-- PlotsBase/src/Colorbars.jl | 5 +--- PlotsBase/src/Commons/Commons.jl | 13 ++++++---- PlotsBase/src/Commons/attrs.jl | 18 +++++-------- PlotsBase/src/Commons/postprocess_attrs.jl | 2 +- PlotsBase/src/PlotsBase.jl | 23 ++++++----------- PlotsBase/src/abstract_backend.jl | 30 +++++++++------------- PlotsBase/src/backends/plotly.jl | 11 ++++---- PlotsBase/src/pipeline.jl | 10 ++------ PlotsBase/src/plot.jl | 3 +-- PlotsBase/src/utils.jl | 10 ++++---- PlotsBase/test/test_backends.jl | 2 +- src/Plots.jl | 21 +++++++-------- test/runtests.jl | 10 ++------ test/test_preferences.jl | 12 +++++---- 15 files changed, 72 insertions(+), 101 deletions(-) diff --git a/PlotsBase/src/Annotations.jl b/PlotsBase/src/Annotations.jl index 791a752a0..cb6b5f949 100644 --- a/PlotsBase/src/Annotations.jl +++ b/PlotsBase/src/Annotations.jl @@ -156,8 +156,7 @@ _annotationfont(sp::Subplot) = font(; _annotation(sp::Subplot, font, lab, pos...; alphabet = "abcdefghijklmnopqrstuvwxyz") = ( pos..., - lab ≡ :auto ? text("($(alphabet[sp[:subplot_index]]))", font) : - _text_label(lab, font), + lab ≡ :auto ? text("($(alphabet[sp[:subplot_index]]))", font) : _text_label(lab, font), ) assign_annotation_coord!(axis, x) = discrete_value!(axis, x)[1] diff --git a/PlotsBase/src/Colorbars.jl b/PlotsBase/src/Colorbars.jl index 6aba98907..9d0238390 100644 --- a/PlotsBase/src/Colorbars.jl +++ b/PlotsBase/src/Colorbars.jl @@ -31,10 +31,7 @@ function update_clims(sp::Subplot, op = process_clims(sp[:clims]))::Tuple{Float6 for series in series_list(sp) if series[:colorbar_entry]::Bool # Avoid calling the inner `update_clims` if at all possible; dynamic dispatch hell - if ( - series[:seriestype] ∈ Commons._z_colored_series && - series[:z] ≢ nothing - ) || + if (series[:seriestype] ∈ Commons._z_colored_series && series[:z] ≢ nothing) || series[:line_z] ≢ nothing || series[:marker_z] ≢ nothing || series[:fill_z] ≢ nothing diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index f8e013323..0a64f3766 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -169,15 +169,18 @@ function color_or_nothing!(plotattributes, k::Symbol) end # cache joined symbols so they can be looked up instead of constructed each time -const _attrsymbolcache = Dict{Symbol,Dict{String,Symbol}}() +const _attrsymbolcache = Dict{Symbol,Dict{Symbol,Symbol}}() -get_attr_symbol(letter::Symbol, keyword::String) = _attrsymbolcache[letter][keyword] -get_attr_symbol(letter::Symbol, keyword::Symbol) = get_attr_symbol(letter, string(keyword)) +get_attr_symbol(letter::Symbol, keyword::Symbol) = _attrsymbolcache[letter][keyword] +get_attr_symbol(letter::Symbol, keyword::String) = get_attr_symbol(letter, Symbol(keyword)) -new_attr_dict!(letter::Symbol) = get!(_attrsymbolcache, letter, Dict{String,Symbol}()) +new_attr_dict!(letter::Symbol)::Dict{Symbol,Symbol} = + get!(_attrsymbolcache, letter, Dict{Symbol,Symbol}()) + +# NOTE: using `keyword::String` allows to disambiguate argument order set_attr_symbol!(letter::Symbol, keyword::String) = let letter_keyword = Symbol(letter, keyword) - _attrsymbolcache[letter][keyword] = letter_keyword + _attrsymbolcache[letter][Symbol(keyword)] = letter_keyword end # ------------------------------------------------------------------------------------ diff --git a/PlotsBase/src/Commons/attrs.jl b/PlotsBase/src/Commons/attrs.jl index 0fd2bfb39..6e9ad60c4 100644 --- a/PlotsBase/src/Commons/attrs.jl +++ b/PlotsBase/src/Commons/attrs.jl @@ -768,19 +768,15 @@ function process_line_attr(plotattributes::AKW, arg) elseif typeof(arg) <: PlotsBase.Stroke arg.width ≡ nothing || (plotattributes[:linewidth] = arg.width) - arg.color ≡ nothing || ( - plotattributes[:linecolor] = - arg.color ≡ :auto ? :auto : plot_color(arg.color) - ) + arg.color ≡ nothing || + (plotattributes[:linecolor] = arg.color ≡ :auto ? :auto : plot_color(arg.color)) arg.alpha ≡ nothing || (plotattributes[:linealpha] = arg.alpha) arg.style ≡ nothing || (plotattributes[:linestyle] = arg.style) elseif typeof(arg) <: PlotsBase.Brush arg.size ≡ nothing || (plotattributes[:fillrange] = arg.size) - arg.color ≡ nothing || ( - plotattributes[:fillcolor] = - arg.color ≡ :auto ? :auto : plot_color(arg.color) - ) + arg.color ≡ nothing || + (plotattributes[:fillcolor] = arg.color ≡ :auto ? :auto : plot_color(arg.color)) arg.alpha ≡ nothing || (plotattributes[:fillalpha] = arg.alpha) arg.style ≡ nothing || (plotattributes[:fillstyle] = arg.style) @@ -849,10 +845,8 @@ function process_fill_attr(plotattributes::AKW, arg) # fr = get(plotattributes, :fillrange, 0) if typeof(arg) <: PlotsBase.Brush arg.size ≡ nothing || (plotattributes[:fillrange] = arg.size) - arg.color ≡ nothing || ( - plotattributes[:fillcolor] = - arg.color ≡ :auto ? :auto : plot_color(arg.color) - ) + arg.color ≡ nothing || + (plotattributes[:fillcolor] = arg.color ≡ :auto ? :auto : plot_color(arg.color)) arg.alpha ≡ nothing || (plotattributes[:fillalpha] = arg.alpha) arg.style ≡ nothing || (plotattributes[:fillstyle] = arg.style) diff --git a/PlotsBase/src/Commons/postprocess_attrs.jl b/PlotsBase/src/Commons/postprocess_attrs.jl index 18fdbd188..67e48da7f 100644 --- a/PlotsBase/src/Commons/postprocess_attrs.jl +++ b/PlotsBase/src/Commons/postprocess_attrs.jl @@ -12,7 +12,7 @@ for letter in (:x, :y, :z) add_aliases(letter_keyword, Symbol(letter, "_", keyword)) end for keyword in (_magic_axis_attrs..., :(_discrete_indices)) - _attrsymbolcache[letter][string(keyword)] = Symbol(letter, keyword) + _attrsymbolcache[letter][keyword] = Symbol(letter, keyword) end end diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index c78fef6f6..d5e242287 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -122,23 +122,16 @@ using .PlotMeasures import .PlotMeasures: Length, AbsoluteLength, Measure, width, height # --------------------------------------------------------- macro ScopeModule(mod::Symbol, parent::Symbol, symbols...) - Expr( - :module, - true, - mod, + import_ex = Expr( + :import, Expr( - :block, - Expr( - :import, - Expr( - :(:), - Expr(:., :., :., parent), - (Expr(:., s isa Expr ? s.args[1] : s) for s in symbols)..., - ), - ), - Expr(:export, (s isa Expr ? s.args[1] : s for s in symbols)...), + :(:), + Expr(:., :., :., parent), + (Expr(:., s isa Expr ? s.args[1] : s) for s in symbols)..., ), - ) |> esc + ) + export_ex = Expr(:export, (s isa Expr ? s.args[1] : s for s in symbols)...) + Expr(:module, true, mod, Expr(:block, import_ex, export_ex)) |> esc end import NaNMath include("Commons/Commons.jl") diff --git a/PlotsBase/src/abstract_backend.jl b/PlotsBase/src/abstract_backend.jl index 987e8e303..ff28ded48 100644 --- a/PlotsBase/src/abstract_backend.jl +++ b/PlotsBase/src/abstract_backend.jl @@ -57,16 +57,16 @@ end CurrentBackend(sym::Symbol) = CurrentBackend(sym, backend_instance(sym)) -""" -Returns the current plotting package name. Initializes package on first call. -""" -backend() = CURRENT_BACKEND.pkg +"returns the current plotting package name. Initializes package on first call." +@inline backend() = CURRENT_BACKEND.pkg -"Returns a list of supported backends" -backends() = _supported_backends +"returns a list of supported backends." +@inline backends() = _supported_backends -backend_name() = CURRENT_BACKEND.sym -backend_instance(sym::Symbol) = _backendType[sym]() +@inline backend_name() = CURRENT_BACKEND.sym +@inline backend_type(sym::Symbol) = get(_backendType, sym, NoBackend) +@inline backend_instance(sym::Symbol) = backend_type(sym)() +@inline backend(type::Type{<:AbstractBackend}) = backend(type()) backend_package_name(sym::Symbol = backend_name()) = get(_backend_packages, sym, nothing) @@ -77,9 +77,7 @@ backend_package_name(::AbstractBackend) = initialized(sym::Symbol) = sym ∈ _initialized_backends -""" -Set the plot backend. -""" +"set the plot backend." function backend(pkg::AbstractBackend) sym = backend_name(pkg) CURRENT_BACKEND.sym = sym @@ -90,7 +88,7 @@ end backend(sym::Symbol) = if sym in _supported_backends if initialized(sym) - backend(backend_instance(sym)) + backend(backend_type(sym)) else name = backend_package_name(sym) @warn "`:$sym` is not initialized, import it first to trigger the extension --- e.g. $(name ≡ nothing ? '`' : string("`import ", name, ";")) $sym()`." @@ -110,9 +108,7 @@ function get_backend_module(name::Symbol) end end -# -- Create backend init functions by hand as the corresponding structs do not -# exist yet - +# create backend init functions by hand as the corresponding structs do not exist yet for be in _supported_backends @eval begin function $be(; kw...) @@ -123,7 +119,6 @@ for be in _supported_backends end end -# --------------------------------------------------------- # create the various `is_xxx_supported` and `supported_xxxs` methods # these methods should be overloaded (dispatched) by each backend in its init_code for sym in (:attr, :seriestype, :marker, :style, :scale) @@ -136,7 +131,6 @@ for sym in (:attr, :seriestype, :marker, :style, :scale) $f2() = $f2(backend()) end end -# ----------------------------------------------------------------------------- function backend_defines(be_type::Symbol, be::Symbol) be_sym = QuoteNode(be) @@ -189,7 +183,7 @@ macro extension_static(be_type, be) PlotsBase._backendType[$be_sym] = $be_type PlotsBase._backendSymbol[$be_type] = $be_sym push!(PlotsBase._initialized_backends, $be_sym) - # ccall(:jl_generating_output, Cint, ()) == 1 && return + ccall(:jl_generating_output, Cint, ()) == 1 && return PlotsBase.extension_init($be_type()) @debug "Initialized $be_type backend in PlotsBase; run `$be()` to activate it." end diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index 68b777b0c..c30fa0b11 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -10,6 +10,7 @@ import JSON using PlotUtils: PlotUtils, ColorGradient, rgba_string, rgb_string +import PlotsBase: right, left, bottom, top, width, height, bbox, ispolar using PlotsBase.Colors: Colorant using PlotsBase.PlotMeasures using PlotsBase.Annotations @@ -403,7 +404,7 @@ function plotly_layout(plt::Plot) ymm = bottom(bb) - tpos[2] * height(bb) halign, valign = sp[:titlefonthalign], sp[:titlefontvalign] end - titlex, titley = xy_mm_to_pcts(xmm, ymm, w * px, h * px) + titlex, titley = PlotsBase.xy_mm_to_pcts(xmm, ymm, w * px, h * px) title_font = font(titlefont(sp), halign = halign, valign = valign) push!( plotattributes_out[:annotations], @@ -740,7 +741,7 @@ function plotly_series(plt::Plot, series::Series) plotattributes_out[:showlegend] = should_add_to_legend(series) if st ≡ :straightline - x, y = straightline_data(series, 100) + x, y = PlotsBase.straightline_data(series, 100) z = series[:z] else x, y, z = series[:x], series[:y], series[:z] @@ -771,8 +772,8 @@ function plotly_series(plt::Plot, series::Series) return plotly_series_segments(series, plotattributes_out, x, y, z, clims) elseif st ≡ :heatmap - x = heatmap_edges(x, sp[:xaxis][:scale]) - y = heatmap_edges(y, sp[:yaxis][:scale]) + x = PlotsBase.heatmap_edges(x, sp[:xaxis][:scale]) + y = PlotsBase.heatmap_edges(y, sp[:yaxis][:scale]) plotattributes_out[:type] = "heatmap" plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z plotattributes_out[:colorscale] = @@ -951,7 +952,7 @@ function plotly_series_shapes(plt::Plot, series::Series, clims) x, y = ( plotly_data(series, letter, data) for - (letter, data) in zip((:x, :y), shape_data(series, 100)) + (letter, data) in zip((:x, :y), PlotsBase.shape_data(series, 100)) ) for (k, segment) in enumerate(segments) diff --git a/PlotsBase/src/pipeline.jl b/PlotsBase/src/pipeline.jl index 736bfcbc4..d4ba2bc5b 100644 --- a/PlotsBase/src/pipeline.jl +++ b/PlotsBase/src/pipeline.jl @@ -247,10 +247,7 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) sps = get(kw, :subplot, :auto) sp = get_subplot( plt, - _cycle( - sps ≡ :auto ? plt.subplots : plt.subplots[sps], - series_idx(kw_list, kw), - ), + _cycle(sps ≡ :auto ? plt.subplots : plt.subplots[sps], series_idx(kw_list, kw)), ) kw[:subplot] = sp @@ -387,10 +384,7 @@ function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T} st = _override_seriestype_check(plotattributes, st) # change to a 3d projection for this subplot? - if ( - RecipesPipeline.needs_3d_axes(st) || - (st ≡ :quiver && plotattributes[:z] ≢ nothing) - ) + if (RecipesPipeline.needs_3d_axes(st) || (st ≡ :quiver && plotattributes[:z] ≢ nothing)) sp.attr[:projection] = "3d" end diff --git a/PlotsBase/src/plot.jl b/PlotsBase/src/plot.jl index 774933f64..dc72a8523 100644 --- a/PlotsBase/src/plot.jl +++ b/PlotsBase/src/plot.jl @@ -254,8 +254,7 @@ function prepare_output(plt::Plot) force_minpad = get(plt, :force_minpad, ()) isempty(force_minpad) || for i in eachindex(plt.layout.grid) plt.layout.grid[i].minpad = Tuple( - i ≡ nothing ? j : i for - (i, j) in zip(force_minpad, plt.layout.grid[i].minpad) + i ≡ nothing ? j : i for (i, j) in zip(force_minpad, plt.layout.grid[i].minpad) ) end diff --git a/PlotsBase/src/utils.jl b/PlotsBase/src/utils.jl index edce32255..8d2658a23 100644 --- a/PlotsBase/src/utils.jl +++ b/PlotsBase/src/utils.jl @@ -94,10 +94,10 @@ function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot) csym, asym = Symbol(s, :color), Symbol(s, :alpha) plotattributes[csym] = if plotattributes[csym] ≡ :auto plot_color(if Commons.has_black_border_for_default(stype) && s ≡ :line - sp[:foreground_color_subplot] - else - scolor - end) + sp[:foreground_color_subplot] + else + scolor + end) elseif plotattributes[csym] ≡ :match plot_color(scolor) else @@ -645,7 +645,7 @@ function with(f::Function, args...; scalefonts = nothing, kw...) scalefonts ≡ nothing || scalefontsizes(scalefonts) # call the function - ret = f() + ret = Base.invokelatest(f) # put the defaults back scalefonts ≡ nothing || resetfontsizes() diff --git a/PlotsBase/test/test_backends.jl b/PlotsBase/test/test_backends.jl index cd737ba9a..0c2d82dc6 100644 --- a/PlotsBase/test/test_backends.jl +++ b/PlotsBase/test/test_backends.jl @@ -1,5 +1,5 @@ -is_pkgeval() || @testset "Examples" begin +is_pkgeval() || @testset "Backends" begin callback(m, pkgname, i) = begin save_func = (; pgfplotsx = m.PlotsBase.pdf, unicodeplots = m.PlotsBase.txt) # fastest `savefig` for each backend pl = m.PlotsBase.current() diff --git a/src/Plots.jl b/src/Plots.jl index 2850b1315..d6619d092 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -13,19 +13,20 @@ using Pkg # the cache will not invalidate when preferences change const PLOTS_DEFAULT_BACKEND = lowercase(load_preference(Plots, "default_backend", "gr")) +if PLOTS_DEFAULT_BACKEND == "gr" + @debug "loading default GR" + import GR +end + function __init__() ccall(:jl_generating_output, Cint, ()) == 1 && return - load_default_backend() + default_backend() end -function load_default_backend() +function default_backend() # environment variable preempts the `Preferences` based mechanism - sym = PlotsBase.CURRENT_BACKEND.sym = - get(ENV, "PLOTS_DEFAULT_BACKEND", PLOTS_DEFAULT_BACKEND) |> lowercase |> Symbol - if (pkg_name = PlotsBase.backend_package_name()) ≡ :GR - @eval import GR - end - PlotsBase.backend(PlotsBase.backend_instance(sym)) + sym = get(ENV, "PLOTS_DEFAULT_BACKEND", PLOTS_DEFAULT_BACKEND) |> lowercase |> Symbol + PlotsBase.backend(PlotsBase.backend_type(sym)) end function set_default_backend!( @@ -68,7 +69,7 @@ end # COV_EXCL_START @setup_workload begin - load_default_backend() + default_backend() @debug PlotsBase.backend_package_name() n = length(PlotsBase._examples) imports = sizehint!(Expr[], n) @@ -111,7 +112,7 @@ end end withenv("GKSwstype" => "nul") do @compile_workload begin - load_default_backend() + default_backend() eval.(imports) eval.(examples) end diff --git a/test/runtests.jl b/test/runtests.jl index 637548de8..0fa2e7b6e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,5 @@ const TEST_PACKAGES = - let val = get( - ENV, - "PLOTSBASE_TEST_PACKAGES", - "GR,UnicodePlots,PythonPlot", - ) + let val = get(ENV, "PLOTSBASE_TEST_PACKAGES", "GR,UnicodePlots,PythonPlot") strip.(split(val, ",")) end using PlotsBase @@ -26,9 +22,7 @@ is_ci() = Plots.PlotsBase.bool_env("CI") # get `Preferences` set backend, if any const PREVIOUS_DEFAULT_BACKEND = load_preference(Plots, "default_backend") -for name in ( - "preferences", -) +for name in ("preferences",) @testset "$name" begin include("test_$name.jl") end diff --git a/test/test_preferences.jl b/test/test_preferences.jl index 370ec5833..35f8e4311 100644 --- a/test/test_preferences.jl +++ b/test/test_preferences.jl @@ -2,19 +2,19 @@ Plots.set_default_backend!() # start with empty preferences withenv("PLOTS_DEFAULT_BACKEND" => "test_invalid_backend") do - @test_logs (:error, r"Unsupported backend.*") Plots.load_default_backend() + @test_logs (:error, r"Unsupported backend.*") Plots.default_backend() end @test_logs (:error, r"Unsupported backend.*") backend(:test_invalid_backend) -@test Plots.load_default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() +@test Plots.default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() withenv("PLOTS_DEFAULT_BACKEND" => "unicodeplots") do @test_logs (:info, r".*environment variable") Plots.diagnostics(devnull) - @test Plots.load_default_backend() == + @test Plots.default_backend() == Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() end -@test Plots.load_default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() +@test Plots.default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() @test Plots.PlotsBase.backend_package_name() ≡ :GR @test Plots.backend_name() ≡ :gr @@ -23,7 +23,9 @@ end @test Plots.PlotsBase.merge_with_base_supported([:annotations, :guide]) isa Set @test Plots.PlotsBase.CurrentBackend(:gr).sym ≡ :gr -@test_logs (:warn, r".*is not compatible with") Plots.set_default_backend!(:test_invalid_backend) +@test_logs (:warn, r".*is not compatible with") Plots.set_default_backend!( + :test_invalid_backend, +) @testset "persistent backend" begin # this test mimics a restart, which is needed after a preferences change From 62ed3a7cca590add41c358716cbffdfd93c25042 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 21:18:15 +0200 Subject: [PATCH 09/58] restructure modules --- PlotsBase/src/Annotations.jl | 24 +- PlotsBase/src/Arrows.jl | 6 +- PlotsBase/src/Axes.jl | 19 +- PlotsBase/src/BezierCurves.jl | 2 +- PlotsBase/src/Colorbars.jl | 29 +- PlotsBase/src/Commons/Commons.jl | 13 +- PlotsBase/src/{Series.jl => DataSeries.jl} | 17 +- PlotsBase/src/Fonts.jl | 12 +- PlotsBase/src/Layouts.jl | 247 ++++++++++++++++++ .../src/{PlotMeasures.jl => Measurements.jl} | 13 +- PlotsBase/src/{PlotsPlots.jl => Plots.jl} | 46 ++-- PlotsBase/src/PlotsBase.jl | 36 +-- PlotsBase/src/Shapes.jl | 18 +- PlotsBase/src/Strokes.jl | 12 +- PlotsBase/src/Subplots.jl | 23 +- PlotsBase/src/Surfaces.jl | 12 +- PlotsBase/src/Ticks.jl | 16 +- PlotsBase/src/backends/plotly.jl | 14 +- PlotsBase/src/examples.jl | 2 +- PlotsBase/src/layouts.jl | 238 +---------------- PlotsBase/src/pipeline.jl | 6 +- PlotsBase/src/plot.jl | 2 +- PlotsBase/src/utils.jl | 16 -- 23 files changed, 433 insertions(+), 390 deletions(-) rename PlotsBase/src/{Series.jl => DataSeries.jl} (97%) create mode 100644 PlotsBase/src/Layouts.jl rename PlotsBase/src/{PlotMeasures.jl => Measurements.jl} (79%) rename PlotsBase/src/{PlotsPlots.jl => Plots.jl} (90%) diff --git a/PlotsBase/src/Annotations.jl b/PlotsBase/src/Annotations.jl index cb6b5f949..b4a8a067b 100644 --- a/PlotsBase/src/Annotations.jl +++ b/PlotsBase/src/Annotations.jl @@ -1,14 +1,8 @@ # internal module module Annotations -using ..PlotsBase.Commons -using ..PlotsBase.Dates -using ..PlotsBase.Fonts: Font, PlotText, text, font -using ..PlotsBase.Shapes: Shape, _shapes -using ..PlotsBase.PlotMeasures: pct -using ..PlotsBase: Series, Subplot, TimeType, Length -using ..PlotsBase: is_2tuple, is3d, discrete_value! -export EachAnn, +export SeriesAnnotations, + EachAnn, series_annotations, series_annotations_shapes!, process_annotation, @@ -16,6 +10,14 @@ export EachAnn, annotations, assign_annotation_coord! +import ..PlotsBase: Series, Subplot, TimeType, is3d, discrete_value! + +using ..Measurements +using ..Commons +using ..Shapes +using ..Dates +using ..Fonts + mutable struct SeriesAnnotations strs::AVec # the labels/names font::Font @@ -69,8 +71,8 @@ function series_annotations(strs::AVec, args...) shp = arg elseif isa(arg, Font) fnt = arg - elseif isa(arg, Symbol) && haskey(_shapes, arg) - shp = _shapes[arg] + elseif isa(arg, Symbol) && haskey(Shapes._shapes, arg) + shp = Shapes._shapes[arg] elseif isa(arg, Number) scalefactor = arg, arg elseif is_2tuple(arg) @@ -238,7 +240,7 @@ locate_annotation(sp::Subplot, rel::Tuple, label::PlotText) = ( map(1:length(rel), (:x, :y, :z)) do i, letter _relative_position( axis_limits(sp, letter)..., - rel[i] * pct, + rel[i] * Measurements.pct, sp[get_attr_symbol(letter, :axis)][:scale], ) end..., diff --git a/PlotsBase/src/Arrows.jl b/PlotsBase/src/Arrows.jl index 8d058d3c2..9c49bdbfc 100644 --- a/PlotsBase/src/Arrows.jl +++ b/PlotsBase/src/Arrows.jl @@ -1,8 +1,9 @@ module Arrows -using ..PlotsBase.Commons export Arrow, arrow, add_arrows +using ..PlotsBase.Commons + # style is :open or :closed (for now) struct Arrow style::Symbol @@ -59,4 +60,5 @@ function add_arrows(func::Function, x::AVec, y::AVec) end end end -end # Arrows + +end # module diff --git a/PlotsBase/src/Axes.jl b/PlotsBase/src/Axes.jl index a99929e74..bd64c1c2d 100644 --- a/PlotsBase/src/Axes.jl +++ b/PlotsBase/src/Axes.jl @@ -1,12 +1,15 @@ module Axes export Axis, Extrema, tickfont, guidefont, widen_factor, scale_inverse_scale_func -export sort_3d_axes, axes_letters, process_axis_arg!, has_ticks +export sort_3d_axes, axes_letters, process_axis_arg!, has_ticks, get_axis, scale_lims! -using PlotsBase: PlotsBase, RecipesPipeline, Subplot, DefaultsDict, TimeType -using PlotsBase.Commons -using PlotsBase.Ticks -using PlotsBase.Fonts +import ..PlotsBase +import ..PlotsBase: Subplot, DefaultsDict, TimeType + +using ..RecipesPipeline +using ..Commons +using ..Ticks +using ..Fonts const default_widen_factor = Ref(1.06) const _widen_seriestypes = ( @@ -51,7 +54,7 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...) attr = DefaultsDict(explicit, Commons._axis_defaults_byletter[letter]) # update the defaults - attr!(Axis([sp], attr), args...; kw...) + PlotsBase.attr!(Axis([sp], attr), args...; kw...) end # properly retrieve from axis.attr, passing `:match` to the correct key @@ -305,7 +308,7 @@ end has_ticks(axis::Axis) = _has_ticks(get(axis, :ticks, nothing)) # update an Axis object with magic args and keywords -function attr!(axis::Axis, args...; kw...) +function PlotsBase.attr!(axis::Axis, args...; kw...) # first process args plotattributes = axis.plotattributes foreach(arg -> process_axis_arg!(plotattributes, arg), args) @@ -380,7 +383,7 @@ function _update_axis( end # update the axis - attr!(axis; kw...) + PlotsBase.attr!(axis; kw...) nothing end diff --git a/PlotsBase/src/BezierCurves.jl b/PlotsBase/src/BezierCurves.jl index cf9eb5119..6eb07fc5d 100644 --- a/PlotsBase/src/BezierCurves.jl +++ b/PlotsBase/src/BezierCurves.jl @@ -19,4 +19,4 @@ end PlotsBase.coords(curve::BezierCurve, n::Integer = 30; range = [0, 1]) = map(curve, Base.range(first(range), stop = last(range), length = n)) -end +end # module diff --git a/PlotsBase/src/Colorbars.jl b/PlotsBase/src/Colorbars.jl index 9d0238390..516a8768f 100644 --- a/PlotsBase/src/Colorbars.jl +++ b/PlotsBase/src/Colorbars.jl @@ -1,16 +1,18 @@ module Colorbars -export colorbar_style, - get_clims, update_clims, hascolorbar, get_colorbar_ticks, _update_subplot_colorbars -using PlotsBase.Commons: Commons, NaNMath, ignorenan_extrema -using PlotsBase.PlotsSeries -using PlotsBase.Subplots: Subplot, series_list -using PlotsBase.Surfaces: AbstractSurface -using PlotsBase.Ticks -using PlotsBase.Ticks: _transform_ticks -import PlotsBase.Commons.get_clims - -# These functions return an operator for use in `get_clims(::Seres, op)` +export colorbar_style, get_clims, update_clims, hascolorbar +export get_colorbar_ticks, _update_subplot_colorbars + +import ..Commons: NaNMath, ignorenan_extrema, get_clims + +using ..Subplots: Subplot, series_list +using ..Surfaces: AbstractSurface +using ..Ticks: _transform_ticks +using ..DataSeries +using ..Commons +using ..Ticks + +# these functions return an operator for use in `get_clims(::Seres, op)` process_clims(lims::Tuple{<:Number,<:Number}) = (zlims -> ifelse.(isfinite.(lims), lims, zlims)) ∘ ignorenan_extrema process_clims(s::Union{Symbol,Nothing,Missing}) = ignorenan_extrema @@ -137,7 +139,8 @@ function get_colorbar_ticks(sp::Subplot; update = true, formatter = sp[:colorbar return sp.attr[:colorbar_optimized_ticks] end -# Dynamic callback from the pipeline if needed +# dynamic callback from the pipeline if needed _update_subplot_colorbars(sp::Subplot) = update_clims(sp) _update_subplot_colorbars(sp::Subplot, series::Series) = update_clims(sp, series) -end # Colorbars + +end # module diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index 0a64f3766..a748b97d4 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -37,7 +37,7 @@ export anynan, ignorenan_maximum, ignorenan_mean, ignorenan_minimum -#exports from args.jl +export istuple, isvector, ismatrix, isscalar, is_2tuple export default, wraptuple, merge_with_base_supported using PlotsBase: PlotsBase, Printf, NaNMath, cgrad @@ -168,6 +168,17 @@ function color_or_nothing!(plotattributes, k::Symbol) nothing end +istuple(::Tuple) = true +istuple(::Any) = false +isvector(::AVec) = true +isvector(::Any) = false +ismatrix(::AMat) = true +ismatrix(::Any) = false +isscalar(::Real) = true +isscalar(::Any) = false + +is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2 + # cache joined symbols so they can be looked up instead of constructed each time const _attrsymbolcache = Dict{Symbol,Dict{Symbol,Symbol}}() diff --git a/PlotsBase/src/Series.jl b/PlotsBase/src/DataSeries.jl similarity index 97% rename from PlotsBase/src/Series.jl rename to PlotsBase/src/DataSeries.jl index a784e5f40..b758aee4e 100644 --- a/PlotsBase/src/Series.jl +++ b/PlotsBase/src/DataSeries.jl @@ -1,4 +1,4 @@ -module PlotsSeries +module DataSeries export Series, should_add_to_legend, @@ -20,11 +20,13 @@ export get_linestyle, get_fillalpha, get_markercolor, get_markeralpha -import PlotsBase.Commons: get_subplot, _series_defaults -using PlotsBase.Commons -using PlotsBase.Commons: get_gradient -using PlotsBase.PlotUtils: ColorGradient, plot_color -using PlotsBase: PlotsBase, DefaultsDict, RecipesPipeline, get_attr_symbol, KW + +import ..Commons: get_gradient, get_subplot, _series_defaults +import ..PlotsBase + +using ..PlotsBase: DefaultsDict, RecipesPipeline, get_attr_symbol, KW +using ..PlotUtils: ColorGradient, plot_color +using ..Commons mutable struct Series plotattributes::DefaultsDict @@ -329,4 +331,5 @@ function warn_on_inconsistent_shape_attrs(series, x, y, z, r) end end end -end # PlotsSeries + +end # module diff --git a/PlotsBase/src/Fonts.jl b/PlotsBase/src/Fonts.jl index 0d87c9553..737d5d271 100644 --- a/PlotsBase/src/Fonts.jl +++ b/PlotsBase/src/Fonts.jl @@ -1,11 +1,12 @@ module Fonts -using PlotsBase.Colors -using PlotsBase.Commons -using PlotsBase.Commons: +using ..Colors +using ..Commons +using ..Commons: _initial_plt_fontsizes, _initial_sp_fontsizes, _initial_ax_fontsizes, _initial_fontsizes + # keep in mind: these will be reexported and are public API -export font, scalefontsizes, resetfontsizes, text, is_horizontal, Font, PlotText +export Font, PlotText, font, scalefontsizes, resetfontsizes, text, is_horizontal mutable struct Font family::AbstractString @@ -174,4 +175,5 @@ text(str, args...; kw...) = PlotText(string(str), font(args...; kw...)) Base.length(t::PlotText) = length(t.str) is_horizontal(t::PlotText) = abs(sind(t.font.rotation)) ≤ sind(45) -end # Fonts + +end # module diff --git a/PlotsBase/src/Layouts.jl b/PlotsBase/src/Layouts.jl new file mode 100644 index 000000000..dd0e51b0c --- /dev/null +++ b/PlotsBase/src/Layouts.jl @@ -0,0 +1,247 @@ +module Layouts + export origin, left, top, right, bottom, bbox, bbox_to_pcts, xy_mm_to_pcts + export ispositive, GridLayout, EmptyLayout, RootLayout + export leftpad, toppad, bottompad, rightpad + + import ..PlotsBase + + using ..RecipesBase: AbstractLayout + using ..Measurements + using ..Commons + # NOTE: (0,0) is the top-left !!! + + to_pixels(m::AbsoluteLength) = m.value / 0.254 + + left(bbox::BoundingBox) = bbox.x0[1] + top(bbox::BoundingBox) = bbox.x0[2] + right(bbox::BoundingBox) = left(bbox) + width(bbox) + bottom(bbox::BoundingBox) = top(bbox) + height(bbox) + origin(bbox::BoundingBox) = left(bbox) + width(bbox) / 2, top(bbox) + height(bbox) / 2 + Base.size(bbox::BoundingBox) = (width(bbox), height(bbox)) + + # Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value) + ispositive(m::Measure) = m.value > 0 + + # union together bounding boxes + function Base.:+(bb1::BoundingBox, bb2::BoundingBox) + # empty boxes don't change the union + ispositive(width(bb1)) || return bb2 + ispositive(height(bb1)) || return bb2 + ispositive(width(bb2)) || return bb1 + ispositive(height(bb2)) || return bb1 + + l = min(left(bb1), left(bb2)) + t = min(top(bb1), top(bb2)) + r = max(right(bb1), right(bb2)) + b = max(bottom(bb1), bottom(bb2)) + BoundingBox(l, t, r - l, b - t) + end + + # convert x,y coordinates from absolute coords to percentages... + # returns x_pct, y_pct + function xy_mm_to_pcts(x::AbsoluteLength, y::AbsoluteLength, figw, figh, flipy = true) + xmm, ymm = x.value, y.value + if flipy + ymm = figh.value - ymm # flip y when origin in bottom-left + end + xmm / figw.value, ymm / figh.value + end + + # convert a bounding box from absolute coords to percentages... + # returns an array of percentages of figure size: [left, bottom, width, height] + function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true) + mms = Float64[f(bb).value for f in (left, bottom, width, height)] + if flipy + mms[2] = figh.value - mms[2] # flip y when origin in bottom-left + end + mms ./ Float64[figw.value, figh.value, figw.value, figh.value] + end + + Base.show(io::IO, bbox::BoundingBox) = print( + io, + "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}", + ) + + # ----------------------------------------------------------- + # AbstractLayout + + Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))") + + make_measure_hor(n::Number) = n * w + make_measure_hor(m::Measure) = m + + make_measure_vert(n::Number) = n * h + make_measure_vert(m::Measure) = m + + """ + bbox(x, y, w, h [,originargs...]) + bbox(layout) + + Create a bounding box for plotting + """ + function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...) + oargs = vcat(oarg1, originargs...) + orighor = :left + origver = :top + for oarg in oargs + if oarg ≡ :center + orighor = origver = oarg + elseif oarg in (:left, :right, :hcenter) + orighor = oarg + elseif oarg in (:top, :bottom, :vcenter) + origver = oarg + else + @warn "Unused origin arg in bbox construction: $oarg" + end + end + bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver) + end + + # create a new bbox + function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top) + x = make_measure_hor(x) + y = make_measure_vert(y) + width = make_measure_hor(width) + height = make_measure_vert(height) + left = if h_anchor ≡ :left + x + elseif h_anchor in (:center, :hcenter) + 0.5w - 0.5width + x + else + 1w - x - width + end + top = if v_anchor ≡ :top + y + elseif v_anchor in (:center, :vcenter) + 0.5h - 0.5height + y + else + 1h - y - height + end + BoundingBox(left, top, width, height) + end + + # this is the available area for drawing everything in this layout... as percentages of total canvas + bbox(layout::AbstractLayout) = layout.bbox + bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb) + + # layouts are recursive, tree-like structures, and most will have a parent field + Base.parent(layout::AbstractLayout) = layout.parent + parent_bbox(layout::AbstractLayout) = bbox(parent(layout)) + + # padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout) + # padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout) + # padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout)) + + update_position!(layout::AbstractLayout) = nothing + update_child_bboxes!( + layout::AbstractLayout, + minimum_perimeter = [0mm, 0mm, 0mm, 0mm]; + kw..., + ) = nothing + + left(layout::AbstractLayout) = left(bbox(layout)) + top(layout::AbstractLayout) = top(bbox(layout)) + right(layout::AbstractLayout) = right(bbox(layout)) + bottom(layout::AbstractLayout) = bottom(bbox(layout)) + width(layout::AbstractLayout) = width(bbox(layout)) + height(layout::AbstractLayout) = height(bbox(layout)) + + # pass these through to the bbox methods if there's no plotarea + plotarea(layout::AbstractLayout) = bbox(layout) + plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb) + + PlotsBase.attr(layout::AbstractLayout, k::Symbol) = layout.attr[k] + PlotsBase.attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v) + PlotsBase.attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v) + # hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k) + + leftpad(layout::AbstractLayout) = 0mm + toppad(layout::AbstractLayout) = 0mm + rightpad(layout::AbstractLayout) = 0mm + bottompad(layout::AbstractLayout) = 0mm + + # ----------------------------------------------------------- + # RootLayout + + # this is the parent of the top-level layout + struct RootLayout <: AbstractLayout end + + Base.show(io::IO, layout::RootLayout) = Base.show_default(io, layout) + Base.parent(::RootLayout) = nothing + parent_bbox(::RootLayout) = DEFAULT_BBOX[] + bbox(::RootLayout) = DEFAULT_BBOX[] + + # ----------------------------------------------------------- + # EmptyLayout + + # contains blank space + mutable struct EmptyLayout <: AbstractLayout + parent::AbstractLayout + bbox::BoundingBox + attr::KW # store label, width, and height for initialization + # label # this is the label that the subplot will take (since we create a layout before initialization) + end + EmptyLayout(parent = RootLayout(); kw...) = EmptyLayout(parent, DEFAULT_BBOX[], KW(kw)) + + Base.size(layout::EmptyLayout) = (0, 0) + Base.length(layout::EmptyLayout) = 0 + Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing + + _update_min_padding!(layout::EmptyLayout) = nothing + _update_inset_padding!(layout::EmptyLayout) = nothing + + # ----------------------------------------------------------- + # GridLayout + + # nested, gridded layout with optional size percentages + mutable struct GridLayout <: AbstractLayout + parent::AbstractLayout + minpad::Tuple # leftpad, toppad, rightpad, bottompad + bbox::BoundingBox + grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion + widths::Vector{Measure} + heights::Vector{Measure} + attr::KW + end + + """ + grid(args...; kw...) + + Create a grid layout for subplots. `args` specify the dimensions, e.g. + `grid(3,2, widths = (0.6,0.4))` creates a grid with three rows and two + columns of different width. + """ + grid(args...; kw...) = GridLayout(args...; kw...) + + function GridLayout( + dims...; + parent = RootLayout(), + widths = zeros(dims[2]), + heights = zeros(dims[1]), + kw..., + ) + grid = Matrix{AbstractLayout}(undef, dims...) + layout = GridLayout( + parent, + DEFAULT_MINPAD[], + DEFAULT_BBOX[], + grid, + Measure[w * pct for w in widths], + Measure[h * pct for h in heights], + # convert(Vector{Float64}, widths), + # convert(Vector{Float64}, heights), + KW(kw), + ) + for i in eachindex(grid) + grid[i] = EmptyLayout(layout) + end + layout + end + + Base.size(layout::GridLayout) = size(layout.grid) + Base.length(layout::GridLayout) = length(layout.grid) + Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r, c] + Base.setindex!(layout::GridLayout, v, r::Int, c::Int) = layout.grid[r, c] = v + Base.setindex!(layout::GridLayout, v, ci::CartesianIndex) = layout.grid[ci] = v + +end # module diff --git a/PlotsBase/src/PlotMeasures.jl b/PlotsBase/src/Measurements.jl similarity index 79% rename from PlotsBase/src/PlotMeasures.jl rename to PlotsBase/src/Measurements.jl index bcbc843ef..8b5efa94a 100644 --- a/PlotsBase/src/PlotMeasures.jl +++ b/PlotsBase/src/Measurements.jl @@ -1,11 +1,12 @@ -module PlotMeasures +module Measurements -export PX_PER_INCH, - DPI, MM_PER_INCH, MM_PER_PX, DEFAULT_BBOX, DEFAULT_MINPAD, DEFAULT_LINEWIDTH +export DEFAULT_BBOX, DEFAULT_MINPAD, DEFAULT_LINEWIDTH +export PX_PER_INCH, DPI, MM_PER_INCH, MM_PER_PX +export Length, AbsoluteLength, Measure, width, height import ..Measures -import ..Measures: - Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h +import ..Measures: Length, AbsoluteLength, Measure, BoundingBox +import ..Measures: mm, cm, inch, pt, width, height, w, h const BBox = Measures.Absolute2DBox export BBox, BoundingBox, mm, cm, inch, px, pct, pt, w, h @@ -37,4 +38,4 @@ mm2inch(mm::Real) = float(mm / MM_PER_INCH) px2mm(px::Real) = float(px * MM_PER_PX) mm2px(mm::Real) = float(mm / MM_PER_PX) -end +end # module diff --git a/PlotsBase/src/PlotsPlots.jl b/PlotsBase/src/Plots.jl similarity index 90% rename from PlotsBase/src/PlotsPlots.jl rename to PlotsBase/src/Plots.jl index b3231354e..048bb6828 100644 --- a/PlotsBase/src/PlotsPlots.jl +++ b/PlotsBase/src/Plots.jl @@ -1,4 +1,4 @@ -module PlotsPlots +module Plots export Plot, PlotOrSubplot, @@ -7,9 +7,7 @@ export Plot, ignorenan_extrema, protect, InputWrapper -import PlotsBase.Axes: _update_axis, scale_lims! -import PlotsBase.Commons: ignorenan_extrema, _cycle -import PlotsBase.Ticks: get_ticks + using PlotsBase: PlotsBase, AbstractPlot, @@ -18,14 +16,19 @@ using PlotsBase: Series, AbstractLayout, RecipesPipeline -using PlotsBase.PlotMeasures -using PlotsBase.Colorbars: _update_subplot_colorbars -using PlotsBase.Subplots: Subplot, _update_subplot_colors, _update_margins -using PlotsBase.Axes: Axis, get_axis -using PlotsBase.PlotUtils: get_color_palette -using PlotsBase.Commons -using PlotsBase.Commons.Frontend -using PlotsBase.Fonts: font + +import ..Subplots: Subplot, _update_subplot_colors, _update_margins +import ..Colorbars: _update_subplot_colorbars +import ..Commons: ignorenan_extrema, _cycle + +using ..PlotUtils +using ..Commons.Frontend +using ..Measurements +using ..Layouts +using ..Commons +using ..Fonts +using ..Ticks +using ..Axes const SubplotMap = Dict{Any,Subplot} mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T} @@ -50,7 +53,7 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T} nothing, Subplot[], SubplotMap(), - PlotsBase.EmptyLayout(), + EmptyLayout(), Subplot[], false, ) @@ -62,9 +65,9 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T} sp = deepcopy(osp) # FIXME: fails `PlotlyJS` ? plt.layout.grid[1, 1] = sp # reset some attributes - sp.minpad = PlotMeasures.DEFAULT_MINPAD[] - sp.bbox = PlotMeasures.DEFAULT_BBOX[] - sp.plotarea = PlotMeasures.DEFAULT_BBOX[] + sp.minpad = DEFAULT_MINPAD[] + sp.bbox = DEFAULT_BBOX[] + sp.plotarea = DEFAULT_BBOX[] sp.plt = plt # change the enclosing plot push!(plt.subplots, sp) plt @@ -218,7 +221,7 @@ function _update_axis_links(plt::Plot, axis::Axis, letter::Symbol) nothing end -function PlotsBase.Axes._update_axis( +function Axes._update_axis( plt::Plot, sp::Subplot, plotattributes_in::AKW, @@ -228,14 +231,14 @@ function PlotsBase.Axes._update_axis( # get (maybe initialize) the axis axis = get_axis(sp, letter) - _update_axis(axis, plotattributes_in, letter, subplot_index) + Axes._update_axis(axis, plotattributes_in, letter, subplot_index) # convert a bool into auto or nothing if isa(axis[:ticks], Bool) axis[:ticks] = axis[:ticks] ? :auto : nothing end - PlotsBase.Axes._update_axis_colors(axis) + Axes._update_axis_colors(axis) _update_axis_links(plt, axis, letter) nothing end @@ -265,7 +268,7 @@ function _update_subplot_attrs( lims_warned = false for letter in (:x, :y, :z) - _update_axis(plt, sp, plotattributes_in, letter, subplot_index) + Axes._update_axis(plt, sp, plotattributes_in, letter, subplot_index) lk = get_attr_symbol(letter, :lims) # warn against using `Range` in x,y,z lims @@ -290,4 +293,5 @@ function scale_lims!(plt::Union{Plot,Subplot}, factor) end Commons.get_size(plt::Plot) = get_size(plt.attr) Commons.get_thickness_scaling(plt::Plot) = get_thickness_scaling(plt.attr) -end # PlotsPlots + +end # module diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index d5e242287..4df53bb8d 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -115,11 +115,16 @@ export plotattr, scalefontsizes, resetfontsizes + +function attr end +function attr! end +function rotate end +function rotate! end + #! format: on import Measures -include("PlotMeasures.jl") -using .PlotMeasures -import .PlotMeasures: Length, AbsoluteLength, Measure, width, height +include("Measurements.jl") +using .Measurements # --------------------------------------------------------- macro ScopeModule(mod::Symbol, parent::Symbol, symbols...) import_ex = Expr( @@ -140,38 +145,35 @@ using .Commons.Frontend # --------------------------------------------------------- include("Fonts.jl") @reexport using .Fonts -using .Fonts: Font, PlotText include("Ticks.jl") using .Ticks -include("Series.jl") -using .PlotsSeries +include("DataSeries.jl") +using .DataSeries +include("Layouts.jl") +using .Layouts include("Subplots.jl") using .Subplots -import .Subplots: plotarea, plotarea!, leftpad, toppad, bottompad, rightpad include("Axes.jl") using .Axes include("Surfaces.jl") +using .Surfaces include("Colorbars.jl") using .Colorbars -include("PlotsPlots.jl") -using .PlotsPlots -include("layouts.jl") +include("Plots.jl") +using .Plots # --------------------------------------------------------- +include("layouts.jl") include("utils.jl") -using .Surfaces include("axes_utils.jl") include("legend.jl") include("Shapes.jl") using .Shapes -using .Shapes: Shape, _shapes, rotate! include("Annotations.jl") using .Annotations -using .Annotations: SeriesAnnotations, process_annotation include("Arrows.jl") using .Arrows include("Strokes.jl") using .Strokes -using .Strokes: Stroke, Brush include("BezierCurves.jl") using .BezierCurves include("themes.jl") @@ -182,15 +184,19 @@ include("recipes.jl") include("animation.jl") include("examples.jl") include("plotattr.jl") + include("backends/nobackend.jl") include("abstract_backend.jl") -include("alignment.jl") const CURRENT_BACKEND = CurrentBackend(:none) + +include("alignment.jl") include("output.jl") include("shorthands.jl") include("backends/web.jl") + include("backends/plotly.jl") using .Plotly + include("init.jl") include("users.jl") diff --git a/PlotsBase/src/Shapes.jl b/PlotsBase/src/Shapes.jl index 81b412ec0..daf36f609 100644 --- a/PlotsBase/src/Shapes.jl +++ b/PlotsBase/src/Shapes.jl @@ -1,7 +1,9 @@ module Shapes -using PlotsBase: PlotsBase, RecipesPipeline -using PlotsBase.Commons +import ..PlotsBase + +using ..RecipesPipeline +using ..Commons # keep in mind: these will be reexported and are public API export Shape, @@ -16,9 +18,7 @@ export Shape, scale!, scale, translate, - translate!, - rotate, - rotate! + translate! const P2 = NTuple{2,Float64} const P3 = NTuple{3,Float64} @@ -205,9 +205,9 @@ rotate_x(x::Real, y::Real, θ::Real, centerx::Real, centery::Real) = rotate_y(x::Real, y::Real, θ::Real, centerx::Real, centery::Real) = ((y - centery) * cos(θ) + (x - centerx) * sin(θ) + centery) -rotate(x::Real, y::Real, θ::Real, c) = (rotate_x(x, y, θ, c...), rotate_y(x, y, θ, c...)) +PlotsBase.rotate(x::Real, y::Real, θ::Real, c) = (rotate_x(x, y, θ, c...), rotate_y(x, y, θ, c...)) -function rotate!(shape::Shape, θ::Real, c = center(shape)) +function PlotsBase.rotate!(shape::Shape, θ::Real, c = center(shape)) x, y = coords(shape) for i in eachindex(x) xi = rotate_x(x[i], y[i], θ, c...) @@ -218,11 +218,11 @@ function rotate!(shape::Shape, θ::Real, c = center(shape)) end "rotate an object in space" -function rotate(shape::Shape, θ::Real, c = center(shape)) +function PlotsBase.rotate(shape::Shape, θ::Real, c = center(shape)) x, y = coords(shape) x_new = rotate_x.(x, y, θ, c...) y_new = rotate_y.(x, y, θ, c...) Shape(x_new, y_new) end -end # Shapes +end # module diff --git a/PlotsBase/src/Strokes.jl b/PlotsBase/src/Strokes.jl index 5fcb8a5b3..3b0452235 100644 --- a/PlotsBase/src/Strokes.jl +++ b/PlotsBase/src/Strokes.jl @@ -1,8 +1,10 @@ module Strokes -export stroke, brush, Stroke, Brush -using PlotsBase.Colors: Colorant -using PlotsBase.Commons: all_alphas, all_reals, all_styles +export Stroke, Brush, stroke, brush + +using ..Colors: Colorant +using ..Commons: all_alphas, all_reals, all_styles + struct Stroke width color @@ -77,6 +79,4 @@ function brush(args...; alpha = nothing) Brush(size, color, alpha) end -# ----------------------------------------------------------------------- - -end # Strokes +end # module diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index 0b76b2f64..bdc71fdbd 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -13,7 +13,6 @@ export Subplot, leftpad, bottompad, rightpad -import PlotsBase.Ticks: get_ticks using PlotsBase: PlotsBase, RecipesPipeline, @@ -22,13 +21,16 @@ using PlotsBase: AbstractLayout, BoundingBox, DefaultsDict -using PlotsBase.RecipesPipeline: RecipesPipeline, Surface, Volume -using PlotsBase.PlotUtils: get_color_palette -using PlotsBase.Commons -using PlotsBase.Commons.Frontend -using PlotsBase.Commons: convert_legend_value, like_surface -using PlotsBase.Fonts -using PlotsBase.PlotMeasures + +import ..Commons: convert_legend_value, like_surface +using ..Measurements +using ..RecipesPipeline: RecipesPipeline, Surface, Volume +using ..PlotUtils: get_color_palette +using ..Commons.Frontend +using ..Layouts +using ..Commons +using ..Fonts +using ..Ticks # a single subplot mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout @@ -42,7 +44,7 @@ mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout o # can store backend-specific data... like a pyplot ax plt # the enclosing Plot object (can't give it a type because of no forward declarations) - Subplot(::T; parent = PlotsBase.RootLayout()) where {T<:AbstractBackend} = new{T}( + Subplot(::T; parent = RootLayout()) where {T<:AbstractBackend} = new{T}( parent, Series[], 0, @@ -292,4 +294,5 @@ end Commons.get_size(sp::Subplot) = Commons.get_size(sp.plt) Commons.get_thickness_scaling(sp::Subplot) = Commons.get_thickness_scaling(sp.plt) -end # Subplots + +end # module diff --git a/PlotsBase/src/Surfaces.jl b/PlotsBase/src/Surfaces.jl index 90c2c7794..be9b2f6d2 100644 --- a/PlotsBase/src/Surfaces.jl +++ b/PlotsBase/src/Surfaces.jl @@ -2,10 +2,11 @@ module Surfaces export SurfaceFunction, Surface -import PlotsBase: PlotsBase, expand_extrema!, Commons -using PlotsBase.Axes: Axis -using RecipesPipeline: AbstractSurface, Surface -using PlotsBase.Commons +import PlotsBase: PlotsBase, expand_extrema! +using ..RecipesPipeline: AbstractSurface, Surface + +using ..Commons +using ..Axes function PlotsBase.expand_extrema!(a::Axis, surf::Surface) ex = a[:extrema] @@ -19,4 +20,5 @@ struct SurfaceFunction <: AbstractSurface end Commons.handle_surface(z::Surface) = permutedims(z.surf) -end + +end # module diff --git a/PlotsBase/src/Ticks.jl b/PlotsBase/src/Ticks.jl index 5ef577836..aa5577f90 100644 --- a/PlotsBase/src/Ticks.jl +++ b/PlotsBase/src/Ticks.jl @@ -1,12 +1,18 @@ module Ticks -export get_ticks, - _has_ticks, _transform_ticks, get_minor_ticks, no_minor_intervals, num_minor_intervals -using PlotsBase.Commons -using PlotsBase.Dates +export get_ticks, _has_ticks, _transform_ticks, get_minor_ticks +export no_minor_intervals, num_minor_intervals, ticks_type + +using ..Commons +using ..Dates const DEFAULT_MINOR_INTERVALS = Ref(5) # 5 intervals -> 4 ticks +ticks_type(ticks::AVec{<:Real}) = :ticks +ticks_type(ticks::AVec{<:AbstractString}) = :labels +ticks_type(ticks::Tuple{<:Union{AVec,Tuple},<:Union{AVec,Tuple}}) = :ticks_and_labels +ticks_type(ticks) = :invalid + # get_ticks from axis symbol :x, :y, or :z get_ticks(ticks::NTuple{2,Any}, args...) = ticks @@ -98,4 +104,4 @@ function get_minor_ticks(sp, axis, ticks_and_labels) minorticks[amin .≤ minorticks .≤ amax] end -end # Ticks +end # module diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index c30fa0b11..ad4d8d1e6 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -8,18 +8,18 @@ import Statistics import UUIDs import JSON -using PlotUtils: PlotUtils, ColorGradient, rgba_string, rgb_string +using PlotUtils -import PlotsBase: right, left, bottom, top, width, height, bbox, ispolar using PlotsBase.Colors: Colorant -using PlotsBase.PlotMeasures +using PlotsBase.Measurements using PlotsBase.Annotations -using PlotsBase.PlotsSeries -using PlotsBase.PlotsPlots +using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Surfaces +using PlotsBase.Layouts using PlotsBase.Commons +using PlotsBase.Plots using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes @@ -404,7 +404,7 @@ function plotly_layout(plt::Plot) ymm = bottom(bb) - tpos[2] * height(bb) halign, valign = sp[:titlefonthalign], sp[:titlefontvalign] end - titlex, titley = PlotsBase.xy_mm_to_pcts(xmm, ymm, w * px, h * px) + titlex, titley = xy_mm_to_pcts(xmm, ymm, w * px, h * px) title_font = font(titlefont(sp), halign = halign, valign = valign) push!( plotattributes_out[:annotations], @@ -624,7 +624,7 @@ function plotly_colorscale(cg::PlotUtils.CategoricalColorGradient, α = nothing) cinds = repeat(1:n, inner = 2) vinds = vcat((i:(i + 1) for i in 1:n)...) map( - i -> [cg.values[vinds[i]], rgba_string(plot_color(color_list(cg)[cinds[i]], α))], + i -> [cg.values[vinds[i]], rgba_string(plot_color(PlotsBase.color_list(cg)[cinds[i]], α))], eachindex(cinds), ) end diff --git a/PlotsBase/src/examples.jl b/PlotsBase/src/examples.jl index 424bf7e33..158d44868 100644 --- a/PlotsBase/src/examples.jl +++ b/PlotsBase/src/examples.jl @@ -460,7 +460,7 @@ const _examples = PlotExample[ ), PlotExample( # 29 "Layouts, margins, label rotation, title location", - :(using PlotsBase.PlotMeasures), # for Measures, e.g. mm and px + :(using PlotsBase.Measurements), # for Measures, e.g. mm and px quote plot( rand(100, 6), diff --git a/PlotsBase/src/layouts.jl b/PlotsBase/src/layouts.jl index 01fcbea18..cd8088bdd 100644 --- a/PlotsBase/src/layouts.jl +++ b/PlotsBase/src/layouts.jl @@ -1,240 +1,4 @@ -# NOTE: (0,0) is the top-left !!! - -to_pixels(m::AbsoluteLength) = m.value / 0.254 - -origin(bbox::BoundingBox) = left(bbox) + width(bbox) / 2, top(bbox) + height(bbox) / 2 -left(bbox::BoundingBox) = bbox.x0[1] -top(bbox::BoundingBox) = bbox.x0[2] -right(bbox::BoundingBox) = left(bbox) + width(bbox) -bottom(bbox::BoundingBox) = top(bbox) + height(bbox) -Base.size(bbox::BoundingBox) = (width(bbox), height(bbox)) - -# Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value) -ispositive(m::Measure) = m.value > 0 - -# union together bounding boxes -function Base.:+(bb1::BoundingBox, bb2::BoundingBox) - # empty boxes don't change the union - ispositive(width(bb1)) || return bb2 - ispositive(height(bb1)) || return bb2 - ispositive(width(bb2)) || return bb1 - ispositive(height(bb2)) || return bb1 - - l = min(left(bb1), left(bb2)) - t = min(top(bb1), top(bb2)) - r = max(right(bb1), right(bb2)) - b = max(bottom(bb1), bottom(bb2)) - BoundingBox(l, t, r - l, b - t) -end - -# convert x,y coordinates from absolute coords to percentages... -# returns x_pct, y_pct -function xy_mm_to_pcts(x::AbsoluteLength, y::AbsoluteLength, figw, figh, flipy = true) - xmm, ymm = x.value, y.value - if flipy - ymm = figh.value - ymm # flip y when origin in bottom-left - end - xmm / figw.value, ymm / figh.value -end - -# convert a bounding box from absolute coords to percentages... -# returns an array of percentages of figure size: [left, bottom, width, height] -function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true) - mms = Float64[f(bb).value for f in (left, bottom, width, height)] - if flipy - mms[2] = figh.value - mms[2] # flip y when origin in bottom-left - end - mms ./ Float64[figw.value, figh.value, figw.value, figh.value] -end - -Base.show(io::IO, bbox::BoundingBox) = print( - io, - "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}", -) - -# ----------------------------------------------------------- -# AbstractLayout - -Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))") - -make_measure_hor(n::Number) = n * w -make_measure_hor(m::Measure) = m - -make_measure_vert(n::Number) = n * h -make_measure_vert(m::Measure) = m - -""" - bbox(x, y, w, h [,originargs...]) - bbox(layout) - -Create a bounding box for plotting -""" -function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...) - oargs = vcat(oarg1, originargs...) - orighor = :left - origver = :top - for oarg in oargs - if oarg ≡ :center - orighor = origver = oarg - elseif oarg in (:left, :right, :hcenter) - orighor = oarg - elseif oarg in (:top, :bottom, :vcenter) - origver = oarg - else - @warn "Unused origin arg in bbox construction: $oarg" - end - end - bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver) -end - -# create a new bbox -function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top) - x = make_measure_hor(x) - y = make_measure_vert(y) - width = make_measure_hor(width) - height = make_measure_vert(height) - left = if h_anchor ≡ :left - x - elseif h_anchor in (:center, :hcenter) - 0.5w - 0.5width + x - else - 1w - x - width - end - top = if v_anchor ≡ :top - y - elseif v_anchor in (:center, :vcenter) - 0.5h - 0.5height + y - else - 1h - y - height - end - BoundingBox(left, top, width, height) -end - -# this is the available area for drawing everything in this layout... as percentages of total canvas -bbox(layout::AbstractLayout) = layout.bbox -bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb) - -# layouts are recursive, tree-like structures, and most will have a parent field -Base.parent(layout::AbstractLayout) = layout.parent -parent_bbox(layout::AbstractLayout) = bbox(parent(layout)) - -# padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout) -# padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout) -# padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout)) - -update_position!(layout::AbstractLayout) = nothing -update_child_bboxes!( - layout::AbstractLayout, - minimum_perimeter = [0mm, 0mm, 0mm, 0mm]; - kw..., -) = nothing - -left(layout::AbstractLayout) = left(bbox(layout)) -top(layout::AbstractLayout) = top(bbox(layout)) -right(layout::AbstractLayout) = right(bbox(layout)) -bottom(layout::AbstractLayout) = bottom(bbox(layout)) -width(layout::AbstractLayout) = width(bbox(layout)) -height(layout::AbstractLayout) = height(bbox(layout)) - -# pass these through to the bbox methods if there's no plotarea -plotarea(layout::AbstractLayout) = bbox(layout) -plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb) - -attr(layout::AbstractLayout, k::Symbol) = layout.attr[k] -attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v) -attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v) -hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k) - -leftpad(layout::AbstractLayout) = 0mm -toppad(layout::AbstractLayout) = 0mm -rightpad(layout::AbstractLayout) = 0mm -bottompad(layout::AbstractLayout) = 0mm - -# ----------------------------------------------------------- -# RootLayout - -# this is the parent of the top-level layout -struct RootLayout <: AbstractLayout end - -Base.show(io::IO, layout::RootLayout) = Base.show_default(io, layout) -Base.parent(::RootLayout) = nothing -parent_bbox(::RootLayout) = DEFAULT_BBOX[] -bbox(::RootLayout) = DEFAULT_BBOX[] - -# ----------------------------------------------------------- -# EmptyLayout - -# contains blank space -mutable struct EmptyLayout <: AbstractLayout - parent::AbstractLayout - bbox::BoundingBox - attr::KW # store label, width, and height for initialization - # label # this is the label that the subplot will take (since we create a layout before initialization) -end -EmptyLayout(parent = RootLayout(); kw...) = EmptyLayout(parent, DEFAULT_BBOX[], KW(kw)) - -Base.size(layout::EmptyLayout) = (0, 0) -Base.length(layout::EmptyLayout) = 0 -Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing - -_update_min_padding!(layout::EmptyLayout) = nothing -_update_inset_padding!(layout::EmptyLayout) = nothing - -# ----------------------------------------------------------- -# GridLayout - -# nested, gridded layout with optional size percentages -mutable struct GridLayout <: AbstractLayout - parent::AbstractLayout - minpad::Tuple # leftpad, toppad, rightpad, bottompad - bbox::BoundingBox - grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion - widths::Vector{Measure} - heights::Vector{Measure} - attr::KW -end - -""" - grid(args...; kw...) - -Create a grid layout for subplots. `args` specify the dimensions, e.g. -`grid(3,2, widths = (0.6,0.4))` creates a grid with three rows and two -columns of different width. -""" -grid(args...; kw...) = GridLayout(args...; kw...) - -function GridLayout( - dims...; - parent = RootLayout(), - widths = zeros(dims[2]), - heights = zeros(dims[1]), - kw..., -) - grid = Matrix{AbstractLayout}(undef, dims...) - layout = GridLayout( - parent, - DEFAULT_MINPAD[], - DEFAULT_BBOX[], - grid, - Measure[w * pct for w in widths], - Measure[h * pct for h in heights], - # convert(Vector{Float64}, widths), - # convert(Vector{Float64}, heights), - KW(kw), - ) - for i in eachindex(grid) - grid[i] = EmptyLayout(layout) - end - layout -end - -Base.size(layout::GridLayout) = size(layout.grid) -Base.length(layout::GridLayout) = length(layout.grid) -Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r, c] -Base.setindex!(layout::GridLayout, v, r::Int, c::Int) = layout.grid[r, c] = v -Base.setindex!(layout::GridLayout, v, ci::CartesianIndex) = layout.grid[ci] = v - leftpad(pad) = pad[1] toppad(pad) = pad[2] rightpad(pad) = pad[3] @@ -491,7 +255,7 @@ end function build_layout(layout::GridLayout, n::Integer, plts::AVec{Plot}) nr, nc = size(layout) subplots = Subplot[] - spmap = PlotsPlots.SubplotMap() + spmap = Plots.SubplotMap() empty = isempty(plts) i = 0 for r in 1:nr, c in 1:nc diff --git a/PlotsBase/src/pipeline.jl b/PlotsBase/src/pipeline.jl index d4ba2bc5b..f66a957e0 100644 --- a/PlotsBase/src/pipeline.jl +++ b/PlotsBase/src/pipeline.jl @@ -287,7 +287,7 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW}) else get(sp_attrs, sp, KW()) end - PlotsPlots._update_subplot_attrs(plt, sp, attr, idx, false) + Plots._update_subplot_attrs(plt, sp, attr, idx, false) end # do we need to link any axes together? @@ -378,8 +378,8 @@ end function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T} st::Symbol = plotattributes[:seriestype] sp::Subplot{T} = plotattributes[:subplot] - sp_idx = PlotsPlots.get_subplot_index(plt, sp) - PlotsPlots._update_subplot_attrs(plt, sp, plotattributes, sp_idx, true) + sp_idx = Plots.get_subplot_index(plt, sp) + Plots._update_subplot_attrs(plt, sp, plotattributes, sp_idx, true) st = _override_seriestype_check(plotattributes, st) diff --git a/PlotsBase/src/plot.jl b/PlotsBase/src/plot.jl index dc72a8523..892df16b0 100644 --- a/PlotsBase/src/plot.jl +++ b/PlotsBase/src/plot.jl @@ -182,7 +182,7 @@ function plot!( # first apply any args for the subplots for (idx, sp) in enumerate(plt.subplots) - PlotsPlots._update_subplot_attrs( + Plots._update_subplot_attrs( plt, sp, idx == ttl_idx ? KW() : plotattributes, diff --git a/PlotsBase/src/utils.jl b/PlotsBase/src/utils.jl index 8d2658a23..a232662c4 100644 --- a/PlotsBase/src/utils.jl +++ b/PlotsBase/src/utils.jl @@ -270,22 +270,6 @@ end isijulia() = :IJulia in nameof.(collect(values(Base.loaded_modules))) isatom() = :Atom in nameof.(collect(values(Base.loaded_modules))) -istuple(::Tuple) = true -istuple(::Any) = false -isvector(::AVec) = true -isvector(::Any) = false -ismatrix(::AMat) = true -ismatrix(::Any) = false -isscalar(::Real) = true -isscalar(::Any) = false - -is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2 - -ticks_type(ticks::AVec{<:Real}) = :ticks -ticks_type(ticks::AVec{<:AbstractString}) = :labels -ticks_type(ticks::Tuple{<:Union{AVec,Tuple},<:Union{AVec,Tuple}}) = :ticks_and_labels -ticks_type(ticks) = :invalid - limsType(lims::Tuple{<:Real,<:Real}) = :limits limsType(lims::Symbol) = lims ≡ :auto ? :auto : :invalid limsType(lims) = :invalid From e4ca654e8c78b7d1d5c70bc46762ab1433823a23 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Mon, 1 Apr 2024 22:15:24 +0200 Subject: [PATCH 10/58] stable `GR` --- PlotsBase/ext/GRExt.jl | 6 +- PlotsBase/ext/GastonExt.jl | 6 +- PlotsBase/ext/HDF5Ext.jl | 6 +- PlotsBase/ext/PGFPlotsXExt.jl | 6 +- PlotsBase/ext/PlotlyJSExt.jl | 2 +- PlotsBase/ext/PythonPlotExt.jl | 17 +-- PlotsBase/ext/UnicodePlotsExt.jl | 6 +- PlotsBase/src/Commons/Commons.jl | 29 ++-- PlotsBase/src/Layouts.jl | 247 ------------------------------- PlotsBase/src/Measurements.jl | 234 +++++++++++++++++++++++++++-- PlotsBase/src/Plots.jl | 1 - PlotsBase/src/PlotsBase.jl | 12 +- PlotsBase/src/Subplots.jl | 11 +- PlotsBase/src/backends/plotly.jl | 1 - PlotsBase/src/layouts.jl | 40 +++-- PlotsBase/test/runtests.jl | 36 ++--- PlotsBase/test/test_axes.jl | 2 +- PlotsBase/test/test_utils.jl | 4 +- 18 files changed, 328 insertions(+), 338 deletions(-) delete mode 100644 PlotsBase/src/Layouts.jl diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index 0f5a0696b..b3427d17c 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -5,16 +5,16 @@ import RecipesPipeline import NaNMath import GR -using PlotsBase.PlotMeasures +using PlotsBase.Measurements using PlotsBase.Annotations -using PlotsBase.PlotsSeries -using PlotsBase.PlotsPlots +using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Commons using PlotsBase.Arrows using PlotsBase.Shapes using PlotsBase.Colors +using PlotsBase.Plots using PlotsBase.Fonts using PlotsBase.Fonts using PlotsBase.Ticks diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index ddaf0c977..e0e04bbd9 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -5,12 +5,12 @@ import PlotUtils import PlotsBase import Gaston -using PlotsBase.PlotMeasures -using PlotsBase.PlotsSeries -using PlotsBase.PlotsPlots +using PlotsBase.Measurements +using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Commons +using PlotsBase.Plots using PlotsBase.Ticks using PlotsBase.Fonts using PlotsBase.Axes diff --git a/PlotsBase/ext/HDF5Ext.jl b/PlotsBase/ext/HDF5Ext.jl index 0b09a63e8..e191dac7e 100644 --- a/PlotsBase/ext/HDF5Ext.jl +++ b/PlotsBase/ext/HDF5Ext.jl @@ -7,12 +7,14 @@ import PlotUtils: PlotUtils, Colors import PlotUtils.ColorSchemes: ColorScheme import PlotUtils.Colors: Colorant -import PlotsBase: PlotsBase, GridLayout, RootLayout, BoundingBox, Length, Plot +import PlotsBase -using PlotsBase.PlotsSeries +using PlotsBase.Measurements +using PlotsBase.DataSeries using PlotsBase.Subplots using PlotsBase.Commons using PlotsBase.Shapes +using PlotsBase.Plots using PlotsBase.Fonts using PlotsBase.Axes diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index 57fb2ea85..9b56b150e 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -11,10 +11,9 @@ import PGFPlotsX import Latexify import Contour -using PlotsBase.PlotMeasures +using PlotsBase.Measurements using PlotsBase.Annotations -using PlotsBase.PlotsSeries -using PlotsBase.PlotsPlots +using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Surfaces @@ -22,6 +21,7 @@ using PlotsBase.Commons using PlotsBase.Colors using PlotsBase.Shapes using PlotsBase.Arrows +using PlotsBase.Plots using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes diff --git a/PlotsBase/ext/PlotlyJSExt.jl b/PlotsBase/ext/PlotlyJSExt.jl index e53d89db2..3b3acea36 100644 --- a/PlotsBase/ext/PlotlyJSExt.jl +++ b/PlotsBase/ext/PlotlyJSExt.jl @@ -1,9 +1,9 @@ module PlotlyJSExt import PlotsBase: PlotsBase, Plot -using PlotsBase.PlotsPlots using PlotsBase.Commons using PlotsBase.Plotly +using PlotsBase.Plots import PlotlyJS: PlotlyJS, WebIO diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index 1e7a39b95..bf284df89 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -2,6 +2,7 @@ module PythonPlotExt import RecipesPipeline import PythonPlot +import NaNMath const PythonCall = PythonPlot.PythonCall const pyisnone = @@ -11,25 +12,21 @@ const mpl = PythonPlot.matplotlib const mpl_toolkits = PythonCall.pynew() const numpy = PythonCall.pynew() -import NaNMath +using PlotUtils import PlotsBase -import PlotsBase.PlotUtils: PlotUtils, ColorGradient, plot_color, color_list, cgrad -import PlotsBase: bbox_to_pcts, right, left, bottom, top, width, height, ticks_type -import PlotsBase: ispositive, ismatrix -import PlotsBase.Commons: Commons, single_color import RecipesPipeline: Surface -using PlotsBase.PlotMeasures +using PlotsBase.Measurements using PlotsBase.Annotations -using PlotsBase.PlotsSeries -using PlotsBase.PlotsPlots +using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Commons using PlotsBase.Colors using PlotsBase.Arrows using PlotsBase.Shapes +using PlotsBase.Plots using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes @@ -460,7 +457,7 @@ _py_thickness_scale(plt::Plot{PythonPlotBackend}, ptsz) = ptsz * plt[:thickness_ # Create the window/figure for this backend. function PlotsBase._create_backend_figure(plt::Plot{PythonPlotBackend}) w, h = - map(s -> PlotsBase.PlotMeasures.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) + map(s -> PlotsBase.Measurements.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) # reuse the current figure? plt[:overwrite_figure] ? PythonPlot.gcf() : PythonPlot.figure() end @@ -890,7 +887,7 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) n = length(dim1) args = if typeof(fillrange) <: Union{Real,AVec} dim1, _cycle(fillrange, rng), dim2 - elseif PlotsBase.is_2tuple(fillrange) + elseif is_2tuple(fillrange) dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng) end diff --git a/PlotsBase/ext/UnicodePlotsExt.jl b/PlotsBase/ext/UnicodePlotsExt.jl index 465a105f3..beffb8538 100644 --- a/PlotsBase/ext/UnicodePlotsExt.jl +++ b/PlotsBase/ext/UnicodePlotsExt.jl @@ -4,16 +4,16 @@ import PlotsBase: PlotsBase, texmath2unicode import RecipesPipeline import UnicodePlots -using PlotsBase.PlotMeasures -using PlotsBase.PlotsSeries +using PlotsBase.Measurements using PlotsBase.Annotations -using PlotsBase.PlotsPlots +using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Commons using PlotsBase.Shapes using PlotsBase.Arrows using PlotsBase.Colors +using PlotsBase.Plots using PlotsBase.Fonts using PlotsBase.Ticks using PlotsBase.Axes diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index a748b97d4..60bc19b55 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -3,7 +3,6 @@ module Commons export AVec, AMat, KW, AKW, TicksArgs, PlotsBase, PLOTS_SEED, _haligns, _valigns, _cbar_width -# Functions export get_subplot, coords, ispolar, @@ -40,15 +39,19 @@ export anynan, export istuple, isvector, ismatrix, isscalar, is_2tuple export default, wraptuple, merge_with_base_supported -using PlotsBase: PlotsBase, Printf, NaNMath, cgrad +export DEFAULT_BBOX, DEFAULT_MINPAD, DEFAULT_LINEWIDTH +export MM_PER_PX, MM_PER_INCH, DPI, PX_PER_INCH + +using PlotsBase: PlotsBase, cgrad import PlotsBase: RecipesPipeline -using PlotsBase.Colors: Colorant, @colorant_str -using PlotsBase.ColorTypes: alpha -using PlotsBase.Measures: mm, BoundingBox -using PlotsBase.PlotUtils: PlotUtils, ColorPalette, plot_color, isdark, ColorGradient -using PlotsBase.RecipesBase -using PlotsBase: DEFAULT_LINEWIDTH -using PlotsBase: Statistics +using ..Colors: Colorant, @colorant_str +using ..ColorTypes: alpha +using ..Measures: mm, BoundingBox +using ..PlotUtils: PlotUtils, ColorPalette, plot_color, isdark, ColorGradient +using ..RecipesBase +using ..Statistics +using ..NaNMath +using ..Printf const AVec = AbstractVector const AMat = AbstractMatrix @@ -56,14 +59,19 @@ const KW = Dict{Symbol,Any} const AKW = AbstractDict{Symbol,Any} const TicksArgs = Union{AVec{T},Tuple{AVec{T},AVec{S}},Symbol} where {T<:Real,S<:AbstractString} + +const DEFAULT_BBOX = Ref(BoundingBox(0mm, 0mm, 0mm, 0mm)) +const DEFAULT_MINPAD = Ref((20mm, 5mm, 2mm, 10mm)) +const DEFAULT_LINEWIDTH = Ref(1) const PLOTS_SEED = 1234 const PX_PER_INCH = 100 const DPI = PX_PER_INCH const MM_PER_INCH = 25.4 const MM_PER_PX = MM_PER_INCH / PX_PER_INCH +const _cbar_width = 5mm + const _haligns = :hcenter, :left, :right const _valigns = :vcenter, :top, :bottom -const _cbar_width = 5mm const _all_scales = [:identity, :ln, :log2, :log10, :asinh, :sqrt] const _log_scales = [:ln, :log2, :log10] const _log_scale_bases = Dict(:ln => ℯ, :log2 => 2.0, :log10 => 10.0) @@ -97,6 +105,7 @@ function ispolar end function expand_extrema! end function axis_limits end function preprocess_attributes! end + # --------------------------------------------------------------- wraptuple(x::Tuple) = x wraptuple(x) = (x,) diff --git a/PlotsBase/src/Layouts.jl b/PlotsBase/src/Layouts.jl deleted file mode 100644 index dd0e51b0c..000000000 --- a/PlotsBase/src/Layouts.jl +++ /dev/null @@ -1,247 +0,0 @@ -module Layouts - export origin, left, top, right, bottom, bbox, bbox_to_pcts, xy_mm_to_pcts - export ispositive, GridLayout, EmptyLayout, RootLayout - export leftpad, toppad, bottompad, rightpad - - import ..PlotsBase - - using ..RecipesBase: AbstractLayout - using ..Measurements - using ..Commons - # NOTE: (0,0) is the top-left !!! - - to_pixels(m::AbsoluteLength) = m.value / 0.254 - - left(bbox::BoundingBox) = bbox.x0[1] - top(bbox::BoundingBox) = bbox.x0[2] - right(bbox::BoundingBox) = left(bbox) + width(bbox) - bottom(bbox::BoundingBox) = top(bbox) + height(bbox) - origin(bbox::BoundingBox) = left(bbox) + width(bbox) / 2, top(bbox) + height(bbox) / 2 - Base.size(bbox::BoundingBox) = (width(bbox), height(bbox)) - - # Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value) - ispositive(m::Measure) = m.value > 0 - - # union together bounding boxes - function Base.:+(bb1::BoundingBox, bb2::BoundingBox) - # empty boxes don't change the union - ispositive(width(bb1)) || return bb2 - ispositive(height(bb1)) || return bb2 - ispositive(width(bb2)) || return bb1 - ispositive(height(bb2)) || return bb1 - - l = min(left(bb1), left(bb2)) - t = min(top(bb1), top(bb2)) - r = max(right(bb1), right(bb2)) - b = max(bottom(bb1), bottom(bb2)) - BoundingBox(l, t, r - l, b - t) - end - - # convert x,y coordinates from absolute coords to percentages... - # returns x_pct, y_pct - function xy_mm_to_pcts(x::AbsoluteLength, y::AbsoluteLength, figw, figh, flipy = true) - xmm, ymm = x.value, y.value - if flipy - ymm = figh.value - ymm # flip y when origin in bottom-left - end - xmm / figw.value, ymm / figh.value - end - - # convert a bounding box from absolute coords to percentages... - # returns an array of percentages of figure size: [left, bottom, width, height] - function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true) - mms = Float64[f(bb).value for f in (left, bottom, width, height)] - if flipy - mms[2] = figh.value - mms[2] # flip y when origin in bottom-left - end - mms ./ Float64[figw.value, figh.value, figw.value, figh.value] - end - - Base.show(io::IO, bbox::BoundingBox) = print( - io, - "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}", - ) - - # ----------------------------------------------------------- - # AbstractLayout - - Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))") - - make_measure_hor(n::Number) = n * w - make_measure_hor(m::Measure) = m - - make_measure_vert(n::Number) = n * h - make_measure_vert(m::Measure) = m - - """ - bbox(x, y, w, h [,originargs...]) - bbox(layout) - - Create a bounding box for plotting - """ - function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...) - oargs = vcat(oarg1, originargs...) - orighor = :left - origver = :top - for oarg in oargs - if oarg ≡ :center - orighor = origver = oarg - elseif oarg in (:left, :right, :hcenter) - orighor = oarg - elseif oarg in (:top, :bottom, :vcenter) - origver = oarg - else - @warn "Unused origin arg in bbox construction: $oarg" - end - end - bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver) - end - - # create a new bbox - function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top) - x = make_measure_hor(x) - y = make_measure_vert(y) - width = make_measure_hor(width) - height = make_measure_vert(height) - left = if h_anchor ≡ :left - x - elseif h_anchor in (:center, :hcenter) - 0.5w - 0.5width + x - else - 1w - x - width - end - top = if v_anchor ≡ :top - y - elseif v_anchor in (:center, :vcenter) - 0.5h - 0.5height + y - else - 1h - y - height - end - BoundingBox(left, top, width, height) - end - - # this is the available area for drawing everything in this layout... as percentages of total canvas - bbox(layout::AbstractLayout) = layout.bbox - bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb) - - # layouts are recursive, tree-like structures, and most will have a parent field - Base.parent(layout::AbstractLayout) = layout.parent - parent_bbox(layout::AbstractLayout) = bbox(parent(layout)) - - # padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout) - # padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout) - # padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout)) - - update_position!(layout::AbstractLayout) = nothing - update_child_bboxes!( - layout::AbstractLayout, - minimum_perimeter = [0mm, 0mm, 0mm, 0mm]; - kw..., - ) = nothing - - left(layout::AbstractLayout) = left(bbox(layout)) - top(layout::AbstractLayout) = top(bbox(layout)) - right(layout::AbstractLayout) = right(bbox(layout)) - bottom(layout::AbstractLayout) = bottom(bbox(layout)) - width(layout::AbstractLayout) = width(bbox(layout)) - height(layout::AbstractLayout) = height(bbox(layout)) - - # pass these through to the bbox methods if there's no plotarea - plotarea(layout::AbstractLayout) = bbox(layout) - plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb) - - PlotsBase.attr(layout::AbstractLayout, k::Symbol) = layout.attr[k] - PlotsBase.attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v) - PlotsBase.attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v) - # hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k) - - leftpad(layout::AbstractLayout) = 0mm - toppad(layout::AbstractLayout) = 0mm - rightpad(layout::AbstractLayout) = 0mm - bottompad(layout::AbstractLayout) = 0mm - - # ----------------------------------------------------------- - # RootLayout - - # this is the parent of the top-level layout - struct RootLayout <: AbstractLayout end - - Base.show(io::IO, layout::RootLayout) = Base.show_default(io, layout) - Base.parent(::RootLayout) = nothing - parent_bbox(::RootLayout) = DEFAULT_BBOX[] - bbox(::RootLayout) = DEFAULT_BBOX[] - - # ----------------------------------------------------------- - # EmptyLayout - - # contains blank space - mutable struct EmptyLayout <: AbstractLayout - parent::AbstractLayout - bbox::BoundingBox - attr::KW # store label, width, and height for initialization - # label # this is the label that the subplot will take (since we create a layout before initialization) - end - EmptyLayout(parent = RootLayout(); kw...) = EmptyLayout(parent, DEFAULT_BBOX[], KW(kw)) - - Base.size(layout::EmptyLayout) = (0, 0) - Base.length(layout::EmptyLayout) = 0 - Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing - - _update_min_padding!(layout::EmptyLayout) = nothing - _update_inset_padding!(layout::EmptyLayout) = nothing - - # ----------------------------------------------------------- - # GridLayout - - # nested, gridded layout with optional size percentages - mutable struct GridLayout <: AbstractLayout - parent::AbstractLayout - minpad::Tuple # leftpad, toppad, rightpad, bottompad - bbox::BoundingBox - grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion - widths::Vector{Measure} - heights::Vector{Measure} - attr::KW - end - - """ - grid(args...; kw...) - - Create a grid layout for subplots. `args` specify the dimensions, e.g. - `grid(3,2, widths = (0.6,0.4))` creates a grid with three rows and two - columns of different width. - """ - grid(args...; kw...) = GridLayout(args...; kw...) - - function GridLayout( - dims...; - parent = RootLayout(), - widths = zeros(dims[2]), - heights = zeros(dims[1]), - kw..., - ) - grid = Matrix{AbstractLayout}(undef, dims...) - layout = GridLayout( - parent, - DEFAULT_MINPAD[], - DEFAULT_BBOX[], - grid, - Measure[w * pct for w in widths], - Measure[h * pct for h in heights], - # convert(Vector{Float64}, widths), - # convert(Vector{Float64}, heights), - KW(kw), - ) - for i in eachindex(grid) - grid[i] = EmptyLayout(layout) - end - layout - end - - Base.size(layout::GridLayout) = size(layout.grid) - Base.length(layout::GridLayout) = length(layout.grid) - Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r, c] - Base.setindex!(layout::GridLayout, v, r::Int, c::Int) = layout.grid[r, c] = v - Base.setindex!(layout::GridLayout, v, ci::CartesianIndex) = layout.grid[ci] = v - -end # module diff --git a/PlotsBase/src/Measurements.jl b/PlotsBase/src/Measurements.jl index 8b5efa94a..9ed82da66 100644 --- a/PlotsBase/src/Measurements.jl +++ b/PlotsBase/src/Measurements.jl @@ -1,29 +1,239 @@ module Measurements -export DEFAULT_BBOX, DEFAULT_MINPAD, DEFAULT_LINEWIDTH -export PX_PER_INCH, DPI, MM_PER_INCH, MM_PER_PX -export Length, AbsoluteLength, Measure, width, height +export GridLayout, EmptyLayout, RootLayout +export leftpad, toppad, bottompad, rightpad +export origin, left, right, bottom, top +export bbox, bbox!, bbox_to_pcts, xy_mm_to_pcts +export Length, AbsoluteLength, Measure +export to_pixels, ispositive import ..Measures import ..Measures: Length, AbsoluteLength, Measure, BoundingBox import ..Measures: mm, cm, inch, pt, width, height, w, h +using ..RecipesBase: AbstractLayout +using ..Commons const BBox = Measures.Absolute2DBox export BBox, BoundingBox, mm, cm, inch, px, pct, pt, w, h +to_pixels(m::AbsoluteLength) = m.value / 0.254 + +left(bbox::BoundingBox) = bbox.x0[1] +top(bbox::BoundingBox) = bbox.x0[2] +right(bbox::BoundingBox) = left(bbox) + width(bbox) +bottom(bbox::BoundingBox) = top(bbox) + height(bbox) +origin(bbox::BoundingBox) = left(bbox) + width(bbox) / 2, top(bbox) + height(bbox) / 2 +Base.size(bbox::BoundingBox) = (width(bbox), height(bbox)) + +make_measure_hor(n::Number) = n * w +make_measure_hor(m::Measure) = m + +make_measure_vert(n::Number) = n * h +make_measure_vert(m::Measure) = m + +""" + bbox(x, y, w, h [,originargs...]) + bbox(layout) + +Create a bounding box for plotting +""" +function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...) + oargs = vcat(oarg1, originargs...) + orighor = :left + origver = :top + for oarg in oargs + if oarg ≡ :center + orighor = origver = oarg + elseif oarg in (:left, :right, :hcenter) + orighor = oarg + elseif oarg in (:top, :bottom, :vcenter) + origver = oarg + else + @warn "Unused origin arg in bbox construction: $oarg" + end + end + bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver) +end + +# create a new bbox +function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top) + x = make_measure_hor(x) + y = make_measure_vert(y) + width = make_measure_hor(width) + height = make_measure_vert(height) + left = if h_anchor ≡ :left + x + elseif h_anchor in (:center, :hcenter) + 0.5w - 0.5width + x + else + 1w - x - width + end + top = if v_anchor ≡ :top + y + elseif v_anchor in (:center, :vcenter) + 0.5h - 0.5height + y + else + 1h - y - height + end + BoundingBox(left, top, width, height) +end +# NOTE: (0,0) is the top-left !!! + +# convert x,y coordinates from absolute coords to percentages... +# returns x_pct, y_pct +function xy_mm_to_pcts(x::AbsoluteLength, y::AbsoluteLength, figw, figh, flipy = true) + xmm, ymm = x.value, y.value + if flipy + ymm = figh.value - ymm # flip y when origin in bottom-left + end + xmm / figw.value, ymm / figh.value +end + +# convert a bounding box from absolute coords to percentages... +# returns an array of percentages of figure size: [left, bottom, width, height] +function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true) + mms = Float64[f(bb).value for f in (left, bottom, width, height)] + if flipy + mms[2] = figh.value - mms[2] # flip y when origin in bottom-left + end + mms ./ Float64[figw.value, figh.value, figw.value, figh.value] +end + +Base.show(io::IO, bbox::BoundingBox) = print( + io, + "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}", +) + +# ----------------------------------------------------------- +# AbstractLayout + +left(layout::AbstractLayout) = left(bbox(layout)) +top(layout::AbstractLayout) = top(bbox(layout)) +right(layout::AbstractLayout) = right(bbox(layout)) +bottom(layout::AbstractLayout) = bottom(bbox(layout)) +width(layout::AbstractLayout) = width(bbox(layout)) +height(layout::AbstractLayout) = height(bbox(layout)) + +leftpad(layout::AbstractLayout) = 0mm +toppad(layout::AbstractLayout) = 0mm +rightpad(layout::AbstractLayout) = 0mm +bottompad(layout::AbstractLayout) = 0mm + +leftpad(pad) = pad[1] +toppad(pad) = pad[2] +rightpad(pad) = pad[3] +bottompad(pad) = pad[4] + +Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))") + +# this is the available area for drawing everything in this layout... as percentages of total canvas +bbox(layout::AbstractLayout) = layout.bbox +bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb) + +# layouts are recursive, tree-like structures, and most will have a parent field +Base.parent(layout::AbstractLayout) = layout.parent +parent_bbox(layout::AbstractLayout) = bbox(parent(layout)) + +# ----------------------------------------------------------- +# RootLayout + +# this is the parent of the top-level layout +struct RootLayout <: AbstractLayout end + +Base.show(io::IO, layout::RootLayout) = Base.show_default(io, layout) +Base.parent(::RootLayout) = nothing +parent_bbox(::RootLayout) = DEFAULT_BBOX[] +bbox(::RootLayout) = DEFAULT_BBOX[] + +# ----------------------------------------------------------- +# EmptyLayout + +# contains blank space +mutable struct EmptyLayout <: AbstractLayout + parent::AbstractLayout + bbox::BoundingBox + attr::KW # store label, width, and height for initialization + # label # this is the label that the subplot will take (since we create a layout before initialization) +end +EmptyLayout(parent = RootLayout(); kw...) = EmptyLayout(parent, DEFAULT_BBOX[], KW(kw)) + +Base.size(layout::EmptyLayout) = (0, 0) +Base.length(layout::EmptyLayout) = 0 +Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing + +# ----------------------------------------------------------- +# GridLayout + +# nested, gridded layout with optional size percentages +mutable struct GridLayout <: AbstractLayout + parent::AbstractLayout + minpad::Tuple # leftpad, toppad, rightpad, bottompad + bbox::BoundingBox + grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion + widths::Vector{Measure} + heights::Vector{Measure} + attr::KW +end + +leftpad(layout::GridLayout) = leftpad(layout.minpad) +toppad(layout::GridLayout) = toppad(layout.minpad) +rightpad(layout::GridLayout) = rightpad(layout.minpad) +bottompad(layout::GridLayout) = bottompad(layout.minpad) + + +function GridLayout( + dims...; + parent = RootLayout(), + widths = zeros(dims[2]), + heights = zeros(dims[1]), + kw..., +) + grid = Matrix{AbstractLayout}(undef, dims...) + layout = GridLayout( + parent, + DEFAULT_MINPAD[], + DEFAULT_BBOX[], + grid, + Measure[w * pct for w in widths], + Measure[h * pct for h in heights], + # convert(Vector{Float64}, widths), + # convert(Vector{Float64}, heights), + KW(kw), + ) + for i in eachindex(grid) + grid[i] = EmptyLayout(layout) + end + layout +end + +Base.size(layout::GridLayout) = size(layout.grid) +Base.length(layout::GridLayout) = length(layout.grid) +Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r, c] +Base.setindex!(layout::GridLayout, v, r::Int, c::Int) = layout.grid[r, c] = v +Base.setindex!(layout::GridLayout, v, ci::CartesianIndex) = layout.grid[ci] = v + +# Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value) +ispositive(m::Measure) = m.value > 0 + +# union together bounding boxes +function Base.:+(bb1::BoundingBox, bb2::BoundingBox) + # empty boxes don't change the union + ispositive(width(bb1)) || return bb2 + ispositive(height(bb1)) || return bb2 + ispositive(width(bb2)) || return bb1 + ispositive(height(bb2)) || return bb1 + + l = min(left(bb1), left(bb2)) + t = min(top(bb1), top(bb2)) + r = max(right(bb1), right(bb2)) + b = max(bottom(bb1), bottom(bb2)) + BoundingBox(l, t, r - l, b - t) +end + # allow pixels and percentages const px = AbsoluteLength(0.254) const pct = Length{:pct,Float64}(1.0) -const PX_PER_INCH = 100 -const DPI = PX_PER_INCH -const MM_PER_INCH = 25.4 -const MM_PER_PX = MM_PER_INCH / PX_PER_INCH -const _cbar_width = 5mm -const DEFAULT_BBOX = Ref(BoundingBox(0mm, 0mm, 0mm, 0mm)) -const DEFAULT_MINPAD = Ref((20mm, 5mm, 2mm, 10mm)) -const DEFAULT_LINEWIDTH = Ref(1) - Base.convert(::Type{<:Measure}, x::Float64) = x * pct Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value) diff --git a/PlotsBase/src/Plots.jl b/PlotsBase/src/Plots.jl index 048bb6828..af0b9d83a 100644 --- a/PlotsBase/src/Plots.jl +++ b/PlotsBase/src/Plots.jl @@ -24,7 +24,6 @@ import ..Commons: ignorenan_extrema, _cycle using ..PlotUtils using ..Commons.Frontend using ..Measurements -using ..Layouts using ..Commons using ..Fonts using ..Ticks diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index 4df53bb8d..38bec53e0 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -116,6 +116,8 @@ export scalefontsizes, resetfontsizes +import Measures: width, height + function attr end function attr! end function rotate end @@ -123,9 +125,8 @@ function rotate! end #! format: on import Measures -include("Measurements.jl") -using .Measurements -# --------------------------------------------------------- +import NaNMath + macro ScopeModule(mod::Symbol, parent::Symbol, symbols...) import_ex = Expr( :import, @@ -138,10 +139,11 @@ macro ScopeModule(mod::Symbol, parent::Symbol, symbols...) export_ex = Expr(:export, (s isa Expr ? s.args[1] : s for s in symbols)...) Expr(:module, true, mod, Expr(:block, import_ex, export_ex)) |> esc end -import NaNMath include("Commons/Commons.jl") using .Commons using .Commons.Frontend +include("Measurements.jl") +using .Measurements # --------------------------------------------------------- include("Fonts.jl") @reexport using .Fonts @@ -149,8 +151,6 @@ include("Ticks.jl") using .Ticks include("DataSeries.jl") using .DataSeries -include("Layouts.jl") -using .Layouts include("Subplots.jl") using .Subplots include("Axes.jl") diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index bdc71fdbd..eec3cfef4 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -27,7 +27,6 @@ using ..Measurements using ..RecipesPipeline: RecipesPipeline, Surface, Volume using ..PlotUtils: get_color_palette using ..Commons.Frontend -using ..Layouts using ..Commons using ..Fonts using ..Ticks @@ -91,12 +90,12 @@ Base.size(sp::Subplot) = (1, 1) Base.length(sp::Subplot) = 1 Base.getindex(sp::Subplot, r::Int, c::Int) = sp -leftpad(sp::Subplot) = sp.minpad[1] -toppad(sp::Subplot) = sp.minpad[2] -rightpad(sp::Subplot) = sp.minpad[3] -bottompad(sp::Subplot) = sp.minpad[4] +PlotsBase.leftpad(sp::Subplot) = sp.minpad[1] +PlotsBase.toppad(sp::Subplot) = sp.minpad[2] +PlotsBase.rightpad(sp::Subplot) = sp.minpad[3] +PlotsBase.bottompad(sp::Subplot) = sp.minpad[4] -function attr!(sp::Subplot; kw...) +function PlotsBase.attr!(sp::Subplot; kw...) plotattributes = KW(kw) PlotsBase.Commons.preprocess_attributes!(plotattributes) for (k, v) in plotattributes diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index ad4d8d1e6..3e6a0a9f9 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -17,7 +17,6 @@ using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots using PlotsBase.Surfaces -using PlotsBase.Layouts using PlotsBase.Commons using PlotsBase.Plots using PlotsBase.Fonts diff --git a/PlotsBase/src/layouts.jl b/PlotsBase/src/layouts.jl index cd8088bdd..6d3df7052 100644 --- a/PlotsBase/src/layouts.jl +++ b/PlotsBase/src/layouts.jl @@ -1,13 +1,35 @@ -leftpad(pad) = pad[1] -toppad(pad) = pad[2] -rightpad(pad) = pad[3] -bottompad(pad) = pad[4] - -leftpad(layout::GridLayout) = leftpad(layout.minpad) -toppad(layout::GridLayout) = toppad(layout.minpad) -rightpad(layout::GridLayout) = rightpad(layout.minpad) -bottompad(layout::GridLayout) = bottompad(layout.minpad) +""" + grid(args...; kw...) + +Create a grid layout for subplots. `args` specify the dimensions, e.g. +`grid(3,2, widths = (0.6,0.4))` creates a grid with three rows and two +columns of different width. +""" +grid(args...; kw...) = GridLayout(args...; kw...) + +# padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout) +# padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout) +# padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout)) + +update_position!(layout::AbstractLayout) = nothing +update_child_bboxes!( + layout::AbstractLayout, + minimum_perimeter = [0mm, 0mm, 0mm, 0mm]; + kw..., +) = nothing + +# pass these through to the bbox methods if there's no plotarea +plotarea(layout::AbstractLayout) = bbox(layout) +plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb) + +_update_min_padding!(layout::EmptyLayout) = nothing +_update_inset_padding!(layout::EmptyLayout) = nothing + +attr(layout::AbstractLayout, k::Symbol) = layout.attr[k] +attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v) +attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v) +# hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k) # here's how this works... first we recursively "update the minimum padding" (which # means to calculate the minimum size needed from the edge of the subplot to plot area) diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 2cae816f3..ea44712c6 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -60,24 +60,24 @@ end for name in ( # "quality", - # "misc", - # "utils", - # "args", - # "defaults", - # "dates", - # "axes", - # "layouts", - # "contours", - # "components", - # "shorthands", - # "recipes", - # "unitful", - # "hdf5plots", - # "pgfplotsx", - # "plotly", - # "animations", - # "output", - # "reference", + "misc", + "utils", + "args", + "defaults", + "dates", + "axes", + "layouts", + "contours", + "components", + "shorthands", + "recipes", + "unitful", + "hdf5plots", + "pgfplotsx", + "plotly", + "animations", + "output", + "reference", "backends", ) @testset "$name" begin diff --git a/PlotsBase/test/test_axes.jl b/PlotsBase/test/test_axes.jl index 5b9c82716..9a9bba0f2 100644 --- a/PlotsBase/test/test_axes.jl +++ b/PlotsBase/test/test_axes.jl @@ -220,7 +220,7 @@ end let pl = plot(1:2) xl, yl = xlims(pl), ylims(pl) - PlotsBase.PlotsPlots.scale_lims!(pl, 1.1) + PlotsBase.scale_lims!(pl, 1.1) @test first(xlims(pl)) < first(xl) @test last(xlims(pl)) > last(xl) @test first(ylims(pl)) < first(yl) diff --git a/PlotsBase/test/test_utils.jl b/PlotsBase/test/test_utils.jl index 5fffd5edf..7479967aa 100644 --- a/PlotsBase/test/test_utils.jl +++ b/PlotsBase/test/test_utils.jl @@ -102,8 +102,8 @@ push!(pl, 1:2, 2:3, 3:4) pl = plot([1, 2, 3], [4, 5, 6]) - @test PlotsBase.PlotsPlots.xmin(pl) == 1 - @test PlotsBase.PlotsPlots.xmax(pl) == 3 + @test PlotsBase.Plots.xmin(pl) == 1 + @test PlotsBase.Plots.xmax(pl) == 3 @test PlotsBase.Commons.ignorenan_extrema(pl) == (1, 3) @test PlotsBase.Commons.get_attr_symbol(:x, "lims") ≡ :xlims From f65ee73c56245871d9e76418ee9b38852b4f4a90 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 2 Apr 2024 00:04:07 +0200 Subject: [PATCH 11/58] checkpoint broken `references` gr --- PlotsBase/ext/GRExt.jl | 1 - PlotsBase/ext/GastonExt.jl | 1 - PlotsBase/ext/HDF5Ext.jl | 1 - PlotsBase/ext/PGFPlotsXExt.jl | 1 - PlotsBase/ext/PythonPlotExt.jl | 10 +- PlotsBase/ext/UnicodePlotsExt.jl | 1 - PlotsBase/src/Annotations.jl | 5 +- PlotsBase/src/Arrows.jl | 2 + PlotsBase/src/Axes.jl | 28 +++--- PlotsBase/src/BezierCurves.jl | 2 + PlotsBase/src/Colorbars.jl | 2 + PlotsBase/src/Commons/Commons.jl | 61 +++++++----- .../{Measurements.jl => Commons/layouts.jl} | 98 ++----------------- PlotsBase/src/Commons/measures.jl | 75 ++++++++++++++ PlotsBase/src/DataSeries.jl | 38 +++---- PlotsBase/src/Plots.jl | 19 ++-- PlotsBase/src/PlotsBase.jl | 28 +----- PlotsBase/src/Shapes.jl | 23 +++-- PlotsBase/src/Strokes.jl | 2 + PlotsBase/src/Subplots.jl | 57 ++++++----- PlotsBase/src/Surfaces.jl | 2 + PlotsBase/src/Ticks.jl | 12 ++- PlotsBase/src/axes_utils.jl | 6 +- PlotsBase/src/backends/plotly.jl | 8 +- PlotsBase/src/examples.jl | 2 +- PlotsBase/src/recipes.jl | 2 +- PlotsBase/test/runtests.jl | 36 +++---- PlotsBase/test/test_components.jl | 2 +- PlotsBase/test/test_layouts.jl | 8 +- PlotsBase/test/test_utils.jl | 24 ++--- 30 files changed, 281 insertions(+), 276 deletions(-) rename PlotsBase/src/{Measurements.jl => Commons/layouts.jl} (64%) create mode 100644 PlotsBase/src/Commons/measures.jl diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index b3427d17c..2775d008f 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -5,7 +5,6 @@ import RecipesPipeline import NaNMath import GR -using PlotsBase.Measurements using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Colorbars diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index e0e04bbd9..f6c935859 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -5,7 +5,6 @@ import PlotUtils import PlotsBase import Gaston -using PlotsBase.Measurements using PlotsBase.DataSeries using PlotsBase.Colorbars using PlotsBase.Subplots diff --git a/PlotsBase/ext/HDF5Ext.jl b/PlotsBase/ext/HDF5Ext.jl index e191dac7e..b79887709 100644 --- a/PlotsBase/ext/HDF5Ext.jl +++ b/PlotsBase/ext/HDF5Ext.jl @@ -9,7 +9,6 @@ import PlotUtils.Colors: Colorant import PlotsBase -using PlotsBase.Measurements using PlotsBase.DataSeries using PlotsBase.Subplots using PlotsBase.Commons diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index 9b56b150e..8ef8001dc 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -11,7 +11,6 @@ import PGFPlotsX import Latexify import Contour -using PlotsBase.Measurements using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Colorbars diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index bf284df89..ee8139409 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -17,7 +17,6 @@ using PlotUtils import PlotsBase import RecipesPipeline: Surface -using PlotsBase.Measurements using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Colorbars @@ -401,9 +400,9 @@ function _py_bbox(obj) fl, fr, fb, ft = bb = _py_extents(obj.get_figure()) l, r, b, t = ex = _py_extents(obj) # @show obj bb ex - x0, y0, width, height = l * px, (ft - t) * px, (r - l) * px, (t - b) * px - # @show width height - BoundingBox(x0, y0, width, height) + x0, y0, w, h = l * px, (ft - t) * px, (r - l) * px, (t - b) * px + # @show w h + BoundingBox(x0, y0, w, h) end _py_bbox(::Nothing) = BoundingBox(0mm, 0mm) @@ -456,8 +455,7 @@ _py_thickness_scale(plt::Plot{PythonPlotBackend}, ptsz) = ptsz * plt[:thickness_ # Create the window/figure for this backend. function PlotsBase._create_backend_figure(plt::Plot{PythonPlotBackend}) - w, h = - map(s -> PlotsBase.Measurements.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) + w, h = map(s -> PlotsBase.Commons.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) # reuse the current figure? plt[:overwrite_figure] ? PythonPlot.gcf() : PythonPlot.figure() end diff --git a/PlotsBase/ext/UnicodePlotsExt.jl b/PlotsBase/ext/UnicodePlotsExt.jl index beffb8538..63568b051 100644 --- a/PlotsBase/ext/UnicodePlotsExt.jl +++ b/PlotsBase/ext/UnicodePlotsExt.jl @@ -4,7 +4,6 @@ import PlotsBase: PlotsBase, texmath2unicode import RecipesPipeline import UnicodePlots -using PlotsBase.Measurements using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Colorbars diff --git a/PlotsBase/src/Annotations.jl b/PlotsBase/src/Annotations.jl index b4a8a067b..79c96ee8a 100644 --- a/PlotsBase/src/Annotations.jl +++ b/PlotsBase/src/Annotations.jl @@ -12,7 +12,6 @@ export SeriesAnnotations, import ..PlotsBase: Series, Subplot, TimeType, is3d, discrete_value! -using ..Measurements using ..Commons using ..Shapes using ..Dates @@ -240,7 +239,7 @@ locate_annotation(sp::Subplot, rel::Tuple, label::PlotText) = ( map(1:length(rel), (:x, :y, :z)) do i, letter _relative_position( axis_limits(sp, letter)..., - rel[i] * Measurements.pct, + rel[i] * pct, sp[get_attr_symbol(letter, :axis)][:scale], ) end..., @@ -253,3 +252,5 @@ locate_annotation(sp::Subplot, pos::Symbol, label::PlotText) = locate_annotation(sp, position_multiplier[pos], label) end # Annotations + +using .Annotations diff --git a/PlotsBase/src/Arrows.jl b/PlotsBase/src/Arrows.jl index 9c49bdbfc..4bc470480 100644 --- a/PlotsBase/src/Arrows.jl +++ b/PlotsBase/src/Arrows.jl @@ -62,3 +62,5 @@ function add_arrows(func::Function, x::AVec, y::AVec) end end # module + +using .Arrows diff --git a/PlotsBase/src/Axes.jl b/PlotsBase/src/Axes.jl index bd64c1c2d..ea099aeeb 100644 --- a/PlotsBase/src/Axes.jl +++ b/PlotsBase/src/Axes.jl @@ -1,10 +1,10 @@ module Axes export Axis, Extrema, tickfont, guidefont, widen_factor, scale_inverse_scale_func -export sort_3d_axes, axes_letters, process_axis_arg!, has_ticks, get_axis, scale_lims! +export sort_3d_axes, axes_letters, process_axis_arg!, has_ticks, get_axis import ..PlotsBase -import ..PlotsBase: Subplot, DefaultsDict, TimeType +import ..PlotsBase: Subplot, DefaultsDict, TimeType, attr! using ..RecipesPipeline using ..Commons @@ -54,14 +54,14 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...) attr = DefaultsDict(explicit, Commons._axis_defaults_byletter[letter]) # update the defaults - PlotsBase.attr!(Axis([sp], attr), args...; kw...) + attr!(Axis([sp], attr), args...; kw...) end # properly retrieve from axis.attr, passing `:match` to the correct key Base.getindex(axis::Axis, k::Symbol) = if (v = axis.plotattributes[k]) ≡ :match - if haskey(Commons.Commons._match_map2, k) - axis.sps[1][Commons.Commons._match_map2[k]] + if haskey(Commons._match_map2, k) + axis.sps[1][Commons._match_map2[k]] else axis[Commons._match_map[k]] end @@ -251,13 +251,15 @@ Scale the limits of the axis specified by `letter` (one of `:x`, `:y`, `:z`) by given `factor` around the limits' middle point. If `letter` is omitted, all axes are affected. """ -function scale_lims!(sp::Subplot, letter, factor) +function Commons.scale_lims!(sp::Subplot, letter, factor) axis = get_axis(sp, letter) from, to = PlotsBase.get_sp_lims(sp, letter) axis[:lims] = scale_lims(from, to, factor, axis[:scale]) end -scale_lims!(factor::Number) = scale_lims!(PlotsBase.current(), factor) -scale_lims!(letter::Symbol, factor) = scale_lims!(PlotsBase.current(), letter, factor) +Commons.scale_lims!(factor::Number) = scale_lims!(PlotsBase.current(), factor) +Commons.scale_lims!(letter::Symbol, factor) = + scale_lims!(PlotsBase.current(), letter, factor) + #---------------------------------------------------------------------- function process_axis_arg!(plotattributes::AKW, arg, letter = "") T = typeof(arg) @@ -383,7 +385,7 @@ function _update_axis( end # update the axis - PlotsBase.attr!(axis; kw...) + attr!(axis; kw...) nothing end @@ -401,7 +403,7 @@ end """ returns (continuous_values, discrete_values) for the ticks on this axis """ -function PlotsBase.get_ticks( +function Commons.get_ticks( sp::Subplot, axis::Axis; update = true, @@ -422,7 +424,7 @@ function PlotsBase.get_ticks( else cvals = axis[:continuous_values] alims = axis_limits(sp, axis[:letter]) - PlotsBase.get_ticks(ticks, cvals, dvals, alims, axis[:scale], formatter) + Commons.get_ticks(ticks, cvals, dvals, alims, axis[:scale], formatter) end end axis.plotattributes[:optimized_ticks] @@ -464,6 +466,8 @@ function PlotsBase.expand_extrema!(axis::Axis, v::AVec{N}) where {N<:Number} ex end +end # Axes + # ------------------------------------------------------------------------- -end # Axes +using .Axes diff --git a/PlotsBase/src/BezierCurves.jl b/PlotsBase/src/BezierCurves.jl index 6eb07fc5d..9c3ebd143 100644 --- a/PlotsBase/src/BezierCurves.jl +++ b/PlotsBase/src/BezierCurves.jl @@ -20,3 +20,5 @@ PlotsBase.coords(curve::BezierCurve, n::Integer = 30; range = [0, 1]) = map(curve, Base.range(first(range), stop = last(range), length = n)) end # module + +using .BezierCurves diff --git a/PlotsBase/src/Colorbars.jl b/PlotsBase/src/Colorbars.jl index 516a8768f..a994c2d31 100644 --- a/PlotsBase/src/Colorbars.jl +++ b/PlotsBase/src/Colorbars.jl @@ -144,3 +144,5 @@ _update_subplot_colorbars(sp::Subplot) = update_clims(sp) _update_subplot_colorbars(sp::Subplot, series::Series) = update_clims(sp, series) end # module + +using .Colorbars diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index 60bc19b55..a08c73636 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -39,20 +39,33 @@ export anynan, export istuple, isvector, ismatrix, isscalar, is_2tuple export default, wraptuple, merge_with_base_supported +export px, pct +export width, height, leftpad, toppad, bottompad, rightpad +export origin, left, right, bottom, top, bbox, bbox! export DEFAULT_BBOX, DEFAULT_MINPAD, DEFAULT_LINEWIDTH export MM_PER_PX, MM_PER_INCH, DPI, PX_PER_INCH -using PlotsBase: PlotsBase, cgrad -import PlotsBase: RecipesPipeline +export GridLayout, EmptyLayout, RootLayout +export BBox, BoundingBox, mm, cm, inch, pt, w, h +export bbox_to_pcts, xy_mm_to_pcts +export Length, AbsoluteLength, Measure +export to_pixels, ispositive, get_ticks, scale_lims! + +import Measures: + Measures, Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, w, h +import PlotUtils: PlotUtils, ColorPalette, plot_color, isdark, ColorGradient +import PlotsBase: PlotsBase, RecipesPipeline, cgrad + using ..Colors: Colorant, @colorant_str using ..ColorTypes: alpha -using ..Measures: mm, BoundingBox -using ..PlotUtils: PlotUtils, ColorPalette, plot_color, isdark, ColorGradient using ..RecipesBase using ..Statistics using ..NaNMath using ..Printf +const width = Measures.width +const height = Measures.height + const AVec = AbstractVector const AMat = AbstractMatrix const KW = Dict{Symbol,Any} @@ -60,16 +73,6 @@ const AKW = AbstractDict{Symbol,Any} const TicksArgs = Union{AVec{T},Tuple{AVec{T},AVec{S}},Symbol} where {T<:Real,S<:AbstractString} -const DEFAULT_BBOX = Ref(BoundingBox(0mm, 0mm, 0mm, 0mm)) -const DEFAULT_MINPAD = Ref((20mm, 5mm, 2mm, 10mm)) -const DEFAULT_LINEWIDTH = Ref(1) -const PLOTS_SEED = 1234 -const PX_PER_INCH = 100 -const DPI = PX_PER_INCH -const MM_PER_INCH = 25.4 -const MM_PER_PX = MM_PER_INCH / PX_PER_INCH -const _cbar_width = 5mm - const _haligns = :hcenter, :left, :right const _valigns = :vcenter, :top, :bottom const _all_scales = [:identity, :ln, :log2, :log10, :asinh, :sqrt] @@ -97,14 +100,24 @@ const _segmenting_vector_attributes = ( const _segmenting_array_attributes = :line_z, :fill_z, :marker_z const _debug = Ref(false) -function get_subplot end -function get_clims end -function series_list end -function coords end -function ispolar end -function expand_extrema! end -function axis_limits end -function preprocess_attributes! end +# docs.julialang.org/en/v1/manual/methods/#Empty-generic-functions +macro generic_functions(args...) + blk = Expr(:block) + foreach(arg -> push!(blk.args, :(function $arg end)), args) + blk |> esc +end + +@generic_functions get_ticks get_subplot get_clims +@generic_functions series_list coords ispolar axis_limits +@generic_functions expand_extrema! preprocess_attributes! scale_lims! + +@generic_functions width height leftpad toppad bottompad rightpad +@generic_functions origin left right bottom top + +include("measures.jl") + +using ..RecipesBase: AbstractLayout +include("layouts.jl") # --------------------------------------------------------------- wraptuple(x::Tuple) = x @@ -117,12 +130,12 @@ all_lineLtypes(arg) = true_or_all_true(a -> get(Commons._typeAliases, a, a) in Commons._all_seriestypes, arg) all_styles(arg) = true_or_all_true(a -> get(Commons._styleAliases, a, a) in Commons._all_styles, arg) -all_shapes(arg) = (true_or_all_true( +all_shapes(arg) = true_or_all_true( a -> get(Commons._marker_aliases, a, a) in Commons._all_markers || a isa PlotsBase.Shape, arg, -)) +) all_alphas(arg) = true_or_all_true( a -> (typeof(a) <: Real && a > 0 && a < 1) || ( diff --git a/PlotsBase/src/Measurements.jl b/PlotsBase/src/Commons/layouts.jl similarity index 64% rename from PlotsBase/src/Measurements.jl rename to PlotsBase/src/Commons/layouts.jl index 9ed82da66..52cc11201 100644 --- a/PlotsBase/src/Measurements.jl +++ b/PlotsBase/src/Commons/layouts.jl @@ -1,30 +1,3 @@ -module Measurements - -export GridLayout, EmptyLayout, RootLayout -export leftpad, toppad, bottompad, rightpad -export origin, left, right, bottom, top -export bbox, bbox!, bbox_to_pcts, xy_mm_to_pcts -export Length, AbsoluteLength, Measure -export to_pixels, ispositive - -import ..Measures -import ..Measures: Length, AbsoluteLength, Measure, BoundingBox -import ..Measures: mm, cm, inch, pt, width, height, w, h -using ..RecipesBase: AbstractLayout -using ..Commons - -const BBox = Measures.Absolute2DBox -export BBox, BoundingBox, mm, cm, inch, px, pct, pt, w, h - -to_pixels(m::AbsoluteLength) = m.value / 0.254 - -left(bbox::BoundingBox) = bbox.x0[1] -top(bbox::BoundingBox) = bbox.x0[2] -right(bbox::BoundingBox) = left(bbox) + width(bbox) -bottom(bbox::BoundingBox) = top(bbox) + height(bbox) -origin(bbox::BoundingBox) = left(bbox) + width(bbox) / 2, top(bbox) + height(bbox) / 2 -Base.size(bbox::BoundingBox) = (width(bbox), height(bbox)) - make_measure_hor(n::Number) = n * w make_measure_hor(m::Measure) = m @@ -77,32 +50,14 @@ function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top) end BoundingBox(left, top, width, height) end -# NOTE: (0,0) is the top-left !!! -# convert x,y coordinates from absolute coords to percentages... -# returns x_pct, y_pct -function xy_mm_to_pcts(x::AbsoluteLength, y::AbsoluteLength, figw, figh, flipy = true) - xmm, ymm = x.value, y.value - if flipy - ymm = figh.value - ymm # flip y when origin in bottom-left - end - xmm / figw.value, ymm / figh.value -end - -# convert a bounding box from absolute coords to percentages... -# returns an array of percentages of figure size: [left, bottom, width, height] -function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true) - mms = Float64[f(bb).value for f in (left, bottom, width, height)] - if flipy - mms[2] = figh.value - mms[2] # flip y when origin in bottom-left - end - mms ./ Float64[figw.value, figh.value, figw.value, figh.value] -end - -Base.show(io::IO, bbox::BoundingBox) = print( - io, - "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}", -) +# NOTE: (0,0) is the top-left !!! +left(bbox::BoundingBox) = bbox.x0[1] +top(bbox::BoundingBox) = bbox.x0[2] +right(bbox::BoundingBox) = left(bbox) + width(bbox) +bottom(bbox::BoundingBox) = top(bbox) + height(bbox) +origin(bbox::BoundingBox) = left(bbox) + width(bbox) / 2, top(bbox) + height(bbox) / 2 +Base.size(bbox::BoundingBox) = (width(bbox), height(bbox)) # ----------------------------------------------------------- # AbstractLayout @@ -180,7 +135,6 @@ toppad(layout::GridLayout) = toppad(layout.minpad) rightpad(layout::GridLayout) = rightpad(layout.minpad) bottompad(layout::GridLayout) = bottompad(layout.minpad) - function GridLayout( dims...; parent = RootLayout(), @@ -211,41 +165,3 @@ Base.length(layout::GridLayout) = length(layout.grid) Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r, c] Base.setindex!(layout::GridLayout, v, r::Int, c::Int) = layout.grid[r, c] = v Base.setindex!(layout::GridLayout, v, ci::CartesianIndex) = layout.grid[ci] = v - -# Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value) -ispositive(m::Measure) = m.value > 0 - -# union together bounding boxes -function Base.:+(bb1::BoundingBox, bb2::BoundingBox) - # empty boxes don't change the union - ispositive(width(bb1)) || return bb2 - ispositive(height(bb1)) || return bb2 - ispositive(width(bb2)) || return bb1 - ispositive(height(bb2)) || return bb1 - - l = min(left(bb1), left(bb2)) - t = min(top(bb1), top(bb2)) - r = max(right(bb1), right(bb2)) - b = max(bottom(bb1), bottom(bb2)) - BoundingBox(l, t, r - l, b - t) -end - -# allow pixels and percentages -const px = AbsoluteLength(0.254) -const pct = Length{:pct,Float64}(1.0) - -Base.convert(::Type{<:Measure}, x::Float64) = x * pct - -Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value) -Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value) -Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value) -Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value) - -inch2px(inches::Real) = float(inches * PX_PER_INCH) -px2inch(px::Real) = float(px / PX_PER_INCH) -inch2mm(inches::Real) = float(inches * MM_PER_INCH) -mm2inch(mm::Real) = float(mm / MM_PER_INCH) -px2mm(px::Real) = float(px * MM_PER_PX) -mm2px(mm::Real) = float(mm / MM_PER_PX) - -end # module diff --git a/PlotsBase/src/Commons/measures.jl b/PlotsBase/src/Commons/measures.jl new file mode 100644 index 000000000..f0e88b92f --- /dev/null +++ b/PlotsBase/src/Commons/measures.jl @@ -0,0 +1,75 @@ + +const DEFAULT_BBOX = Ref(BoundingBox(0mm, 0mm, 0mm, 0mm)) +const DEFAULT_MINPAD = Ref((20mm, 5mm, 2mm, 10mm)) +const DEFAULT_LINEWIDTH = Ref(1) +const PLOTS_SEED = 1234 +const PX_PER_INCH = 100 +const DPI = PX_PER_INCH +const MM_PER_INCH = 25.4 +const MM_PER_PX = MM_PER_INCH / PX_PER_INCH +const _cbar_width = 5mm + +# allow pixels and percentages +const px = Measures.AbsoluteLength(0.254) +const pct = Measures.Length{:pct,Float64}(1.0) + +const BBox = Measures.Absolute2DBox + +to_pixels(m::AbsoluteLength) = m.value / 0.254 + +# convert x,y coordinates from absolute coords to percentages... +# returns x_pct, y_pct +function xy_mm_to_pcts(x::AbsoluteLength, y::AbsoluteLength, figw, figh, flipy = true) + xmm, ymm = x.value, y.value + if flipy + ymm = figh.value - ymm # flip y when origin in bottom-left + end + xmm / figw.value, ymm / figh.value +end + +# convert a bounding box from absolute coords to percentages... +# returns an array of percentages of figure size: [left, bottom, width, height] +function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true) + mms = Float64[f(bb).value for f in (left, bottom, width, height)] + if flipy + mms[2] = figh.value - mms[2] # flip y when origin in bottom-left + end + mms ./ Float64[figw.value, figh.value, figw.value, figh.value] +end + +Base.show(io::IO, bbox::BoundingBox) = print( + io, + "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}", +) + +# Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value) +ispositive(m::Measure) = m.value > 0 + +# union together bounding boxes +function Base.:+(bb1::BoundingBox, bb2::BoundingBox) + # empty boxes don't change the union + ispositive(width(bb1)) || return bb2 + ispositive(height(bb1)) || return bb2 + ispositive(width(bb2)) || return bb1 + ispositive(height(bb2)) || return bb1 + + l = min(left(bb1), left(bb2)) + t = min(top(bb1), top(bb2)) + r = max(right(bb1), right(bb2)) + b = max(bottom(bb1), bottom(bb2)) + BoundingBox(l, t, r - l, b - t) +end + +Base.convert(::Type{<:Measure}, x::Float64) = x * Commons.pct + +Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value) +Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value) +Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value) +Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value) + +inch2px(inches::Real) = float(inches * PX_PER_INCH) +px2inch(px::Real) = float(px / PX_PER_INCH) +inch2mm(inches::Real) = float(inches * MM_PER_INCH) +mm2inch(mm::Real) = float(mm / MM_PER_INCH) +px2mm(px::Real) = float(px * MM_PER_PX) +mm2px(mm::Real) = float(mm / MM_PER_PX) diff --git a/PlotsBase/src/DataSeries.jl b/PlotsBase/src/DataSeries.jl index b758aee4e..087c38d6d 100644 --- a/PlotsBase/src/DataSeries.jl +++ b/PlotsBase/src/DataSeries.jl @@ -20,7 +20,7 @@ export get_linestyle, get_fillalpha, get_markercolor, get_markeralpha - + import ..Commons: get_gradient, get_subplot, _series_defaults import ..PlotsBase @@ -38,23 +38,6 @@ Base.get(series::Series, k::Symbol, v) = get(series.plotattributes, k, v) Base.push!(series::Series, args...) = extend_series!(series, args...) Base.append!(series::Series, args...) = extend_series!(series, args...) -# TODO: consider removing -attr(series::Series, k::Symbol) = series.plotattributes[k] -attr!(series::Series, v, k::Symbol) = (series.plotattributes[k] = v) -function attr!(series::Series; kw...) - plotattributes = KW(kw) - PlotsBase.Commons.preprocess_attributes!(plotattributes) - for (k, v) in plotattributes - if haskey(_series_defaults, k) - series[k] = v - else - @warn "unused key $k in series attr" - end - end - PlotsBase._series_updated(series[:subplot].plt, series) - series -end - should_add_to_legend(series::Series) = series.plotattributes[:primary] && series.plotattributes[:label] != "" && @@ -333,3 +316,22 @@ function warn_on_inconsistent_shape_attrs(series, x, y, z, r) end end # module + +using .DataSeries + +# TODO: consider removing +attr(series::Series, k::Symbol) = series.plotattributes[k] +attr!(series::Series, v, k::Symbol) = (series.plotattributes[k] = v) +function attr!(series::Series; kw...) + plotattributes = KW(kw) + Commons.preprocess_attributes!(plotattributes) + for (k, v) in plotattributes + if haskey(_series_defaults, k) + series[k] = v + else + @warn "unused key $k in series attr" + end + end + _series_updated(series[:subplot].plt, series) + series +end \ No newline at end of file diff --git a/PlotsBase/src/Plots.jl b/PlotsBase/src/Plots.jl index af0b9d83a..1646e1739 100644 --- a/PlotsBase/src/Plots.jl +++ b/PlotsBase/src/Plots.jl @@ -23,7 +23,6 @@ import ..Commons: ignorenan_extrema, _cycle using ..PlotUtils using ..Commons.Frontend -using ..Measurements using ..Commons using ..Fonts using ..Ticks @@ -177,12 +176,12 @@ Base.ndims(plt::Plot) = 2 # clear out series list, but retain subplots Base.empty!(plt::Plot) = foreach(sp -> empty!(sp.series_list), plt.subplots) -PlotsBase.get_subplot(plt::Plot, sp::Subplot) = sp -PlotsBase.get_subplot(plt::Plot, i::Integer) = plt.subplots[i] -PlotsBase.get_subplot(plt::Plot, k) = plt.spmap[k] -PlotsBase.series_list(plt::Plot) = plt.series_list +Commons.get_subplot(plt::Plot, sp::Subplot) = sp +Commons.get_subplot(plt::Plot, i::Integer) = plt.subplots[i] +Commons.get_subplot(plt::Plot, k) = plt.spmap[k] +Commons.series_list(plt::Plot) = plt.series_list -get_ticks(p::Plot, s::Symbol) = map(sp -> get_ticks(sp, s), p.subplots) +Commons.get_ticks(p::Plot, s::Symbol) = map(sp -> get_ticks(sp, s), p.subplots) get_subplot_index(plt::Plot, sp::Subplot) = findfirst(x -> x ≡ sp, plt.subplots) PlotsBase.RecipesPipeline.preprocess_attributes!(plt::Plot, plotattributes::AKW) = @@ -282,11 +281,11 @@ function _update_subplot_attrs( PlotsBase.Subplots._update_subplot_periphery(sp, anns) end -function scale_lims!(plt::Plot, letter, factor) +function Commons.scale_lims!(plt::Plot, letter, factor) foreach(sp -> scale_lims!(sp, letter, factor), plt.subplots) plt end -function scale_lims!(plt::Union{Plot,Subplot}, factor) +function Commons.scale_lims!(plt::Union{Plot,Subplot}, factor) foreach(letter -> scale_lims!(plt, letter, factor), (:x, :y, :z)) plt end @@ -294,3 +293,7 @@ Commons.get_size(plt::Plot) = get_size(plt.attr) Commons.get_thickness_scaling(plt::Plot) = get_thickness_scaling(plt.attr) end # module + +# ------------------------------------------------------------------- + +using .Plots diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index 38bec53e0..32945c1fe 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -116,15 +116,7 @@ export scalefontsizes, resetfontsizes -import Measures: width, height - -function attr end -function attr! end -function rotate end -function rotate! end - #! format: on -import Measures import NaNMath macro ScopeModule(mod::Symbol, parent::Symbol, symbols...) @@ -142,40 +134,29 @@ end include("Commons/Commons.jl") using .Commons using .Commons.Frontend -include("Measurements.jl") -using .Measurements + +Commons.@generic_functions attr attr! rotate rotate! + # --------------------------------------------------------- include("Fonts.jl") @reexport using .Fonts include("Ticks.jl") -using .Ticks include("DataSeries.jl") -using .DataSeries include("Subplots.jl") -using .Subplots include("Axes.jl") -using .Axes include("Surfaces.jl") -using .Surfaces include("Colorbars.jl") -using .Colorbars include("Plots.jl") -using .Plots # --------------------------------------------------------- include("layouts.jl") include("utils.jl") include("axes_utils.jl") include("legend.jl") include("Shapes.jl") -using .Shapes include("Annotations.jl") -using .Annotations include("Arrows.jl") -using .Arrows include("Strokes.jl") -using .Strokes include("BezierCurves.jl") -using .BezierCurves include("themes.jl") include("plot.jl") include("pipeline.jl") @@ -193,10 +174,7 @@ include("alignment.jl") include("output.jl") include("shorthands.jl") include("backends/web.jl") - include("backends/plotly.jl") -using .Plotly - include("init.jl") include("users.jl") diff --git a/PlotsBase/src/Shapes.jl b/PlotsBase/src/Shapes.jl index daf36f609..2c8b9e55e 100644 --- a/PlotsBase/src/Shapes.jl +++ b/PlotsBase/src/Shapes.jl @@ -205,24 +205,29 @@ rotate_x(x::Real, y::Real, θ::Real, centerx::Real, centery::Real) = rotate_y(x::Real, y::Real, θ::Real, centerx::Real, centery::Real) = ((y - centery) * cos(θ) + (x - centerx) * sin(θ) + centery) -PlotsBase.rotate(x::Real, y::Real, θ::Real, c) = (rotate_x(x, y, θ, c...), rotate_y(x, y, θ, c...)) +end # module + +# ------------------------------------------------------------------- + +using .Shapes -function PlotsBase.rotate!(shape::Shape, θ::Real, c = center(shape)) +rotate(x::Real, y::Real, θ::Real, c) = + (Shapes.rotate_x(x, y, θ, c...), Shapes.rotate_y(x, y, θ, c...)) + +function rotate!(shape::Shape, θ::Real, c = center(shape)) x, y = coords(shape) for i in eachindex(x) - xi = rotate_x(x[i], y[i], θ, c...) - yi = rotate_y(x[i], y[i], θ, c...) + xi = Shapes.rotate_x(x[i], y[i], θ, c...) + yi = Shapes.rotate_y(x[i], y[i], θ, c...) x[i], y[i] = xi, yi end shape end "rotate an object in space" -function PlotsBase.rotate(shape::Shape, θ::Real, c = center(shape)) +function rotate(shape::Shape, θ::Real, c = center(shape)) x, y = coords(shape) - x_new = rotate_x.(x, y, θ, c...) - y_new = rotate_y.(x, y, θ, c...) + x_new = Shapes.rotate_x.(x, y, θ, c...) + y_new = Shapes.rotate_y.(x, y, θ, c...) Shape(x_new, y_new) end - -end # module diff --git a/PlotsBase/src/Strokes.jl b/PlotsBase/src/Strokes.jl index 3b0452235..793b895f5 100644 --- a/PlotsBase/src/Strokes.jl +++ b/PlotsBase/src/Strokes.jl @@ -80,3 +80,5 @@ function brush(args...; alpha = nothing) end end # module + +using .Strokes diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index eec3cfef4..37ca2389f 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -8,11 +8,7 @@ export Subplot, get_series_color, needs_any_3d_axes, plotarea, - plotarea!, - toppad, - leftpad, - bottompad, - rightpad + plotarea! using PlotsBase: PlotsBase, RecipesPipeline, @@ -23,7 +19,6 @@ using PlotsBase: DefaultsDict import ..Commons: convert_legend_value, like_surface -using ..Measurements using ..RecipesPipeline: RecipesPipeline, Surface, Volume using ..PlotUtils: get_color_palette using ..Commons.Frontend @@ -59,8 +54,8 @@ end # properly retrieve from sp.attr, passing `:match` to the correct key Base.getindex(sp::Subplot, k::Symbol) = if (v = sp.attr[k]) ≡ :match - if haskey(Commons.Commons._match_map2, k) - sp.plt[Commons.Commons._match_map2[k]] + if haskey(Commons._match_map2, k) + sp.plt[Commons._match_map2[k]] else sp[Commons._match_map[k]] end @@ -90,29 +85,11 @@ Base.size(sp::Subplot) = (1, 1) Base.length(sp::Subplot) = 1 Base.getindex(sp::Subplot, r::Int, c::Int) = sp -PlotsBase.leftpad(sp::Subplot) = sp.minpad[1] -PlotsBase.toppad(sp::Subplot) = sp.minpad[2] -PlotsBase.rightpad(sp::Subplot) = sp.minpad[3] -PlotsBase.bottompad(sp::Subplot) = sp.minpad[4] - -function PlotsBase.attr!(sp::Subplot; kw...) - plotattributes = KW(kw) - PlotsBase.Commons.preprocess_attributes!(plotattributes) - for (k, v) in plotattributes - if haskey(_subplot_defaults, k) - sp[k] = v - else - @warn "unused key $k in subplot attr" - end - end - sp -end - PlotsBase.series_list(sp::Subplot) = sp.series_list # filter(series -> series.plotattributes[:subplot] ≡ sp, sp.plt.series_list) PlotsBase.RecipesPipeline.is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d" PlotsBase.ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar" -get_ticks(sp::Subplot, s::Symbol) = get_ticks(sp, sp[get_attr_symbol(s, :axis)]) +Commons.get_ticks(sp::Subplot, s::Symbol) = get_ticks(sp, sp[get_attr_symbol(s, :axis)]) # converts a symbol or string into a Colorant or ColorGradient # and assigns a color automatically @@ -291,7 +268,29 @@ function PlotsBase.expand_extrema!(sp::Subplot, xmin, xmax, ymin, ymax) expand_extrema!(sp[:yaxis], (ymin, ymax)) end -Commons.get_size(sp::Subplot) = Commons.get_size(sp.plt) -Commons.get_thickness_scaling(sp::Subplot) = Commons.get_thickness_scaling(sp.plt) +Commons.get_size(sp::Subplot) = get_size(sp.plt) +Commons.get_thickness_scaling(sp::Subplot) = get_thickness_scaling(sp.plt) end # module + +# ------------------------------------------------------------------- + +using .Subplots + +Commons.leftpad(sp::Subplot) = sp.minpad[1] +Commons.toppad(sp::Subplot) = sp.minpad[2] +Commons.rightpad(sp::Subplot) = sp.minpad[3] +Commons.bottompad(sp::Subplot) = sp.minpad[4] + +function attr!(sp::Subplot; kw...) + plotattributes = KW(kw) + PlotsBase.Commons.preprocess_attributes!(plotattributes) + for (k, v) in plotattributes + if haskey(_subplot_defaults, k) + sp[k] = v + else + @warn "unused key $k in subplot attr" + end + end + sp +end diff --git a/PlotsBase/src/Surfaces.jl b/PlotsBase/src/Surfaces.jl index be9b2f6d2..e7acc6e23 100644 --- a/PlotsBase/src/Surfaces.jl +++ b/PlotsBase/src/Surfaces.jl @@ -22,3 +22,5 @@ end Commons.handle_surface(z::Surface) = permutedims(z.surf) end # module + +using .Surfaces diff --git a/PlotsBase/src/Ticks.jl b/PlotsBase/src/Ticks.jl index aa5577f90..3bce3ccfc 100644 --- a/PlotsBase/src/Ticks.jl +++ b/PlotsBase/src/Ticks.jl @@ -1,6 +1,6 @@ module Ticks -export get_ticks, _has_ticks, _transform_ticks, get_minor_ticks +export _has_ticks, _transform_ticks, get_minor_ticks export no_minor_intervals, num_minor_intervals, ticks_type using ..Commons @@ -15,11 +15,11 @@ ticks_type(ticks) = :invalid # get_ticks from axis symbol :x, :y, or :z -get_ticks(ticks::NTuple{2,Any}, args...) = ticks -get_ticks(::Nothing, cvals::T, args...) where {T} = T[], String[] -get_ticks(ticks::Bool, args...) = +Commons.get_ticks(ticks::NTuple{2,Any}, args...) = ticks +Commons.get_ticks(::Nothing, cvals::T, args...) where {T} = T[], String[] +Commons.get_ticks(ticks::Bool, args...) = ticks ? get_ticks(:auto, args...) : get_ticks(nothing, args...) -get_ticks(::T, args...) where {T} = +Commons.get_ticks(::T, args...) where {T} = throw(ArgumentError("Unknown ticks type in get_ticks: $T")) # do not specify array item type to also catch e.g. "xlabel=[]" and "xlabel=([],[])" @@ -105,3 +105,5 @@ function get_minor_ticks(sp, axis, ticks_and_labels) end end # module + +using .Ticks diff --git a/PlotsBase/src/axes_utils.jl b/PlotsBase/src/axes_utils.jl index e057eac4c..735ce0c26 100644 --- a/PlotsBase/src/axes_utils.jl +++ b/PlotsBase/src/axes_utils.jl @@ -75,7 +75,7 @@ function optimal_ticks_and_labels(ticks, alims, scale, formatter) unscaled_ticks, labels end -Ticks.get_ticks(ticks::Symbol, cvals::T, dvals, args...) where {T} = +Commons.get_ticks(ticks::Symbol, cvals::T, dvals, args...) where {T} = if ticks ≡ :none T[], String[] elseif !isempty(dvals) @@ -91,9 +91,9 @@ Ticks.get_ticks(ticks::Symbol, cvals::T, dvals, args...) where {T} = optimal_ticks_and_labels(nothing, args...) end -Ticks.get_ticks(ticks::AVec, cvals, dvals, args...) = +Commons.get_ticks(ticks::AVec, cvals, dvals, args...) = optimal_ticks_and_labels(ticks, args...) -Ticks.get_ticks(ticks::Int, dvals, cvals, args...) = +Commons.get_ticks(ticks::Int, dvals, cvals, args...) = if isempty(dvals) optimal_ticks_and_labels(ticks, args...) else diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index 3e6a0a9f9..277cfcd60 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -11,7 +11,6 @@ import JSON using PlotUtils using PlotsBase.Colors: Colorant -using PlotsBase.Measurements using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Colorbars @@ -623,7 +622,10 @@ function plotly_colorscale(cg::PlotUtils.CategoricalColorGradient, α = nothing) cinds = repeat(1:n, inner = 2) vinds = vcat((i:(i + 1) for i in 1:n)...) map( - i -> [cg.values[vinds[i]], rgba_string(plot_color(PlotsBase.color_list(cg)[cinds[i]], α))], + i -> [ + cg.values[vinds[i]], + rgba_string(plot_color(PlotsBase.color_list(cg)[cinds[i]], α)), + ], eachindex(cinds), ) end @@ -1321,3 +1323,5 @@ function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict) end end # module + +using .Plotly diff --git a/PlotsBase/src/examples.jl b/PlotsBase/src/examples.jl index 158d44868..ea40b8a44 100644 --- a/PlotsBase/src/examples.jl +++ b/PlotsBase/src/examples.jl @@ -460,7 +460,7 @@ const _examples = PlotExample[ ), PlotExample( # 29 "Layouts, margins, label rotation, title location", - :(using PlotsBase.Measurements), # for Measures, e.g. mm and px + :(using PlotsBase.Commons), # for Measures, e.g. mm and px quote plot( rand(100, 6), diff --git a/PlotsBase/src/recipes.jl b/PlotsBase/src/recipes.jl index 9c57db750..977ab3c37 100644 --- a/PlotsBase/src/recipes.jl +++ b/PlotsBase/src/recipes.jl @@ -977,7 +977,7 @@ lens!(args...; kwargs...) = plot!(args...; seriestype = :lens, kwargs...) export lens! @recipe function f(::Type{Val{:lens}}, plt::AbstractPlot) # COV_EXCL_LINE sp_index, inset_bbox = plotattributes[:inset_subplots] - width(inset_bbox) isa Measures.Length{:w,<:Real} || + width(inset_bbox) isa Commons.Length{:w,<:Real} || throw(ArgumentError("Inset bounding box needs to in relative coordinates.")) sp = plt.subplots[sp_index] xscale = sp[:xaxis][:scale] diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index ea44712c6..4fb500665 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -59,26 +59,26 @@ else end for name in ( - # "quality", - "misc", - "utils", - "args", - "defaults", - "dates", - "axes", - "layouts", - "contours", - "components", - "shorthands", - "recipes", - "unitful", - "hdf5plots", - "pgfplotsx", - "plotly", - "animations", - "output", + # "misc", + # "utils", + # "args", + # "defaults", + # "dates", + # "axes", + # "layouts", + # "contours", + # "components", + # "shorthands", + # "recipes", + # "unitful", + # "hdf5plots", + # "pgfplotsx", + # "plotly", + # "animations", + # "output", "reference", "backends", + "quality", ) @testset "$name" begin # skip the majority of tests if we only want to update reference images or under `PkgEval` (timeout limit) diff --git a/PlotsBase/test/test_components.jl b/PlotsBase/test/test_components.jl index 0018d4850..62a84c6ac 100644 --- a/PlotsBase/test/test_components.jl +++ b/PlotsBase/test/test_components.jl @@ -55,7 +55,7 @@ @test square2.y ≈ coordsRotated2[2, :] # unrotate the new square in place - rotate!(square2, 2) + PlotsBase.rotate!(square2, 2) @test square2.x ≈ coords[1, :] @test square2.y ≈ coords[2, :] end diff --git a/PlotsBase/test/test_layouts.jl b/PlotsBase/test/test_layouts.jl index 52cc7b0aa..90cf6f4a1 100644 --- a/PlotsBase/test/test_layouts.jl +++ b/PlotsBase/test/test_layouts.jl @@ -108,16 +108,16 @@ end show(io, PlotsBase.DEFAULT_BBOX[]) show(io, pl.layout) - @test PlotsBase.make_measure_hor(1PlotsBase.mm) == 1PlotsBase.mm - @test PlotsBase.make_measure_vert(1PlotsBase.mm) == 1PlotsBase.mm + @test PlotsBase.Commons.make_measure_hor(1PlotsBase.mm) == 1PlotsBase.mm + @test PlotsBase.Commons.make_measure_vert(1PlotsBase.mm) == 1PlotsBase.mm @test PlotsBase.parent(pl.layout) isa PlotsBase.RootLayout - show(io, PlotsBase.parent_bbox(pl.layout)) + show(io, PlotsBase.Commons.parent_bbox(pl.layout)) rl = PlotsBase.RootLayout() show(io, rl) @test parent(rl) ≡ nothing - @test PlotsBase.parent_bbox(rl) == PlotsBase.DEFAULT_BBOX[] + @test PlotsBase.Commons.parent_bbox(rl) == PlotsBase.DEFAULT_BBOX[] @test PlotsBase.bbox(rl) == PlotsBase.DEFAULT_BBOX[] @test PlotsBase.origin(PlotsBase.DEFAULT_BBOX[]) == (0PlotsBase.mm, 0PlotsBase.mm) for h_anchor in (:left, :right, :hcenter), v_anchor in (:top, :bottom, :vcenter) diff --git a/PlotsBase/test/test_utils.jl b/PlotsBase/test/test_utils.jl index 7479967aa..ea179267a 100644 --- a/PlotsBase/test/test_utils.jl +++ b/PlotsBase/test/test_utils.jl @@ -49,12 +49,12 @@ @test PlotsBase.nansplit([1, 2, NaN, 3, 4]) == [[1.0, 2.0], [3.0, 4.0]] @test PlotsBase.nanvcat([1, NaN]) |> length == 4 - @test PlotsBase.PlotMeasures.inch2px(1) isa AbstractFloat - @test PlotsBase.PlotMeasures.px2inch(1) isa AbstractFloat - @test PlotsBase.PlotMeasures.inch2mm(1) isa AbstractFloat - @test PlotsBase.PlotMeasures.mm2inch(1) isa AbstractFloat - @test PlotsBase.PlotMeasures.px2mm(1) isa AbstractFloat - @test PlotsBase.PlotMeasures.mm2px(1) isa AbstractFloat + @test PlotsBase.Commons.inch2px(1) isa AbstractFloat + @test PlotsBase.Commons.px2inch(1) isa AbstractFloat + @test PlotsBase.Commons.inch2mm(1) isa AbstractFloat + @test PlotsBase.Commons.mm2inch(1) isa AbstractFloat + @test PlotsBase.Commons.px2mm(1) isa AbstractFloat + @test PlotsBase.Commons.mm2px(1) isa AbstractFloat pl = plot() @test xlims() isa Tuple @@ -130,23 +130,23 @@ let pl = plot(1:2) series = first(pl.series_list) label = "fancy label" - PlotsBase.PlotsSeries.attr!(series; label) + PlotsBase.attr!(series; label) @test series[:label] == label - @test PlotsBase.PlotsSeries.attr(series, :label) == label + @test PlotsBase.attr(series, :label) == label label = "another label" - PlotsBase.PlotsSeries.attr!(series, label, :label) - @test PlotsBase.PlotsSeries.attr(series, :label) == label + PlotsBase.attr!(series, label, :label) + @test PlotsBase.attr(series, :label) == label sp = first(pl.subplots) title = "fancy title" - PlotsBase.Subplots.attr!(sp; title) + PlotsBase.attr!(sp; title) @test sp[:title] == title end end @testset "NaN-separated Segments" begin - segments(args...) = collect(PlotsBase.PlotsSeries.iter_segments(args...)) + segments(args...) = collect(PlotsBase.DataSeries.iter_segments(args...)) nan10 = fill(NaN, 10) @test segments(11:20) == [1:10] From 7a026f303b2de9567a2f6d6abc11d92dbc136f62 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 2 Apr 2024 00:25:55 +0200 Subject: [PATCH 12/58] broken viewport --- PlotsBase/ext/GRExt.jl | 16 +++++++-------- PlotsBase/ext/GastonExt.jl | 8 ++++---- PlotsBase/ext/HDF5Ext.jl | 10 +++++---- PlotsBase/ext/PGFPlotsXExt.jl | 4 ++-- PlotsBase/ext/PythonPlotExt.jl | 14 ++++++------- PlotsBase/ext/UnicodePlotsExt.jl | 16 +++++++-------- PlotsBase/src/Commons/measures.jl | 4 ++-- PlotsBase/test/runtests.jl | 34 +++++++++++++++---------------- 8 files changed, 54 insertions(+), 52 deletions(-) diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index 2775d008f..5a5818d69 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -633,10 +633,10 @@ end function gr_viewport_from_bbox(sp::Subplot{GRBackend}, bb::BoundingBox, w, h, vp_canvas) viewport = GRViewport( - vp_canvas.xmax * (PlotsBase.left(bb) / w), - vp_canvas.xmax * (PlotsBase.right(bb) / w), - vp_canvas.ymax * (1 - PlotsBase.bottom(bb) / h), - vp_canvas.ymax * (1 - PlotsBase.top(bb) / h), + vp_canvas.xmax * (left(bb) / w), + vp_canvas.xmax * (right(bb) / w), + vp_canvas.ymax * (1 - bottom(bb) / h), + vp_canvas.ymax * (1 - top(bb) / h), ) hascolorbar(sp) && (viewport.xmax -= 0.1(1 + 0.5gr_is3d(sp))) viewport @@ -1112,8 +1112,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, vp_canvas::GRViewport) PlotsBase._update_min_padding!(sp) # the viewports for this subplot and the whole plot - vp_sp = gr_viewport_from_bbox(sp, PlotsBase.bbox(sp), w, h, vp_canvas) - vp_plt = gr_viewport_from_bbox(sp, PlotsBase.plotarea(sp), w, h, vp_canvas) + vp_sp = gr_viewport_from_bbox(sp, bbox(sp), w, h, vp_canvas) + vp_plt = gr_viewport_from_bbox(sp, plotarea(sp), w, h, vp_canvas) # update plot viewport leg = gr_get_legend_geometry(vp_plt, sp) @@ -1971,7 +1971,7 @@ function gr_draw_segments(series, x, y, z, fillrange, clims) (x ≡ nothing || length(x) ≤ 1) && return if fillrange ≢ nothing # prepare fill-in GR.setfillintstyle(GR.INTSTYLE_SOLID) - fr_from, fr_to = PlotsBase.is_2tuple(fillrange) ? fillrange : (y, fillrange) + fr_from, fr_to = is_2tuple(fillrange) ? fillrange : (y, fillrange) end # draw the line(s) @@ -2229,7 +2229,7 @@ for (mime, fmt) in ( "image/svg+xml" => "svg", ) @eval function PlotsBase._show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend}) - dpi_factor = $fmt == "png" ? plt[:dpi] / PlotsBase.DPI : 1 + dpi_factor = $fmt == "png" ? plt[:dpi] / DPI : 1 filepath = tempname() * "." * $fmt # workaround windows bug github.com/JuliaLang/julia/issues/46989 touch(filepath) diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index f6c935859..3e546fa32 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -206,7 +206,7 @@ function gaston_saveopts(plt::Plot{GastonBackend}) saveopts = ["size " * join(plt[:size], ',')] # scale all plot elements to match PlotsBase.jl DPI standard - scaling = plt[:dpi] / PlotsBase.DPI + scaling = plt[:dpi] / DPI push!( saveopts, @@ -229,7 +229,7 @@ function gaston_get_subplots(n, plt_subplots, layout) nr, nc = size(layout) sps = Array{Any}(nothing, nr, nc) for r in 1:nr, c in 1:nc # NOTE: col major - sps[r, c] = if (l = layout[r, c]) isa PlotsBase.GridLayout + sps[r, c] = if (l = layout[r, c]) isa GridLayout n, sub = gaston_get_subplots(n, plt_subplots, l) size(sub) == (1, 1) ? only(sub) : sub else @@ -288,7 +288,7 @@ function gaston_multiplot_pos_size(layout, parent_xy_wh) # width and height (pct) are multiplicative (parent) w = layout.widths[c].value * parent_xy_wh[3] h = layout.heights[r].value * parent_xy_wh[4] - if isa(l, PlotsBase.EmptyLayout) + if isa(l, EmptyLayout) dat[r, c] = (c - 1) * w, (r - 1) * h, w, h, nothing else # previous position (origin) @@ -298,7 +298,7 @@ function gaston_multiplot_pos_size(layout, parent_xy_wh) prev_c isa Array && (prev_c = prev_c[end, end]) x = prev_c ≢ nothing ? prev_c[1] + prev_c[3] : parent_xy_wh[1] y = prev_r ≢ nothing ? prev_r[2] + prev_r[4] : parent_xy_wh[2] - dat[r, c] = if l isa PlotsBase.GridLayout + dat[r, c] = if l isa GridLayout sub = gaston_multiplot_pos_size(l, (x, y, w, h)) size(sub) == (1, 1) ? only(sub) : sub else diff --git a/PlotsBase/ext/HDF5Ext.jl b/PlotsBase/ext/HDF5Ext.jl index b79887709..9598b6488 100644 --- a/PlotsBase/ext/HDF5Ext.jl +++ b/PlotsBase/ext/HDF5Ext.jl @@ -9,9 +9,11 @@ import PlotUtils.Colors: Colorant import PlotsBase +using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Subplots using PlotsBase.Commons +using PlotsBase.Arrows using PlotsBase.Shapes using PlotsBase.Plots using PlotsBase.Fonts @@ -108,7 +110,7 @@ const _hdf5_seriestypes = [ :wireframe, ] const _hdf5_styles = [:auto, :solid, :dash, :dot, :dashdot] -const _hdf5_markers = vcat(PlotsBase.Commons._all_markers, :pixel) +const _hdf5_markers = vcat(Commons._all_markers, :pixel) const _hdf5_scales = [:identity, :ln, :log2, :log10] # Additional constants @@ -150,14 +152,14 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1 # Sub-structure types: "DEFAULTSDICT" => DefaultsDict, - "FONT" => PlotsBase.Font, + "FONT" => Font, "BOUNDINGBOX" => BoundingBox, "GRIDLAYOUT" => GridLayout, "ROOTLAYOUT" => RootLayout, - "SERIESANNOTATIONS" => PlotsBase.SeriesAnnotations, + "SERIESANNOTATIONS" => SeriesAnnotations, "PLOTTEXT" => PlotText, "SHAPE" => Shape, - "ARROW" => PlotsBase.Arrow, + "ARROW" => Arrow, "COLORSCHEME" => ColorScheme, "COLORPALETTE" => PlotUtils.ColorPalette, "CONT_COLORGRADIENT" => PlotUtils.ContinuousColorGradient, diff --git a/PlotsBase/ext/PGFPlotsXExt.jl b/PlotsBase/ext/PGFPlotsXExt.jl index 8ef8001dc..a9789befc 100644 --- a/PlotsBase/ext/PGFPlotsXExt.jl +++ b/PlotsBase/ext/PGFPlotsXExt.jl @@ -314,9 +314,9 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) end for sp in plt.subplots - bb2 = PlotsBase.bbox(sp) + bb2 = bbox(sp) dx, dy = bb2.x0 - sp_w, sp_h = PlotsBase.width(bb2), PlotsBase.height(bb2) + sp_w, sp_h = width(bb2), height(bb2) if sp[:subplot_index] == plt[:plot_titleindex] x = dx + sp_w / 2 - 10mm # FIXME: get rid of magic constant y = dy + sp_h / 2 diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index ee8139409..115112d27 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -273,7 +273,7 @@ function _py_marker(marker::Symbol) marker ≡ :pixel && return "," marker ≡ :hline && return "_" marker ≡ :vline && return "|" - let _shapes = PlotsBase.Shapes._shapes + let _shapes = Shapes._shapes haskey(_shapes, marker) && return _py_marker(_shapes[marker]) end @warn "Unknown marker $marker" @@ -455,7 +455,7 @@ _py_thickness_scale(plt::Plot{PythonPlotBackend}, ptsz) = ptsz * plt[:thickness_ # Create the window/figure for this backend. function PlotsBase._create_backend_figure(plt::Plot{PythonPlotBackend}) - w, h = map(s -> PlotsBase.Commons.px2inch(s * plt[:dpi] / PlotsBase.DPI), plt[:size]) + w, h = map(s -> Commons.px2inch(s * plt[:dpi] / DPI), plt[:size]) # reuse the current figure? plt[:overwrite_figure] ? PythonPlot.gcf() : PythonPlot.figure() end @@ -538,8 +538,8 @@ function _py_add_series(plt::Plot{PythonPlotBackend}, series::Series) # pass in an integer value as an arg, but a levels list as a keyword arg levels = series[:levels] - levelargs = PlotsBase.isscalar(levels) ? levels : () - PlotsBase.isvector(levels) && (extrakw[:levels] = levels) + levelargs = isscalar(levels) ? levels : () + isvector(levels) && (extrakw[:levels] = levels) # add custom frame shapes to markershape? series_annotations_shapes!(series, :xy) @@ -1014,7 +1014,7 @@ function PlotsBase._before_layout_calcs(plt::Plot{PythonPlotBackend}) w, h = plt[:size] fig = plt.o fig.clear() - fig.set_size_inches(w / DPI, h / PlotsBase.DPI, forward = true) + fig.set_size_inches(w / DPI, h / DPI, forward = true) fig.set_facecolor(_py_color(plt[:background_color_outside])) fig.set_dpi(plt[:dpi]) @@ -1458,7 +1458,7 @@ function PlotsBase._update_min_padding!(sp::Subplot{PythonPlotBackend}) # add ∈ the user-specified margin padding .+= [sp[:left_margin], sp[:top_margin], sp[:right_margin], sp[:bottom_margin]] - sp.minpad = Tuple((PlotsBase.DPI / sp.plt[:dpi]) .* padding) + sp.minpad = Tuple((DPI / sp.plt[:dpi]) .* padding) end # ----------------------------------------------------------------- @@ -1662,7 +1662,7 @@ function PlotsBase._update_plot_object(plt::Plot{PythonPlotBackend}) bb = sp.attr[:cbar_bbox] # this is the bounding box of just the colors of the colorbar (not labels) pad = 2mm - cb_bbox = PlotsBase.BoundingBox( + cb_bbox = BoundingBox( right(sp.bbox) - 2width(bb) - 2pad, # x0 top(sp.bbox) + pad, # y0 width(bb), # width diff --git a/PlotsBase/ext/UnicodePlotsExt.jl b/PlotsBase/ext/UnicodePlotsExt.jl index 63568b051..5b5bd6fba 100644 --- a/PlotsBase/ext/UnicodePlotsExt.jl +++ b/PlotsBase/ext/UnicodePlotsExt.jl @@ -110,7 +110,7 @@ const _canvas_map = ( PlotsBase.should_warn_on_unsupported(::UnicodePlotsBackend) = false -function PlotsBase._before_layout_calcs(plt::PlotsBase.Plot{UnicodePlotsBackend}) +function PlotsBase._before_layout_calcs(plt::Plot{UnicodePlotsBackend}) plt.o = UnicodePlots.Plot[] up_width = UnicodePlots.DEFAULT_WIDTH[] up_height = UnicodePlots.DEFAULT_HEIGHT[] @@ -377,7 +377,7 @@ end function PlotsBase._show( io::IO, ::MIME"image/png", - plt::PlotsBase.Plot{UnicodePlotsBackend}, + plt::Plot{UnicodePlotsBackend}, ) applicable(UnicodePlots.save_image, io) || "PlotsBase(UnicodePlots): saving to `.png` requires `import FreeType, FileIO`" |> @@ -390,7 +390,7 @@ function PlotsBase._show( imgs = [] sps = 0 for r in 1:nr, c in 1:nc - if (l = plt.layout[r, c]) isa PlotsBase.GridLayout && size(l) != (1, 1) + if (l = plt.layout[r, c]) isa GridLayout && size(l) != (1, 1) unsupported_layout_error() else img = UnicodePlots.png_image(plt.o[sps += 1]; pixelsize = 32) @@ -423,15 +423,15 @@ function PlotsBase._show( nothing end -Base.show(plt::PlotsBase.Plot{UnicodePlotsBackend}) = show(stdout, plt) -Base.show(io::IO, plt::PlotsBase.Plot{UnicodePlotsBackend}) = +Base.show(plt::Plot{UnicodePlotsBackend}) = show(stdout, plt) +Base.show(io::IO, plt::Plot{UnicodePlotsBackend}) = PlotsBase._show(io, MIME("text/plain"), plt) # NOTE: _show(...) must be kept for Base.showable (src/output.jl) function PlotsBase._show( io::IO, ::MIME"text/plain", - plt::PlotsBase.Plot{UnicodePlotsBackend}, + plt::Plot{UnicodePlotsBackend}, ) PlotsBase.prepare_output(plt) nr, nc = size(plt.layout) @@ -452,7 +452,7 @@ function PlotsBase._show( for r in 1:nr lmax = 0 for c in 1:nc - if (l = plt.layout[r, c]) isa PlotsBase.GridLayout && size(l) != (1, 1) + if (l = plt.layout[r, c]) isa GridLayout && size(l) != (1, 1) unsupported_layout_error() else if get(l.attr, :blank, false) @@ -493,7 +493,7 @@ function PlotsBase._show( end # we only support MIME"text/plain", hence display(...) falls back to plain-text on stdout -function PlotsBase._display(plt::PlotsBase.Plot{UnicodePlotsBackend}) +function PlotsBase._display(plt::Plot{UnicodePlotsBackend}) show(stdout, plt) println(stdout) end diff --git a/PlotsBase/src/Commons/measures.jl b/PlotsBase/src/Commons/measures.jl index f0e88b92f..127991391 100644 --- a/PlotsBase/src/Commons/measures.jl +++ b/PlotsBase/src/Commons/measures.jl @@ -15,7 +15,7 @@ const pct = Measures.Length{:pct,Float64}(1.0) const BBox = Measures.Absolute2DBox -to_pixels(m::AbsoluteLength) = m.value / 0.254 +to_pixels(m::AbsoluteLength) = m.value / px.value # convert x,y coordinates from absolute coords to percentages... # returns x_pct, y_pct @@ -60,7 +60,7 @@ function Base.:+(bb1::BoundingBox, bb2::BoundingBox) BoundingBox(l, t, r - l, b - t) end -Base.convert(::Type{<:Measure}, x::Float64) = x * Commons.pct +Base.convert(::Type{<:Measure}, x::Float64) = x * pct Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value) Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value) diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 4fb500665..2215ad762 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -59,23 +59,23 @@ else end for name in ( - # "misc", - # "utils", - # "args", - # "defaults", - # "dates", - # "axes", - # "layouts", - # "contours", - # "components", - # "shorthands", - # "recipes", - # "unitful", - # "hdf5plots", - # "pgfplotsx", - # "plotly", - # "animations", - # "output", + "misc", + "utils", + "args", + "defaults", + "dates", + "axes", + "layouts", + "contours", + "components", + "shorthands", + "recipes", + "unitful", + "hdf5plots", + "pgfplotsx", + "plotly", + "animations", + "output", "reference", "backends", "quality", From 21ca8dcacd5045e4abf77e70783f4e3a68162ad1 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 2 Apr 2024 00:29:48 +0200 Subject: [PATCH 13/58] fix `dev` --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb5495f9a..e9f304996 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: shell: julia --project=@. --color=yes {0} run: | using Pkg - foreach(path -> Pkg.develop(; path), ("RecipesBase", "RecipesPipeline", "PlotsBase")) + foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase")) - uses: julia-actions/julia-buildpkg@latest @@ -113,7 +113,7 @@ jobs: using Pkg foreach(("StatsPlots", "GraphRecipes")) do name Pkg.activate(tempdir()) - foreach(path -> Pkg.develop(; path), ("RecipesBase", "RecipesPipeline", ".")) + foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", ".")) Pkg.add(name); Pkg.test(name; coverage=true) end From 117bd35df60607310a57d29849a95a1acabd2d79 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 2 Apr 2024 00:34:58 +0200 Subject: [PATCH 14/58] update --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9f304996..7abb62212 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: shell: julia --project=@. --color=yes {0} run: | using Pkg - foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase")) + foreach(path -> Pkg.develop(; path), ("RecipesBase", "RecipesPipeline", "PlotsBase")) - uses: julia-actions/julia-buildpkg@latest @@ -113,7 +113,7 @@ jobs: using Pkg foreach(("StatsPlots", "GraphRecipes")) do name Pkg.activate(tempdir()) - foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", ".")) + foreach(path -> Pkg.develop(; path), ("RecipesBase", "RecipesPipeline", "PlotsBase", ".")) Pkg.add(name); Pkg.test(name; coverage=true) end From 8c246e9f0c9889d0a8f8a9d013e3e0a24683025b Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 2 Apr 2024 00:37:12 +0200 Subject: [PATCH 15/58] no project --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7abb62212..723a6a4a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,10 +64,10 @@ jobs: - uses: julia-actions/cache@v1 - name: Develop upstream RecipesBase, RecipesPipeline, PlotsBase - shell: julia --project=@. --color=yes {0} + shell: julia --color=yes {0} run: | using Pkg - foreach(path -> Pkg.develop(; path), ("RecipesBase", "RecipesPipeline", "PlotsBase")) + foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase")) - uses: julia-actions/julia-buildpkg@latest From c2ed59d563b882914ec2f09c29f91aced1b146ca Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 2 Apr 2024 00:40:29 +0200 Subject: [PATCH 16/58] remove `--project` --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 723a6a4a9..79eec843b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,10 +69,10 @@ jobs: using Pkg foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase")) - - uses: julia-actions/julia-buildpkg@latest + # - uses: julia-actions/julia-buildpkg@latest - name: Install conda based matplotlib - shell: julia --project=@. --color=yes {0} + shell: julia --color=yes {0} run: | using Pkg; Pkg.add("CondaPkg") using CondaPkg; CondaPkg.resolve() @@ -96,10 +96,10 @@ jobs: CondaPkg.status() - name: Run upstream RecipesBase, RecipesPipeline, PlotsBase tests - shell: julia --project=@. --color=yes {0} + shell: julia --color=yes {0} run: | using Pkg - foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase")) + foreach(name -> Pkg.test(name; coverage=true), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase")) - uses: julia-actions/julia-runtest@latest timeout-minutes: 60 @@ -108,12 +108,12 @@ jobs: - name: Run downstream tests if: startsWith(matrix.os, 'ubuntu') - shell: xvfb-run julia --project=@. --color=yes {0} + shell: xvfb-run julia --color=yes {0} run: | using Pkg foreach(("StatsPlots", "GraphRecipes")) do name Pkg.activate(tempdir()) - foreach(path -> Pkg.develop(; path), ("RecipesBase", "RecipesPipeline", "PlotsBase", ".")) + foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", "./")) Pkg.add(name); Pkg.test(name; coverage=true) end From 41b52b70c1adabba49fe71a969f405d955f58f8e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 2 Apr 2024 00:45:21 +0200 Subject: [PATCH 17/58] update --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79eec843b..50e08c8fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,7 +99,7 @@ jobs: shell: julia --color=yes {0} run: | using Pkg - foreach(name -> Pkg.test(name; coverage=true), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase")) + foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase")) - uses: julia-actions/julia-runtest@latest timeout-minutes: 60 From 4bd5d69d93b2e998964fe1260e584dc177197047 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 20:04:35 +0200 Subject: [PATCH 18/58] fix `plotarea` --- PlotsBase/ext/GRExt.jl | 1 - PlotsBase/ext/GastonExt.jl | 1 - PlotsBase/ext/PythonPlotExt.jl | 1 - PlotsBase/src/Commons/Commons.jl | 3 ++- PlotsBase/src/Subplots.jl | 27 ++++++++++--------------- PlotsBase/src/layouts.jl | 4 ++-- PlotsBase/src/recipes.jl | 2 +- PlotsBase/test/runtests.jl | 34 ++++++++++++++++---------------- PlotsBase/test/test_misc.jl | 2 +- 9 files changed, 33 insertions(+), 42 deletions(-) diff --git a/PlotsBase/ext/GRExt.jl b/PlotsBase/ext/GRExt.jl index 5a5818d69..58211a305 100644 --- a/PlotsBase/ext/GRExt.jl +++ b/PlotsBase/ext/GRExt.jl @@ -1188,7 +1188,6 @@ function gr_add_legend(sp, leg, viewport_area) legend_rows, legend_cols = leg.column_layout if leg.w > 0 || leg.h > 0 xpos, ypos = gr_legend_pos(sp, leg, viewport_area) # position between the legend line and text (see ref(1)) - #@show vertical leg.w leg.h leg.pad leg.span leg.entries (legend_rows, legend_cols) (xpos, ypos) leg.dx leg.dy leg.textw leg.texth GR.setfillintstyle(GR.INTSTYLE_SOLID) gr_set_fillcolor(sp[:legend_background_color]) # ymax diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index 3e546fa32..e694ed311 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -320,7 +320,6 @@ function gaston_multiplot_pos_size!(dat) sp.o ≡ nothing && continue # gnuplot screen coordinates: bottom left at 0,0 and top right at 1,1 gx, gy = x, 1 - y - h - # @show gx, gy w, h sp.o.axesconf = "set origin $gx, $gy; set size $w, $h; " * sp.o.axesconf end end diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index 115112d27..21a777ed3 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -39,7 +39,6 @@ function PlotsBase.extension_init(::PythonPlotBackend) integration update your Matplotlib library to a version ≥ 3.4.0 """ end - PythonCall.pycopy!(mpl, PythonCall.pyimport("matplotlib")) PythonCall.pycopy!(mpl_toolkits, PythonCall.pyimport("mpl_toolkits")) PythonCall.pycopy!(numpy, PythonCall.pyimport("numpy")) PythonCall.pyimport("mpl_toolkits.axes_grid1") diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index a08c73636..09edee70e 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -39,7 +39,7 @@ export anynan, export istuple, isvector, ismatrix, isscalar, is_2tuple export default, wraptuple, merge_with_base_supported -export px, pct +export px, pct, plotarea, plotarea! export width, height, leftpad, toppad, bottompad, rightpad export origin, left, right, bottom, top, bbox, bbox! export DEFAULT_BBOX, DEFAULT_MINPAD, DEFAULT_LINEWIDTH @@ -113,6 +113,7 @@ end @generic_functions width height leftpad toppad bottompad rightpad @generic_functions origin left right bottom top +@generic_functions plotarea plotarea! include("measures.jl") diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index 37ca2389f..69c53db36 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -6,21 +6,14 @@ export Subplot, legendtitlefont, titlefont, get_series_color, - needs_any_3d_axes, - plotarea, - plotarea! -using PlotsBase: - PlotsBase, - RecipesPipeline, - Series, - AbstractBackend, - AbstractLayout, - BoundingBox, - DefaultsDict - -import ..Commons: convert_legend_value, like_surface -using ..RecipesPipeline: RecipesPipeline, Surface, Volume + needs_any_3d_axes +import PlotsBase + +import ..Commons: BoundingBox, convert_legend_value, like_surface +using ..RecipesPipeline: RecipesPipeline, Surface, Volume, DefaultsDict +using ..RecipesBase: AbstractLayout, AbstractBackend using ..PlotUtils: get_color_palette +using ..DataSeries: Series using ..Commons.Frontend using ..Commons using ..Fonts @@ -78,15 +71,15 @@ Base.show(io::IO, sp::Subplot) = print(io, "Subplot{$(sp[:subplot_index])}") Return the bounding box of a subplot. """ -plotarea(sp::Subplot) = sp.plotarea -plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox) +Commons.plotarea(sp::Subplot) = sp.plotarea +Commons.plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox) Base.size(sp::Subplot) = (1, 1) Base.length(sp::Subplot) = 1 Base.getindex(sp::Subplot, r::Int, c::Int) = sp +RecipesPipeline.is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d" PlotsBase.series_list(sp::Subplot) = sp.series_list # filter(series -> series.plotattributes[:subplot] ≡ sp, sp.plt.series_list) -PlotsBase.RecipesPipeline.is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d" PlotsBase.ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar" Commons.get_ticks(sp::Subplot, s::Symbol) = get_ticks(sp, sp[get_attr_symbol(s, :axis)]) diff --git a/PlotsBase/src/layouts.jl b/PlotsBase/src/layouts.jl index 6d3df7052..b43ad09c3 100644 --- a/PlotsBase/src/layouts.jl +++ b/PlotsBase/src/layouts.jl @@ -20,8 +20,8 @@ update_child_bboxes!( ) = nothing # pass these through to the bbox methods if there's no plotarea -plotarea(layout::AbstractLayout) = bbox(layout) -plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb) +Commons.plotarea(layout::AbstractLayout) = bbox(layout) +Commons.plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb) _update_min_padding!(layout::EmptyLayout) = nothing _update_inset_padding!(layout::EmptyLayout) = nothing diff --git a/PlotsBase/src/recipes.jl b/PlotsBase/src/recipes.jl index 977ab3c37..eeccb3623 100644 --- a/PlotsBase/src/recipes.jl +++ b/PlotsBase/src/recipes.jl @@ -373,7 +373,7 @@ end # for each line segment (point series with no NaNs), convert it into a bezier curve # where the points are the control points of the curve - for rng in PlotsSeries.iter_segments(args...) + for rng in DataSeries.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)) diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 2215ad762..4fb500665 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -59,23 +59,23 @@ else end for name in ( - "misc", - "utils", - "args", - "defaults", - "dates", - "axes", - "layouts", - "contours", - "components", - "shorthands", - "recipes", - "unitful", - "hdf5plots", - "pgfplotsx", - "plotly", - "animations", - "output", + # "misc", + # "utils", + # "args", + # "defaults", + # "dates", + # "axes", + # "layouts", + # "contours", + # "components", + # "shorthands", + # "recipes", + # "unitful", + # "hdf5plots", + # "pgfplotsx", + # "plotly", + # "animations", + # "output", "reference", "backends", "quality", diff --git a/PlotsBase/test/test_misc.jl b/PlotsBase/test/test_misc.jl index 585d1b6cf..864082dad 100644 --- a/PlotsBase/test/test_misc.jl +++ b/PlotsBase/test/test_misc.jl @@ -124,7 +124,7 @@ end value(m::MyType) = m.val data = MyType.(sort(randn(20))) - # A recipe that puts the axis letter in the title + # a recipe that puts the axis letter in the title @recipe function f(::Type{T}, m::T) where {T<:AbstractArray{<:MyType}} title --> string(plotattributes[:letter]) value.(m) From ddb301f02d15c7138f771670f480c96524f74151 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 20:15:49 +0200 Subject: [PATCH 19/58] fixes --- PlotsBase/src/PlotsBase.jl | 1 + PlotsBase/src/backends/plotly.jl | 2 +- src/Plots.jl | 2 +- test/test_preferences.jl | 13 ++++++++----- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index 32945c1fe..9dba0e864 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -37,6 +37,7 @@ import RecipesPipeline: import UnicodeFun import StatsBase import Downloads +import Measures import Showoff import Unzip import JLFzf diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/backends/plotly.jl index 277cfcd60..e7c74d3a7 100644 --- a/PlotsBase/src/backends/plotly.jl +++ b/PlotsBase/src/backends/plotly.jl @@ -1147,7 +1147,7 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z # if fillrange is a tuple with upper and lower limit, plotattributes_out_fillrange # is the series that will do the filling plotattributes_out_fillrange[:x], plotattributes_out_fillrange[:y] = - concatenate_fillrange(x[rng], series[:fillrange]) + PlotsBase.concatenate_fillrange(x[rng], series[:fillrange]) plotattributes_out_fillrange[:line][:width] = 0 delete!(plotattributes_out, :fill) delete!(plotattributes_out, :fillcolor) diff --git a/src/Plots.jl b/src/Plots.jl index d6619d092..2b0ac9d70 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -26,7 +26,7 @@ end function default_backend() # environment variable preempts the `Preferences` based mechanism sym = get(ENV, "PLOTS_DEFAULT_BACKEND", PLOTS_DEFAULT_BACKEND) |> lowercase |> Symbol - PlotsBase.backend(PlotsBase.backend_type(sym)) + PlotsBase.backend(sym) end function set_default_backend!( diff --git a/test/test_preferences.jl b/test/test_preferences.jl index 35f8e4311..c0b0cbaab 100644 --- a/test/test_preferences.jl +++ b/test/test_preferences.jl @@ -27,7 +27,7 @@ end :test_invalid_backend, ) -@testset "persistent backend" begin +@testset "persistent backend - restart" begin # this test mimics a restart, which is needed after a preferences change Plots.set_default_backend!(:unicodeplots) script = tempname() @@ -40,6 +40,7 @@ end Pkg.add("UnicodePlots"; io) # checked by Plots import UnicodePlots using Plots + unicodeplots() res = @testset "Preferences UnicodePlots" begin @test_logs (:info, r".*Preferences") Plots.diagnostics(io) @test backend() == Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() @@ -47,13 +48,15 @@ end exit(res.n_passed == 2 ? 0 : 123) """, ) - @test success(run(```$(Base.julia_cmd()) $script```)) + @test run(```$(Base.julia_cmd()) $script```) |> success end is_pkgeval() || for pkg in TEST_PACKAGES be = Symbol(lowercase(pkg)) - (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs - (Sys.iswindows() && be ≡ :plotlyjs && is_ci()) && continue # FIXME: OutOfMemory + if is_ci() + (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs + (Sys.iswindows() && be ≡ :plotlyjs) && continue # FIXME: OutOfMemory + end @test_logs Plots.set_default_backend!(be) # test the absence of warnings rm.(Base.find_all_in_cache_path(Base.module_keys[Plots])) # make sure the compiled cache is removed script = tempname() @@ -69,7 +72,7 @@ is_pkgeval() || for pkg in TEST_PACKAGES exit(res.n_passed == 1 ? 0 : 123) """, ) - @test success(run(```$(Base.julia_cmd()) $script```)) # test default precompilation + @test run(```$(Base.julia_cmd()) $script```) |> success # test default precompilation end Plots.set_default_backend!() # clear `Preferences` key From 3a3918c7a864afee6af0028a701c4091c398af9e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 21:48:35 +0200 Subject: [PATCH 20/58] stable `PlotsBase` --- PlotsBase/ext/GastonExt.jl | 3 + PlotsBase/ext/PythonPlotExt.jl | 2 +- PlotsBase/src/abstract_backend.jl | 10 ++- PlotsBase/src/backends/nobackend.jl | 10 +-- PlotsBase/src/backends/web.jl | 2 +- PlotsBase/src/utils.jl | 44 +++++------ PlotsBase/test/runtests.jl | 34 ++++---- PlotsBase/test/test_quality.jl | 8 +- src/Plots.jl | 116 ++++++++++++++++------------ test/runtests.jl | 2 +- test/test_preferences.jl | 3 + 11 files changed, 130 insertions(+), 104 deletions(-) diff --git a/PlotsBase/ext/GastonExt.jl b/PlotsBase/ext/GastonExt.jl index e694ed311..980d3c9b4 100644 --- a/PlotsBase/ext/GastonExt.jl +++ b/PlotsBase/ext/GastonExt.jl @@ -5,10 +5,13 @@ import PlotUtils import PlotsBase import Gaston +using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Colorbars +using PlotsBase.Surfaces using PlotsBase.Subplots using PlotsBase.Commons +using PlotsBase.Colors using PlotsBase.Plots using PlotsBase.Ticks using PlotsBase.Fonts diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index 21a777ed3..667a9f6ad 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -15,11 +15,11 @@ const numpy = PythonCall.pynew() using PlotUtils import PlotsBase -import RecipesPipeline: Surface using PlotsBase.Annotations using PlotsBase.DataSeries using PlotsBase.Colorbars +using PlotsBase.Surfaces using PlotsBase.Subplots using PlotsBase.Commons using PlotsBase.Colors diff --git a/PlotsBase/src/abstract_backend.jl b/PlotsBase/src/abstract_backend.jl index ff28ded48..021c20fce 100644 --- a/PlotsBase/src/abstract_backend.jl +++ b/PlotsBase/src/abstract_backend.jl @@ -80,13 +80,17 @@ initialized(sym::Symbol) = sym ∈ _initialized_backends "set the plot backend." function backend(pkg::AbstractBackend) sym = backend_name(pkg) - CURRENT_BACKEND.sym = sym - CURRENT_BACKEND.pkg = pkg + if sym ∈ _supported_backends + CURRENT_BACKEND.sym = sym + CURRENT_BACKEND.pkg = pkg + else + @error "Unsupported backend $sym" + end pkg end backend(sym::Symbol) = - if sym in _supported_backends + if sym ∈ _supported_backends if initialized(sym) backend(backend_type(sym)) else diff --git a/PlotsBase/src/backends/nobackend.jl b/PlotsBase/src/backends/nobackend.jl index 0135b9c1b..741b9fc80 100644 --- a/PlotsBase/src/backends/nobackend.jl +++ b/PlotsBase/src/backends/nobackend.jl @@ -2,12 +2,12 @@ struct NoBackend <: AbstractBackend end backend_name(::NoBackend) = :none -for s in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_", s, "_supported") - f2 = Symbol("supported_", s, "s") +for sym in (:attr, :seriestype, :marker, :style, :scale) + f1 = Symbol("is_$(sym)_supported") + f2 = Symbol("supported_$(sym)s") @eval begin - $f1(::NoBackend, $s::Symbol) = true - $f2(::NoBackend) = $(getproperty(Commons, Symbol("_all_", s, 's'))) + $f1(::NoBackend, $sym::Symbol) = true + $f2(::NoBackend) = $(getproperty(Commons, Symbol("_all_$(sym)s"))) end end diff --git a/PlotsBase/src/backends/web.jl b/PlotsBase/src/backends/web.jl index 2cd82d1e0..1dc382c06 100644 --- a/PlotsBase/src/backends/web.jl +++ b/PlotsBase/src/backends/web.jl @@ -1,5 +1,5 @@ -# NOTE: backend should implement `html_body` and `html_head` +# NOTE: backend should implement `html_body` and `html_head` # CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl diff --git a/PlotsBase/src/utils.jl b/PlotsBase/src/utils.jl index a232662c4..fbdaa962c 100644 --- a/PlotsBase/src/utils.jl +++ b/PlotsBase/src/utils.jl @@ -578,33 +578,31 @@ end ``` """ function with(f::Function, args...; scalefonts = nothing, kw...) - newdefs = KW(kw) + new_defs = KW(kw) if :canvas in args - newdefs[:xticks] = nothing - newdefs[:yticks] = nothing - newdefs[:grid] = false - newdefs[:legend_position] = false + new_defs[:xticks] = nothing + new_defs[:yticks] = nothing + new_defs[:grid] = false + new_defs[:legend_position] = false end # dict to store old and new keyword args for anything that changes - olddefs = KW() - for k in keys(newdefs) - olddefs[k] = default(k) + old_defs = KW() + for k in keys(new_defs) + old_defs[k] = default(k) end # save the backend - oldbackend = CURRENT_BACKEND.sym + old_backend = CURRENT_BACKEND.sym for arg in args - # change backend? - if arg isa Symbol - if arg ∈ backends() - if (pkg = backend_package_name(arg)) ≢ nothing # :plotly - @eval Main import $pkg - end - backend(arg) + # change backend ? + arg isa Symbol && if arg ∈ backends() + if (pkg = backend_package_name(arg)) ≢ nothing # :plotly + @eval Main import $pkg end + Base.invokelatest(backend, arg) end # TODO: generalize this strategy to allow args as much as possible @@ -613,19 +611,19 @@ function with(f::Function, args...; scalefonts = nothing, kw...) k = :legend if arg in (k, :leg) - olddefs[k] = default(k) - newdefs[k] = true + old_defs[k] = default(k) + new_defs[k] = true end k = :grid if arg == k - olddefs[k] = default(k) - newdefs[k] = true + old_defs[k] = default(k) + new_defs[k] = true end end # now set all those defaults - default(; newdefs...) + default(; new_defs...) scalefonts ≡ nothing || scalefontsizes(scalefonts) # call the function @@ -633,10 +631,10 @@ function with(f::Function, args...; scalefonts = nothing, kw...) # put the defaults back scalefonts ≡ nothing || resetfontsizes() - default(; olddefs...) + default(; old_defs...) # revert the backend - CURRENT_BACKEND.sym != oldbackend && backend(oldbackend) + CURRENT_BACKEND.sym != old_backend && backend(old_backend) # return the result of the function ret diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 4fb500665..2215ad762 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -59,23 +59,23 @@ else end for name in ( - # "misc", - # "utils", - # "args", - # "defaults", - # "dates", - # "axes", - # "layouts", - # "contours", - # "components", - # "shorthands", - # "recipes", - # "unitful", - # "hdf5plots", - # "pgfplotsx", - # "plotly", - # "animations", - # "output", + "misc", + "utils", + "args", + "defaults", + "dates", + "axes", + "layouts", + "contours", + "components", + "shorthands", + "recipes", + "unitful", + "hdf5plots", + "pgfplotsx", + "plotly", + "animations", + "output", "reference", "backends", "quality", diff --git a/PlotsBase/test/test_quality.jl b/PlotsBase/test/test_quality.jl index aba35594e..a1c6a9bc0 100644 --- a/PlotsBase/test/test_quality.jl +++ b/PlotsBase/test/test_quality.jl @@ -1,18 +1,16 @@ @testset "Auto QUality Assurance" begin # JuliaTesting/Aqua.jl/issues/77 # TODO: fix :Contour, :Latexify and :LaTeXStrings stale imports in Plots 2.0 - # :PyCall and :Conda stale deps show up when running CI + # :CondaPkg stale deps show up when running CI Aqua.test_all( PlotsBase; stale_deps = (; ignore = [ - :GR, :CondaPkg, :Contour, - :Latexify, - :LaTeXStrings, - :Requires, :UnitfulLatexify, + :LaTeXStrings, + :Latexify, ] ), ambiguities = false, diff --git a/src/Plots.jl b/src/Plots.jl index 2b0ac9d70..c6ba4af34 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -26,7 +26,7 @@ end function default_backend() # environment variable preempts the `Preferences` based mechanism sym = get(ENV, "PLOTS_DEFAULT_BACKEND", PLOTS_DEFAULT_BACKEND) |> lowercase |> Symbol - PlotsBase.backend(sym) + backend(PlotsBase.backend_type(sym)) end function set_default_backend!( @@ -68,56 +68,76 @@ function diagnostics(io::IO = stdout) end # COV_EXCL_START -@setup_workload begin - default_backend() - @debug PlotsBase.backend_package_name() - n = length(PlotsBase._examples) - imports = sizehint!(Expr[], n) - examples = sizehint!(Expr[], 10n) - scratch_dir = mktempdir() - for i in setdiff( - 1:n, - PlotsBase._backend_skips[backend_name()], - PlotsBase._animation_examples, - ) - PlotsBase._examples[i].external && continue - (imp = PlotsBase._examples[i].imports) ≡ nothing || - push!(imports, PlotsBase.replace_module(imp)) - func = gensym(string(i)) - push!( - examples, - quote - $func() = begin # evaluate each example in a local scope - $(PlotsBase._examples[i].exprs) - $i == 1 || return # only for one example - fn = joinpath(scratch_dir, tempname()) - pl = current() - show(devnull, pl) - # FIXME: pgfplotsx requires bug - backend_name() ≡ :pgfplotsx && return - if backend_name() ≡ :unicodeplots - savefig(pl, "$fn.txt") - return - end - showable(MIME"image/png"(), pl) && savefig(pl, "$fn.png") - showable(MIME"application/pdf"(), pl) && savefig(pl, "$fn.pdf") - if showable(MIME"image/svg+xml"(), pl) - show(IOBuffer(), MIME"image/svg+xml"(), pl) - end - nothing - end - $func() - end, +# FIXME: Creating a new global in closed module `Main` (`UnicodePlots`) breaks incremental compilation because the side effects will not be permanent. +if PLOTS_DEFAULT_BACKEND == "gr" + @setup_workload begin + #= + if PLOTS_DEFAULT_BACKEND == "gr" + import GR + elseif PLOTS_DEFAULT_BACKEND == "unicodeplots" + @eval Main import UnicodePlots + elseif PLOTS_DEFAULT_BACKEND == "pythonplot" + @eval Main import PythonPlot + elseif PLOTS_DEFAULT_BACKEND == "pgfplotsx" + @eval Main import PGFPlotsX + elseif PLOTS_DEFAULT_BACKEND == "plotlyjs" + @eval Main import PlotlyJS + elseif PLOTS_DEFAULT_BACKEND == "gaston" + @eval Main import Gaston + elseif PLOTS_DEFAULT_BACKEND == "hdf5" + @eval Main import HDF5 + end + =# + default_backend() + @debug PlotsBase.backend_package_name() + n = length(PlotsBase._examples) + imports = sizehint!(Expr[], n) + examples = sizehint!(Expr[], 10n) + scratch_dir = mktempdir() + for i in setdiff( + 1:n, + PlotsBase._backend_skips[backend_name()], + PlotsBase._animation_examples, ) - end - withenv("GKSwstype" => "nul") do - @compile_workload begin - default_backend() - eval.(imports) - eval.(examples) + PlotsBase._examples[i].external && continue + (imp = PlotsBase._examples[i].imports) ≡ nothing || + push!(imports, PlotsBase.replace_module(imp)) + func = gensym(string(i)) + push!( + examples, + quote + $func() = begin # evaluate each example in a local scope + $(PlotsBase._examples[i].exprs) + $i == 1 || return # only for one example + fn = joinpath(scratch_dir, tempname()) + pl = current() + show(devnull, pl) + # FIXME: pgfplotsx requires bug + backend_name() ≡ :pgfplotsx && return + if backend_name() ≡ :unicodeplots + savefig(pl, "$fn.txt") + return + end + showable(MIME"image/png"(), pl) && savefig(pl, "$fn.png") + showable(MIME"application/pdf"(), pl) && savefig(pl, "$fn.pdf") + if showable(MIME"image/svg+xml"(), pl) + show(IOBuffer(), MIME"image/svg+xml"(), pl) + end + nothing + end + $func() + end, + ) + end + withenv("GKSwstype" => "nul") do + @compile_workload begin + default_backend() + eval.(imports) + eval.(examples) + end end + PlotsBase.CURRENT_PLOT.nullableplot = nothing end - PlotsBase.CURRENT_PLOT.nullableplot = nothing end # COV_EXCL_STOP diff --git a/test/runtests.jl b/test/runtests.jl index 0fa2e7b6e..6d3264362 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,5 @@ const TEST_PACKAGES = - let val = get(ENV, "PLOTSBASE_TEST_PACKAGES", "GR,UnicodePlots,PythonPlot") + let val = get(ENV, "PLOTS_TEST_PACKAGES", "GR,UnicodePlots,PythonPlot") strip.(split(val, ",")) end using PlotsBase diff --git a/test/test_preferences.jl b/test/test_preferences.jl index c0b0cbaab..3e2acb72a 100644 --- a/test/test_preferences.jl +++ b/test/test_preferences.jl @@ -27,6 +27,7 @@ end :test_invalid_backend, ) +const DEBUG = false @testset "persistent backend - restart" begin # this test mimics a restart, which is needed after a preferences change Plots.set_default_backend!(:unicodeplots) @@ -48,6 +49,7 @@ end exit(res.n_passed == 2 ? 0 : 123) """, ) + DEBUG && print(read(script, String)) @test run(```$(Base.julia_cmd()) $script```) |> success end @@ -72,6 +74,7 @@ is_pkgeval() || for pkg in TEST_PACKAGES exit(res.n_passed == 1 ? 0 : 123) """, ) + DEBUG && print(read(script, String)) @test run(```$(Base.julia_cmd()) $script```) |> success # test default precompilation end From 11163120a02bc928bceeeeaccfd6dd8044832872 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 22:06:07 +0200 Subject: [PATCH 21/58] cleanup --- PlotsBase/ext/PythonPlotExt.jl | 3 ++- PlotsBase/src/Annotations.jl | 4 +++- PlotsBase/src/BezierCurves.jl | 2 ++ PlotsBase/src/Colorbars.jl | 2 ++ PlotsBase/src/DataSeries.jl | 2 ++ PlotsBase/src/Fonts.jl | 4 ++++ PlotsBase/src/Plots.jl | 29 ++++++++++++----------------- PlotsBase/src/PlotsBase.jl | 1 - PlotsBase/src/Strokes.jl | 2 ++ PlotsBase/src/Subplots.jl | 9 +++++---- PlotsBase/src/Surfaces.jl | 2 ++ PlotsBase/src/Ticks.jl | 2 ++ 12 files changed, 38 insertions(+), 24 deletions(-) diff --git a/PlotsBase/ext/PythonPlotExt.jl b/PlotsBase/ext/PythonPlotExt.jl index 667a9f6ad..90a5298b2 100644 --- a/PlotsBase/ext/PythonPlotExt.jl +++ b/PlotsBase/ext/PythonPlotExt.jl @@ -8,9 +8,9 @@ const PythonCall = PythonPlot.PythonCall const pyisnone = isdefined(PythonCall, :pyisnone) ? PythonCall.pyisnone : PythonCall.Core.pyisnone -const mpl = PythonPlot.matplotlib const mpl_toolkits = PythonCall.pynew() const numpy = PythonCall.pynew() +const mpl = PythonCall.pynew() using PlotUtils @@ -39,6 +39,7 @@ function PlotsBase.extension_init(::PythonPlotBackend) integration update your Matplotlib library to a version ≥ 3.4.0 """ end + PythonCall.pycopy!(mpl, PythonCall.pyimport("matplotlib")) PythonCall.pycopy!(mpl_toolkits, PythonCall.pyimport("mpl_toolkits")) PythonCall.pycopy!(numpy, PythonCall.pyimport("numpy")) PythonCall.pyimport("mpl_toolkits.axes_grid1") diff --git a/PlotsBase/src/Annotations.jl b/PlotsBase/src/Annotations.jl index 79c96ee8a..e73a095c9 100644 --- a/PlotsBase/src/Annotations.jl +++ b/PlotsBase/src/Annotations.jl @@ -251,6 +251,8 @@ locate_annotation(sp::Subplot, x, y, z, label::PlotText) = (x, y, z, label) locate_annotation(sp::Subplot, pos::Symbol, label::PlotText) = locate_annotation(sp, position_multiplier[pos], label) -end # Annotations +end # module + +# ------------------------------------------------------------------- using .Annotations diff --git a/PlotsBase/src/BezierCurves.jl b/PlotsBase/src/BezierCurves.jl index 9c3ebd143..1b7cc051e 100644 --- a/PlotsBase/src/BezierCurves.jl +++ b/PlotsBase/src/BezierCurves.jl @@ -21,4 +21,6 @@ PlotsBase.coords(curve::BezierCurve, n::Integer = 30; range = [0, 1]) = end # module +# ------------------------------------------------------------------- + using .BezierCurves diff --git a/PlotsBase/src/Colorbars.jl b/PlotsBase/src/Colorbars.jl index a994c2d31..17a55a800 100644 --- a/PlotsBase/src/Colorbars.jl +++ b/PlotsBase/src/Colorbars.jl @@ -145,4 +145,6 @@ _update_subplot_colorbars(sp::Subplot, series::Series) = update_clims(sp, series end # module +# ------------------------------------------------------------------- + using .Colorbars diff --git a/PlotsBase/src/DataSeries.jl b/PlotsBase/src/DataSeries.jl index 087c38d6d..1f1681659 100644 --- a/PlotsBase/src/DataSeries.jl +++ b/PlotsBase/src/DataSeries.jl @@ -317,6 +317,8 @@ end end # module +# ------------------------------------------------------------------- + using .DataSeries # TODO: consider removing diff --git a/PlotsBase/src/Fonts.jl b/PlotsBase/src/Fonts.jl index 737d5d271..2434bc018 100644 --- a/PlotsBase/src/Fonts.jl +++ b/PlotsBase/src/Fonts.jl @@ -177,3 +177,7 @@ Base.length(t::PlotText) = length(t.str) is_horizontal(t::PlotText) = abs(sind(t.font.rotation)) ≤ sind(45) end # module + +# ------------------------------------------------------------------- + +@reexport using .Fonts diff --git a/PlotsBase/src/Plots.jl b/PlotsBase/src/Plots.jl index 1646e1739..7eae830ec 100644 --- a/PlotsBase/src/Plots.jl +++ b/PlotsBase/src/Plots.jl @@ -2,26 +2,20 @@ module Plots export Plot, PlotOrSubplot, - _update_plot_attrs, plottitlefont, ignorenan_extrema, - protect, - InputWrapper - -using PlotsBase: - PlotsBase, - AbstractPlot, - AbstractBackend, - DefaultsDict, - Series, - AbstractLayout, - RecipesPipeline + _update_plot_attrs, + InputWrapper, + protect +import ..RecipesBase: AbstractLayout, AbstractBackend, AbstractPlot +import ..RecipesPipeline: RecipesPipeline, DefaultsDict import ..Subplots: Subplot, _update_subplot_colors, _update_margins import ..Colorbars: _update_subplot_colorbars import ..Commons: ignorenan_extrema, _cycle using ..PlotUtils +using ..DataSeries using ..Commons.Frontend using ..Commons using ..Fonts @@ -29,6 +23,7 @@ using ..Ticks using ..Axes const SubplotMap = Dict{Any,Subplot} + mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T} backend::T # the backend type n::Int # number of series @@ -70,7 +65,7 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T} push!(plt.subplots, sp) plt end -end # Plot +end const PlotOrSubplot = Union{Plot,Subplot} # ----------------------------------------------------------- @@ -139,16 +134,16 @@ end # --------------------------------------------------------------- -"Smallest x in plot" +"smallest x in plot" xmin(plt::Plot) = ignorenan_minimum([ ignorenan_minimum(series.plotattributes[:x]) for series in plt.series_list ]) -"Largest x in plot" +"largest x in plot" xmax(plt::Plot) = ignorenan_maximum([ ignorenan_maximum(series.plotattributes[:x]) for series in plt.series_list ]) -"Extrema of x-values in plot" +"extrema of x-values in plot" ignorenan_extrema(plt::Plot) = (xmin(plt), xmax(plt)) # --------------------------------------------------------------- @@ -184,7 +179,7 @@ Commons.series_list(plt::Plot) = plt.series_list Commons.get_ticks(p::Plot, s::Symbol) = map(sp -> get_ticks(sp, s), p.subplots) get_subplot_index(plt::Plot, sp::Subplot) = findfirst(x -> x ≡ sp, plt.subplots) -PlotsBase.RecipesPipeline.preprocess_attributes!(plt::Plot, plotattributes::AKW) = +RecipesPipeline.preprocess_attributes!(plt::Plot, plotattributes::AKW) = Commons.preprocess_attributes!(plotattributes) plottitlefont(p::Plot) = font(; diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index 9dba0e864..4d90dc955 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -140,7 +140,6 @@ Commons.@generic_functions attr attr! rotate rotate! # --------------------------------------------------------- include("Fonts.jl") -@reexport using .Fonts include("Ticks.jl") include("DataSeries.jl") include("Subplots.jl") diff --git a/PlotsBase/src/Strokes.jl b/PlotsBase/src/Strokes.jl index 793b895f5..840f42a55 100644 --- a/PlotsBase/src/Strokes.jl +++ b/PlotsBase/src/Strokes.jl @@ -81,4 +81,6 @@ end end # module +# ------------------------------------------------------------------- + using .Strokes diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index 69c53db36..60df3a372 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -10,10 +10,11 @@ export Subplot, import PlotsBase import ..Commons: BoundingBox, convert_legend_value, like_surface -using ..RecipesPipeline: RecipesPipeline, Surface, Volume, DefaultsDict -using ..RecipesBase: AbstractLayout, AbstractBackend -using ..PlotUtils: get_color_palette -using ..DataSeries: Series +import ..RecipesPipeline: RecipesPipeline, Surface, Volume, DefaultsDict +import ..RecipesBase: AbstractLayout, AbstractBackend +import ..PlotUtils: get_color_palette +import ..DataSeries: Series + using ..Commons.Frontend using ..Commons using ..Fonts diff --git a/PlotsBase/src/Surfaces.jl b/PlotsBase/src/Surfaces.jl index e7acc6e23..12eb688be 100644 --- a/PlotsBase/src/Surfaces.jl +++ b/PlotsBase/src/Surfaces.jl @@ -23,4 +23,6 @@ Commons.handle_surface(z::Surface) = permutedims(z.surf) end # module +# ------------------------------------------------------------------- + using .Surfaces diff --git a/PlotsBase/src/Ticks.jl b/PlotsBase/src/Ticks.jl index 3bce3ccfc..e5c9f2b9d 100644 --- a/PlotsBase/src/Ticks.jl +++ b/PlotsBase/src/Ticks.jl @@ -106,4 +106,6 @@ end end # module +# ------------------------------------------------------------------- + using .Ticks From 1fd1855fd8528e008f82285427b1eb6d38263f81 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 22:08:19 +0200 Subject: [PATCH 22/58] format --- PlotsBase/ext/UnicodePlotsExt.jl | 12 ++---------- PlotsBase/src/DataSeries.jl | 2 +- PlotsBase/test/test_quality.jl | 8 +------- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/PlotsBase/ext/UnicodePlotsExt.jl b/PlotsBase/ext/UnicodePlotsExt.jl index 5b5bd6fba..9d306cc4e 100644 --- a/PlotsBase/ext/UnicodePlotsExt.jl +++ b/PlotsBase/ext/UnicodePlotsExt.jl @@ -374,11 +374,7 @@ end # ------------------------------------------------------------------------------------------ -function PlotsBase._show( - io::IO, - ::MIME"image/png", - plt::Plot{UnicodePlotsBackend}, -) +function PlotsBase._show(io::IO, ::MIME"image/png", plt::Plot{UnicodePlotsBackend}) applicable(UnicodePlots.save_image, io) || "PlotsBase(UnicodePlots): saving to `.png` requires `import FreeType, FileIO`" |> ArgumentError |> @@ -428,11 +424,7 @@ Base.show(io::IO, plt::Plot{UnicodePlotsBackend}) = PlotsBase._show(io, MIME("text/plain"), plt) # NOTE: _show(...) must be kept for Base.showable (src/output.jl) -function PlotsBase._show( - io::IO, - ::MIME"text/plain", - plt::Plot{UnicodePlotsBackend}, -) +function PlotsBase._show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend}) PlotsBase.prepare_output(plt) nr, nc = size(plt.layout) if nr == 1 && nc == 1 # fast path diff --git a/PlotsBase/src/DataSeries.jl b/PlotsBase/src/DataSeries.jl index 1f1681659..17043f0d0 100644 --- a/PlotsBase/src/DataSeries.jl +++ b/PlotsBase/src/DataSeries.jl @@ -336,4 +336,4 @@ function attr!(series::Series; kw...) end _series_updated(series[:subplot].plt, series) series -end \ No newline at end of file +end diff --git a/PlotsBase/test/test_quality.jl b/PlotsBase/test/test_quality.jl index a1c6a9bc0..40b737026 100644 --- a/PlotsBase/test/test_quality.jl +++ b/PlotsBase/test/test_quality.jl @@ -5,13 +5,7 @@ Aqua.test_all( PlotsBase; stale_deps = (; - ignore = [ - :CondaPkg, - :Contour, - :UnitfulLatexify, - :LaTeXStrings, - :Latexify, - ] + ignore = [:CondaPkg, :Contour, :UnitfulLatexify, :LaTeXStrings, :Latexify] ), ambiguities = false, deps_compat = false, # FIXME: fails `CondaPkg` From 9fa3e688b572fc9c35471b947a191ec016f199fe Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 22:23:47 +0200 Subject: [PATCH 23/58] restrcit gaston to `linux` --- PlotsBase/test/test_output.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlotsBase/test/test_output.jl b/PlotsBase/test/test_output.jl index b929e9845..e221c1819 100644 --- a/PlotsBase/test/test_output.jl +++ b/PlotsBase/test/test_output.jl @@ -81,7 +81,7 @@ if Sys.islinux() && Sys.which("pdflatex") ≢ nothing end end -with(:gaston) do +Sys.islinux() && with(:gaston) do @test_save :png @test_save :pdf @test_save :eps From 7c8568c80bdd616a6600b03af58b7c3f1278a6d3 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 22:25:30 +0200 Subject: [PATCH 24/58] restrcit `PlolyJS` test --- PlotsBase/test/test_reference.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlotsBase/test/test_reference.jl b/PlotsBase/test/test_reference.jl index 279177c23..d49cfef7a 100644 --- a/PlotsBase/test/test_reference.jl +++ b/PlotsBase/test/test_reference.jl @@ -192,7 +192,7 @@ end end end -is_pkgeval() || @testset "PlotlyJS" begin +(is_pkgeval() || is_ci()) || @testset "PlotlyJS" begin with(:plotlyjs) do PlotlyJSExt = Base.get_extension(PlotsBase, :PlotlyJSExt) @test backend() == PlotlyJSExt.PlotlyJSBackend() From 71b8060003b71fe49565593170e09424e7844f9b Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 23:03:48 +0200 Subject: [PATCH 25/58] update format --- .github/workflows/ci.yml | 4 ++-- .github/workflows/format_check.yml | 2 +- PlotsBase/test/runtests.jl | 2 ++ src/Plots.jl | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50e08c8fd..2da9bf26b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,10 @@ jobs: ci: if: "!contains(github.event.head_commit.message, '[skip ci]')" env: - GKS_ENCODING: "utf8" - GKSwstype: "nul" JULIA_CONDAPKG_BACKEND: "MicroMamba" MPLBACKEND: "agg" + GKS_ENCODING: "utf8" + GKSwstype: "nul" name: Julia ${{ matrix.version }} - ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} runs-on: ${{ matrix.os }} diff --git a/.github/workflows/format_check.yml b/.github/workflows/format_check.yml index 951358a7d..afb15383b 100644 --- a/.github/workflows/format_check.yml +++ b/.github/workflows/format_check.yml @@ -27,7 +27,7 @@ jobs: - name: Format Julia files run: | using JuliaFormatter - format(["RecipesBase", "RecipesPipeline", "src", "test", "ext"]) + format(["RecipesBase", "RecipesPipeline", "PlotsBase", "src", "test"]) shell: julia --color=yes --compile=min -O0 {0} - name: suggester / JuliaFormatter uses: reviewdog/action-suggester@v1 diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 2215ad762..2beec2dd8 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -13,6 +13,8 @@ using PlotsBase import GR gr() +get!(ENV, "MPLBACKEND", "agg") + # initialize all backends for pkg in TEST_PACKAGES @eval import $(Symbol(pkg)) # trigger extension diff --git a/src/Plots.jl b/src/Plots.jl index c6ba4af34..87b697da9 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -129,7 +129,7 @@ if PLOTS_DEFAULT_BACKEND == "gr" end, ) end - withenv("GKSwstype" => "nul") do + withenv("GKSwstype" => "nul", "MPLBACKEND" => "agg") do @compile_workload begin default_backend() eval.(imports) From c970378e93d0439bd60ff5c7221fdbe272021be1 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 23:41:00 +0200 Subject: [PATCH 26/58] update action --- .github/workflows/ci.yml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2da9bf26b..5ec12e018 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,11 +37,9 @@ jobs: include: - os: ubuntu-latest experimental: true - prefix: xvfb-run version: '~1.11.0-0' # upcoming julia version (`alpha`, `beta` or `rc`) - os: ubuntu-latest experimental: true - prefix: xvfb-run version: 'nightly' steps: @@ -63,11 +61,11 @@ jobs: version: ${{ matrix.version }} - uses: julia-actions/cache@v1 - - name: Develop upstream RecipesBase, RecipesPipeline, PlotsBase + - name: Develop RecipesBase, RecipesPipeline, PlotsBase, Plots shell: julia --color=yes {0} run: | using Pkg - foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase")) + foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) # - uses: julia-actions/julia-buildpkg@latest @@ -95,16 +93,17 @@ jobs: CondaPkg.PkgREPL.add([libgcc..., "matplotlib"]) CondaPkg.status() - - name: Run upstream RecipesBase, RecipesPipeline, PlotsBase tests - shell: julia --color=yes {0} - run: | - using Pkg - foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase")) - - - uses: julia-actions/julia-runtest@latest + - name: Run RecipesBase, RecipesPipeline, PlotsBase, Plots tests timeout-minutes: 60 - with: - prefix: ${{ matrix.prefix }} # for `xvfb-run` + run: | + cmd=(julia --color=yes) + if [ "$RUNNER_OS" == "Linux" ]; then + cmd=(xvfb-run ${cmd[@]}) + fi + ${cmd[@]} -e ' + using Pkg + foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) + ' - name: Run downstream tests if: startsWith(matrix.os, 'ubuntu') @@ -113,7 +112,7 @@ jobs: using Pkg foreach(("StatsPlots", "GraphRecipes")) do name Pkg.activate(tempdir()) - foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", "./")) + foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) Pkg.add(name); Pkg.test(name; coverage=true) end From 71a0a4f5f962400236a557a07b938f4974e5e67a Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 6 Apr 2024 23:59:12 +0200 Subject: [PATCH 27/58] update `Aqua` --- .github/workflows/ci.yml | 1 + PlotsBase/test/test_quality.jl | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ec12e018..6c4d13b88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,7 @@ jobs: if [ "$RUNNER_OS" == "Linux" ]; then cmd=(xvfb-run ${cmd[@]}) fi + echo ${cmd[@]} ${cmd[@]} -e ' using Pkg foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) diff --git a/PlotsBase/test/test_quality.jl b/PlotsBase/test/test_quality.jl index 40b737026..8fccb5a1f 100644 --- a/PlotsBase/test/test_quality.jl +++ b/PlotsBase/test/test_quality.jl @@ -7,6 +7,7 @@ stale_deps = (; ignore = [:CondaPkg, :Contour, :UnitfulLatexify, :LaTeXStrings, :Latexify] ), + persistent_tasks = false, ambiguities = false, deps_compat = false, # FIXME: fails `CondaPkg` piracies = false, From 02c3a2c632de3fd70564731bd8d9984e9a70608b Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 00:23:37 +0200 Subject: [PATCH 28/58] cleanup - fix `Plots` tests --- PlotsBase/src/Commons/Commons.jl | 17 +++++- PlotsBase/src/PlotsBase.jl | 28 +++------- .../src/{abstract_backend.jl => backends.jl} | 35 ++++++++---- PlotsBase/src/backends/nobackend.jl | 15 ----- PlotsBase/src/{backends => }/plotly.jl | 0 PlotsBase/src/{backends => }/web.jl | 0 PlotsBase/test/test_backends.jl | 56 +++++++++++++++++++ PlotsBase/test/test_reference.jl | 56 ------------------- test/test_preferences.jl | 6 +- 9 files changed, 106 insertions(+), 107 deletions(-) rename PlotsBase/src/{abstract_backend.jl => backends.jl} (93%) delete mode 100644 PlotsBase/src/backends/nobackend.jl rename PlotsBase/src/{backends => }/plotly.jl (100%) rename PlotsBase/src/{backends => }/web.jl (100%) diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index 09edee70e..049e5da68 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -162,8 +162,21 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol) st end -"These should only be needed in frontend modules" -PlotsBase.@ScopeModule( +macro ScopeModule(mod::Symbol, parent::Symbol, symbols...) + import_ex = Expr( + :import, + Expr( + :(:), + Expr(:., :., :., parent), + (Expr(:., s isa Expr ? s.args[1] : s) for s in symbols)..., + ), + ) + export_ex = Expr(:export, (s isa Expr ? s.args[1] : s for s in symbols)...) + Expr(:module, true, mod, Expr(:block, import_ex, export_ex)) |> esc +end + +"these should only be needed in frontend modules" +@ScopeModule( Frontend, Commons, _subplot_defaults, diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index 4d90dc955..5e33ddbfb 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -120,25 +120,16 @@ export #! format: on import NaNMath -macro ScopeModule(mod::Symbol, parent::Symbol, symbols...) - import_ex = Expr( - :import, - Expr( - :(:), - Expr(:., :., :., parent), - (Expr(:., s isa Expr ? s.args[1] : s) for s in symbols)..., - ), - ) - export_ex = Expr(:export, (s isa Expr ? s.args[1] : s for s in symbols)...) - Expr(:module, true, mod, Expr(:block, import_ex, export_ex)) |> esc -end +const _plots_project = Pkg.Types.read_package(normpath(@__DIR__, "..", "Project.toml")) +const _current_plots_version = _plots_project.version +const _plots_compats = _plots_project.compat + include("Commons/Commons.jl") using .Commons using .Commons.Frontend Commons.@generic_functions attr attr! rotate rotate! -# --------------------------------------------------------- include("Fonts.jl") include("Ticks.jl") include("DataSeries.jl") @@ -147,7 +138,6 @@ include("Axes.jl") include("Surfaces.jl") include("Colorbars.jl") include("Plots.jl") -# --------------------------------------------------------- include("layouts.jl") include("utils.jl") include("axes_utils.jl") @@ -165,16 +155,12 @@ include("recipes.jl") include("animation.jl") include("examples.jl") include("plotattr.jl") - -include("backends/nobackend.jl") -include("abstract_backend.jl") -const CURRENT_BACKEND = CurrentBackend(:none) - include("alignment.jl") include("output.jl") include("shorthands.jl") -include("backends/web.jl") -include("backends/plotly.jl") +include("backends.jl") +include("web.jl") +include("plotly.jl") include("init.jl") include("users.jl") diff --git a/PlotsBase/src/abstract_backend.jl b/PlotsBase/src/backends.jl similarity index 93% rename from PlotsBase/src/abstract_backend.jl rename to PlotsBase/src/backends.jl index 021c20fce..9c312f406 100644 --- a/PlotsBase/src/abstract_backend.jl +++ b/PlotsBase/src/backends.jl @@ -1,6 +1,18 @@ -const _plots_project = Pkg.Types.read_package(normpath(@__DIR__, "..", "Project.toml")) -const _current_plots_version = _plots_project.version -const _plots_compats = _plots_project.compat +struct NoBackend <: AbstractBackend end + +backend_name(::NoBackend) = :none + +for sym in (:attr, :seriestype, :marker, :style, :scale) + f1 = Symbol("is_$(sym)_supported") + f2 = Symbol("supported_$(sym)s") + @eval begin + $f1(::NoBackend, $sym::Symbol) = true + $f2(::NoBackend) = $(getproperty(Commons, Symbol("_all_$(sym)s"))) + end +end + +_display(::Plot{NoBackend}) = + @info "No backend activated yet. Load the backend library and call the activation function to do so.\nE.g. `import GR; gr()` activates the GR backend." const _backendSymbol = Dict{DataType,Symbol}(NoBackend => :none) const _backendType = Dict{Symbol,DataType}(:none => NoBackend) @@ -8,10 +20,6 @@ const _backend_packages = (gaston = :Gaston, gr = :GR, unicodeplots = :Unico const _initialized_backends = Set{Symbol}() const _supported_backends = keys(_backend_packages) -const _plots_deps = let toml = Pkg.TOML.parsefile(normpath(@__DIR__, "..", "Project.toml")) - merge(toml["deps"], toml["extras"]) -end - function _check_installed(backend::Union{Module,AbstractString,Symbol}; warn = true) sym = Symbol(lowercase(string(backend))) if warn && !haskey(_backend_packages, sym) @@ -55,6 +63,10 @@ mutable struct CurrentBackend pkg::AbstractBackend end +@inline backend_type(sym::Symbol) = get(_backendType, sym, NoBackend) +@inline backend_instance(sym::Symbol) = backend_type(sym)() +@inline backend(type::Type{<:AbstractBackend}) = backend(type()) + CurrentBackend(sym::Symbol) = CurrentBackend(sym, backend_instance(sym)) "returns the current plotting package name. Initializes package on first call." @@ -63,12 +75,11 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, backend_instance(sym)) "returns a list of supported backends." @inline backends() = _supported_backends -@inline backend_name() = CURRENT_BACKEND.sym -@inline backend_type(sym::Symbol) = get(_backendType, sym, NoBackend) -@inline backend_instance(sym::Symbol) = backend_type(sym)() -@inline backend(type::Type{<:AbstractBackend}) = backend(type()) -backend_package_name(sym::Symbol = backend_name()) = get(_backend_packages, sym, nothing) +const CURRENT_BACKEND = CurrentBackend(:none) + +@inline backend_name() = CURRENT_BACKEND.sym +@inline backend_package_name(sym::Symbol = backend_name()) = get(_backend_packages, sym, nothing) # Traits to be implemented by the extensions backend_name(::AbstractBackend) = @info "`backend_name(::Backend) not implemented." diff --git a/PlotsBase/src/backends/nobackend.jl b/PlotsBase/src/backends/nobackend.jl deleted file mode 100644 index 741b9fc80..000000000 --- a/PlotsBase/src/backends/nobackend.jl +++ /dev/null @@ -1,15 +0,0 @@ -struct NoBackend <: AbstractBackend end - -backend_name(::NoBackend) = :none - -for sym in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_$(sym)_supported") - f2 = Symbol("supported_$(sym)s") - @eval begin - $f1(::NoBackend, $sym::Symbol) = true - $f2(::NoBackend) = $(getproperty(Commons, Symbol("_all_$(sym)s"))) - end -end - -_display(::Plot{NoBackend}) = - @info "No backend activated yet. Load the backend library and call the activation function to do so.\nE.g. `import GR; gr()` activates the GR backend." diff --git a/PlotsBase/src/backends/plotly.jl b/PlotsBase/src/plotly.jl similarity index 100% rename from PlotsBase/src/backends/plotly.jl rename to PlotsBase/src/plotly.jl diff --git a/PlotsBase/src/backends/web.jl b/PlotsBase/src/web.jl similarity index 100% rename from PlotsBase/src/backends/web.jl rename to PlotsBase/src/web.jl diff --git a/PlotsBase/test/test_backends.jl b/PlotsBase/test/test_backends.jl index 0c2d82dc6..1c1ba442e 100644 --- a/PlotsBase/test/test_backends.jl +++ b/PlotsBase/test/test_backends.jl @@ -1,3 +1,58 @@ +@testset "UnicodePlots" begin + with(:unicodeplots) do + @test backend() == PlotsBase.backend_instance(:unicodeplots) + + io = IOContext(IOBuffer(), :color => true) + + # lets just make sure it runs without error + pl = plot(rand(10)) + @test show(io, pl) isa Nothing + + pl = bar(randn(10)) + @test show(io, pl) isa Nothing + + pl = plot([1, 2], [3, 4]) + annotate!(pl, [(1.5, 3.2, PlotsBase.text("Test", :red, :center))]) + hline!(pl, [3.1]) + @test show(io, pl) isa Nothing + + pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) + hline!(pl, [3.1]) + annotate!( + pl, + [(Dates.Date(2019, 1, 15), 3.2, PlotsBase.text("Test", :red, :center))], + ) + @test show(io, pl) isa Nothing + + pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) + annotate!(pl, [(Dates.Date(2019, 1, 15), 3.2, :auto)]) + hline!(pl, [3.1]) + @test show(io, pl) isa Nothing + + pl = plot(map(plot, 1:4)..., layout = (2, 2)) + @test show(io, pl) isa Nothing + + pl = plot(map(plot, 1:3)..., layout = (2, 2)) + @test show(io, pl) isa Nothing + + pl = plot(map(plot, 1:2)..., layout = @layout([° _; _ °])) + @test show(io, pl) isa Nothing + + redirect_stdout(devnull) do + show(plot(1:2)) + end + end +end + +(is_pkgeval() || is_ci()) || @testset "PlotlyJS" begin + with(:plotlyjs) do + PlotlyJSExt = Base.get_extension(PlotsBase, :PlotlyJSExt) + @test backend() == PlotlyJSExt.PlotlyJSBackend() + pl = plot(rand(10)) + @test pl isa Plot + display(pl) + end +end is_pkgeval() || @testset "Backends" begin callback(m, pkgname, i) = begin @@ -16,3 +71,4 @@ is_pkgeval() || @testset "Backends" begin closeall() end end + diff --git a/PlotsBase/test/test_reference.jl b/PlotsBase/test/test_reference.jl index d49cfef7a..1dab47d3e 100644 --- a/PlotsBase/test/test_reference.jl +++ b/PlotsBase/test/test_reference.jl @@ -133,52 +133,6 @@ with(:pgfplotsx) do end =# -@testset "UnicodePlots" begin - with(:unicodeplots) do - @test backend() == PlotsBase.backend_instance(:unicodeplots) - - io = IOContext(IOBuffer(), :color => true) - - # lets just make sure it runs without error - pl = plot(rand(10)) - @test show(io, pl) isa Nothing - - pl = bar(randn(10)) - @test show(io, pl) isa Nothing - - pl = plot([1, 2], [3, 4]) - annotate!(pl, [(1.5, 3.2, PlotsBase.text("Test", :red, :center))]) - hline!(pl, [3.1]) - @test show(io, pl) isa Nothing - - pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) - hline!(pl, [3.1]) - annotate!( - pl, - [(Dates.Date(2019, 1, 15), 3.2, PlotsBase.text("Test", :red, :center))], - ) - @test show(io, pl) isa Nothing - - pl = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) - annotate!(pl, [(Dates.Date(2019, 1, 15), 3.2, :auto)]) - hline!(pl, [3.1]) - @test show(io, pl) isa Nothing - - pl = plot(map(plot, 1:4)..., layout = (2, 2)) - @test show(io, pl) isa Nothing - - pl = plot(map(plot, 1:3)..., layout = (2, 2)) - @test show(io, pl) isa Nothing - - pl = plot(map(plot, 1:2)..., layout = @layout([° _; _ °])) - @test show(io, pl) isa Nothing - - redirect_stdout(devnull) do - show(plot(1:2)) - end - end -end - @testset "GR - reference images" begin with(:gr) do # NOTE: use `ENV["VISUAL_REGRESSION_TESTS_AUTO"] = true;` to automatically replace reference images @@ -191,13 +145,3 @@ end ) end end - -(is_pkgeval() || is_ci()) || @testset "PlotlyJS" begin - with(:plotlyjs) do - PlotlyJSExt = Base.get_extension(PlotsBase, :PlotlyJSExt) - @test backend() == PlotlyJSExt.PlotlyJSBackend() - pl = plot(rand(10)) - @test pl isa Plot - display(pl) - end -end diff --git a/test/test_preferences.jl b/test/test_preferences.jl index 3e2acb72a..683afca1a 100644 --- a/test/test_preferences.jl +++ b/test/test_preferences.jl @@ -32,12 +32,16 @@ const DEBUG = false # this test mimics a restart, which is needed after a preferences change Plots.set_default_backend!(:unicodeplots) script = tempname() + plots_dir = escape_string(pkgdir(Plots)) write( script, """ using Pkg, Test; io = (devnull, stdout)[1] # toggle for debugging Pkg.activate(; temp = true, io) - Pkg.develop(; path = "$(escape_string(pkgdir(Plots)))", io) + Pkg.develop(; path = "$(joinpath(plots_dir, "RecipesBase"))", io) + Pkg.develop(; path = "$(joinpath(plots_dir, "RecipesPipeline"))", io) + Pkg.develop(; path = "$(joinpath(plots_dir, "PlotsBase"))", io) + Pkg.develop(; path = "$plots_dir", io) Pkg.add("UnicodePlots"; io) # checked by Plots import UnicodePlots using Plots From d29b00e78659da2b754341b70c3206362f1e571f Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 09:22:27 +0200 Subject: [PATCH 29/58] move preferences back to `PlotsBase` --- PlotsBase/Project.toml | 4 ++ PlotsBase/ext/HDF5Ext.jl | 2 +- PlotsBase/src/PlotsBase.jl | 17 ++++-- PlotsBase/src/backends.jl | 8 +-- PlotsBase/src/preferences.jl | 49 ++++++++++++++++ PlotsBase/test/runtests.jl | 2 + PlotsBase/test/test_preferences.jl | 94 ++++++++++++++++++++++++++++++ PlotsBase/test/test_reference.jl | 4 +- Project.toml | 4 -- src/Plots.jl | 77 +++++------------------- test/runtests.jl | 16 +---- test/test_preferences.jl | 85 --------------------------- 12 files changed, 183 insertions(+), 179 deletions(-) create mode 100644 PlotsBase/src/preferences.jl create mode 100644 PlotsBase/test/test_preferences.jl delete mode 100644 test/test_preferences.jl diff --git a/PlotsBase/Project.toml b/PlotsBase/Project.toml index 3fbb625ce..a6f55b7e1 100644 --- a/PlotsBase/Project.toml +++ b/PlotsBase/Project.toml @@ -19,6 +19,8 @@ NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" PlotThemes = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043" +PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +Preferences = "21216c6a-2e73-6563-6e65-726566657250" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -87,6 +89,8 @@ PlotThemes = "2, 3" PlotUtils = "1" PlotlyJS = "0.18" PlotlyKaleido = "2.2.2" +PrecompileTools = "1" +Preferences = "1" Printf = "1" PythonPlot = "1" Random = "1" diff --git a/PlotsBase/ext/HDF5Ext.jl b/PlotsBase/ext/HDF5Ext.jl index 9598b6488..cbd39498c 100644 --- a/PlotsBase/ext/HDF5Ext.jl +++ b/PlotsBase/ext/HDF5Ext.jl @@ -571,7 +571,7 @@ PlotsBase.hdf5plot_write( name::String = "_unnamed", ) = HDF5.h5open(path, "w") do file - HDF5.write_dataset(file, "VERSION_INFO", string(PlotsBase._current_plots_version)) + HDF5.write_dataset(file, "VERSION_INFO", string(PlotsBase._version)) _write(HDF5.create_group(file, h5plotpath(name)), plt) end diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index 5e33ddbfb..b12e7fe30 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -8,7 +8,7 @@ if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@max_m end using Pkg, Dates, Printf, Statistics, Base64, LinearAlgebra, SparseArrays, Random -using Reexport, RelocatableFolders +using PrecompileTools, Preferences, Reexport, RelocatableFolders using Base.Meta @reexport using RecipesBase @reexport using PlotThemes @@ -120,9 +120,9 @@ export #! format: on import NaNMath -const _plots_project = Pkg.Types.read_package(normpath(@__DIR__, "..", "Project.toml")) -const _current_plots_version = _plots_project.version -const _plots_compats = _plots_project.compat +const _project = Pkg.Types.read_package(normpath(@__DIR__, "..", "Project.toml")) +const _version = _project.version +const _compat = _project.compat include("Commons/Commons.jl") using .Commons @@ -161,7 +161,16 @@ include("shorthands.jl") include("backends.jl") include("web.jl") include("plotly.jl") +include("preferences.jl") include("init.jl") include("users.jl") +# COV_EXCL_START +@setup_workload begin + @compile_workload begin + # TODO: backend agnostic statements + end +end +# COV_EXCL_STOP + end diff --git a/PlotsBase/src/backends.jl b/PlotsBase/src/backends.jl index 9c312f406..dcbcafd53 100644 --- a/PlotsBase/src/backends.jl +++ b/PlotsBase/src/backends.jl @@ -23,15 +23,15 @@ const _supported_backends = keys(_backend_packages) function _check_installed(backend::Union{Module,AbstractString,Symbol}; warn = true) sym = Symbol(lowercase(string(backend))) if warn && !haskey(_backend_packages, sym) - @warn "backend `$sym` is not compatible with `Plots`." + @warn "backend `$sym` is not compatible with `PlotsBase`." return end # lowercase -> CamelCase, falling back to the given input for `PlotlyBase` ... str = string(get(_backend_packages, sym, backend)) - str == "Plotly" && (str *= "Base") # FIXME: `Plots` inconsistency, `plotly` should be named `plotlybase` + str == "Plotly" && (str *= "Base") # FIXME: `PlotsBase` inconsistency, `plotly` should be named `plotlybase` # check supported - if warn && !haskey(_plots_compats, str) - @warn "backend `$str` is not compatible with `Plots`." + if warn && !haskey(_compat, str) + @warn "backend `$str` is not compatible with `PlotsBase`." return end # check installed diff --git a/PlotsBase/src/preferences.jl b/PlotsBase/src/preferences.jl new file mode 100644 index 000000000..882bb3df3 --- /dev/null +++ b/PlotsBase/src/preferences.jl @@ -0,0 +1,49 @@ +# from github.com/JuliaPackaging/Preferences.jl/blob/master/README.md: +# "Preferences that are accessed during compilation are automatically marked as compile-time preferences" +# ==> this must always be done during precompilation, otherwise +# the cache will not invalidate when preferences change +const DEFAULT_BACKEND = lowercase(load_preference(PlotsBase, "default_backend", "gr")) + +function default_backend() + # environment variable preempts the `Preferences` based mechanism + sym = get(ENV, "PLOTSBASE_DEFAULT_BACKEND", DEFAULT_BACKEND) |> lowercase |> Symbol + backend(PlotsBase.backend_type(sym)) +end + +function set_default_backend!( + backend::Union{Nothing,AbstractString,Symbol} = nothing; + force = true, + kw..., +) + if backend ≡ nothing + delete_preferences!(PlotsBase, "default_backend"; force, kw...) + else + # NOTE: `_check_installed` already throws a warning + if (value = lowercase(string(backend))) |> PlotsBase._check_installed ≢ nothing + set_preferences!(PlotsBase, "default_backend" => value; force, kw...) + end + end + nothing +end + +function diagnostics(io::IO = stdout) + origin = if has_preference(PlotsBase, "default_backend") + "`Preferences`" + elseif haskey(ENV, "PLOTSBASE_DEFAULT_BACKEND") + "environment variable" + else + "fallback" + end + if (be = backend_name()) ≡ :none + @info "no `PlotsBase` backends currently initialized" + else + be_name = string(PlotsBase.backend_package_name(be)) + @info "selected `PlotsBase` backend: $be_name, from $origin" + Pkg.status( + ["PlotsBase", "RecipesBase", "RecipesPipeline", be_name]; + mode = Pkg.PKGMODE_MANIFEST, + io, + ) + end + nothing +end diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 2beec2dd8..346103c8b 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -36,6 +36,7 @@ using RecipesPipeline using FilePathsBase using LaTeXStrings using RecipesBase +using Preferences using TestImages using Unitful using FileIO @@ -80,6 +81,7 @@ for name in ( "output", "reference", "backends", + "preferences", "quality", ) @testset "$name" begin diff --git a/PlotsBase/test/test_preferences.jl b/PlotsBase/test/test_preferences.jl new file mode 100644 index 000000000..6f0ac731d --- /dev/null +++ b/PlotsBase/test/test_preferences.jl @@ -0,0 +1,94 @@ +# get `Preferences` set backend, if any +const PREVIOUS_DEFAULT_BACKEND = load_preference(PlotsBase, "default_backend") +# ----------------------------------------------------------------------------- + +PlotsBase.set_default_backend!() # start with empty preferences + +withenv("PLOTSBASE_DEFAULT_BACKEND" => "test_invalid_backend") do + @test_logs (:error, r"Unsupported backend.*") PlotsBase.default_backend() +end +@test_logs (:error, r"Unsupported backend.*") backend(:test_invalid_backend) + +@test PlotsBase.default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() + +withenv("PLOTSBASE_DEFAULT_BACKEND" => "unicodeplots") do + @test_logs (:info, r".*environment variable") PlotsBase.diagnostics(devnull) + @test PlotsBase.default_backend() == + Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() +end + +@test PlotsBase.default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() +@test PlotsBase.backend_package_name() ≡ :GR +@test PlotsBase.backend_name() ≡ :gr + +@test_logs (:info, r".*fallback") PlotsBase.diagnostics(devnull) + +@test PlotsBase.merge_with_base_supported([:annotations, :guide]) isa Set +@test PlotsBase.CurrentBackend(:gr).sym ≡ :gr + +@test_logs (:warn, r".*is not compatible with") PlotsBase.set_default_backend!( + :test_invalid_backend, +) + +const DEBUG = false +@testset "persistent backend - restart" begin + # this test mimics a restart, which is needed after a preferences change + PlotsBase.set_default_backend!(:unicodeplots) + script = tempname() + dn = pkgdir(PlotsBase) + write( + script, + """ + using Pkg, Test; io = (devnull, stdout)[1] # toggle for debugging + Pkg.activate(; temp = true, io) + Pkg.develop(; path = "$(joinpath(dn, "..", "RecipesBase"))", io) + Pkg.develop(; path = "$(joinpath(dn, "..", "RecipesPipeline"))", io) + Pkg.develop(; path = "$dn", io) + Pkg.add("UnicodePlots"; io) # checked by Plots + import UnicodePlots + using PlotsBase + unicodeplots() + res = @testset "Preferences UnicodePlots" begin + @test_logs (:info, r".*Preferences") PlotsBase.diagnostics(io) + @test backend() == Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() + end + exit(res.n_passed == 2 ? 0 : 123) + """, + ) + DEBUG && print(read(script, String)) + @test run(```$(Base.julia_cmd()) $script```) |> success +end + +is_pkgeval() || for pkg in TEST_PACKAGES + be = Symbol(lowercase(pkg)) + if is_ci() + (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs + (Sys.iswindows() && be ≡ :plotlyjs) && continue # FIXME: OutOfMemory + end + @test_logs PlotsBase.set_default_backend!(be) # test the absence of warnings + rm.(Base.find_all_in_cache_path(Base.module_keys[PlotsBase])) # make sure the compiled cache is removed + script = tempname() + write( + script, + """ + import $pkg + using Test, PlotsBase + $be() + res = @testset "Persistent backend $pkg" begin + @test PlotsBase.backend_name() ≡ :$be + end + exit(res.n_passed == 1 ? 0 : 123) + """, + ) + DEBUG && print(read(script, String)) + @test run(```$(Base.julia_cmd()) $script```) |> success # test default precompilation +end + +PlotsBase.set_default_backend!() # clear `Preferences` key + +# ----------------------------------------------------------------------------- +if PREVIOUS_DEFAULT_BACKEND ≡ nothing + delete_preferences!(PlotsBase, "default_backend") # restore the absence of a preference +else + set_default_backend!(PREVIOUS_DEFAULT_BACKEND) # reset to previous state +end diff --git a/PlotsBase/test/test_reference.jl b/PlotsBase/test/test_reference.jl index 1dab47d3e..203454434 100644 --- a/PlotsBase/test/test_reference.jl +++ b/PlotsBase/test/test_reference.jl @@ -35,7 +35,7 @@ function checkout_reference_dir(dn::AbstractString) sleep(20i) end end - if (ver = PlotsBase._current_plots_version).prerelease |> isempty + if (ver = PlotsBase._version).prerelease |> isempty try tag = LibGit2.GitObject(repo, "v$ver") hash = string(LibGit2.target(tag)) @@ -77,7 +77,7 @@ function image_comparison_tests( example = PlotsBase._examples[idx] @info "Testing plot: $pkg:$idx:$(example.header)" - ver = PlotsBase._current_plots_version + ver = PlotsBase._version ver = VersionNumber(ver.major, ver.minor, ver.patch) reffn = reference_file(pkg, ver, idx) newfn = joinpath(reference_path(pkg, ver), ref_name(idx) * ".png") diff --git a/Project.toml b/Project.toml index 8186cee2d..356ee6e00 100644 --- a/Project.toml +++ b/Project.toml @@ -5,18 +5,14 @@ version = "1.41.0" [deps] GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" PlotsBase = "c52230a3-c5da-43a3-9e85-260fcdfdc737" PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -Preferences = "21216c6a-2e73-6563-6e65-726566657250" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] GR = "0, 1" -Pkg = "1" PlotsBase = "1.41" PrecompileTools = "1" -Preferences = "1" Reexport = "1" julia = "1.9" diff --git a/src/Plots.jl b/src/Plots.jl index 87b697da9..202f17d10 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -1,94 +1,43 @@ module Plots +using PrecompileTools using Reexport @reexport using PlotsBase -using PrecompileTools -using Preferences -using Pkg - -# from github.com/JuliaPackaging/Preferences.jl/blob/master/README.md: -# "Preferences that are accessed during compilation are automatically marked as compile-time preferences" -# ==> this must always be done during precompilation, otherwise -# the cache will not invalidate when preferences change -const PLOTS_DEFAULT_BACKEND = lowercase(load_preference(Plots, "default_backend", "gr")) - -if PLOTS_DEFAULT_BACKEND == "gr" +if PlotsBase.DEFAULT_BACKEND == "gr" @debug "loading default GR" import GR end function __init__() ccall(:jl_generating_output, Cint, ()) == 1 && return - default_backend() -end - -function default_backend() - # environment variable preempts the `Preferences` based mechanism - sym = get(ENV, "PLOTS_DEFAULT_BACKEND", PLOTS_DEFAULT_BACKEND) |> lowercase |> Symbol - backend(PlotsBase.backend_type(sym)) -end + PlotsBase.default_backend() -function set_default_backend!( - backend::Union{Nothing,AbstractString,Symbol} = nothing; - force = true, - kw..., -) - if backend ≡ nothing - delete_preferences!(Plots, "default_backend"; force, kw...) - else - # NOTE: `_check_installed` already throws a warning - if (value = lowercase(string(backend))) |> PlotsBase._check_installed ≢ nothing - set_preferences!(Plots, "default_backend" => value; force, kw...) - end - end - nothing -end - -function diagnostics(io::IO = stdout) - origin = if has_preference(Plots, "default_backend") - "`Preferences`" - elseif haskey(ENV, "PLOTS_DEFAULT_BACKEND") - "environment variable" - else - "fallback" - end - if (be = backend_name()) ≡ :none - @info "no `Plots` backends currently initialized" - else - be_name = string(PlotsBase.backend_package_name(be)) - @info "selected `Plots` backend: $be_name, from $origin" - Pkg.status( - ["Plots", "PlotsBase", "RecipesBase", "RecipesPipeline", be_name]; - mode = Pkg.PKGMODE_MANIFEST, - io, - ) - end nothing end # COV_EXCL_START # FIXME: Creating a new global in closed module `Main` (`UnicodePlots`) breaks incremental compilation because the side effects will not be permanent. -if PLOTS_DEFAULT_BACKEND == "gr" +if PlotsBase.DEFAULT_BACKEND == "gr" @setup_workload begin #= - if PLOTS_DEFAULT_BACKEND == "gr" + if PlotsBase.DEFAULT_BACKEND == "gr" import GR - elseif PLOTS_DEFAULT_BACKEND == "unicodeplots" + elseif PlotsBase.DEFAULT_BACKEND == "unicodeplots" @eval Main import UnicodePlots - elseif PLOTS_DEFAULT_BACKEND == "pythonplot" + elseif PlotsBase.DEFAULT_BACKEND == "pythonplot" @eval Main import PythonPlot - elseif PLOTS_DEFAULT_BACKEND == "pgfplotsx" + elseif PlotsBase.DEFAULT_BACKEND == "pgfplotsx" @eval Main import PGFPlotsX - elseif PLOTS_DEFAULT_BACKEND == "plotlyjs" + elseif PlotsBase.DEFAULT_BACKEND == "plotlyjs" @eval Main import PlotlyJS - elseif PLOTS_DEFAULT_BACKEND == "gaston" + elseif PlotsBase.DEFAULT_BACKEND == "gaston" @eval Main import Gaston - elseif PLOTS_DEFAULT_BACKEND == "hdf5" + elseif PlotsBase.DEFAULT_BACKEND == "hdf5" @eval Main import HDF5 end =# - default_backend() + PlotsBase.default_backend() @debug PlotsBase.backend_package_name() n = length(PlotsBase._examples) imports = sizehint!(Expr[], n) @@ -131,7 +80,7 @@ if PLOTS_DEFAULT_BACKEND == "gr" end withenv("GKSwstype" => "nul", "MPLBACKEND" => "agg") do @compile_workload begin - default_backend() + PlotsBase.default_backend() eval.(imports) eval.(examples) end diff --git a/test/runtests.jl b/test/runtests.jl index 6d3264362..0789cecae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,25 +11,11 @@ for pkg in TEST_PACKAGES end gr() -using Preferences using Plots using Test -is_auto() = Plots.PlotsBase.bool_env("VISUAL_REGRESSION_TESTS_AUTO") -is_pkgeval() = Plots.PlotsBase.bool_env("JULIA_PKGEVAL") -is_ci() = Plots.PlotsBase.bool_env("CI") - -# get `Preferences` set backend, if any -const PREVIOUS_DEFAULT_BACKEND = load_preference(Plots, "default_backend") - -for name in ("preferences",) +for name in () @testset "$name" begin include("test_$name.jl") end end - -if PREVIOUS_DEFAULT_BACKEND ≡ nothing - delete_preferences!(Plots, "default_backend") # restore the absence of a preference -else - Plots.set_default_backend!(PREVIOUS_DEFAULT_BACKEND) # reset to previous state -end diff --git a/test/test_preferences.jl b/test/test_preferences.jl deleted file mode 100644 index 683afca1a..000000000 --- a/test/test_preferences.jl +++ /dev/null @@ -1,85 +0,0 @@ - -Plots.set_default_backend!() # start with empty preferences - -withenv("PLOTS_DEFAULT_BACKEND" => "test_invalid_backend") do - @test_logs (:error, r"Unsupported backend.*") Plots.default_backend() -end -@test_logs (:error, r"Unsupported backend.*") backend(:test_invalid_backend) - -@test Plots.default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() - -withenv("PLOTS_DEFAULT_BACKEND" => "unicodeplots") do - @test_logs (:info, r".*environment variable") Plots.diagnostics(devnull) - @test Plots.default_backend() == - Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() -end - -@test Plots.default_backend() == Base.get_extension(PlotsBase, :GRExt).GRBackend() -@test Plots.PlotsBase.backend_package_name() ≡ :GR -@test Plots.backend_name() ≡ :gr - -@test_logs (:info, r".*fallback") Plots.diagnostics(devnull) - -@test Plots.PlotsBase.merge_with_base_supported([:annotations, :guide]) isa Set -@test Plots.PlotsBase.CurrentBackend(:gr).sym ≡ :gr - -@test_logs (:warn, r".*is not compatible with") Plots.set_default_backend!( - :test_invalid_backend, -) - -const DEBUG = false -@testset "persistent backend - restart" begin - # this test mimics a restart, which is needed after a preferences change - Plots.set_default_backend!(:unicodeplots) - script = tempname() - plots_dir = escape_string(pkgdir(Plots)) - write( - script, - """ - using Pkg, Test; io = (devnull, stdout)[1] # toggle for debugging - Pkg.activate(; temp = true, io) - Pkg.develop(; path = "$(joinpath(plots_dir, "RecipesBase"))", io) - Pkg.develop(; path = "$(joinpath(plots_dir, "RecipesPipeline"))", io) - Pkg.develop(; path = "$(joinpath(plots_dir, "PlotsBase"))", io) - Pkg.develop(; path = "$plots_dir", io) - Pkg.add("UnicodePlots"; io) # checked by Plots - import UnicodePlots - using Plots - unicodeplots() - res = @testset "Preferences UnicodePlots" begin - @test_logs (:info, r".*Preferences") Plots.diagnostics(io) - @test backend() == Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() - end - exit(res.n_passed == 2 ? 0 : 123) - """, - ) - DEBUG && print(read(script, String)) - @test run(```$(Base.julia_cmd()) $script```) |> success -end - -is_pkgeval() || for pkg in TEST_PACKAGES - be = Symbol(lowercase(pkg)) - if is_ci() - (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs - (Sys.iswindows() && be ≡ :plotlyjs) && continue # FIXME: OutOfMemory - end - @test_logs Plots.set_default_backend!(be) # test the absence of warnings - rm.(Base.find_all_in_cache_path(Base.module_keys[Plots])) # make sure the compiled cache is removed - script = tempname() - write( - script, - """ - import $pkg - using Test, Plots - $be() - res = @testset "Persistent backend $pkg" begin - @test Plots.backend_name() ≡ :$be - end - exit(res.n_passed == 1 ? 0 : 123) - """, - ) - DEBUG && print(read(script, String)) - @test run(```$(Base.julia_cmd()) $script```) |> success # test default precompilation -end - -Plots.set_default_backend!() # clear `Preferences` key From 1bd21ace66953646086dd6a81c61c0ca7aac8187 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 09:25:51 +0200 Subject: [PATCH 30/58] simplifications --- PlotsBase/test/runtests.jl | 6 ++++-- test/runtests.jl | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 346103c8b..925f8f441 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -17,8 +17,10 @@ get!(ENV, "MPLBACKEND", "agg") # initialize all backends for pkg in TEST_PACKAGES - @eval import $(Symbol(pkg)) # trigger extension - getproperty(PlotsBase, Symbol(lowercase(pkg)))() + @eval begin + import $(Symbol(pkg)) # trigger extension + $(Symbol(lowercase(pkg)))() + end end import Unitful: m, s, cm, DimensionError diff --git a/test/runtests.jl b/test/runtests.jl index 0789cecae..d988093ec 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,8 +6,10 @@ using PlotsBase # initialize all backends for pkg in TEST_PACKAGES - @eval import $(Symbol(pkg)) # trigger extension - getproperty(PlotsBase, Symbol(lowercase(pkg)))() + @eval begin + import $(Symbol(pkg)) # trigger extension + $(Symbol(lowercase(pkg)))() + end end gr() From 86200b21a2b100d3c5e9528b398589e259e4a77c Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 09:28:28 +0200 Subject: [PATCH 31/58] format --- PlotsBase/src/backends.jl | 4 ++-- PlotsBase/test/test_backends.jl | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/PlotsBase/src/backends.jl b/PlotsBase/src/backends.jl index dcbcafd53..61d0e217f 100644 --- a/PlotsBase/src/backends.jl +++ b/PlotsBase/src/backends.jl @@ -75,11 +75,11 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, backend_instance(sym)) "returns a list of supported backends." @inline backends() = _supported_backends - const CURRENT_BACKEND = CurrentBackend(:none) @inline backend_name() = CURRENT_BACKEND.sym -@inline backend_package_name(sym::Symbol = backend_name()) = get(_backend_packages, sym, nothing) +@inline backend_package_name(sym::Symbol = backend_name()) = + get(_backend_packages, sym, nothing) # Traits to be implemented by the extensions backend_name(::AbstractBackend) = @info "`backend_name(::Backend) not implemented." diff --git a/PlotsBase/test/test_backends.jl b/PlotsBase/test/test_backends.jl index 1c1ba442e..fc5a4f157 100644 --- a/PlotsBase/test/test_backends.jl +++ b/PlotsBase/test/test_backends.jl @@ -71,4 +71,3 @@ is_pkgeval() || @testset "Backends" begin closeall() end end - From ed7b13f3b044049a141ded9afb3d38bd06415b50 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 09:29:54 +0200 Subject: [PATCH 32/58] change versions --- PlotsBase/Project.toml | 2 +- Project.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PlotsBase/Project.toml b/PlotsBase/Project.toml index a6f55b7e1..f2df17c9a 100644 --- a/PlotsBase/Project.toml +++ b/PlotsBase/Project.toml @@ -1,6 +1,6 @@ name = "PlotsBase" uuid = "c52230a3-c5da-43a3-9e85-260fcdfdc737" -version = "1.41.0" +version = "0.1" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/Project.toml b/Project.toml index 356ee6e00..cb4539f3b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Plots" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" author = ["Tom Breloff (@tbreloff)"] -version = "1.41.0" +version = "2.0.0" [deps] GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" @@ -11,7 +11,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] GR = "0, 1" -PlotsBase = "1.41" +PlotsBase = "0.1" PrecompileTools = "1" Reexport = "1" julia = "1.9" From 27b9d37bb805753f386fd877151e7ab840b8409f Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:07:55 +0200 Subject: [PATCH 33/58] rework ci --- .github/workflows/ci.yml | 9 +--- PlotsBase/src/Subplots.jl | 4 +- PlotsBase/test/test_preferences.jl | 6 +-- ci/downstream.jl | 67 ++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 ci/downstream.jl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c4d13b88..34ccebc77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,14 +108,7 @@ jobs: - name: Run downstream tests if: startsWith(matrix.os, 'ubuntu') - shell: xvfb-run julia --color=yes {0} - run: | - using Pkg - foreach(("StatsPlots", "GraphRecipes")) do name - Pkg.activate(tempdir()) - foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) - Pkg.add(name); Pkg.test(name; coverage=true) - end + run: xvfb-run julia --color=yes ci/downstream.jl - uses: julia-actions/julia-processcoverage@latest if: startsWith(matrix.os, 'ubuntu') diff --git a/PlotsBase/src/Subplots.jl b/PlotsBase/src/Subplots.jl index 60df3a372..c41b4c1b2 100644 --- a/PlotsBase/src/Subplots.jl +++ b/PlotsBase/src/Subplots.jl @@ -12,8 +12,8 @@ import PlotsBase import ..Commons: BoundingBox, convert_legend_value, like_surface import ..RecipesPipeline: RecipesPipeline, Surface, Volume, DefaultsDict import ..RecipesBase: AbstractLayout, AbstractBackend -import ..PlotUtils: get_color_palette import ..DataSeries: Series +import ..PlotUtils using ..Commons.Frontend using ..Commons @@ -155,7 +155,7 @@ end function _update_subplot_colors(sp::Subplot) # background colors color_or_nothing!(sp.attr, :background_color_subplot) - sp.attr[:color_palette] = get_color_palette(sp.attr[:color_palette], 30) + sp.attr[:color_palette] = PlotUtils.get_color_palette(sp.attr[:color_palette], 30) color_or_nothing!(sp.attr, :legend_background_color) color_or_nothing!(sp.attr, :background_color_inside) diff --git a/PlotsBase/test/test_preferences.jl b/PlotsBase/test/test_preferences.jl index 6f0ac731d..8022b926e 100644 --- a/PlotsBase/test/test_preferences.jl +++ b/PlotsBase/test/test_preferences.jl @@ -35,14 +35,14 @@ const DEBUG = false # this test mimics a restart, which is needed after a preferences change PlotsBase.set_default_backend!(:unicodeplots) script = tempname() - dn = pkgdir(PlotsBase) + dn = pkgdir(PlotsBase) |> escape_string write( script, """ using Pkg, Test; io = (devnull, stdout)[1] # toggle for debugging Pkg.activate(; temp = true, io) - Pkg.develop(; path = "$(joinpath(dn, "..", "RecipesBase"))", io) - Pkg.develop(; path = "$(joinpath(dn, "..", "RecipesPipeline"))", io) + Pkg.develop(; path = joinpath("$dn", "..", "RecipesBase"), io) + Pkg.develop(; path = joinpath("$dn", "..", "RecipesPipeline"), io) Pkg.develop(; path = "$dn", io) Pkg.add("UnicodePlots"; io) # checked by Plots import UnicodePlots diff --git a/ci/downstream.jl b/ci/downstream.jl new file mode 100644 index 000000000..1e06a5403 --- /dev/null +++ b/ci/downstream.jl @@ -0,0 +1,67 @@ +using Pkg, Plots, Test + +LibGit2 = Pkg.GitTools.LibGit2 +TOML = Pkg.TOML + +failsafe_clone_checkout(path, url) = begin + local repo + for i ∈ 1:6 + try + repo = Pkg.GitTools.ensure_clone(stdout, path, url) + break + catch err + @warn err + sleep(20i) + end + end + + @assert isfile(joinpath(path, "Project.toml")) "spurious network error: clone failed, bailing out" + + name, _ = splitext(basename(url)) + registries = joinpath(first(DEPOT_PATH), "registries") + general = joinpath(registries, "General") + versions = joinpath(general, name[1:1], name, "Versions.toml") + if !isfile(versions) + mkpath(general) + run(setenv(`tar xf $general.tar.gz`; dir = general)) + end + @assert isfile(versions) + + stable = maximum(VersionNumber.(keys(TOML.parse(read(versions, String))))) + tag = LibGit2.GitObject(repo, "v$stable") + hash = string(LibGit2.target(tag)) + LibGit2.checkout!(repo, hash) + nothing +end + +fake_supported_version!(path) = begin + toml = joinpath(path, "Project.toml") + # fake the supported Plots version for testing (for `Pkg.develop`) + Plots_version = Pkg.Types.read_package(normpath(@__DIR__, ".." "Project.toml")).version + parsed_toml = TOML.parse(read(toml, String)) + parsed_toml["compat"]["Plots"] = string(Plots_version) + open(toml, "w") do io + TOML.print(io, parsed_toml) + end + nothing +end + +test_stable(pkg::String) = begin + Pkg.activate(tempdir()) + tmpd = mktempdir() + + for dn ∈ ("RecipesBase", "RecipesPipeline", "PlotsBase", "") + Pkg.develop(; path = joinpath(@__DIR__, "..", dn)) + end + + pkg_dir = joinpath(tmpd, "$pkg.jl") + failsafe_clone_checkout(pkg_dir, "https://github.com/JuliaPlots/$pkg.jl") + fake_supported_version!(pkg_dir) + + Pkg.develop(; path = pkg_dir) + Pkg.test(pkg; coverage = true) + nothing +end + +test_stable("StatsPlots") +test_stable("GraphRecipes") From 8713d25b420acafbb9411bc915238f98c61ee06e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:08:52 +0200 Subject: [PATCH 34/58] format --- ci/downstream.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 1e06a5403..9ffb057ae 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -5,7 +5,7 @@ TOML = Pkg.TOML failsafe_clone_checkout(path, url) = begin local repo - for i ∈ 1:6 + for i in 1:6 try repo = Pkg.GitTools.ensure_clone(stdout, path, url) break @@ -37,7 +37,8 @@ end fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") # fake the supported Plots version for testing (for `Pkg.develop`) - Plots_version = Pkg.Types.read_package(normpath(@__DIR__, ".." "Project.toml")).version + Plots_version = + Pkg.Types.read_package(normpath(@__DIR__, "..""Project.toml")).version parsed_toml = TOML.parse(read(toml, String)) parsed_toml["compat"]["Plots"] = string(Plots_version) open(toml, "w") do io @@ -50,7 +51,7 @@ test_stable(pkg::String) = begin Pkg.activate(tempdir()) tmpd = mktempdir() - for dn ∈ ("RecipesBase", "RecipesPipeline", "PlotsBase", "") + for dn in ("RecipesBase", "RecipesPipeline", "PlotsBase", "") Pkg.develop(; path = joinpath(@__DIR__, "..", dn)) end From 4419a7cf2c9cec3751d25b5644481a477ee327fe Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:10:34 +0200 Subject: [PATCH 35/58] test downstream --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34ccebc77..05fbaa9de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: echo ${cmd[@]} ${cmd[@]} -e ' using Pkg - foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) + # foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) ' - name: Run downstream tests From 628da0777924ce0aeebc67fcc746f9b97372a481 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:11:32 +0200 Subject: [PATCH 36/58] update ci --- .github/workflows/ci.yml | 2 +- ci/downstream.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05fbaa9de..073928a78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,7 +113,7 @@ jobs: - uses: julia-actions/julia-processcoverage@latest if: startsWith(matrix.os, 'ubuntu') with: - directories: RecipesBase/src,RecipesPipeline/src,src + directories: RecipesBase/src,RecipesPipeline/src,PlotsBase/src,src - uses: codecov/codecov-action@v4 if: startsWith(matrix.os, 'ubuntu') with: diff --git a/ci/downstream.jl b/ci/downstream.jl index 9ffb057ae..7c2ddac1e 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -60,7 +60,7 @@ test_stable(pkg::String) = begin fake_supported_version!(pkg_dir) Pkg.develop(; path = pkg_dir) - Pkg.test(pkg; coverage = true) + Pkg.test(pkg) nothing end From 72a3e7c124aca9907b963cbf4471993efe917029 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:14:14 +0200 Subject: [PATCH 37/58] move ci scripts --- .github/workflows/ci.yml | 23 +---------------------- ci/matplotlib.jl | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 ci/matplotlib.jl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 073928a78..639c4fc3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,28 +70,7 @@ jobs: # - uses: julia-actions/julia-buildpkg@latest - name: Install conda based matplotlib - shell: julia --color=yes {0} - run: | - using Pkg; Pkg.add("CondaPkg") - using CondaPkg; CondaPkg.resolve() - libgcc = if Sys.islinux() - # see discourse.julialang.org/t/glibcxx-version-not-found/82209/8 - # julia 1.8.3 is built with libstdc++.so.6.0.29, so we must restrict to this version (gcc 11.3.0, not gcc 12.2.0) - # see gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html - specs = Dict( - v"3.4.29" => ">=11.1,<12.1", - v"3.4.30" => ">=12.1,<13.1", - v"3.4.31" => ">=13.1,<14.1", - v"3.4.32" => ">=14.1,<15.1", - v"3.4.33" => ">=15.1,<16.1", - # ... keep this up-to-date with gcc 16 - )[Base.BinaryPlatforms.detect_libstdcxx_version()] - ("libgcc-ng$specs", "libstdcxx-ng$specs") - else - () - end - CondaPkg.PkgREPL.add([libgcc..., "matplotlib"]) - CondaPkg.status() + run: julia --color=yes ci/matpllotlib.jl - name: Run RecipesBase, RecipesPipeline, PlotsBase, Plots tests timeout-minutes: 60 diff --git a/ci/matplotlib.jl b/ci/matplotlib.jl new file mode 100644 index 000000000..1e62ab74d --- /dev/null +++ b/ci/matplotlib.jl @@ -0,0 +1,25 @@ +using Pkg +Pkg.add("CondaPkg") + +using CondaPkg +CondaPkg.resolve() + +libgcc = if Sys.islinux() + # see discourse.julialang.org/t/glibcxx-version-not-found/82209/8 + # julia 1.8.3 is built with libstdc++.so.6.0.29, so we must restrict to this version (gcc 11.3.0, not gcc 12.2.0) + # see gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html + specs = Dict( + v"3.4.29" => ">=11.1,<12.1", + v"3.4.30" => ">=12.1,<13.1", + v"3.4.31" => ">=13.1,<14.1", + v"3.4.32" => ">=14.1,<15.1", + v"3.4.33" => ">=15.1,<16.1", + # ... keep this up-to-date with gcc 16 + )[Base.BinaryPlatforms.detect_libstdcxx_version()] + ("libgcc-ng$specs", "libstdcxx-ng$specs") +else + () +end + +CondaPkg.PkgREPL.add([libgcc..., "matplotlib"]) +CondaPkg.status() From 3c8517356a3f2258b96cc653006ec68bf1a84f01 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:18:51 +0200 Subject: [PATCH 38/58] fix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 639c4fc3c..16e324303 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: # - uses: julia-actions/julia-buildpkg@latest - name: Install conda based matplotlib - run: julia --color=yes ci/matpllotlib.jl + run: julia --color=yes ci/matplotlib.jl - name: Run RecipesBase, RecipesPipeline, PlotsBase, Plots tests timeout-minutes: 60 From 3dfd80000f87c1e199c6db1fd9efad85fee1da54 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:23:24 +0200 Subject: [PATCH 39/58] fix --- ci/downstream.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 7c2ddac1e..d3dd02799 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -38,7 +38,7 @@ fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") # fake the supported Plots version for testing (for `Pkg.develop`) Plots_version = - Pkg.Types.read_package(normpath(@__DIR__, "..""Project.toml")).version + Pkg.Types.read_package(normpath(@__DIR__, "..", "Project.toml")).version parsed_toml = TOML.parse(read(toml, String)) parsed_toml["compat"]["Plots"] = string(Plots_version) open(toml, "w") do io From fa1e32289c678b7622689c44544cd27e7b161c8f Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:28:43 +0200 Subject: [PATCH 40/58] update --- .github/workflows/ci.yml | 3 ++- ci/downstream.jl | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16e324303..554b7ecac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,13 +66,14 @@ jobs: run: | using Pkg foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) + Pkg.precompile() # - uses: julia-actions/julia-buildpkg@latest - name: Install conda based matplotlib run: julia --color=yes ci/matplotlib.jl - - name: Run RecipesBase, RecipesPipeline, PlotsBase, Plots tests + - name: Test RecipesBase, RecipesPipeline, PlotsBase, Plots timeout-minutes: 60 run: | cmd=(julia --color=yes) diff --git a/ci/downstream.jl b/ci/downstream.jl index d3dd02799..1a4c6c9a2 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -27,7 +27,8 @@ failsafe_clone_checkout(path, url) = begin end @assert isfile(versions) - stable = maximum(VersionNumber.(keys(TOML.parse(read(versions, String))))) + version_dict = TOML.parse(read(versions, String)) + stable = VersionNumber.(keys(version_dict)) |> maximum tag = LibGit2.GitObject(repo, "v$stable") hash = string(LibGit2.target(tag)) LibGit2.checkout!(repo, hash) @@ -44,6 +45,7 @@ fake_supported_version!(path) = begin open(toml, "w") do io TOML.print(io, parsed_toml) end + print(read(toml, String)) nothing end From 80c0d974bf35886dd7cc723fab660d3a33dc09ac Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:31:41 +0200 Subject: [PATCH 41/58] debug ci --- ci/downstream.jl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 1a4c6c9a2..9cafeeee0 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -50,19 +50,19 @@ fake_supported_version!(path) = begin end test_stable(pkg::String) = begin - Pkg.activate(tempdir()) - tmpd = mktempdir() - - for dn in ("RecipesBase", "RecipesPipeline", "PlotsBase", "") - Pkg.develop(; path = joinpath(@__DIR__, "..", dn)) - end + Pkg.activate(; temp = true) + mktempdir() do + for dn in ("RecipesBase", "RecipesPipeline", "PlotsBase", "") + Pkg.develop(; path = joinpath(@__DIR__, "..", dn)) + end - pkg_dir = joinpath(tmpd, "$pkg.jl") - failsafe_clone_checkout(pkg_dir, "https://github.com/JuliaPlots/$pkg.jl") - fake_supported_version!(pkg_dir) + pkg_dir = joinpath(tmpd, "$pkg.jl") + failsafe_clone_checkout(pkg_dir, "https://github.com/JuliaPlots/$pkg.jl") + fake_supported_version!(pkg_dir) - Pkg.develop(; path = pkg_dir) - Pkg.test(pkg) + Pkg.develop(; path = pkg_dir) + Pkg.test(pkg) + end nothing end From dc8cbe259c634b9095abba67f8f9e78a1e10dca4 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:33:55 +0200 Subject: [PATCH 42/58] fix --- ci/downstream.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 9cafeeee0..ad444cbba 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -1,7 +1,7 @@ using Pkg, Plots, Test -LibGit2 = Pkg.GitTools.LibGit2 -TOML = Pkg.TOML +const LibGit2 = Pkg.GitTools.LibGit2 +const TOML = Pkg.TOML failsafe_clone_checkout(path, url) = begin local repo @@ -51,7 +51,7 @@ end test_stable(pkg::String) = begin Pkg.activate(; temp = true) - mktempdir() do + mktempdir() do tmpd for dn in ("RecipesBase", "RecipesPipeline", "PlotsBase", "") Pkg.develop(; path = joinpath(@__DIR__, "..", dn)) end From de8233106cc66e7cde70dc3b24b51e0a5156c477 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:41:52 +0200 Subject: [PATCH 43/58] update versions --- .github/workflows/ci.yml | 6 +++--- ci/downstream.jl | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 554b7ecac..0b90047c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get -y update - sudo apt-get -y install gnuplot poppler-utils texlive-{latex-base,latex-extra,luatex} g++ + sudo apt-get -y install g++ gnuplot poppler-utils texlive-{latex-base,latex-extra,luatex} sudo fc-cache -vr - name: Set LD_PRELOAD @@ -65,8 +65,8 @@ jobs: shell: julia --color=yes {0} run: | using Pkg - foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) - Pkg.precompile() + # foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) + # Pkg.precompile() # - uses: julia-actions/julia-buildpkg@latest diff --git a/ci/downstream.jl b/ci/downstream.jl index ad444cbba..3ac8c8063 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -38,9 +38,18 @@ end fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") # fake the supported Plots version for testing (for `Pkg.develop`) + RecipesBase_version = + Pkg.Types.read_package(normpath(@__DIR__, "..", "RecipesBase", "Project.toml")).version + RecipesPipeline_version = + Pkg.Types.read_package(normpath(@__DIR__, "..", "RecipesPipeline", "Project.toml")).version + PlotsBase_version = + Pkg.Types.read_package(normpath(@__DIR__, "..", "PlotsBase", "Project.toml")).version Plots_version = Pkg.Types.read_package(normpath(@__DIR__, "..", "Project.toml")).version parsed_toml = TOML.parse(read(toml, String)) + parsed_toml["compat"]["RecipesBase"] = string(RecipesBase_version) + parsed_toml["compat"]["RecipesPipeline"] = string(RecipesPipeline_version) + parsed_toml["compat"]["PlotsBase"] = string(PlotsBase_version) parsed_toml["compat"]["Plots"] = string(Plots_version) open(toml, "w") do io TOML.print(io, parsed_toml) From 11698564b10d6793d810281e25d5c08a0c7f48ee Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:46:47 +0200 Subject: [PATCH 44/58] update --- ci/downstream.jl | 23 +++++++++-------------- ci/matplotlib.jl | 26 +++++++++++++------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 3ac8c8063..9f88c9c05 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -1,4 +1,4 @@ -using Pkg, Plots, Test +using Pkg const LibGit2 = Pkg.GitTools.LibGit2 const TOML = Pkg.TOML @@ -35,22 +35,17 @@ failsafe_clone_checkout(path, url) = begin nothing end +pkg_version(name) = + string(Pkg.Types.read_package(normpath(@__DIR__, "..", name, "Project.toml")).version) + fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") - # fake the supported Plots version for testing (for `Pkg.develop`) - RecipesBase_version = - Pkg.Types.read_package(normpath(@__DIR__, "..", "RecipesBase", "Project.toml")).version - RecipesPipeline_version = - Pkg.Types.read_package(normpath(@__DIR__, "..", "RecipesPipeline", "Project.toml")).version - PlotsBase_version = - Pkg.Types.read_package(normpath(@__DIR__, "..", "PlotsBase", "Project.toml")).version - Plots_version = - Pkg.Types.read_package(normpath(@__DIR__, "..", "Project.toml")).version + # fake supported versions for testing (for `Pkg.develop`) parsed_toml = TOML.parse(read(toml, String)) - parsed_toml["compat"]["RecipesBase"] = string(RecipesBase_version) - parsed_toml["compat"]["RecipesPipeline"] = string(RecipesPipeline_version) - parsed_toml["compat"]["PlotsBase"] = string(PlotsBase_version) - parsed_toml["compat"]["Plots"] = string(Plots_version) + parsed_toml["compat"]["RecipesBase"] = pkg_version("RecipesBase") + parsed_toml["compat"]["RecipesPipeline"] = pkg_version("RecipesPipeline") + parsed_toml["compat"]["PlotsBase"] = pkg_version("PlotsBase") + parsed_toml["compat"]["Plots"] = pkg_version("Plots") open(toml, "w") do io TOML.print(io, parsed_toml) end diff --git a/ci/matplotlib.jl b/ci/matplotlib.jl index 1e62ab74d..4c657a3b0 100644 --- a/ci/matplotlib.jl +++ b/ci/matplotlib.jl @@ -5,20 +5,20 @@ using CondaPkg CondaPkg.resolve() libgcc = if Sys.islinux() - # see discourse.julialang.org/t/glibcxx-version-not-found/82209/8 - # julia 1.8.3 is built with libstdc++.so.6.0.29, so we must restrict to this version (gcc 11.3.0, not gcc 12.2.0) - # see gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html - specs = Dict( - v"3.4.29" => ">=11.1,<12.1", - v"3.4.30" => ">=12.1,<13.1", - v"3.4.31" => ">=13.1,<14.1", - v"3.4.32" => ">=14.1,<15.1", - v"3.4.33" => ">=15.1,<16.1", - # ... keep this up-to-date with gcc 16 - )[Base.BinaryPlatforms.detect_libstdcxx_version()] - ("libgcc-ng$specs", "libstdcxx-ng$specs") + # see discourse.julialang.org/t/glibcxx-version-not-found/82209/8 + # julia 1.8.3 is built with libstdc++.so.6.0.29, so we must restrict to this version (gcc 11.3.0, not gcc 12.2.0) + # see gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html + specs = Dict( + v"3.4.29" => ">=11.1,<12.1", + v"3.4.30" => ">=12.1,<13.1", + v"3.4.31" => ">=13.1,<14.1", + v"3.4.32" => ">=14.1,<15.1", + v"3.4.33" => ">=15.1,<16.1", + # ... keep this up-to-date with gcc 16 + )[Base.BinaryPlatforms.detect_libstdcxx_version()] + ("libgcc-ng$specs", "libstdcxx-ng$specs") else - () + () end CondaPkg.PkgREPL.add([libgcc..., "matplotlib"]) From e4dc889c901117ef4aa14f66692238312f8d8ac9 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:51:14 +0200 Subject: [PATCH 45/58] update --- .github/workflows/ci.yml | 48 +++++++++++++++++++--------------------- ci/downstream.jl | 2 +- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b90047c5..7e8eba069 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,8 @@ jobs: fail-fast: false matrix: version: - - '1.9' # minimal declared julia compat in `Project.toml` - - '1' # latest stable + - '1' # latest stable + - '1.9' # minimal declared julia compat in `Project.toml` experimental: - false os: [ubuntu-latest, windows-latest, macos-latest] @@ -61,32 +61,30 @@ jobs: version: ${{ matrix.version }} - uses: julia-actions/cache@v1 - - name: Develop RecipesBase, RecipesPipeline, PlotsBase, Plots - shell: julia --color=yes {0} - run: | - using Pkg - # foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) - # Pkg.precompile() - - # - uses: julia-actions/julia-buildpkg@latest + # - name: Develop RecipesBase, RecipesPipeline, PlotsBase, Plots + # shell: julia --color=yes {0} + # run: | + # using Pkg + # foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) + # Pkg.precompile() - - name: Install conda based matplotlib - run: julia --color=yes ci/matplotlib.jl + # - name: Install conda based matplotlib + # run: julia --color=yes ci/matplotlib.jl - - name: Test RecipesBase, RecipesPipeline, PlotsBase, Plots - timeout-minutes: 60 - run: | - cmd=(julia --color=yes) - if [ "$RUNNER_OS" == "Linux" ]; then - cmd=(xvfb-run ${cmd[@]}) - fi - echo ${cmd[@]} - ${cmd[@]} -e ' - using Pkg - # foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) - ' + # - name: Test RecipesBase, RecipesPipeline, PlotsBase, Plots + # timeout-minutes: 60 + # run: | + # cmd=(julia --color=yes) + # if [ "$RUNNER_OS" == "Linux" ]; then + # cmd=(xvfb-run ${cmd[@]}) + # fi + # echo ${cmd[@]} + # ${cmd[@]} -e ' + # using Pkg + # foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) + # ' - - name: Run downstream tests + - name: Test downstream packages if: startsWith(matrix.os, 'ubuntu') run: xvfb-run julia --color=yes ci/downstream.jl diff --git a/ci/downstream.jl b/ci/downstream.jl index 9f88c9c05..978a67e2b 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -45,7 +45,7 @@ fake_supported_version!(path) = begin parsed_toml["compat"]["RecipesBase"] = pkg_version("RecipesBase") parsed_toml["compat"]["RecipesPipeline"] = pkg_version("RecipesPipeline") parsed_toml["compat"]["PlotsBase"] = pkg_version("PlotsBase") - parsed_toml["compat"]["Plots"] = pkg_version("Plots") + parsed_toml["compat"]["Plots"] = pkg_version("") open(toml, "w") do io TOML.print(io, parsed_toml) end From f4c8eb1942a5e91f57bd8d8058996f0316856efc Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 10:59:48 +0200 Subject: [PATCH 46/58] update --- ci/downstream.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 978a67e2b..7be5bd3e1 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -41,11 +41,11 @@ pkg_version(name) = fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") # fake supported versions for testing (for `Pkg.develop`) - parsed_toml = TOML.parse(read(toml, String)) - parsed_toml["compat"]["RecipesBase"] = pkg_version("RecipesBase") - parsed_toml["compat"]["RecipesPipeline"] = pkg_version("RecipesPipeline") - parsed_toml["compat"]["PlotsBase"] = pkg_version("PlotsBase") - parsed_toml["compat"]["Plots"] = pkg_version("") + compat = TOML.parse(read(toml, String))["compat"] + haskey(compat, "RecipesBase") && (compat["RecipesBase"] = pkg_version("RecipesBase")) + haskey(compat, "RecipesPipeline") && (compat["RecipesPipeline"] = pkg_version("RecipesPipeline")) + haskey(compat, "PlotsBase") && (compat["PlotsBase"] = pkg_version("PlotsBase")) + haskey(compat, "Plots") && (compat["Plots"] = pkg_version("")) open(toml, "w") do io TOML.print(io, parsed_toml) end From 53099d2e73aeb17a358a997eb726f41e39f8056d Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 11:02:33 +0200 Subject: [PATCH 47/58] update --- ci/downstream.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 7be5bd3e1..d5e8e12dc 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -41,7 +41,8 @@ pkg_version(name) = fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") # fake supported versions for testing (for `Pkg.develop`) - compat = TOML.parse(read(toml, String))["compat"] + parsed_toml = OML.parse(read(toml, String)) + compat = parsed_toml["compat"] haskey(compat, "RecipesBase") && (compat["RecipesBase"] = pkg_version("RecipesBase")) haskey(compat, "RecipesPipeline") && (compat["RecipesPipeline"] = pkg_version("RecipesPipeline")) haskey(compat, "PlotsBase") && (compat["PlotsBase"] = pkg_version("PlotsBase")) From 3928a865af9d2ace73db00563c8d5ef2bed97f01 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 11:06:05 +0200 Subject: [PATCH 48/58] update --- ci/downstream.jl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index d5e8e12dc..62a09ded1 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -38,15 +38,18 @@ end pkg_version(name) = string(Pkg.Types.read_package(normpath(@__DIR__, "..", name, "Project.toml")).version) +maybe_pin_version!(dict::AbstractDict, name::AbstractString, ver::AbstractString) = + haskey(dict, name) && (compat[name] = ver) + fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") # fake supported versions for testing (for `Pkg.develop`) - parsed_toml = OML.parse(read(toml, String)) + parsed_toml = TOML.parse(read(toml, String)) compat = parsed_toml["compat"] - haskey(compat, "RecipesBase") && (compat["RecipesBase"] = pkg_version("RecipesBase")) - haskey(compat, "RecipesPipeline") && (compat["RecipesPipeline"] = pkg_version("RecipesPipeline")) - haskey(compat, "PlotsBase") && (compat["PlotsBase"] = pkg_version("PlotsBase")) - haskey(compat, "Plots") && (compat["Plots"] = pkg_version("")) + maybe_pin_version!(compat, "RecipesBase", pkg_version("RecipesBase")) + maybe_pin_version!(compat, "RecipesPipeline", pkg_version("RecipesPipeline")) + maybe_pin_version!(compat, "PlotsBase", pkg_version("PlotsBase")) + maybe_pin_version!(compat, "Plots", pkg_version("")) open(toml, "w") do io TOML.print(io, parsed_toml) end @@ -54,7 +57,7 @@ fake_supported_version!(path) = begin nothing end -test_stable(pkg::String) = begin +test_stable(pkg::AbstractString) = begin Pkg.activate(; temp = true) mktempdir() do tmpd for dn in ("RecipesBase", "RecipesPipeline", "PlotsBase", "") From 061454f4817bab911a3f443a63a78bd063b3fd76 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 11:06:46 +0200 Subject: [PATCH 49/58] update --- ci/downstream.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 62a09ded1..50acb7f99 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -36,7 +36,7 @@ failsafe_clone_checkout(path, url) = begin end pkg_version(name) = - string(Pkg.Types.read_package(normpath(@__DIR__, "..", name, "Project.toml")).version) + Pkg.Types.read_package(normpath(@__DIR__, "..", name, "Project.toml")).version |> string maybe_pin_version!(dict::AbstractDict, name::AbstractString, ver::AbstractString) = haskey(dict, name) && (compat[name] = ver) From a848ec9dc409545d60c96ba8d68545d9a8ffb43e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 11:08:09 +0200 Subject: [PATCH 50/58] update --- ci/downstream.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/downstream.jl b/ci/downstream.jl index 50acb7f99..1631d84c2 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -39,7 +39,7 @@ pkg_version(name) = Pkg.Types.read_package(normpath(@__DIR__, "..", name, "Project.toml")).version |> string maybe_pin_version!(dict::AbstractDict, name::AbstractString, ver::AbstractString) = - haskey(dict, name) && (compat[name] = ver) + haskey(dict, name) && (dict[name] = ver) fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") From 18c36d3275386b31cbd820d8e603e77278eddf71 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 11:14:23 +0200 Subject: [PATCH 51/58] update --- .github/workflows/ci.yml | 13 +++++++------ ci/downstream.jl | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e8eba069..3e12d62f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,12 +45,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Ubuntu LaTeX dependencies - if: startsWith(matrix.os, 'ubuntu') - run: | - sudo apt-get -y update - sudo apt-get -y install g++ gnuplot poppler-utils texlive-{latex-base,latex-extra,luatex} - sudo fc-cache -vr + # - name: Ubuntu LaTeX dependencies + # if: startsWith(matrix.os, 'ubuntu') + # run: | + # sudo apt-get -y update + # sudo apt-get -y install g++ gnuplot poppler-utils texlive-{latex-base,latex-extra,luatex} + # sudo fc-cache -vr - name: Set LD_PRELOAD if: startsWith(matrix.os, 'ubuntu') @@ -59,6 +59,7 @@ jobs: - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.version }} + - uses: julia-actions/cache@v1 # - name: Develop RecipesBase, RecipesPipeline, PlotsBase, Plots diff --git a/ci/downstream.jl b/ci/downstream.jl index 1631d84c2..780d557c7 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -39,7 +39,7 @@ pkg_version(name) = Pkg.Types.read_package(normpath(@__DIR__, "..", name, "Project.toml")).version |> string maybe_pin_version!(dict::AbstractDict, name::AbstractString, ver::AbstractString) = - haskey(dict, name) && (dict[name] = ver) + haskey(dict, name) && (dict[name] = "=$ver") fake_supported_version!(path) = begin toml = joinpath(path, "Project.toml") @@ -74,5 +74,5 @@ test_stable(pkg::AbstractString) = begin nothing end -test_stable("StatsPlots") test_stable("GraphRecipes") +test_stable("StatsPlots") From 32ad43a398612a8d600d5282bad84f10006e2a20 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 11:20:49 +0200 Subject: [PATCH 52/58] restore CI --- .github/workflows/ci.yml | 53 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e12d62f6..4fbcef493 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,12 +45,12 @@ jobs: steps: - uses: actions/checkout@v4 - # - name: Ubuntu LaTeX dependencies - # if: startsWith(matrix.os, 'ubuntu') - # run: | - # sudo apt-get -y update - # sudo apt-get -y install g++ gnuplot poppler-utils texlive-{latex-base,latex-extra,luatex} - # sudo fc-cache -vr + - name: Ubuntu LaTeX dependencies + if: startsWith(matrix.os, 'ubuntu') + run: | + sudo apt-get -y update + sudo apt-get -y install g++ gnuplot poppler-utils texlive-{latex-base,latex-extra,luatex} + sudo fc-cache -vr - name: Set LD_PRELOAD if: startsWith(matrix.os, 'ubuntu') @@ -62,29 +62,28 @@ jobs: - uses: julia-actions/cache@v1 - # - name: Develop RecipesBase, RecipesPipeline, PlotsBase, Plots - # shell: julia --color=yes {0} - # run: | - # using Pkg - # foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) - # Pkg.precompile() - - # - name: Install conda based matplotlib - # run: julia --color=yes ci/matplotlib.jl + - name: Develop RecipesBase, RecipesPipeline, PlotsBase, Plots + shell: julia --color=yes {0} + run: | + using Pkg + foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) + Pkg.precompile() - # - name: Test RecipesBase, RecipesPipeline, PlotsBase, Plots - # timeout-minutes: 60 - # run: | - # cmd=(julia --color=yes) - # if [ "$RUNNER_OS" == "Linux" ]; then - # cmd=(xvfb-run ${cmd[@]}) - # fi - # echo ${cmd[@]} - # ${cmd[@]} -e ' - # using Pkg - # foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) - # ' + - name: Install conda based matplotlib + run: julia --color=yes ci/matplotlib.jl + - name: Test RecipesBase, RecipesPipeline, PlotsBase, Plots + timeout-minutes: 60 + run: | + cmd=(julia --color=yes) + if [ "$RUNNER_OS" == "Linux" ]; then + cmd=(xvfb-run ${cmd[@]}) + fi + echo ${cmd[@]} + ${cmd[@]} -e ' + using Pkg + foreach(name -> Pkg.test(name; coverage=true), ("RecipesBase", "RecipesPipeline", "PlotsBase", "Plots")) + ' - name: Test downstream packages if: startsWith(matrix.os, 'ubuntu') run: xvfb-run julia --color=yes ci/downstream.jl From e97cb386653808be5b0c406597782ed7124dcee4 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 11:27:41 +0200 Subject: [PATCH 53/58] update workflow --- .github/workflows/ci.yml | 13 ++++++++----- ci/downstream.jl | 8 ++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fbcef493..2a996b021 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,10 @@ jobs: ci: if: "!contains(github.event.head_commit.message, '[skip ci]')" env: - JULIA_CONDAPKG_BACKEND: "MicroMamba" - MPLBACKEND: "agg" - GKS_ENCODING: "utf8" - GKSwstype: "nul" + JULIA_CONDAPKG_BACKEND: MicroMamba + MPLBACKEND: agg + GKS_ENCODING: utf8 + GKSwstype: nul name: Julia ${{ matrix.version }} - ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} runs-on: ${{ matrix.os }} @@ -63,13 +63,16 @@ jobs: - uses: julia-actions/cache@v1 - name: Develop RecipesBase, RecipesPipeline, PlotsBase, Plots + env: + JULIA_PKG_PRECOMPILE_AUTO: 0 shell: julia --color=yes {0} run: | using Pkg foreach(path -> Pkg.develop(; path), ("./RecipesBase", "./RecipesPipeline", "./PlotsBase", ".")) - Pkg.precompile() - name: Install conda based matplotlib + env: + JULIA_PKG_PRECOMPILE_AUTO: 0 run: julia --color=yes ci/matplotlib.jl - name: Test RecipesBase, RecipesPipeline, PlotsBase, Plots diff --git a/ci/downstream.jl b/ci/downstream.jl index 780d557c7..faf53f45f 100644 --- a/ci/downstream.jl +++ b/ci/downstream.jl @@ -41,9 +41,9 @@ pkg_version(name) = maybe_pin_version!(dict::AbstractDict, name::AbstractString, ver::AbstractString) = haskey(dict, name) && (dict[name] = "=$ver") -fake_supported_version!(path) = begin +"fake supported Plots ecosystem versions for using `Pkg.develop`" +fake_supported_versions!(path) = begin toml = joinpath(path, "Project.toml") - # fake supported versions for testing (for `Pkg.develop`) parsed_toml = TOML.parse(read(toml, String)) compat = parsed_toml["compat"] maybe_pin_version!(compat, "RecipesBase", pkg_version("RecipesBase")) @@ -53,7 +53,7 @@ fake_supported_version!(path) = begin open(toml, "w") do io TOML.print(io, parsed_toml) end - print(read(toml, String)) + # print(read(toml, String)) # debug nothing end @@ -66,7 +66,7 @@ test_stable(pkg::AbstractString) = begin pkg_dir = joinpath(tmpd, "$pkg.jl") failsafe_clone_checkout(pkg_dir, "https://github.com/JuliaPlots/$pkg.jl") - fake_supported_version!(pkg_dir) + fake_supported_versions!(pkg_dir) Pkg.develop(; path = pkg_dir) Pkg.test(pkg) From bcd82d0be4137f7dccf04484287415d693c94792 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 12:23:08 +0200 Subject: [PATCH 54/58] add `PlotsBase` precompile statements --- PlotsBase/src/Commons/Commons.jl | 2 +- PlotsBase/src/PlotsBase.jl | 25 +++++++++++- PlotsBase/src/backends.jl | 67 ++++++++++++++------------------ PlotsBase/src/examples.jl | 14 +++---- src/Plots.jl | 7 ++-- 5 files changed, 65 insertions(+), 50 deletions(-) diff --git a/PlotsBase/src/Commons/Commons.jl b/PlotsBase/src/Commons/Commons.jl index 049e5da68..a58e53b30 100644 --- a/PlotsBase/src/Commons/Commons.jl +++ b/PlotsBase/src/Commons/Commons.jl @@ -222,7 +222,7 @@ get_attr_symbol(letter::Symbol, keyword::Symbol) = _attrsymbolcache[letter][keyw get_attr_symbol(letter::Symbol, keyword::String) = get_attr_symbol(letter, Symbol(keyword)) new_attr_dict!(letter::Symbol)::Dict{Symbol,Symbol} = - get!(_attrsymbolcache, letter, Dict{Symbol,Symbol}()) + get!(() -> Dict{Symbol,Symbol}(), _attrsymbolcache, letter) # NOTE: using `keyword::String` allows to disambiguate argument order set_attr_symbol!(letter::Symbol, keyword::String) = diff --git a/PlotsBase/src/PlotsBase.jl b/PlotsBase/src/PlotsBase.jl index b12e7fe30..09ad15f27 100644 --- a/PlotsBase/src/PlotsBase.jl +++ b/PlotsBase/src/PlotsBase.jl @@ -167,9 +167,32 @@ include("users.jl") # COV_EXCL_START @setup_workload begin + backend(:none) + n = length(_examples) + imports = sizehint!(Expr[], n) + examples = sizehint!(Expr[], 10n) + scratch_dir = mktempdir() + for i in setdiff(1:n, _backend_skips[backend_name()], _animation_examples) + _examples[i].external && continue + (imp = _examples[i].imports) ≡ nothing || push!(imports, imp) + func = gensym(string(i)) + push!(examples, quote + $func() = begin # evaluate each example in a local scope + $(_examples[i].exprs) + $i == 1 || return # trigger display only for one example + fn = joinpath(scratch_dir, tempname()) + show(devnull, current()) + nothing + end + $func() + end) + end @compile_workload begin - # TODO: backend agnostic statements + backend(:none) + eval.(imports) + eval.(examples) end + CURRENT_PLOT.nullableplot = nothing end # COV_EXCL_STOP diff --git a/PlotsBase/src/backends.jl b/PlotsBase/src/backends.jl index 61d0e217f..7ed013875 100644 --- a/PlotsBase/src/backends.jl +++ b/PlotsBase/src/backends.jl @@ -1,24 +1,28 @@ +const _default_supported_syms = :attr, :seriestype, :marker, :style, :scale + +_f1_sym(sym::Symbol) = Symbol("is_$(sym)_supported") +_f2_sym(sym::Symbol) = Symbol("supported_$(sym)s") + struct NoBackend <: AbstractBackend end backend_name(::NoBackend) = :none +should_warn_on_unsupported(::NoBackend) = false -for sym in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_$(sym)_supported") - f2 = Symbol("supported_$(sym)s") +for sym in _default_supported_syms @eval begin - $f1(::NoBackend, $sym::Symbol) = true - $f2(::NoBackend) = $(getproperty(Commons, Symbol("_all_$(sym)s"))) + $(_f1_sym(sym))(::NoBackend, $sym::Symbol) = true + $(_f2_sym(sym))(::NoBackend) = Commons.$(Symbol("_all_$(sym)s")) end end _display(::Plot{NoBackend}) = - @info "No backend activated yet. Load the backend library and call the activation function to do so.\nE.g. `import GR; gr()` activates the GR backend." + @warn "No backend activated yet. Load the backend library and call the activation function to do so.\nE.g. `import GR; gr()` activates the GR backend." const _backendSymbol = Dict{DataType,Symbol}(NoBackend => :none) const _backendType = Dict{Symbol,DataType}(:none => NoBackend) -const _backend_packages = (gaston = :Gaston, gr = :GR, unicodeplots = :UnicodePlots, pgfplotsx = :PGFPlotsX, pythonplot = :PythonPlot, plotly = nothing, plotlyjs = :PlotlyJS, hdf5 = :HDF5) -const _initialized_backends = Set{Symbol}() +const _backend_packages = (unicodeplots = :UnicodePlots, pythonplot = :PythonPlot, pgfplotsx = :PGFPlotsX, plotlyjs = :PlotlyJS, gaston = :Gaston, plotly = nothing, none = nothing, hdf5 = :HDF5, gr = :GR) const _supported_backends = keys(_backend_packages) +const _initialized_backends = Set([:none]) function _check_installed(backend::Union{Module,AbstractString,Symbol}; warn = true) sym = Symbol(lowercase(string(backend))) @@ -106,7 +110,7 @@ backend(sym::Symbol) = backend(backend_type(sym)) else name = backend_package_name(sym) - @warn "`:$sym` is not initialized, import it first to trigger the extension --- e.g. $(name ≡ nothing ? '`' : string("`import ", name, ";")) $sym()`." + @warn "`:$sym` is not initialized, import it first to trigger the extension --- e.g. `$(name ≡ nothing ? "" : "import $name; ")$sym()`." backend() end else @@ -136,9 +140,9 @@ end # create the various `is_xxx_supported` and `supported_xxxs` methods # these methods should be overloaded (dispatched) by each backend in its init_code -for sym in (:attr, :seriestype, :marker, :style, :scale) - f1 = Symbol("is_$(sym)_supported") - f2 = Symbol("supported_$(sym)s") +for sym in _default_supported_syms + f1 = _f1_sym(sym) + f2 = _f2_sym(sym) @eval begin $f1(::AbstractBackend, $sym) = false $f1(be::AbstractBackend, $sym::AbstractVector) = all(v -> $f1(be, v), $sym) @@ -167,29 +171,21 @@ function backend_defines(be_type::Symbol, be::Symbol) ... PlotsBase.supported_scales(::GRbackend) -> ::Vector{Symbol} =# - for sym in (:attr, :seriestype, :marker, :style, :scale) + for sym in _default_supported_syms be_syms = Symbol("_$(be)_$(sym)s") - f1 = Symbol("is_$(sym)_supported") - f2 = Symbol("supported_$(sym)s") push!( blk.args, - :(PlotsBase.$f1(::$be_type, $sym::Symbol)::Bool = $sym in $be_syms), - :(PlotsBase.$f2(::$be_type)::Vector = sort!(collect($be_syms))), + :(PlotsBase.$(_f1_sym(sym))(::$be_type, $sym::Symbol)::Bool = $sym in $be_syms), + :(PlotsBase.$(_f2_sym(sym))(::$be_type)::Vector = sort!(collect($be_syms))), ) end blk end +"extra init step for an extension" extension_init(::AbstractBackend) = nothing -""" -function __init__() - PlotsBase._backendType[sym] = GRBackend - PlotsBase._backendSymbol[GRBackend] = sym - push!(PlotsBase._initialized_backends, sym) - @debug "Initializing GR backend in PlotsBase; run `gr()` to activate it." -end -""" +"generate extension `__init__` function, and common defines" macro extension_static(be_type, be) be_sym = QuoteNode(be) quote @@ -211,9 +207,7 @@ const _already_warned = Dict{Symbol,Set{Symbol}}() function warn_on_unsupported_attrs(pkg::AbstractBackend, plotattributes) _to_warn = Set{Symbol}() bend = backend_name(pkg) - already_warned = get!(_already_warned, bend) do - Set{Symbol}() - end + already_warned = get!(() -> Set{Symbol}(), _already_warned, bend) extra_kwargs = Dict{Symbol,Any}() for k in PlotsBase.explicitkeys(plotattributes) (is_attr_supported(pkg, k) && k ∉ keys(Commons._deprecated_attributes)) && continue @@ -227,7 +221,7 @@ function warn_on_unsupported_attrs(pkg::AbstractBackend, plotattributes) if !isempty(_to_warn) && get(plotattributes, :warn_on_unsupported, should_warn_on_unsupported(pkg)) - for k in sort(collect(_to_warn)) + for k in sort!(collect(_to_warn)) push!(already_warned, k) if k in keys(Commons._deprecated_attributes) @warn """ @@ -255,14 +249,13 @@ end function warn_on_unsupported_scales(pkg::AbstractBackend, plotattributes::AKW) get(plotattributes, :warn_on_unsupported, should_warn_on_unsupported(pkg)) || return for k in (:xscale, :yscale, :zscale, :scale) - if haskey(plotattributes, k) - v = plotattributes[k] - if !all(is_scale_supported.(Ref(pkg), v)) - @warn """ - scale $v is unsupported with $pkg. - Choose from: $(supported_scales(pkg)) - """ - end + haskey(plotattributes, k) || continue + v = plotattributes[k] + if !all(is_scale_supported.(Ref(pkg), v)) + @warn """ + scale $v is unsupported with $pkg. + Choose from: $(supported_scales(pkg)) + """ end end end diff --git a/PlotsBase/src/examples.jl b/PlotsBase/src/examples.jl index ea40b8a44..f120e2360 100644 --- a/PlotsBase/src/examples.jl +++ b/PlotsBase/src/examples.jl @@ -1254,9 +1254,11 @@ const _examples = PlotExample[ ] # Some constants for PlotDocs and PlotReferenceImages -_animation_examples = [2, 31] +_animation_examples = [02, 31] _backend_skips = Dict( - :gr => [25, 30], # TODO: add back when StatsPlots is available + :none => Int[], + :pythonplot => Int[], + :gr => [25, 30], # TODO: add back when StatsPlots is available :plotlyjs => [ 21, 24, @@ -1274,7 +1276,7 @@ _backend_skips = Dict( 66, # bar: vector-valued `color` unsupported ], :pgfplotsx => [ - 6, # images + 06, # images 16, # pgfplots thinks the upper panel is too small 32, # spy 49, # polar heatmap @@ -1283,8 +1285,8 @@ _backend_skips = Dict( 62, # fillstyle unsupported ], :unicodeplots => [ - 5, # limits issue - 6, # embedded images supported, but requires `using ImageInTerminal`, disable for docs + 05, # limits issue + 06, # embedded images supported, but requires `using ImageInTerminal`, disable for docs 16, # nested layout unsupported 21, # custom markers unsupported 26, # nested layout unsupported @@ -1313,8 +1315,6 @@ _backend_skips = Dict( ) _backend_skips[:plotly] = _backend_skips[:plotlyjs] -_backend_skips[:pythonplot] = Int[] - # --------------------------------------------------------------------------------- # replace `f(args...)` with `f(rng, args...)` for `f ∈ (rand, randn)` replace_rand(ex) = ex diff --git a/src/Plots.jl b/src/Plots.jl index 202f17d10..dace3c1fd 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -17,8 +17,7 @@ function __init__() end # COV_EXCL_START -# FIXME: Creating a new global in closed module `Main` (`UnicodePlots`) breaks incremental compilation because the side effects will not be permanent. -if PlotsBase.DEFAULT_BACKEND == "gr" +if PlotsBase.DEFAULT_BACKEND == "gr" # FIXME: Creating a new global in closed module `Main` (`UnicodePlots`) breaks incremental compilation because the side effects will not be permanent. @setup_workload begin #= if PlotsBase.DEFAULT_BACKEND == "gr" @@ -57,7 +56,7 @@ if PlotsBase.DEFAULT_BACKEND == "gr" quote $func() = begin # evaluate each example in a local scope $(PlotsBase._examples[i].exprs) - $i == 1 || return # only for one example + $i == 1 || return # trigger display only for one example fn = joinpath(scratch_dir, tempname()) pl = current() show(devnull, pl) @@ -70,7 +69,7 @@ if PlotsBase.DEFAULT_BACKEND == "gr" showable(MIME"image/png"(), pl) && savefig(pl, "$fn.png") showable(MIME"application/pdf"(), pl) && savefig(pl, "$fn.pdf") if showable(MIME"image/svg+xml"(), pl) - show(IOBuffer(), MIME"image/svg+xml"(), pl) + show(PipeBuffer(), MIME"image/svg+xml"(), pl) end nothing end From b1ca5da0f70220f8bf0ea3e9ffa52e18475e4d97 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 13:13:43 +0200 Subject: [PATCH 55/58] update tests --- PlotsBase/src/backends.jl | 79 +++++++++++++++++++------------------- PlotsBase/src/utils.jl | 4 +- PlotsBase/test/runtests.jl | 13 ++++--- test/runtests.jl | 17 +++++--- 4 files changed, 60 insertions(+), 53 deletions(-) diff --git a/PlotsBase/src/backends.jl b/PlotsBase/src/backends.jl index 7ed013875..d14fc76c0 100644 --- a/PlotsBase/src/backends.jl +++ b/PlotsBase/src/backends.jl @@ -24,28 +24,28 @@ const _backend_packages = (unicodeplots = :UnicodePlots, pythonplot = :Pytho const _supported_backends = keys(_backend_packages) const _initialized_backends = Set([:none]) -function _check_installed(backend::Union{Module,AbstractString,Symbol}; warn = true) - sym = Symbol(lowercase(string(backend))) +function _check_installed(pkg::Union{Module,AbstractString,Symbol}; warn = true) + name = Symbol(lowercase(string(pkg))) if warn && !haskey(_backend_packages, sym) - @warn "backend `$sym` is not compatible with `PlotsBase`." + @warn "backend `$name` is not compatible with `PlotsBase`." return end # lowercase -> CamelCase, falling back to the given input for `PlotlyBase` ... - str = string(get(_backend_packages, sym, backend)) - str == "Plotly" && (str *= "Base") # FIXME: `PlotsBase` inconsistency, `plotly` should be named `plotlybase` + pkg_str = string(get(_backend_packages, name, pkg)) + pkg_str == "Plotly" && (pkg_str *= "Base") # FIXME: `PlotsBase` inconsistency, `plotly` should be named `plotlybase` # check supported - if warn && !haskey(_compat, str) - @warn "backend `$str` is not compatible with `PlotsBase`." + if warn && !haskey(_compat, pkg_str) + @warn "package `$pkg_str` is not compatible with `PlotsBase`." return end # check installed - pkg_id = Base.identify_package(str) + pkg_id = Base.identify_package(pkg_str) version = if pkg_id ≡ nothing nothing else get(Pkg.dependencies(), pkg_id.uuid, (; version = nothing)).version end - version ≡ nothing && @warn "backend `$str` is not installed." + version ≡ nothing && @warn "`package $pkg_str` is not installed." version end @@ -63,66 +63,67 @@ guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefontsize] * pt closeall(::AbstractBackend) = nothing mutable struct CurrentBackend - sym::Symbol - pkg::AbstractBackend + name::Symbol + instance::AbstractBackend end -@inline backend_type(sym::Symbol) = get(_backendType, sym, NoBackend) -@inline backend_instance(sym::Symbol) = backend_type(sym)() +@inline backend_type(name::Symbol) = _backendType[name] +@inline backend_instance(name::Symbol) = backend_type(name)() @inline backend(type::Type{<:AbstractBackend}) = backend(type()) -CurrentBackend(sym::Symbol) = CurrentBackend(sym, backend_instance(sym)) +CurrentBackend(name::Symbol) = CurrentBackend(name, backend_instance(name)) -"returns the current plotting package name. Initializes package on first call." -@inline backend() = CURRENT_BACKEND.pkg +const CURRENT_BACKEND = CurrentBackend(:none) + +"returns the current plotting package backend. Initializes package on first call." +@inline backend() = CURRENT_BACKEND.instance "returns a list of supported backends." @inline backends() = _supported_backends -const CURRENT_BACKEND = CurrentBackend(:none) - -@inline backend_name() = CURRENT_BACKEND.sym -@inline backend_package_name(sym::Symbol = backend_name()) = - get(_backend_packages, sym, nothing) +@inline backend_name() = CURRENT_BACKEND.name +@inline backend_package_name(name::Symbol = backend_name()) = + get(_backend_packages, name, nothing) # Traits to be implemented by the extensions backend_name(::AbstractBackend) = @info "`backend_name(::Backend) not implemented." backend_package_name(::AbstractBackend) = @info "`backend_package_name(::Backend) not implemented." -initialized(sym::Symbol) = sym ∈ _initialized_backends +initialized(name::Symbol) = name ∈ _initialized_backends "set the plot backend." -function backend(pkg::AbstractBackend) - sym = backend_name(pkg) - if sym ∈ _supported_backends - CURRENT_BACKEND.sym = sym - CURRENT_BACKEND.pkg = pkg +function backend(backend::AbstractBackend) + name = backend_name(backend) + instance = backend_instance(name) + if name ∈ _supported_backends + CURRENT_BACKEND.name = name + CURRENT_BACKEND.instance = instance else - @error "Unsupported backend $sym" + @error "Unsupported backend $name" end - pkg + instance end -backend(sym::Symbol) = - if sym ∈ _supported_backends - if initialized(sym) - backend(backend_type(sym)) +backend(name::Symbol) = + if name ∈ _supported_backends + if initialized(name) + backend(backend_type(name)) else - name = backend_package_name(sym) - @warn "`:$sym` is not initialized, import it first to trigger the extension --- e.g. `$(name ≡ nothing ? "" : "import $name; ")$sym()`." + pkg_name = backend_package_name(name) + @warn "`:$name` is not initialized, import it first to trigger the extension --- e.g. `$(pkg_name ≡ nothing ? "" : "import $pkg_name; ")$name()`." backend() end else - @error "Unsupported backend $sym" + @error "Unsupported backend $name" end -function get_backend_module(name::Symbol) - ext = Base.get_extension(@__MODULE__, Symbol(name, "Ext")) +function get_backend_module(pkg_name::Symbol) + ext = Base.get_extension(@__MODULE__, Symbol("$(pkg_name)Ext")) if !isnothing(ext) return ext, ext.get_concrete_backend() else - @error "Extension $name is not loaded yet, run `import $name` to load it" + @error "Extension $pkg_name is not loaded yet, run `import $pkg_name` to load it" return nothing end end diff --git a/PlotsBase/src/utils.jl b/PlotsBase/src/utils.jl index fbdaa962c..d6a37db48 100644 --- a/PlotsBase/src/utils.jl +++ b/PlotsBase/src/utils.jl @@ -594,7 +594,7 @@ function with(f::Function, args...; scalefonts = nothing, kw...) end # save the backend - old_backend = CURRENT_BACKEND.sym + old_backend = backend_name() for arg in args # change backend ? @@ -634,7 +634,7 @@ function with(f::Function, args...; scalefonts = nothing, kw...) default(; old_defs...) # revert the backend - CURRENT_BACKEND.sym != old_backend && backend(old_backend) + old_backend != backend_name() && backend(old_backend) # return the result of the function ret diff --git a/PlotsBase/test/runtests.jl b/PlotsBase/test/runtests.jl index 925f8f441..a6f90621d 100644 --- a/PlotsBase/test/runtests.jl +++ b/PlotsBase/test/runtests.jl @@ -4,22 +4,23 @@ const TEST_PACKAGES = "PLOTSBASE_TEST_PACKAGES", "GR,UnicodePlots,PythonPlot,PGFPlotsX,PlotlyJS,Gaston", ) - strip.(split(val, ",")) + Symbol.(strip.(split(val, ","))) end -const TEST_BACKENDS = Symbol.(lowercase.(TEST_PACKAGES)) +const TEST_BACKENDS = NamedTuple(p => Symbol(lowercase(string(p))) for p in TEST_PACKAGES) + +get!(ENV, "MPLBACKEND", "agg") using PlotsBase +# always initialize GR import GR gr() -get!(ENV, "MPLBACKEND", "agg") - # initialize all backends for pkg in TEST_PACKAGES @eval begin - import $(Symbol(pkg)) # trigger extension - $(Symbol(lowercase(pkg)))() + import $pkg # trigger extension + $(TEST_BACKENDS[pkg])() end end diff --git a/test/runtests.jl b/test/runtests.jl index d988093ec..c1d2b40b1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,14 +1,16 @@ const TEST_PACKAGES = let val = get(ENV, "PLOTS_TEST_PACKAGES", "GR,UnicodePlots,PythonPlot") - strip.(split(val, ",")) + Symbol.(strip.(split(val, ","))) end +const TEST_BACKENDS = NamedTuple(p => Symbol(lowercase(string(p))) for p in TEST_PACKAGES) + using PlotsBase # initialize all backends for pkg in TEST_PACKAGES @eval begin - import $(Symbol(pkg)) # trigger extension - $(Symbol(lowercase(pkg)))() + import $pkg # trigger extension + $(TEST_BACKENDS[pkg])() end end gr() @@ -16,8 +18,11 @@ gr() using Plots using Test -for name in () - @testset "$name" begin - include("test_$name.jl") +for pkg in TEST_PACKAGES + @testset "simple plots using $pkg" begin + @eval $(TEST_BACKENDS[pkg])() + pl = plot(1:2) + @test pl isa PlotsBase.Plot + show(devnull, pl) end end From fdde312e899d1aaa40ea3fed09b070830d8f6292 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 13:40:03 +0200 Subject: [PATCH 56/58] update preference - simplifications --- PlotsBase/src/backends.jl | 20 +++++++++----------- PlotsBase/src/preferences.jl | 10 +++++----- PlotsBase/test/test_preferences.jl | 4 ++-- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/PlotsBase/src/backends.jl b/PlotsBase/src/backends.jl index d14fc76c0..3d47e93e0 100644 --- a/PlotsBase/src/backends.jl +++ b/PlotsBase/src/backends.jl @@ -26,7 +26,7 @@ const _initialized_backends = Set([:none]) function _check_installed(pkg::Union{Module,AbstractString,Symbol}; warn = true) name = Symbol(lowercase(string(pkg))) - if warn && !haskey(_backend_packages, sym) + if warn && !haskey(_backend_packages, name) @warn "backend `$name` is not compatible with `PlotsBase`." return end @@ -90,12 +90,9 @@ backend_name(::AbstractBackend) = @info "`backend_name(::Backend) not implemente backend_package_name(::AbstractBackend) = @info "`backend_package_name(::Backend) not implemented." -initialized(name::Symbol) = name ∈ _initialized_backends - "set the plot backend." -function backend(backend::AbstractBackend) - name = backend_name(backend) - instance = backend_instance(name) +function backend(instance::AbstractBackend) + name = backend_name(instance) if name ∈ _supported_backends CURRENT_BACKEND.name = name CURRENT_BACKEND.instance = instance @@ -107,7 +104,7 @@ end backend(name::Symbol) = if name ∈ _supported_backends - if initialized(name) + if name ∈ _initialized_backends backend(backend_type(name)) else pkg_name = backend_package_name(name) @@ -120,12 +117,13 @@ backend(name::Symbol) = function get_backend_module(pkg_name::Symbol) ext = Base.get_extension(@__MODULE__, Symbol("$(pkg_name)Ext")) - if !isnothing(ext) - return ext, ext.get_concrete_backend() - else + concrete_backend = if ext ≡ nothing @error "Extension $pkg_name is not loaded yet, run `import $pkg_name` to load it" - return nothing + nothing + else + ext.get_concrete_backend() end + ext, concrete_backend end # create backend init functions by hand as the corresponding structs do not exist yet diff --git a/PlotsBase/src/preferences.jl b/PlotsBase/src/preferences.jl index 882bb3df3..e58f5394a 100644 --- a/PlotsBase/src/preferences.jl +++ b/PlotsBase/src/preferences.jl @@ -6,8 +6,8 @@ const DEFAULT_BACKEND = lowercase(load_preference(PlotsBase, "default_backend", function default_backend() # environment variable preempts the `Preferences` based mechanism - sym = get(ENV, "PLOTSBASE_DEFAULT_BACKEND", DEFAULT_BACKEND) |> lowercase |> Symbol - backend(PlotsBase.backend_type(sym)) + name = get(ENV, "PLOTSBASE_DEFAULT_BACKEND", DEFAULT_BACKEND) |> lowercase |> Symbol + backend(name) end function set_default_backend!( @@ -37,10 +37,10 @@ function diagnostics(io::IO = stdout) if (be = backend_name()) ≡ :none @info "no `PlotsBase` backends currently initialized" else - be_name = string(PlotsBase.backend_package_name(be)) - @info "selected `PlotsBase` backend: $be_name, from $origin" + pkg_name = string(PlotsBase.backend_package_name(be)) + @info "selected `PlotsBase` backend: $pkg_name, from $origin" Pkg.status( - ["PlotsBase", "RecipesBase", "RecipesPipeline", be_name]; + ["PlotsBase", "RecipesBase", "RecipesPipeline", pkg_name]; mode = Pkg.PKGMODE_MANIFEST, io, ) diff --git a/PlotsBase/test/test_preferences.jl b/PlotsBase/test/test_preferences.jl index 8022b926e..c088158db 100644 --- a/PlotsBase/test/test_preferences.jl +++ b/PlotsBase/test/test_preferences.jl @@ -24,7 +24,7 @@ end @test_logs (:info, r".*fallback") PlotsBase.diagnostics(devnull) @test PlotsBase.merge_with_base_supported([:annotations, :guide]) isa Set -@test PlotsBase.CurrentBackend(:gr).sym ≡ :gr +@test PlotsBase.CurrentBackend(:gr).name ≡ :gr @test_logs (:warn, r".*is not compatible with") PlotsBase.set_default_backend!( :test_invalid_backend, @@ -60,7 +60,7 @@ const DEBUG = false end is_pkgeval() || for pkg in TEST_PACKAGES - be = Symbol(lowercase(pkg)) + be = TEST_BACKENDS[pkg] if is_ci() (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs (Sys.iswindows() && be ≡ :plotlyjs) && continue # FIXME: OutOfMemory From 6a83e8f2958d23b2b6051319d595e67893a2ff50 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 14:03:07 +0200 Subject: [PATCH 57/58] update test --- PlotsBase/test/test_preferences.jl | 46 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/PlotsBase/test/test_preferences.jl b/PlotsBase/test/test_preferences.jl index c088158db..ae6cac8b7 100644 --- a/PlotsBase/test/test_preferences.jl +++ b/PlotsBase/test/test_preferences.jl @@ -48,7 +48,7 @@ const DEBUG = false import UnicodePlots using PlotsBase unicodeplots() - res = @testset "Preferences UnicodePlots" begin + res = @testset "[subtest] preferences UnicodePlots" begin @test_logs (:info, r".*Preferences") PlotsBase.diagnostics(io) @test backend() == Base.get_extension(PlotsBase, :UnicodePlotsExt).UnicodePlotsBackend() end @@ -60,28 +60,30 @@ const DEBUG = false end is_pkgeval() || for pkg in TEST_PACKAGES - be = TEST_BACKENDS[pkg] - if is_ci() - (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs - (Sys.iswindows() && be ≡ :plotlyjs) && continue # FIXME: OutOfMemory - end - @test_logs PlotsBase.set_default_backend!(be) # test the absence of warnings - rm.(Base.find_all_in_cache_path(Base.module_keys[PlotsBase])) # make sure the compiled cache is removed - script = tempname() - write( - script, - """ - import $pkg - using Test, PlotsBase - $be() - res = @testset "Persistent backend $pkg" begin - @test PlotsBase.backend_name() ≡ :$be + @testset "persistent backend $pkg" begin + be = TEST_BACKENDS[pkg] + if is_ci() + (Sys.isapple() && be ≡ :gaston) && continue # FIXME: hangs + (Sys.iswindows() && be ≡ :plotlyjs) && continue # FIXME: OutOfMemory end - exit(res.n_passed == 1 ? 0 : 123) - """, - ) - DEBUG && print(read(script, String)) - @test run(```$(Base.julia_cmd()) $script```) |> success # test default precompilation + @test_logs PlotsBase.set_default_backend!(be) # test the absence of warnings + rm.(Base.find_all_in_cache_path(Base.module_keys[PlotsBase])) # make sure the compiled cache is removed + script = tempname() + write( + script, + """ + import $pkg + using Test, PlotsBase + $be() + res = @testset "[subtest] persistent backend $pkg" begin + @test PlotsBase.backend_name() ≡ :$be + end + exit(res.n_passed == 1 ? 0 : 123) + """, + ) + DEBUG && print(read(script, String)) + @test run(```$(Base.julia_cmd()) $script```) |> success # test default precompilation + end end PlotsBase.set_default_backend!() # clear `Preferences` key From ea6342de742cd44a520a5c3feb48cac1eedec43e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 7 Apr 2024 14:08:20 +0200 Subject: [PATCH 58/58] update --- PlotsBase/src/backends.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PlotsBase/src/backends.jl b/PlotsBase/src/backends.jl index 3d47e93e0..c1817a502 100644 --- a/PlotsBase/src/backends.jl +++ b/PlotsBase/src/backends.jl @@ -39,8 +39,7 @@ function _check_installed(pkg::Union{Module,AbstractString,Symbol}; warn = true) return end # check installed - pkg_id = Base.identify_package(pkg_str) - version = if pkg_id ≡ nothing + version = if (pkg_id = Base.identify_package(pkg_str)) ≡ nothing nothing else get(Pkg.dependencies(), pkg_id.uuid, (; version = nothing)).version