From c0824bdc628345b4859ebcafc437cbfd63e63ccb Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Thu, 25 Mar 2021 11:22:07 +0900 Subject: [PATCH] Colorbar enhancements (#3346) * CompatHelper: bump compat for "Showoff" to "1.0" * fix series-segments for empty series * fix wireframe on pyplot * colorbar redesign * minimal working version * reduce code duplication for colorbar ticks * fix aspect_ratio in GR with legend=:outertopright * fix GR test failure * new release [skip ci] * colorbar scale supported * Added weights example to ? histogram Helps to clarify the use of weights (which differs from StatsBase functions) * Update precompile_*.jl file * minor version bump [skip ci] * working prototype * fixed formatting, added colorbar docs * colorbar redesign * minimal working version * reduce code duplication for colorbar ticks * fix GR test failure * colorbar scale supported * working prototype * fixed formatting, added colorbar docs Co-authored-by: Daniel Schwabeneder --- src/Plots.jl | 1 + src/arg_desc.jl | 313 ++++++++++++++++---------------- src/args.jl | 392 +++++++++++++++++++++-------------------- src/axes.jl | 93 +++++----- src/backends.jl | 4 + src/backends/gr.jl | 10 +- src/backends/pyplot.jl | 33 ++-- src/colorbars.jl | 90 ++++++++++ src/utils.jl | 73 -------- 9 files changed, 532 insertions(+), 477 deletions(-) create mode 100644 src/colorbars.jl diff --git a/src/Plots.jl b/src/Plots.jl index 5378fb73..e7a71878 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -193,6 +193,7 @@ const _plotly_min_js_filename = "plotly-1.57.1.min.js" include("types.jl") include("utils.jl") +include("colorbars.jl") include("axes.jl") include("args.jl") include("components.jl") diff --git a/src/arg_desc.jl b/src/arg_desc.jl index a5139c1b..2a5b9d0c 100644 --- a/src/arg_desc.jl +++ b/src/arg_desc.jl @@ -2,164 +2,171 @@ const _arg_desc = KW( # series args -:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.", -:seriescolor => "Color Type. The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot", -:seriesalpha => "Number in [0,1]. The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color.", -:seriestype => "Symbol. This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined.", -:linestyle => "Symbol. Style of the line (for path and bar stroke). Choose from $(_allStyles)", -:linewidth => "Number. Width of the line (in pixels)", -:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).", -:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.", -:fillrange => "Number or AbstractVector. Fills area between fillrange and y for line-types, sets the base for bar/stick types, and similar for other types.", -:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.", -:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.", -:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).", -:markercolor => "Color Type. Color of the interior of the marker or shape. `:match` will take the value from `:seriescolor`.", -:markeralpha => "Number in [0,1]. The alpha/opacity override for the marker interior. `nothing` (the default) means it will take the alpha value of markercolor.", -:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.", -:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)", -:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)", -:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.", -:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.", -:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(minimum(x), stop = maximum(x), length = 25)`", -:smooth => "Bool. Add a regression line?", -:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.", -:x => "Various. Input data. First Dimension", -:y => "Various. Input data. Second Dimension", -:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.", -:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.", -:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).", -:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.", -:levels => "Integer, NTuple{2,Integer}, or AbstractVector. Levels or number of levels (or x-levels/y-levels) for a contour type.", -:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).", -:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)", -:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).", -:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?", -:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)", -:yerror => "AbstractVector or 2-Tuple of Vectors. y (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)", -:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.", -:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.", -:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.", -:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes).", -:weights => "AbstractVector. Used in histogram types for weighted counts.", -:show_empty_bins => "Bool. Whether empty bins in a 2D histogram are colored as 0 (true), or transparent (the default).", -:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.", -:contour_labels => "Bool. Show labels at the contour lines?", -:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.", -:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.", -:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.", -:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.", -:hover => "nothing or vector of strings. Text to display when hovering over each data point.", -:colorbar_entry => "Bool. Include this series in the color bar? Set to `false` to exclude.", +:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.", +:seriescolor => "Color Type. The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot", +:seriesalpha => "Number in [0,1]. The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color.", +:seriestype => "Symbol. This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined.", +:linestyle => "Symbol. Style of the line (for path and bar stroke). Choose from $(_allStyles)", +:linewidth => "Number. Width of the line (in pixels)", +:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).", +:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.", +:fillrange => "Number or AbstractVector. Fills area between fillrange and y for line-types, sets the base for bar/stick types, and similar for other types.", +:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.", +:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.", +:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).", +:markercolor => "Color Type. Color of the interior of the marker or shape. `:match` will take the value from `:seriescolor`.", +:markeralpha => "Number in [0,1]. The alpha/opacity override for the marker interior. `nothing` (the default) means it will take the alpha value of markercolor.", +:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.", +:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)", +:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)", +:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.", +:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.", +:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(minimum(x), stop = maximum(x), length = 25)`", +:smooth => "Bool. Add a regression line?", +:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.", +:x => "Various. Input data. First Dimension", +:y => "Various. Input data. Second Dimension", +:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.", +:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.", +:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).", +:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.", +:levels => "Integer, NTuple{2,Integer}, or AbstractVector. Levels or number of levels (or x-levels/y-levels) for a contour type.", +:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).", +:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)", +:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).", +:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?", +:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)", +:yerror => "AbstractVector or 2-Tuple of Vectors. y (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)", +:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.", +:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.", +:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.", +:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes).", +:weights => "AbstractVector. Used in histogram types for weighted counts.", +:show_empty_bins => "Bool. Whether empty bins in a 2D histogram are colored as 0 (true), or transparent (the default).", +:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.", +:contour_labels => "Bool. Show labels at the contour lines?", +:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.", +:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.", +:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.", +:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.", +:hover => "nothing or vector of strings. Text to display when hovering over each data point.", +:colorbar_entry => "Bool. Include this series in the color bar? Set to `false` to exclude.", # plot args -:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)", -:background_color => "Color Type. Base color for all backgrounds.", -:background_color_outside => "Color Type or `:match` (matches `:background_color`). Color outside the plot area(s)", -:foreground_color => "Color Type. Base color for all foregrounds.", -:size => "NTuple{2,Int}. (width_px, height_px) of the whole Plot", -:pos => "NTuple{2,Int}. (left_px, top_px) position of the GUI window (note: currently unimplemented)", -:window_title => "String. Title of the standalone gui-window.", -:show => "Bool. Should this command open/refresh a GUI/display? This allows displaying in scripts or functions without explicitly calling `display`", -:layout => "Integer (number of subplots), NTuple{2,Integer} (grid dimensions), AbstractLayout (for example `grid(2,2)`), or the return from the `@layout` macro. This builds the layout of subplots.", -:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).", -:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).", -:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.", -:tex_output_standalone => "Bool. When writing tex output, should the source include a preamble for a standalone document class.", -:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots", -:dpi => "Number. Dots Per Inch of output figures", -:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.", -:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.", -:extra_kwargs => "Either one of (`:plot`, `:subplot`, `:series`) to specify for which element extra keyword args are collected or a KW (Dict{Symbol,Any}) to pass a map of extra keyword args which may be specific to a backend. Default: `:series`.\n Example: `pgfplotsx(); scatter(1:5, extra_kwargs=Dict(:subplot=>Dict(\"axis line shift\" => \"10pt\"))`", -:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides", -:warn_on_unsupported => "Bool. Warn on unsupported attributes, series types and marker shapes", +:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)", +:background_color => "Color Type. Base color for all backgrounds.", +:background_color_outside => "Color Type or `:match` (matches `:background_color`). Color outside the plot area(s)", +:foreground_color => "Color Type. Base color for all foregrounds.", +:size => "NTuple{2,Int}. (width_px, height_px) of the whole Plot", +:pos => "NTuple{2,Int}. (left_px, top_px) position of the GUI window (note: currently unimplemented)", +:window_title => "String. Title of the standalone gui-window.", +:show => "Bool. Should this command open/refresh a GUI/display? This allows displaying in scripts or functions without explicitly calling `display`", +:layout => "Integer (number of subplots), NTuple{2,Integer} (grid dimensions), AbstractLayout (for example `grid(2,2)`), or the return from the `@layout` macro. This builds the layout of subplots.", +:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).", +:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).", +:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.", +:tex_output_standalone => "Bool. When writing tex output, should the source include a preamble for a standalone document class.", +:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots", +:dpi => "Number. Dots Per Inch of output figures", +:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.", +:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.", +:extra_kwargs => "Either one of (`:plot`, `:subplot`, `:series`) to specify for which element extra keyword args are collected or a KW (Dict{Symbol,Any}) to pass a map of extra keyword args which may be specific to a backend. Default: `:series`.\n Example: `pgfplotsx(); scatter(1:5, extra_kwargs=Dict(:subplot=>Dict(\"axis line shift\" => \"10pt\"))`", +:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides", +:warn_on_unsupported => "Bool. Warn on unsupported attributes, series types and marker shapes", # subplot args -:title => "String. Subplot title.", -:titlelocation => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`", -:titlefontfamily => "String or Symbol. Font family of subplot title.", -:titlefontsize => "Integer. Font pointsize of subplot title.", -:titlefonthalign => "Symbol. Font horizontal alignment of subplot title: :hcenter, :left, :right or :center", -:titlefontvalign => "Symbol. Font vertical alignment of subplot title: :vcenter, :top, :bottom or :center", -:titlefontrotation => "Real. Font rotation of subplot title", -:titlefontcolor => "Color Type. Font color of subplot title", -:background_color_subplot => "Color Type or `:match` (matches `:background_color`). Base background color of the subplot.", -:background_color_legend => "Color Type or `:match` (matches `:background_color_subplot`). Background color of the legend.", -:background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).", -:foreground_color_subplot => "Color Type or `:match` (matches `:foreground_color`). Base foreground color of the subplot.", -:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.", -:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.", -:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.", -:legend => "Bool (show the legend?) or (x,y) tuple or Symbol (legend position) or angle or (angle,inout) tuple. Bottom left corner of legend is placed at (x,y). Symbol values: `:none`; `:best`; `:inline`; `:inside`; `:legend`; any valid combination of `:(outer ?)(top/bottom ?)(right/left ?)`, i.e.: `:top`, `:topright`, `:outerleft`, `:outerbottomright` ... (note: only some may be supported in each backend). Legend is positioned at (angle degrees) (so (90,:outer) is roughly equivalent to :outertop), close to the inside of the axes or the outside if inout=:outer.", -:legendfontfamily => "String or Symbol. Font family of legend entries.", -:legendfontsize => "Integer. Font pointsize of legend entries.", -:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center", -:legendfontvalign => "Symbol. Font vertical alignment of legend entries: :vcenter, :top, :bottom or :center", -:legendfontrotation => "Real. Font rotation of legend entries", -:legendfontcolor => "Color Type. Font color of legend entries", -:legendtitle => "String. Legend title.", -:legendtitlefontfamily => "String or Symbol. Font family of the legend title.", -:legendtitlefontsize => "Integer. Font pointsize the legend title.", -:legendtitlefonthalign => "Symbol. Font horizontal alignment of the legend title: :hcenter, :left, :right or :center", -:legendtitlefontvalign => "Symbol. Font vertical alignment of the legend title: :vcenter, :top, :bottom or :center", -:legendtitlefontrotation => "Real. Font rotation of the legend title", -:legendtitlefontcolor => "Color Type. Font color of the legend title", -:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)", -:clims => "`:auto`, NTuple{2,Number}, or a function that takes series data in and returns NTuple{2,Number}. Fixes the limits of the colorbar.", -:legendfont => "Font. Font of legend items.", -:legendtitlefont => "Font. Font of the legend title.", -:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.", -:projection => "Symbol or String. '3d' or 'polar'", -:aspect_ratio => "Symbol (:equal or :none) or Number. Plot area is resized so that 1 y-unit is the same size as `aspect_ratio` x-units. With `:none`, images inherit aspect ratio of the plot area.", -:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.", -:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.", -:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.", -:right_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the right of the subplot.", -:bottom_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the bottom of the subplot.", -:subplot_index => "Integer. Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list.", -:colorbar_title => "String. Title of colorbar.", -:framestyle => "Symbol. Style of the axes frame. Choose from $(_allFramestyles)", -:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots", +:title => "String. Subplot title.", +:titlelocation => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`", +:titlefontfamily => "String or Symbol. Font family of subplot title.", +:titlefontsize => "Integer. Font pointsize of subplot title.", +:titlefonthalign => "Symbol. Font horizontal alignment of subplot title: :hcenter, :left, :right or :center", +:titlefontvalign => "Symbol. Font vertical alignment of subplot title: :vcenter, :top, :bottom or :center", +:titlefontrotation => "Real. Font rotation of subplot title", +:titlefontcolor => "Color Type. Font color of subplot title", +:background_color_subplot => "Color Type or `:match` (matches `:background_color`). Base background color of the subplot.", +:background_color_legend => "Color Type or `:match` (matches `:background_color_subplot`). Background color of the legend.", +:background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).", +:foreground_color_subplot => "Color Type or `:match` (matches `:foreground_color`). Base foreground color of the subplot.", +:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.", +:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.", +:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.", +:legend => "Bool (show the legend?) or (x,y) tuple or Symbol (legend position) or angle or (angle,inout) tuple. Bottom left corner of legend is placed at (x,y). Symbol values: `:none`; `:best`; `:inline`; `:inside`; `:legend`; any valid combination of `:(outer ?)(top/bottom ?)(right/left ?)`, i.e.: `:top`, `:topright`, `:outerleft`, `:outerbottomright` ... (note: only some may be supported in each backend). Legend is positioned at (angle degrees) (so (90,:outer) is roughly equivalent to :outertop), close to the inside of the axes or the outside if inout=:outer.", +:legendfontfamily => "String or Symbol. Font family of legend entries.", +:legendfontsize => "Integer. Font pointsize of legend entries.", +:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center", +:legendfontvalign => "Symbol. Font vertical alignment of legend entries: :vcenter, :top, :bottom or :center", +:legendfontrotation => "Real. Font rotation of legend entries", +:legendfontcolor => "Color Type. Font color of legend entries", +:legendtitle => "String. Legend title.", +:legendtitlefontfamily => "String or Symbol. Font family of the legend title.", +:legendtitlefontsize => "Integer. Font pointsize the legend title.", +:legendtitlefonthalign => "Symbol. Font horizontal alignment of the legend title: :hcenter, :left, :right or :center", +:legendtitlefontvalign => "Symbol. Font vertical alignment of the legend title: :vcenter, :top, :bottom or :center", +:legendtitlefontrotation => "Real. Font rotation of the legend title", +:legendtitlefontcolor => "Color Type. Font color of the legend title", +:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)", +:clims => "`:auto`, NTuple{2,Number}, or a function that takes series data in and returns NTuple{2,Number}. Fixes the limits of the colorbar.", +:colorbar_fontfamily => "String or Symbol. Font family of colobar entries.", +:colorbar_ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`", +:colorbar_tickfontfamily => "String or Symbol. Font family of colorbar tick labels.", +:colorbar_tickfontsize => "Integer. Font pointsize of colorbar tick entries.", +:colorbar_tickfontcolor => "Color Type. Font color of colorbar tick entries", +:colorbar_scale => "Symbol. Scale of the colorbar axis: `:none`, `:ln`, `:log2`, `:log10`", +:colorbar_formatter => "Function, :scientific, :plain or :auto. A method which converts a number to a string for tick labeling.", +:legendfont => "Font. Font of legend items.", +:legendtitlefont => "Font. Font of the legend title.", +:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.", +:projection => "Symbol or String. '3d' or 'polar'", +:aspect_ratio => "Symbol (:equal or :none) or Number. Plot area is resized so that 1 y-unit is the same size as `aspect_ratio` x-units. With `:none`, images inherit aspect ratio of the plot area.", +:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.", +:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.", +:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.", +:right_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the right of the subplot.", +:bottom_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the bottom of the subplot.", +:subplot_index => "Integer. Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list.", +:colorbar_title => "String. Title of colorbar.", +:framestyle => "Symbol. Style of the axes frame. Choose from $(_allFramestyles)", +:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots", # axis args -:guide => "String. Axis guide (label).", -:guide_position => "Symbol. Position of axis guides: :top, :bottom, :left or :right", -:lims => "NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example). `:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]", -:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`", -:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`", -:rotation => "Number. Degrees rotation of tick labels.", -:flip => "Bool. Should we flip (reverse) the axis?", -:formatter => "Function, :scientific, :plain or :auto. A method which converts a number to a string for tick labeling.", -:tickfontfamily => "String or Symbol. Font family of tick labels.", -:tickfontsize => "Integer. Font pointsize of tick labels.", -:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center", -:tickfontvalign => "Symbol. Font vertical alignment of tick labels: :vcenter, :top, :bottom or :center", -:tickfontrotation => "Real. Font rotation of tick labels", -:tickfontcolor => "Color Type. Font color of tick labels", -:guidefontfamily => "String or Symbol. Font family of axes guides.", -:guidefontsize => "Integer. Font pointsize of axes guides.", -:guidefonthalign => "Symbol. Font horizontal alignment of axes guides: :hcenter, :left, :right or :center", -:guidefontvalign => "Symbol. Font vertical alignment of axes guides: :vcenter, :top, :bottom or :center", -:guidefontrotation => "Real. Font rotation of axes guides", -:guidefontcolor => "Color Type. Font color of axes guides", -:foreground_color_axis => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis ticks.", -:foreground_color_border => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of plot area border (spines).", -:foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.", -:foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).", -:mirror => "Bool. Switch the side of the tick labels (right or top).", -:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`", -:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.", -:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.", -:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)", -:gridlinewidth => "Number. Width of the grid lines (in pixels)", +:guide => "String. Axis guide (label).", +:guide_position => "Symbol. Position of axis guides: :top, :bottom, :left or :right", +:lims => "NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example). `:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]", +:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`", +:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`", +:rotation => "Number. Degrees rotation of tick labels.", +:flip => "Bool. Should we flip (reverse) the axis?", +:formatter => "Function, :scientific, :plain or :auto. A method which converts a number to a string for tick labeling.", +:tickfontfamily => "String or Symbol. Font family of tick labels.", +:tickfontsize => "Integer. Font pointsize of tick labels.", +:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center", +:tickfontvalign => "Symbol. Font vertical alignment of tick labels: :vcenter, :top, :bottom or :center", +:tickfontrotation => "Real. Font rotation of tick labels", +:tickfontcolor => "Color Type. Font color of tick labels", +:guidefontfamily => "String or Symbol. Font family of axes guides.", +:guidefontsize => "Integer. Font pointsize of axes guides.", +:guidefonthalign => "Symbol. Font horizontal alignment of axes guides: :hcenter, :left, :right or :center", +:guidefontvalign => "Symbol. Font vertical alignment of axes guides: :vcenter, :top, :bottom or :center", +:guidefontrotation => "Real. Font rotation of axes guides", +:guidefontcolor => "Color Type. Font color of axes guides", +:foreground_color_axis => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis ticks.", +:foreground_color_border => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of plot area border (spines).", +:foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.", +:foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).", +:mirror => "Bool. Switch the side of the tick labels (right or top).", +:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`", +:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.", +:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.", +:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)", +:gridlinewidth => "Number. Width of the grid lines (in pixels)", :foreground_color_minor_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of minor grid lines.", -:minorgrid => "Bool. Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines", -:minorticks => "Integer. Intervals to divide the gap between major ticks into", -:minorgridalpha => "Number in [0,1]. The alpha/opacity override for the minorgrid lines.", -:minorgridstyle => "Symbol. Style of the minor grid lines. Choose from $(_allStyles)", -:minorgridlinewidth => "Number. Width of the minor grid lines (in pixels)", -:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`", -:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`", -:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.", -:draw_arrow => "Bool. Draw arrow at the end of the axis.", +:minorgrid => "Bool. Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines", +:minorticks => "Integer. Intervals to divide the gap between major ticks into", +:minorgridalpha => "Number in [0,1]. The alpha/opacity override for the minorgrid lines.", +:minorgridstyle => "Symbol. Style of the minor grid lines. Choose from $(_allStyles)", +:minorgridlinewidth => "Number. Width of the minor grid lines (in pixels)", +:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`", +:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`", +:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.", +:draw_arrow => "Bool. Draw arrow at the end of the axis.", ) diff --git a/src/args.jl b/src/args.jl index 3a3c963c..2452dfdf 100644 --- a/src/args.jl +++ b/src/args.jl @@ -151,54 +151,54 @@ const _shape_keys = Symbol[ const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes)))) const _markerAliases = Dict{Symbol,Symbol}( - :n => :none, - :no => :none, - :a => :auto, - :ellipse => :circle, - :c => :circle, - :circ => :circle, - :square => :rect, - :sq => :rect, - :r => :rect, - :d => :diamond, - :^ => :utriangle, - :ut => :utriangle, - :utri => :utriangle, - :uptri => :utriangle, - :uptriangle => :utriangle, - :v => :dtriangle, - :V => :dtriangle, - :dt => :dtriangle, - :dtri => :dtriangle, - :downtri => :dtriangle, - :downtriangle => :dtriangle, - :> => :rtriangle, - :rt => :rtriangle, - :rtri => :rtriangle, + :n => :none, + :no => :none, + :a => :auto, + :ellipse => :circle, + :c => :circle, + :circ => :circle, + :square => :rect, + :sq => :rect, + :r => :rect, + :d => :diamond, + :^ => :utriangle, + :ut => :utriangle, + :utri => :utriangle, + :uptri => :utriangle, + :uptriangle => :utriangle, + :v => :dtriangle, + :V => :dtriangle, + :dt => :dtriangle, + :dtri => :dtriangle, + :downtri => :dtriangle, + :downtriangle => :dtriangle, + :> => :rtriangle, + :rt => :rtriangle, + :rtri => :rtriangle, :righttri => :rtriangle, :righttriangle => :rtriangle, - :< => :ltriangle, - :lt => :ltriangle, - :ltri => :ltriangle, + :< => :ltriangle, + :lt => :ltriangle, + :ltri => :ltriangle, :lighttri => :ltriangle, :lighttriangle => :ltriangle, - # :+ => :cross, - :plus => :cross, - # :x => :xcross, - :X => :xcross, - :star => :star5, - :s => :star5, - :star1 => :star5, - :s2 => :star8, - :star2 => :star8, - :p => :pentagon, - :pent => :pentagon, - :h => :hexagon, - :hex => :hexagon, - :hep => :heptagon, - :o => :octagon, - :oct => :octagon, - :spike => :vline, + # :+ => :cross, + :plus => :cross, + # :x => :xcross, + :X => :xcross, + :star => :star5, + :s => :star5, + :star1 => :star5, + :s2 => :star8, + :star2 => :star8, + :p => :pentagon, + :pent => :pentagon, + :h => :hexagon, + :hex => :hexagon, + :hep => :heptagon, + :o => :octagon, + :oct => :octagon, + :spike => :vline, ) const _positionAliases = Dict{Symbol,Symbol}( @@ -273,58 +273,58 @@ const _bar_width = 0.8 # ----------------------------------------------------------------------------- const _series_defaults = KW( - :label => :auto, - :colorbar_entry => true, - :seriescolor => :auto, - :seriesalpha => nothing, - :seriestype => :path, - :linestyle => :solid, - :linewidth => :auto, - :linecolor => :auto, - :linealpha => nothing, - :fillrange => nothing, # ribbons, areas, etc - :fillcolor => :match, - :fillalpha => nothing, - :markershape => :none, - :markercolor => :match, - :markeralpha => nothing, - :markersize => 4, - :markerstrokestyle => :solid, - :markerstrokewidth => 1, - :markerstrokecolor => :match, - :markerstrokealpha => nothing, - :bins => :auto, # number of bins for hists - :smooth => false, # regression line? - :group => nothing, # groupby vector - :x => nothing, - :y => nothing, - :z => nothing, # depth for contour, surface, etc - :marker_z => nothing, # value for color scale - :line_z => nothing, - :fill_z => nothing, - :levels => 15, - :orientation => :vertical, - :bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side) - :bar_width => nothing, - :bar_edges => false, - :xerror => nothing, - :yerror => nothing, - :zerror => nothing, - :ribbon => nothing, - :quiver => nothing, - :arrow => nothing, # allows for adding arrows to line/path... call `arrow(args...)` - :normalize => false, # do we want a normalized histogram? - :weights => nothing, # optional weights for histograms (1D and 2D) - :show_empty_bins => false, # should empty bins in 2D histogram be colored as zero (otherwise they are transparent) - :contours => false, # add contours to 3d surface and wireframe plots - :contour_labels => false, - :subplot => :auto, # which subplot(s) does this series belong to? + :label => :auto, + :colorbar_entry => true, + :seriescolor => :auto, + :seriesalpha => nothing, + :seriestype => :path, + :linestyle => :solid, + :linewidth => :auto, + :linecolor => :auto, + :linealpha => nothing, + :fillrange => nothing, # ribbons, areas, etc + :fillcolor => :match, + :fillalpha => nothing, + :markershape => :none, + :markercolor => :match, + :markeralpha => nothing, + :markersize => 4, + :markerstrokestyle => :solid, + :markerstrokewidth => 1, + :markerstrokecolor => :match, + :markerstrokealpha => nothing, + :bins => :auto, # number of bins for hists + :smooth => false, # regression line? + :group => nothing, # groupby vector + :x => nothing, + :y => nothing, + :z => nothing, # depth for contour, surface, etc + :marker_z => nothing, # value for color scale + :line_z => nothing, + :fill_z => nothing, + :levels => 15, + :orientation => :vertical, + :bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side) + :bar_width => nothing, + :bar_edges => false, + :xerror => nothing, + :yerror => nothing, + :zerror => nothing, + :ribbon => nothing, + :quiver => nothing, + :arrow => nothing, # allows for adding arrows to line/path... call `arrow(args...)` + :normalize => false, # do we want a normalized histogram? + :weights => nothing, # optional weights for histograms (1D and 2D) + :show_empty_bins => false, # should empty bins in 2D histogram be colored as zero (otherwise they are transparent) + :contours => false, # add contours to 3d surface and wireframe plots + :contour_labels => false, + :subplot => :auto, # which subplot(s) does this series belong to? :series_annotations => nothing, # a list of annotations which apply to the coordinates of this series :primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow # one logical series to be broken up (path and markers, for example) :hover => nothing, # text to display when hovering over the data points :stride => (1,1), # array stride for wireframe/surface, the first element is the row stride and the second is the column stride. - :connections => nothing, # tuple of arrays to specifiy connectivity of a 3d mesh + :connections => nothing, # tuple of arrays to specifiy connectivity of a 3d mesh :extra_kwargs => Dict() ) @@ -363,104 +363,115 @@ const _plot_defaults = KW( const _subplot_defaults = KW( - :title => "", - :titlelocation => :center, # also :left or :right - :fontfamily_subplot => :match, - :titlefontfamily => :match, - :titlefontsize => 14, - :titlefonthalign => :hcenter, - :titlefontvalign => :vcenter, - :titlefontrotation => 0.0, - :titlefontcolor => :match, - :background_color_subplot => :match, # default for other bg colors... match takes plot default - :background_color_legend => :match, # background of legend - :background_color_inside => :match, # background inside grid - :foreground_color_subplot => :match, # default for other fg colors... match takes plot default - :foreground_color_legend => :match, # foreground of legend - :foreground_color_title => :match, # title color - :color_palette => :auto, - :legend => :best, - :legendtitle => nothing, - :colorbar => :legend, - :clims => :auto, - :legendfontfamily => :match, - :legendfontsize => 8, - :legendfonthalign => :hcenter, - :legendfontvalign => :vcenter, - :legendfontrotation => 0.0, - :legendfontcolor => :match, - :legendtitlefontfamily => :match, - :legendtitlefontsize => 11, - :legendtitlefonthalign => :hcenter, - :legendtitlefontvalign => :vcenter, - :legendtitlefontrotation => 0.0, - :legendtitlefontcolor => :match, - :annotations => [], # annotation tuples... list of (x,y,annotation) - :projection => :none, # can also be :polar or :3d - :aspect_ratio => :auto, # choose from :none or :equal - :margin => 1mm, - :left_margin => :match, - :top_margin => :match, - :right_margin => :match, - :bottom_margin => :match, - :subplot_index => -1, - :colorbar_title => "", - :colorbar_titlefontsize => 10, - :colorbar_title_location => :center, # also :left or :right - :colorbar_fontfamily => :match, - :colorbar_titlefontfamily => :match, - :colorbar_titlefonthalign => :hcenter, - :colorbar_titlefontvalign => :vcenter, - :colorbar_titlefontrotation => 0.0, - :colorbar_titlefontcolor => :match, - :framestyle => :axes, - :camera => (30,30), - :extra_kwargs => Dict() + :title => "", + :titlelocation => :center, # also :left or :right + :fontfamily_subplot => :match, + :titlefontfamily => :match, + :titlefontsize => 14, + :titlefonthalign => :hcenter, + :titlefontvalign => :vcenter, + :titlefontrotation => 0.0, + :titlefontcolor => :match, + :background_color_subplot => :match, # default for other bg colors... match takes plot default + :background_color_legend => :match, # background of legend + :background_color_inside => :match, # background inside grid + :foreground_color_subplot => :match, # default for other fg colors... match takes plot default + :foreground_color_legend => :match, # foreground of legend + :foreground_color_title => :match, # title color + :color_palette => :auto, + :legend => :best, + :legendtitle => nothing, + :colorbar => :legend, + :clims => :auto, + :colorbar_fontfamily => :match, + :colorbar_ticks => :auto, + :colorbar_tickfontfamily => :match, + :colorbar_tickfontsize => 8, + :colorbar_tickfonthalign => :hcenter, + :colorbar_tickfontvalign => :vcenter, + :colorbar_tickfontrotation => 0.0, + :colorbar_tickfontcolor => :match, + :colorbar_scale => :identity, + :colorbar_formatter => :auto, + :colorbar_discrete_values => [], + :colorbar_continuous_values => zeros(0), + :legendfontfamily => :match, + :legendfontsize => 8, + :legendfonthalign => :hcenter, + :legendfontvalign => :vcenter, + :legendfontrotation => 0.0, + :legendfontcolor => :match, + :legendtitlefontfamily => :match, + :legendtitlefontsize => 11, + :legendtitlefonthalign => :hcenter, + :legendtitlefontvalign => :vcenter, + :legendtitlefontrotation => 0.0, + :legendtitlefontcolor => :match, + :annotations => [], # annotation tuples... list of (x,y,annotation) + :projection => :none, # can also be :polar or :3d + :aspect_ratio => :auto, # choose from :none or :equal + :margin => 1mm, + :left_margin => :match, + :top_margin => :match, + :right_margin => :match, + :bottom_margin => :match, + :subplot_index => -1, + :colorbar_title => "", + :colorbar_titlefontsize => 10, + :colorbar_title_location => :center, # also :left or :right + :colorbar_titlefontfamily => :match, + :colorbar_titlefonthalign => :hcenter, + :colorbar_titlefontvalign => :vcenter, + :colorbar_titlefontrotation => 0.0, + :colorbar_titlefontcolor => :match, + :framestyle => :axes, + :camera => (30,30), + :extra_kwargs => Dict() ) const _axis_defaults = KW( - :guide => "", - :guide_position => :auto, - :lims => :auto, - :ticks => :auto, - :scale => :identity, - :rotation => 0, - :flip => false, - :link => [], - :tickfontfamily => :match, - :tickfontsize => 8, - :tickfonthalign => :hcenter, - :tickfontvalign => :vcenter, - :tickfontrotation => 0.0, - :tickfontcolor => :match, - :guidefontfamily => :match, - :guidefontsize => 11, - :guidefonthalign => :hcenter, - :guidefontvalign => :vcenter, - :guidefontrotation => 0.0, - :guidefontcolor => :match, - :foreground_color_axis => :match, # axis border/tick colors, - :foreground_color_border => :match, # plot area border/spines, - :foreground_color_text => :match, # tick text color, - :foreground_color_guide => :match, # guide text color, - :discrete_values => [], - :formatter => :auto, - :mirror => false, - :grid => true, - :foreground_color_grid => :match, # grid color - :gridalpha => 0.1, - :gridstyle => :solid, - :gridlinewidth => 0.5, + :guide => "", + :guide_position => :auto, + :lims => :auto, + :ticks => :auto, + :scale => :identity, + :rotation => 0, + :flip => false, + :link => [], + :tickfontfamily => :match, + :tickfontsize => 8, + :tickfonthalign => :hcenter, + :tickfontvalign => :vcenter, + :tickfontrotation => 0.0, + :tickfontcolor => :match, + :guidefontfamily => :match, + :guidefontsize => 11, + :guidefonthalign => :hcenter, + :guidefontvalign => :vcenter, + :guidefontrotation => 0.0, + :guidefontcolor => :match, + :foreground_color_axis => :match, # axis border/tick colors, + :foreground_color_border => :match, # plot area border/spines, + :foreground_color_text => :match, # tick text color, + :foreground_color_guide => :match, # guide text color, + :discrete_values => [], + :formatter => :auto, + :mirror => false, + :grid => true, + :foreground_color_grid => :match, # grid color + :gridalpha => 0.1, + :gridstyle => :solid, + :gridlinewidth => 0.5, :foreground_color_minor_grid => :match, # grid color - :minorgridalpha => 0.05, - :minorgridstyle => :solid, - :minorgridlinewidth => 0.5, - :tick_direction => :in, - :minorticks => false, - :minorgrid => false, - :showaxis => true, - :widen => true, - :draw_arrow => false, + :minorgridalpha => 0.05, + :minorgridstyle => :solid, + :minorgridlinewidth => 0.5, + :tick_direction => :in, + :minorticks => false, + :minorgrid => false, + :showaxis => true, + :widen => true, + :draw_arrow => false, ) const _suppress_warnings = Set{Symbol}([ @@ -1329,10 +1340,10 @@ const _match_map = KW( :background_color_inside => :background_color_subplot, :foreground_color_legend => :foreground_color_subplot, :foreground_color_title => :foreground_color_subplot, - :left_margin => :margin, - :top_margin => :margin, - :right_margin => :margin, - :bottom_margin => :margin, + :left_margin => :margin, + :top_margin => :margin, + :right_margin => :margin, + :bottom_margin => :margin, :titlefontfamily => :fontfamily_subplot, :titlefontcolor => :foreground_color_subplot, :legendfontfamily => :fontfamily_subplot, @@ -1342,6 +1353,8 @@ const _match_map = KW( :colorbar_fontfamily => :fontfamily_subplot, :colorbar_titlefontfamily => :fontfamily_subplot, :colorbar_titlefontcolor => :foreground_color_subplot, + :colorbar_tickfontfamily => :fontfamily_subplot, + :colorbar_tickfontcolor => :foreground_color_subplot, :plot_titlefontfamily => :fontfamily, :plot_titlefontcolor => :foreground_color, :tickfontcolor => :foreground_color_text, @@ -1580,6 +1593,7 @@ function _update_subplot_args(plt::Plot, sp::Subplot, plotattributes_in, subplot lims_warned = true end end + _update_subplot_colorbars(sp) end # ----------------------------------------------------------------------------- diff --git a/src/axes.jl b/src/axes.jl index 64bee8c6..7173999e 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -137,11 +137,10 @@ const _label_func_tex = Dict{Symbol,Function}( labelfunc_tex(scale::Symbol) = get(_label_func_tex, scale, convert_sci_unicode) -function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing) - amin, amax = axis_limits(sp, axis[:letter]) +function optimal_ticks_and_labels(ticks, alims, scale, formatter) + amin, amax = alims # scale the limits - scale = axis[:scale] sf = RecipesPipeline.scale_func(scale) # If the axis input was a Date or DateTime use a special logic to find @@ -152,7 +151,7 @@ function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing) # rather than on the input format # TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime if ticks === nothing && scale == :identity - if axis[:formatter] == RecipesPipeline.dateformatter + if formatter == RecipesPipeline.dateformatter # optimize_datetime_ticks returns ticks and labels(!) based on # integers/floats corresponding to the DateTime type. Thus, the axes # limits, which resulted from converting the Date type to integers, @@ -163,7 +162,7 @@ function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing) k_min = 2, k_max = 4) # Now the ticks are converted back to floats corresponding to Dates. return ticks / 864e5, labels - elseif axis[:formatter] == RecipesPipeline.datetimeformatter + elseif formatter == RecipesPipeline.datetimeformatter return optimize_datetime_ticks(amin, amax; k_min = 2, k_max = 4) end end @@ -187,14 +186,13 @@ function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing) # chosen ticks is not too much bigger than amin - amax: strict_span = false, ) - axis[:lims] = map(RecipesPipeline.inverse_scale_func(scale), (viewmin, viewmax)) + # axis[:lims] = map(RecipesPipeline.inverse_scale_func(scale), (viewmin, viewmax)) else scaled_ticks = map(sf, (filter(t -> amin <= t <= amax, ticks))) end unscaled_ticks = map(RecipesPipeline.inverse_scale_func(scale), scaled_ticks) labels = if any(isfinite, unscaled_ticks) - formatter = axis[:formatter] if formatter in (:auto, :plain, :scientific, :engineering) map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, formatter)) elseif formatter == :latex @@ -221,52 +219,53 @@ end # return (continuous_values, discrete_values) for the ticks on this axis function get_ticks(sp::Subplot, axis::Axis; update = true) if update || !haskey(axis.plotattributes, :optimized_ticks) + dvals = axis[:discrete_values] ticks = _transform_ticks(axis[:ticks]) - if ticks in (:none, nothing, false) - axis.plotattributes[:optimized_ticks] = nothing + axis.plotattributes[:optimized_ticks] = if ticks isa Symbol && ticks !== :none && + ispolar(sp) && axis[:letter] === :x && !isempty(dvals) + collect(0:pi/4:7pi/4), string.(0:45:315) else - # treat :native ticks as :auto - ticks = ticks == :native ? :auto : ticks - - dvals = axis[:discrete_values] - cv, dv = if typeof(ticks) <: Symbol - if !isempty(dvals) - # discrete ticks... - n = length(dvals) - rng = if ticks == :auto && n > 15 - Δ = ceil(Int, n / 10) - Δ:Δ:n - else # if ticks == :all - 1:n - end - axis[:continuous_values][rng], dvals[rng] - elseif ispolar(axis.sps[1]) && axis[:letter] == :x - #force theta axis to be full circle - (collect(0:pi/4:7pi/4), string.(0:45:315)) - else - # compute optimal ticks and labels - optimal_ticks_and_labels(sp, axis) - end - elseif typeof(ticks) <: Union{AVec, Int} - if !isempty(dvals) && typeof(ticks) <: Int - rng = Int[round(Int,i) for i in range(1, stop=length(dvals), length=ticks)] - axis[:continuous_values][rng], dvals[rng] - else - # override ticks, but get the labels - optimal_ticks_and_labels(sp, axis, ticks) - end - elseif typeof(ticks) <: NTuple{2, Any} - # assuming we're passed (ticks, labels) - ticks - else - error("Unknown ticks type in get_ticks: $(typeof(ticks))") - end - axis.plotattributes[:optimized_ticks] = (cv, dv) + cvals = axis[:continuous_values] + alims = axis_limits(sp, axis[:letter]) + scale = axis[:scale] + formatter = axis[:formatter] + get_ticks(ticks, cvals, dvals, alims, scale, formatter) end end - axis.plotattributes[:optimized_ticks] + return axis.plotattributes[:optimized_ticks] end +function get_ticks(ticks::Symbol, cvals::T, dvals, args...) where T + if ticks === :none + return T[], String[] + elseif !isempty(dvals) + n = length(dvals) + if ticks === :all || n < 16 + return cvals, string.(dvals) + else + Δ = ceil(Int, n / 10) + rng = Δ:Δ:n + return cvals[rng], string.(dvals[rng]) + end + else + return optimal_ticks_and_labels(nothing, args...) + end +end +get_ticks(ticks::AVec, cvals, dvals, args...) = optimal_ticks_and_labels(ticks, args...) +function get_ticks(ticks::Int, dvals, cvals, args...) + if !isempty(dvals) + rng = round.(Int, range(1, stop=length(dvals), length=ticks)) + cvals[rng], string.(dvals[rng]) + else + optimal_ticks_and_labels(ticks, args...) + end +end +get_ticks(ticks::NTuple{2, Any}, args...) = ticks +get_ticks(::Nothing, cvals::T, args...) where T = T[], String[] +get_ticks(ticks::Bool, args...) = + ticks ? get_ticks(:auto, args...) : get_ticks(nothing, args...) +get_ticks(::T, args...) where T = error("Unknown ticks type in get_ticks: $T") + _transform_ticks(ticks) = ticks _transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks) _transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2]) diff --git a/src/backends.jl b/src/backends.jl index 6164441d..c6c54f22 100644 --- a/src/backends.jl +++ b/src/backends.jl @@ -531,6 +531,10 @@ const _pyplot_attr = merge_with_base_supported([ :guidefontfamily, :guidefontsize, :guidefontcolor, :grid, :gridalpha, :gridstyle, :gridlinewidth, :legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry, + :colorbar_ticks, :colorbar_tickfontfamily, :colorbar_tickfontsize, + :colorbar_tickfonthalign, :colorbar_tickfontvalign, + :colorbar_tickfontrotation, :colorbar_tickfontcolor, + :colorbar_scale, :marker_z, :line_z, :fill_z, :levels, :ribbon, :quiver, :arrow, diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 79e2e60c..5e1425c7 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -743,7 +743,7 @@ function _update_min_padding!(sp::Subplot{GRBackend}) xticks, yticks, zticks = get_ticks(sp, xaxis), get_ticks(sp, yaxis), get_ticks(sp, zaxis) # Add margin for x and y ticks h = 0mm - if !(xticks in (nothing, false, :none)) + if !isempty(first(xticks)) gr_set_font( tickfont(xaxis), halign = (:left, :hcenter, :right)[sign(xaxis[:rotation]) + 2], @@ -754,7 +754,7 @@ function _update_min_padding!(sp::Subplot{GRBackend}) l = 0.01 + last(gr_get_ticks_size(xticks, xaxis[:rotation])) h = max(h, 1mm + get_size(sp)[2] * l * px) end - if !(yticks in (nothing, false, :none)) + if !isempty(first(yticks)) gr_set_font( tickfont(yaxis), halign = (:left, :hcenter, :right)[sign(yaxis[:rotation]) + 2], @@ -774,7 +774,7 @@ function _update_min_padding!(sp::Subplot{GRBackend}) end end - if !(zticks in (nothing, false, :none)) + if !isempty(first(zticks)) gr_set_font( tickfont(zaxis), halign = (zaxis[:mirror] ? :left : :right), @@ -825,7 +825,7 @@ function _update_min_padding!(sp::Subplot{GRBackend}) else # Add margin for x and y ticks xticks, yticks = get_ticks(sp, sp[:xaxis]), get_ticks(sp, sp[:yaxis]) - if !(xticks in (nothing, false, :none)) + if !isempty(first(xticks)) gr_set_tickfont(sp, :x) l = 0.01 + last(gr_get_ticks_size(xticks, sp[:xaxis][:rotation])) h = 1mm + get_size(sp)[2] * l * px @@ -835,7 +835,7 @@ function _update_min_padding!(sp::Subplot{GRBackend}) bottompad += h end end - if !(yticks in (nothing, false, :none)) + if !isempty(first(yticks)) gr_set_tickfont(sp, :y) l = 0.01 + first(gr_get_ticks_size(yticks, sp[:yaxis][:rotation])) w = 1mm + get_size(sp)[1] * l * px diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index dbcd1813..aca6bb64 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -807,31 +807,37 @@ function py_compute_axis_minval(sp::Subplot, axis::Axis) minval end -function py_set_scale(ax, sp::Subplot, axis::Axis) - scale = axis[:scale] - letter = axis[:letter] +function py_set_scale(ax, sp::Subplot, scale::Symbol, letter::Symbol) scale in supported_scales() || return @warn("Unhandled scale value in pyplot: $scale") func = getproperty(ax, Symbol("set_", letter, "scale")) if PyPlot.version ≥ v"3.3" # https://matplotlib.org/3.3.0/api/api_changes.html - letter = Symbol("") + pyletter = Symbol("") + else + pyletter = letter end kw = KW() arg = if scale == :identity "linear" else - kw[Symbol(:base,letter)] = if scale == :ln + kw[Symbol(:base, pyletter)] = if scale == :ln ℯ elseif scale == :log2 2 elseif scale == :log10 10 end - kw[Symbol(:linthresh,letter)] = NaNMath.max(1e-16, py_compute_axis_minval(sp, axis)) + axis = sp[Symbol(letter, :axis)] + kw[Symbol(:linthresh, pyletter)] = NaNMath.max(1e-16, py_compute_axis_minval(sp, axis)) "symlog" end func(arg; kw...) end +function py_set_scale(ax, sp::Subplot, axis::Axis) + scale = axis[:scale] + letter = axis[:letter] + py_set_scale(ax, sp, scale, letter) +end function py_set_axis_colors(sp, ax, a::Axis) for (loc, spine) in ax.spines @@ -972,24 +978,31 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) end - cb."set_label"(sp[:colorbar_title],size=py_thickness_scale(plt, sp[:yaxis][:guidefontsize]),family=sp[:yaxis][:guidefontfamily], color = py_color(sp[:yaxis][:guidefontcolor])) + cb."set_label"(sp[:colorbar_title],size=py_thickness_scale(plt, sp[:colorbar_titlefontsize]),family=sp[:colorbar_titlefontfamily], color = py_color(sp[:colorbar_titlefontcolor])) # cb."formatter".set_useOffset(false) # This for some reason does not work, must be a pyplot bug, instead this is a workaround: cb."formatter".set_powerlimits((-Inf, Inf)) cb."update_ticks"() + env = "\\mathregular" # matches the outer fonts https://matplotlib.org/tutorials/text/mathtext.html + ticks = get_colorbar_ticks(sp) + if sp[:colorbar] in (:top, :bottom) axis = sp[:xaxis] # colorbar inherits from x axis cbar_axis = cb."ax"."xaxis" + ticks_letter=:x else axis = sp[:yaxis] # colorbar inherits from y axis cbar_axis = cb."ax"."yaxis" + ticks_letter=:y end + py_set_scale(cb.ax, sp, sp[:colorbar_scale], ticks_letter) + sp[:colorbar_ticks] == :native ? nothing : py_set_ticks(cb.ax, ticks, ticks_letter, env) for lab in cbar_axis."get_ticklabels"() - lab."set_fontsize"(py_thickness_scale(plt, axis[:tickfontsize])) - lab."set_family"(axis[:tickfontfamily]) - lab."set_color"(py_color(axis[:tickfontcolor])) + lab."set_fontsize"(py_thickness_scale(plt, sp[:colorbar_tickfontsize])) + lab."set_family"(sp[:colorbar_tickfontfamily]) + lab."set_color"(py_color(sp[:colorbar_tickfontcolor])) end # Adjust thickness of the cbar ticks diff --git a/src/colorbars.jl b/src/colorbars.jl new file mode 100644 index 00000000..b0f9996a --- /dev/null +++ b/src/colorbars.jl @@ -0,0 +1,90 @@ +# 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 +# don't specialize on ::Function otherwise python functions won't work +process_clims(f) = f + +function get_clims(sp::Subplot, op=process_clims(sp[:clims])) + zmin, zmax = Inf, -Inf + for series in series_list(sp) + if series[:colorbar_entry] + zmin, zmax = _update_clims(zmin, zmax, get_clims(series, op)...) + end + end + return zmin <= zmax ? (zmin, zmax) : (NaN, NaN) +end + +function get_clims(sp::Subplot, series::Series, op=process_clims(sp[:clims])) + zmin, zmax = if series[:colorbar_entry] + get_clims(sp, op) + else + get_clims(series, op) + end + return zmin <= zmax ? (zmin, zmax) : (NaN, NaN) +end + +""" + get_clims(::Series, op=Plots.ignorenan_extrema) + +Finds the limits for the colorbar by taking the "z-values" for the series and passing them into `op`, +which must return the tuple `(zmin, zmax)`. The default op is the extrema of the finite +values of the input. +""" +function get_clims(series::Series, op=ignorenan_extrema) + zmin, zmax = Inf, -Inf + z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin) + for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z]) + if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing, Real}) + zmin, zmax = _update_clims(zmin, zmax, op(vals.surf)...) + elseif (vals !== nothing) && (eltype(vals) <: Union{Missing, Real}) + zmin, zmax = _update_clims(zmin, zmax, op(vals)...) + end + end + return zmin <= zmax ? (zmin, zmax) : (NaN, NaN) +end + +_update_clims(zmin, zmax, emin, emax) = NaNMath.min(zmin, emin), NaNMath.max(zmax, emax) + +@enum ColorbarStyle cbar_gradient cbar_fill cbar_lines + +function colorbar_style(series::Series) + colorbar_entry = series[:colorbar_entry] + if !(colorbar_entry isa Bool) + @warn "Non-boolean colorbar_entry ignored." + colorbar_entry = true + end + + if !colorbar_entry + nothing + elseif isfilledcontour(series) + cbar_fill + elseif iscontour(series) + cbar_lines + elseif series[:seriestype] ∈ (:heatmap,:surface) || + any(series[z] !== nothing for z ∈ [:marker_z,:line_z,:fill_z]) + cbar_gradient + else + nothing + end +end + +hascolorbar(series::Series) = colorbar_style(series) !== nothing +hascolorbar(sp::Subplot) = sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp)) + +function get_colorbar_ticks(sp::Subplot; update = true) + if update || !haskey(sp.attr, :colorbar_optimized_ticks) + ticks = _transform_ticks(sp[:colorbar_ticks]) + cvals = sp[:colorbar_continuous_values] + dvals = sp[:colorbar_discrete_values] + clims = get_clims(sp) + scale = sp[:colorbar_scale] + formatter = sp[:colorbar_formatter] + sp.attr[:colorbar_optimized_ticks] = + get_ticks(ticks, cvals, dvals, clims, scale, formatter) + end + return sp.attr[:colorbar_optimized_ticks] +end + +function _update_subplot_colorbars(sp::Subplot) + # Dynamic callback from the pipeline if needed +end diff --git a/src/utils.jl b/src/utils.jl index a85f9d90..0bfd61ff 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -428,79 +428,6 @@ xlims(sp_idx::Int = 1) = xlims(current(), sp_idx) ylims(sp_idx::Int = 1) = ylims(current(), sp_idx) zlims(sp_idx::Int = 1) = zlims(current(), sp_idx) -# 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 -# don't specialize on ::Function otherwise python functions won't work -process_clims(f) = f - -function get_clims(sp::Subplot, op=process_clims(sp[:clims])) - zmin, zmax = Inf, -Inf - for series in series_list(sp) - if series[:colorbar_entry] - zmin, zmax = _update_clims(zmin, zmax, get_clims(series, op)...) - end - end - return zmin <= zmax ? (zmin, zmax) : (NaN, NaN) -end - -function get_clims(sp::Subplot, series::Series, op=process_clims(sp[:clims])) - zmin, zmax = if series[:colorbar_entry] - get_clims(sp, op) - else - get_clims(series, op) - end - return zmin <= zmax ? (zmin, zmax) : (NaN, NaN) -end - -""" - get_clims(::Series, op=Plots.ignorenan_extrema) - -Finds the limits for the colorbar by taking the "z-values" for the series and passing them into `op`, -which must return the tuple `(zmin, zmax)`. The default op is the extrema of the finite -values of the input. -""" -function get_clims(series::Series, op=ignorenan_extrema) - zmin, zmax = Inf, -Inf - z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin) - for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z]) - if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing, Real}) - zmin, zmax = _update_clims(zmin, zmax, op(vals.surf)...) - elseif (vals !== nothing) && (eltype(vals) <: Union{Missing, Real}) - zmin, zmax = _update_clims(zmin, zmax, op(vals)...) - end - end - return zmin <= zmax ? (zmin, zmax) : (NaN, NaN) -end - -_update_clims(zmin, zmax, emin, emax) = NaNMath.min(zmin, emin), NaNMath.max(zmax, emax) - -@enum ColorbarStyle cbar_gradient cbar_fill cbar_lines - -function colorbar_style(series::Series) - colorbar_entry = series[:colorbar_entry] - if !(colorbar_entry isa Bool) - @warn "Non-boolean colorbar_entry ignored." - colorbar_entry = true - end - - if !colorbar_entry - nothing - elseif isfilledcontour(series) - cbar_fill - elseif iscontour(series) - cbar_lines - elseif series[:seriestype] ∈ (:heatmap,:surface) || - any(series[z] !== nothing for z ∈ [:marker_z,:line_z,:fill_z]) - cbar_gradient - else - nothing - end -end - -hascolorbar(series::Series) = colorbar_style(series) !== nothing -hascolorbar(sp::Subplot) = sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp)) - iscontour(series::Series) = series[:seriestype] in (:contour, :contour3d) isfilledcontour(series::Series) = iscontour(series) && series[:fillrange] !== nothing