Package {maidr}


Type: Package
Title: Multimodal Access and Interactive Data Representation
Version: 0.5.0
URL: https://github.com/xability/r-maidr, https://r.maidr.ai/
BugReports: https://github.com/xability/r-maidr/issues
Description: Provides accessible, interactive visualizations through the 'MAIDR' (Multimodal Access and Interactive Data Representation) system. Converts 'ggplot2' and Base R plots into accessible HTML/SVG formats with keyboard navigation, screen reader support, and 'sonification' capabilities. Supports bar charts (simple, grouped, stacked), pie charts, histograms, line plots, step plots, scatter plots, box plots, violin plots, candlestick (OHLC) charts, heat maps, density/smooth curves, faceted plots, multi-panel layouts (including patchwork), and multi-layered plot combinations. Also makes 'plotly', 'highcharter' and 'echarts4r' 'htmlwidgets' accessible by attaching the matching 'MAIDR' JavaScript adapter. Enables data exploration for users with visual impairments through multiple sensory modalities. For more details see the 'MAIDR' project https://maidr.ai/.
License: GPL (≥ 3)
Encoding: UTF-8
Language: en
Depends: R (≥ 4.0.0)
Imports: base64enc, curl, ggplot2, ggplotify, grid, htmltools, htmlwidgets, jsonlite, methods, shiny, stats, svglite (≥ 2.1.1), xml2, rlang, R6
Suggests: testthat (≥ 3.0.0), lintr, styler, goodpractice, cyclocomp, knitr, rmarkdown, quarto, patchwork, scales, tibble, tidyquant, quantmod, TTR, zoo, xts, pkgload, hexbin, vioplot, wordcloud, sm, gridGraphics, pROC, yardstick, plotly, highcharter, echarts4r
Config/testthat/edition: 3
VignetteBuilder: knitr
Config/roxygen2/version: 8.1.0
NeedsCompilation: no
Packaged: 2026-09-29 02:29:16 UTC; jseo1005
Author: JooYoung Seo [aut, cph, cre], Niranjan Kalaiselvan [aut]
Maintainer: JooYoung Seo <jseo1005@illinois.edu>
Repository: CRAN
Date/Publication: 2026-09-29 21:20:22 UTC

maidr: Multimodal Access and Interactive Data Representation

Description

The 'maidr' package provides accessible, interactive visualizations through the MAIDR (Multimodal Access and Interactive Data Representation) system. It converts 'ggplot2' and Base R plots into accessible HTML/SVG formats with keyboard navigation, screen reader support, and sonification capabilities. This enables users with visual impairments to independently explore and understand data visualizations through multiple sensory modalities.

Main Functions

Supported Plot Types

The package supports a wide variety of plot types from both 'ggplot2' and Base R plotting systems:

ggplot2 plots:

Base R plots:

Experimental plot types: The types above are stable. The package also reads a longer list of charts as prototypes: none has been through a user study, and each may change without a deprecation period. In these docs an experimental type is marked [experimental] after its name; a type with no mark is stable. Among them:

The full list is under "Experimental Plot Types" in the README.

Accessibility Features

Integration

The package integrates seamlessly with:

Getting Started

To create your first accessible plot:

library(maidr)
library(ggplot2)

# Create a ggplot2 plot
p <- ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_boxplot()

# Display as interactive MAIDR plot
show(p)

# Or save to HTML file
save_html(p, file = "my_plot.html")

For Base R plots:

library(maidr)

# Create a Base R plot
barplot(table(mtcars$cyl))

# Display as interactive MAIDR plot
show()

Learn More

Author(s)

Maintainer: JooYoung Seo jseo1005@illinois.edu [copyright holder]

Authors:

See Also

Useful links:


Function Classification Maps

Description

Maps of function names to their classification levels

Usage

.base_r_function_classes

One rebuilt frame per layer, so a facet does not pay for it per panel

Description

process_facet_panel() runs once per panel, so a jittered layer asked for its undisplaced positions once per panel too – and each of those rebuilds the whole plot, every panel of it. Measured by counting ggplot2::ggplot_build calls through a facet of P panels: 3, 5 and 9 calls for P of 2, 4 and 8. One build is the original; the other P are the same answer computed P times over, so the work is quadratic in the panel count while the answer never varies – undisplaced_layer_data() depends only on the plot and the layer index.

Usage

.jitter_cache

Details

Keyed on the layer itself rather than on the plot. ggplot2 Layer objects are ggproto, ggproto objects are environments, and identical() on two environments is a pointer comparison – so the lookup is O(1) and exact, where hashing or deep-comparing the plot could cost as much as the rebuild it saves. A layer belongs to one plot, so its identity settles the question.

One entry per layer index, each holding the layer it was computed from, and the whole cache emptied when a plot starts being processed.

All three parts are load-bearing. The index bounds the cache at the number of layers a plot has and stops two jittered layers evicting each other once per panel, which is the cost this exists to remove. The layer catches the ordinary case of a second plot built from its own geom_jitter(). And the reset catches the case the layer cannot: ggplot2 documents a layer as reusable across plots, and +.gg appends the same ggproto object rather than a clone, so

shared <- geom_jitter()
p1 <- ggplot(df1, aes(g, score)) + shared
p2 <- ggplot(df2, aes(g, score)) + shared

gives two plots that are identical() at that layer. Measured before the reset went in: p2 was announced with every one of df1's values, and the row-count check waved it through because the two frames were the same length.


Base R Device-Scoped Storage

Description

This module provides device-scoped storage for Base R plot calls, enabling proper isolation between devices and preventing call accumulation.

Usage

.maidr_base_r_session

chartSeries TA Advisory Warning State

Description

Environment used to suppress repeat chartSeries TA advisory warnings within a single session.

Usage

.maidr_chartseries_ta_warned

fourfoldplot() Decline Advisory State

Description

Environment holding the decline reasons already explained in this session, so the advisory below is not repeated for every call.

Usage

.maidr_fourfoldplot_declined

Details

A character vector rather than the single flag .maidr_chartseries_ta_warned carries, because fourfoldplot() declines for three different reasons and a session that draws a default-std chart and then a 2x2xk one should hear both explanations rather than only the first.


ggplot2 Print Method Interception

Description

Intercepts ggplot2's print method so that typing a ggplot object name at the console automatically renders it in the MAIDR interactive viewer.

Usage

.maidr_ggplot_state

Base R Function Patching System

Description

This module provides function patching capabilities for Base R plotting functions. It intercepts Base R plotting calls and records them for processing by the MAIDR system.

Usage

.maidr_patching_env

vioplot's default whisker reach, in interquartile ranges

Description

vioplot's default whisker reach, in interquartile ranges

Usage

.maidr_vioplot_default_range

Grobs vioplot draws, one of each per violin

Description

Measured by echoing a two-group call through gridGraphics::grid.echo():

Usage

.maidr_vioplot_grob_kinds

Details

graphics-plot-1-box-1      polygon  n=4     <- the plot frame, NOT a violin
graphics-plot-1-polygon-1  polygon  n=200   <- violin body
graphics-plot-1-lines-1    lines    n=2     <- whisker, lower to upper
graphics-plot-1-rect-1     rect     n=1     <- the quartile box
graphics-plot-1-points-1   points   n=1     <- the median dot

The body carries exactly twice the evaluation points sm.density returns, mirrored about the category position, which is what confirms the replayed curve is the drawn one.

The patterns below are anchored, which is defensive rather than a fix for anything observed. Measured, the two things it is tempting to credit it with are not true: graphics-plot-11-polygon-1 is excluded by the - delimiter whether the pattern is anchored or not, and graphics-plot-1-box-1 – the panel frame, which is a polygon grob – never enters this search because it is not named polygon. What the trailing $ genuinely excludes is a longer name beginning the same way, such as -polygon-1-extra; gridSVG emits none today, so this keeps a name it does not own from being collected if that ever changes.


Wrap vioplot's entry point once its namespace is available

Description

vioplot is in Suggests, so if it loads after maidr its vioplot() has not been wrapped yet and user calls would go unrecorded. Same shape as the quantmod hook above, and registered beside it in .onLoad.

Usage

.maidr_vioplot_onload_hook(...)

wordcloud()'s own defaults, replicated so the reading matches the drawing

Description

Only the two that decide which terms are drawn. The rest — scale, rot.per, colors — decide how they look, and a reading has nothing to do with them.

Usage

.maidr_wordcloud_defaults

Wrap wordcloud's entry point once its namespace is available

Description

Same shape and same reason as the vioplot hook above: wordcloud is in Suggests, so a call made after a late library(wordcloud) would otherwise go unrecorded entirely.

Usage

.maidr_wordcloud_onload_hook(...)

Base R System Adapter

Description

Adapter for the Base R plotting system. This adapter uses function patching to intercept Base R plotting calls and detect plot types.

Format

An R6 class inheriting from SystemAdapter

Super class

SystemAdapter -> BaseRAdapter

Methods

Public methods


BaseRAdapter$new()

Initialize the Base R adapter

Usage
BaseRAdapter$new()

BaseRAdapter$formula_frame_missing()

Was a formula call recorded without the frame it drew from?

A formula reader takes its rows from the model frame kept at record time. When that frame could not be built – a subset written as an expression with nothing to evaluate it in, a data that no longer resolves – the reader has nothing to announce, and a claimed layer with nothing in it exports as an interactive chart that says nothing. Declining the type sends the chart to the picture instead.

Usage
BaseRAdapter$formula_frame_missing(layer)
Arguments
layer

The recorded call entry

Returns

TRUE when the call carries a formula but no frame


BaseRAdapter$formula_call()

Was the call handed a formula?

Either written in the call, or – ⁠fmla <- y ~ x; plot(fmla)⁠ – bound to a name the recorder resolved.

Usage
BaseRAdapter$formula_call(layer)
Arguments
layer

The recorded call entry

Returns

TRUE when the call carries a formula


BaseRAdapter$formula_scatter_readable()

Does a recorded formula plot() draw a numeric scatter?

plot.formula() draws a scatter only for a numeric response over one numeric predictor; a factor predictor reaches plot.factor() and a box plot, and a longer right-hand side is plot.default() over the first term. Only the two-column numeric frame is read as points.

Usage
BaseRAdapter$formula_scatter_readable(layer)
Arguments
layer

The recorded call entry

Returns

TRUE when the frame is a numeric pair


BaseRAdapter$can_handle()

Check if this adapter can handle a plot object

Usage
BaseRAdapter$can_handle(plot_object)
Arguments
plot_object

The plot object to check (should be NULL for Base R)

Returns

TRUE if Base R plotting is active, FALSE otherwise


BaseRAdapter$detect_layer_type()

Detect the type of a single layer from Base R plot calls

Usage
BaseRAdapter$detect_layer_type(layer, plot_object = NULL)
Arguments
layer

The plot call entry from our logger

plot_object

The parent plot object (NULL for Base R)

Returns

String indicating the layer type (e.g., "bar", "dodged_bar", "stacked_bar", "smooth", "line", "point")


BaseRAdapter$is_dodged_barplot()

Check if a barplot call represents a dodged bar plot

Usage
BaseRAdapter$is_dodged_barplot(args)
Arguments
args

The arguments from the barplot call

Returns

TRUE if this is a dodged bar plot, FALSE otherwise


BaseRAdapter$is_stacked_barplot()

Check if a barplot call represents a stacked bar plot

Usage
BaseRAdapter$is_stacked_barplot(args)
Arguments
args

The arguments from the barplot call

Returns

TRUE if this is a stacked bar plot, FALSE otherwise


BaseRAdapter$is_normalized_barplot()

Check if a barplot call draws a 100% stacked bar

Base R has no position = "fill" to read: barplot() takes no normalisation argument at all, and the idiomatic way to draw a 100% stacked bar is to normalise the matrix first, as barplot(prop.table(m, 2)). The only signal left is the drawn geometry.

So this reads what the chart shows rather than guessing what the author meant, and the two are the same thing here: when every column sums to 1, every bar is drawn to a common full height and each segment is that category's share. A chart like that IS a 100% stacked bar whatever the numbers were before they reached barplot().

Deliberately narrow. It does not also accept columns summing to 100, because a matrix of raw counts can total 100 by coincidence and nothing about the drawing would distinguish that from percentages. And it needs two or more rows, because a single series stacked against nothing is not a stack.

Usage
BaseRAdapter$is_normalized_barplot(args)
Arguments
args

The arguments from the barplot call

Returns

TRUE if every column of the height matrix sums to 1


BaseRAdapter$create_orchestrator()

Create an orchestrator for this system (Base R)

Usage
BaseRAdapter$create_orchestrator(plot_object = NULL)
Arguments
plot_object

The plot object to process (NULL for Base R)

Returns

PlotOrchestrator instance


BaseRAdapter$get_system_name()

Get the system name

Usage
BaseRAdapter$get_system_name()
Returns

System name string


BaseRAdapter$get_adapter()

Get a reference to this adapter (for use by orchestrator)

Usage
BaseRAdapter$get_adapter()
Returns

Self reference


BaseRAdapter$has_facets()

Check if plot has facets (Base R doesn't support facets)

Usage
BaseRAdapter$has_facets(plot_object = NULL)
Arguments
plot_object

The plot object (ignored for Base R)

Returns

FALSE (Base R doesn't support facets)


BaseRAdapter$is_patchwork()

Check if plot is a patchwork plot (Base R doesn't support patchwork)

Usage
BaseRAdapter$is_patchwork(plot_object = NULL)
Arguments
plot_object

The plot object (ignored for Base R)

Returns

FALSE (Base R doesn't support patchwork)


BaseRAdapter$get_plot_calls()

Get recorded plot calls for processing

Usage
BaseRAdapter$get_plot_calls(device_id = grDevices::dev.cur())
Arguments
device_id

Graphics device ID (defaults to current device)

Returns

List of recorded plot calls


BaseRAdapter$clear_plot_calls()

Clear recorded plot calls (for cleanup)

Usage
BaseRAdapter$clear_plot_calls(device_id = grDevices::dev.cur())
Arguments
device_id

Graphics device ID (defaults to current device)


BaseRAdapter$initialize_patching()

Initialize function patching

Usage
BaseRAdapter$initialize_patching()
Returns

NULL (invisible)


BaseRAdapter$restore_functions()

Restore original functions

Usage
BaseRAdapter$restore_functions()
Returns

NULL (invisible)


BaseRAdapter$clone()

The objects of this class are cloneable with this method.

Usage
BaseRAdapter$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Association Plot Layer Processor

Description

Processes Base R assocplot() layers – a Cohen–Friendly association plot, which draws one tile per cell of a two-way table whose signed height is that cell's Pearson residual, (observed - expected) / sqrt(expected).

Read as a heat layer, because a named grid of one number per cell is what the chart states and row-then-column is how a reader navigates a contingency table. Measured on assocplot(HairEyeColor[, , 1]), the rects the drawing produces carry the residuals exactly:

       Brown    Blue   Hazel   Green
Black  2.780  -2.059   0.184  -1.408
Brown  0.391  -0.246   0.185  -0.465
Red   -0.562  -0.658   0.532   1.485
Blond -3.273   3.271  -0.988   1.097

Nothing is inferred from the drawing: assocplot() is handed the table, so the recorded call carries every number the trace wants. It returns NULL, which is why the reading comes from the argument – the shape bxp()'s reading took in #265.

Two things this deliberately does not do

The tile width is dropped. Each tile is drawn sqrt(expected) wide, so the marginals are on the chart as a second encoding. A heat layer has no width, and the residual is what an association plot exists to show – the eye reads height and sign, and the width is why a cell's box is wider rather than a value the reader is asked to compare. Announcing it in z instead would replace the number the chart is about.

It is not a mosaic. mosaicplot() is read as one and the two look alike, but a mosaic's tiles tile the space and carry proportions of a whole. These float above and below a baseline and carry residuals, which are signed and sum to nothing. Calling it a mosaic would tell a reader the areas are shares of a total when they are a departure from an expectation.

Super class

LayerProcessor -> BaseRAssocplotLayerProcessor

Methods

Public methods

Inherited methods

BaseRAssocplotLayerProcessor$process()

Process the association plot layer.

Usage
BaseRAssocplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (kept for interface compatibility)

layout

Unused for Base R (kept for interface compatibility)

built

Unused for Base R (kept for interface compatibility)

gt

Gtable object used for selector generation (optional)

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Information about the recorded plot call

Returns

List with data, selectors, type, title and axes


BaseRAssocplotLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRAssocplotLayerProcessor$needs_reordering()
Returns

FALSE


BaseRAssocplotLayerProcessor$extract_data()

Read the residual grid out of the recorded call.

The grid is the table transposed, and its rows reversed. That is not a convention chosen here but the relation assocplot() has to its argument, measured from the drawn rects: the first dimension runs across the x axis and the second up the y axis, bottom to top. It is the same relation image() has to its matrix, and the heatmap processor transposes for the same reason.

A table with a zero margin has cells whose expected count is zero, and a residual there is 0/0. assocplot() draws nothing for such a cell; the grid has to keep a place for it, so it carries 0 – the departure from an expectation of nothing being nothing.

Usage
BaseRAssocplotLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

List with points, x and y, empty when there is no table


BaseRAssocplotLayerProcessor$pearson_residuals()

The Pearson residual of every cell.

(observed - expected) / sqrt(expected), with the expected counts taken from the margins the same way assocplot() takes them.

Usage
BaseRAssocplotLayerProcessor$pearson_residuals(table)
Arguments
table

A two-way table

Returns

A numeric matrix of the same shape


BaseRAssocplotLayerProcessor$recorded_table()

The two-way table the call was handed, when it is one.

Only a two-dimensional table is read. assocplot() itself accepts no more – it stops on anything else – so this declines the same inputs the function does.

Usage
BaseRAssocplotLayerProcessor$recorded_table(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

A 2-D table, or NULL


BaseRAssocplotLayerProcessor$extract_axis_titles()

Name the axes from the table's own dimension names.

assocplot() labels its axes with names(dimnames(x)) unless the author overrides them, so those are the words a reader should be given. z names what the numbers are rather than a dimension of the table: the grid holds residuals, and a reader told "Eye" for the value would be told a level name where a number is.

Usage
BaseRAssocplotLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Canonical axes list


BaseRAssocplotLayerProcessor$extract_main_title()

The title the call was given, if any.

Usage
BaseRAssocplotLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Character scalar, empty when the author wrote no title


BaseRAssocplotLayerProcessor$generate_selectors()

Address the tiles the chart drew.

assocplot() draws every cell into ONE rect grob – measured, a 4x4 table gives graphics-plot-N-rect-1 holding sixteen rects – so the cells need no picking out of the drawing the way a gantt's bars do.

Usage
BaseRAssocplotLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  extracted_data = NULL
)
Arguments
layer_info

Information about the recorded plot call

gt

Gtable object (optional)

extracted_data

The grid this layer emitted

Returns

List of selectors, one per cell


BaseRAssocplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRAssocplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Bar Plot Layer Processor

Description

Processes Base R bar plot layers based on recorded plot calls

Super class

LayerProcessor -> BaseRBarplotLayerProcessor

Methods

Public methods

Inherited methods

BaseRBarplotLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRBarplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

grob_id

Unused; present for the processor interface

panel_id

Unused; present for the processor interface

panel_ctx

Unused; present for the processor interface

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRBarplotLayerProcessor$is_horizontal()

Check whether this barplot call used horiz = TRUE

Usage
BaseRBarplotLayerProcessor$is_horizontal(layer_info)
Arguments
layer_info

Layer information

Returns

Logical


BaseRBarplotLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRBarplotLayerProcessor$needs_reordering()
Returns

FALSE


BaseRBarplotLayerProcessor$extract_data()

One point per bar, read from the recorded height

Usage
BaseRBarplotLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of points


BaseRBarplotLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

barplot() writes no title of its own, so an author who wrote none leaves both axes nameless. A bar chart always plots categories against their measured heights, whether or not the heights arrived named, so that is what the defaults say. horiz = TRUE puts the heights on the visual x axis – the same swap extract_data() applies to the points.

Usage
BaseRBarplotLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRBarplotLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRBarplotLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRBarplotLayerProcessor$generate_selectors()

Generate the CSS selectors that address this layer's drawn elements

Usage
BaseRBarplotLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

Returns

List of selectors


BaseRBarplotLayerProcessor$find_rect_grobs()

Recursively find rect grobs in the grob tree (like ggplot2 does)

Usage
BaseRBarplotLayerProcessor$find_rect_grobs(grob, call_index)
Arguments
grob

The grob tree to search

call_index

The plot call index to match

Returns

Character vector of grob names


BaseRBarplotLayerProcessor$generate_selectors_from_grob()

Generate selectors from grob tree (like ggplot2 does)

Usage
BaseRBarplotLayerProcessor$generate_selectors_from_grob(grob, call_index)
Arguments
grob

The grob tree to search

call_index

The plot call index

Returns

List of selectors


BaseRBarplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRBarplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Biplot Processor

Description

Reads biplot() as the two things it draws: the observations in principal component space, and the variables' loadings on the same components.

It is a grid, and for a reason the other grids do not have. pairs(), lag.plot() and termplot() are read as grids because they draw several panels. A biplot draws its two halves on top of each other – but on two different pairs of axes, which is the whole trick of the chart. Measured, the two plot.xy calls run over different ranges, and the quantities behind them are different sizes again:

scores   PC1 range  -1.716 ..  2.199
loadings PC1 range  -0.962 .. -0.012

Announcing both against one axis pair would misstate every loading. So the two are given a cell each – one row, two columns – which is the only way the existing grammar can say "these have separate scales" without inventing a second axis on one layer.

It draws no points at all. Both plot.xy calls are type = "n"; every mark on the page is a label or an arrow. Measured on the export of a ten-observation, four-variable fit:

graphics-plot-1-text-1     10 children   the observations
graphics-plot-2-text-1      4 children   the variables
graphics-plot-2-arrows-1    4 children   the arrows

Both text grobs are addressable per datum, in data order, which is the shape lag.plot()'s labelled panels already established – one g per label rather than one use per symbol. The arrows are not emitted separately: an arrow and its label name the same variable and sit at the same place, so the label is the mark a reader is moved to.

The values are the caller's, not the drawing's. biplot() apportions the two halves between the axes so that both fit one page – it divides the scores by sdev * sqrt(n) and multiplies the loadings by it, so neither set is drawn at its own scale. What a reader wants is the pair that mean something: the scores, an observation's coordinate in component space, and the loadings, a variable's weight on each component. Those are announced. This is the same choice stars() makes, where the radii on the page are shares of a column's range and the reading hands over the readings instead.

Super classes

LayerProcessor -> BaseRPointLayerProcessor -> BaseRBiplotLayerProcessor

Methods

Public methods

Inherited methods

BaseRBiplotLayerProcessor$process()

Emit the observations and the variables, a cell each

Usage
BaseRBiplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

Unused; the selectors are built rather than searched for.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A multi-panel result, or NULL when nothing was read


BaseRBiplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRBiplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Boxplot Layer Processor

Description

Processes Base R boxplot layers by extracting statistical summaries and generating selectors for boxplot components.

Super class

LayerProcessor -> BaseRBoxplotLayerProcessor

Methods

Public methods

Inherited methods

BaseRBoxplotLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRBoxplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRBoxplotLayerProcessor$read_stats()

The five-number summaries the drawn boxes came from

A boxplot() call carries the observations, so the summaries have to be recomputed from them – which boxplot(plot = FALSE) does, using the same code path the drawing did, rather than a reimplementation of it here. graphics::boxplot is named directly so the replay does not go back through maidr's own wrapper and record a second call.

Overridable because bxp() is handed the summaries already computed and draws exactly the same marks from them: everything below this method – the outlier grouping, the polygon and segment indices, the shift each box with no outliers puts on the ones after it – is the same reading either way, and only where the summaries come from differs (#262).

Usage
BaseRBoxplotLayerProcessor$read_stats(args)
Arguments
args

Recorded argument list

Returns

The boxplot.stats-shaped list, or NULL when it cannot be had


BaseRBoxplotLayerProcessor$extract_data()

One five-number summary per group, recomputed from the recorded observations

Usage
BaseRBoxplotLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of box summaries


BaseRBoxplotLayerProcessor$generate_selectors()

Selectors for each box's polygon, whiskers, median and outliers

Usage
BaseRBoxplotLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  extracted_data = NULL
)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

extracted_data

The data already extracted for this layer (optional)

Returns

List of selectors


BaseRBoxplotLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

boxplot() records no title unless the author wrote one, but the formula method derives both from the formula itself and draws them, so a y ~ g call already names its axes: the response on the value axis and the grouping terms on the category axis. Everything else falls back to what a box plot always shows – groups against their distributions. horizontal = TRUE swaps which visual axis is which, exactly as boxplot.formula()'s own defaults do.

Usage
BaseRBoxplotLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRBoxplotLayerProcessor$extract_formula_labels()

Read the axis titles boxplot.formula() derives from its formula

boxplot.formula() builds them out of the model frame's column names: the response column names the value axis and the remaining columns, joined with " : ", name the category axis. Building the same model frame reproduces the drawn titles for expressions (log(mpg) ~ cyl) and for . alike, where deparsing the formula's terms would not.

Usage
BaseRBoxplotLayerProcessor$extract_formula_labels(args, frame = NULL)
Arguments
args

Recorded argument list

frame

The model frame kept by the recording (optional)

Returns

List with response and groups, or NULL when this call is not the formula method or the model frame cannot be rebuilt


BaseRBoxplotLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRBoxplotLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRBoxplotLayerProcessor$determine_orientation()

Which way the boxes were drawn, from the recorded horizontal flag

Usage
BaseRBoxplotLayerProcessor$determine_orientation(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

"horz" or "vert"


BaseRBoxplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRBoxplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R bxp() Layer Processor

Description

Reads a graphics::bxp() call as the box plot it draws.

bxp() is the drawing half of boxplot(): boxplot.default() computes the five-number summaries and then hands them to bxp(), which puts the boxes, whiskers, medians and outliers on the page. Calling it directly is how a caller draws boxes from summaries they already have – from boxplot(plot = FALSE), from boxplot.stats(), or computed elsewhere entirely – and it is one of the twelve calls #262 found drawing while the save reported no plot at all.

What is the same, and what is not

The marks are identical. Drawn off-screen and echoed through gridGraphics, bxp(z) and the boxplot() call that produced z emit the same grob names in the same order – polygon-1, segments-1, points-1, ... – because the same code drew them. Every selector BaseRBoxplotLayerProcessor builds, including the index shift each box with no outliers puts on the boxes after it, therefore addresses a bxp() chart unchanged, and this class inherits all of it.

The one difference is where the summaries come from. boxplot() is handed observations, so its processor replays boxplot(plot = FALSE) to recover them; bxp() is handed the summaries themselves, in its first argument. Replaying boxplot() on that would read the six-element list as six groups of numbers and summarise them – so the only thing this class overrides is read_stats().

Two things bxp() shares with boxplot() are shared including their limits: horizontal = TRUE means the same thing to both, and ⁠at =⁠ repositions boxes without reordering the drawing in either, so a non-monotonic at reads in drawing order here exactly as it already does for boxplot().

Super classes

LayerProcessor -> BaseRBoxplotLayerProcessor -> BaseRBxpLayerProcessor

Methods

Public methods

Inherited methods

BaseRBxpLayerProcessor$read_stats()

The summaries bxp() was handed

bxp()'s first formal is z, and it draws nothing without a numeric z$stats with five rows – so a recorded call that reached this package has one. It is still checked rather than assumed: a shape that does not answer leaves the layer empty and the figure falls back to the picture it already was, where reaching past it would raise out of process() with nothing to catch it.

The positional half looks for the first unnamed argument rather than for slot 1. match_recorded_args() keeps the author's order and leaves only the dispatch argument unnamed, wherever it was written, so bxp(horizontal = TRUE, z) records z in slot 2 – a call R itself accepts and draws. Reading slot 1 there hands TRUE to the check below and leaves the layer empty. resolve_xy_args() resolves a positional argument the same way, for the same reason. Raised in review of #265.

Usage
BaseRBxpLayerProcessor$read_stats(args)
Arguments
args

Recorded argument list

Returns

The boxplot.stats-shaped list, or NULL when it is not one


BaseRBxpLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRBxpLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Candlestick Layer Processor

Description

Processes Base R candlestick chart layers produced by quantmod::chartSeries(x, type = "candlesticks").

Each xts row becomes a single navigable CandlestickPoint with value, open, high, low, close, computed trend (Bull / Bear / Neutral), volatility (high - low) and optional volume (when quantmod::has.Vo() is TRUE).

Selectors are derived from the gridSVG export of the chartSeries grob (captured via ggplotify::as.grob()). chartSeries draws candle bodies via a single vectorized rect() call (each candle body is one SVG ⁠<rect>⁠ child of ⁠graphics-plot-<N>-rect-*⁠) and upper/lower wicks via segments() calls (one SVG ⁠<polyline>⁠ per wick under ⁠graphics-plot-<N>-segments-*⁠).

Super class

LayerProcessor -> BaseRCandlestickLayerProcessor

Methods

Public methods

Inherited methods

BaseRCandlestickLayerProcessor$process()

Process the layer: the candlestick layer, plus a volume bar layer when addVo() was requested

Usage
BaseRCandlestickLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

layer_info

Layer information with the recorded call

Returns

A candlestick layer, or a multi-layer list with the volume bars


BaseRCandlestickLayerProcessor$has_add_vo()

Detect whether the chartSeries call draws the addVo() volume panel, which it does by default

Usage
BaseRCandlestickLayerProcessor$has_add_vo(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Logical


BaseRCandlestickLayerProcessor$build_volume_layer()

Build a "bar" layer carrying volume data

Usage
BaseRCandlestickLayerProcessor$build_volume_layer(layer_info, gt, candle_data)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

candle_data

The candlestick data already extracted


BaseRCandlestickLayerProcessor$generate_volume_selectors()

Generate selectors for the addVo() volume bar panel

chartSeries(TA = "addVo()") creates a second plotting window. In the gridSVG export that maps to a second ⁠graphics-plot-<N>⁠ group (typically N = 2). Returns a per-bar selector list so each volume bar can be individually highlighted on navigation; matches the bar layer contract used by the Base R barplot processor.

Usage
BaseRCandlestickLayerProcessor$generate_volume_selectors(
  layer_info,
  gt,
  n_bars
)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

n_bars

Number of volume bars

Returns

List of selectors, one per volume bar


BaseRCandlestickLayerProcessor$extract_data()

Extract OHLC data points from the chartSeries call

Usage
BaseRCandlestickLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer info containing the recorded plot call

Returns

List of CandlestickPoint dicts


BaseRCandlestickLayerProcessor$generate_selectors()

Generate CSS selectors for the candlestick layer

Returns ONE CandlestickSelector object (matching the maidr JS frontend contract in src/model/candlestick.ts::mapToSvgElements). The returned object has these named character-vector keys:

gridSVG emits a child id ⁠<group-id>.1.<i>⁠ for each primitive in a vectorized draw call. The frontend iterates each string array (collectElements(arr)) and picks the i-th element via ⁠getElementAt(*, i)⁠, so per-candle selectors are required for single-candle highlighting on arrow-key navigation.

IMPORTANT: do NOT return an array of objects (e.g. ⁠[[body, wick], ...]⁠). The frontend's Array.isArray() branch would then take selectors[0] (the first dict) and pass it to querySelectorAll, yielding a JS ⁠SyntaxError: '[object Object]' is not a valid selector⁠. The boxplot pattern of per-item dicts does NOT apply here because each chart model has its own contract.

Usage
BaseRCandlestickLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  extracted_data = NULL
)
Arguments
layer_info

Layer info (used for fallback plot index)

gt

The captured chartSeries grob (from ggplotify::as.grob)

extracted_data

Previously extracted data (used for count)

Returns

Named list (single CandlestickSelector) or list() when grobs cannot be located.


BaseRCandlestickLayerProcessor$extract_axis_titles()

The axis titles, defaulting to Date and Price

Usage
BaseRCandlestickLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Canonical axes list


BaseRCandlestickLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRCandlestickLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRCandlestickLayerProcessor$format_x_values()

Format a vector of x-axis index values to character

Usage
BaseRCandlestickLayerProcessor$format_x_values(idx)
Arguments
idx

Integer x-axis positions

Returns

Character vector


BaseRCandlestickLayerProcessor$collect_grob_names()

Recursively collect all grob names in a grob tree

Usage
BaseRCandlestickLayerProcessor$collect_grob_names(g)
Arguments
g

A grob

Returns

Character vector of grob names


BaseRCandlestickLayerProcessor$sort_ids()

Sort grob ids by trailing integer suffix

Usage
BaseRCandlestickLayerProcessor$sort_ids(ids)
Arguments
ids

Grob ids

Returns

The ids in numeric order of their suffix


BaseRCandlestickLayerProcessor$find_grob_by_name()

Find the grob node whose name matches id

Usage
BaseRCandlestickLayerProcessor$find_grob_by_name(g, id)
Arguments
g

A grob

id

The grob name to find

Returns

The grob, or NULL when no name matches


BaseRCandlestickLayerProcessor$grob_coord_count()

Count the number of primitive coordinates a grob carries

Usage
BaseRCandlestickLayerProcessor$grob_coord_count(g)
Arguments
g

A grob

Returns

Integer


BaseRCandlestickLayerProcessor$pick_largest_child_group()

Pick the rect-id whose grob has the most coordinates

Usage
BaseRCandlestickLayerProcessor$pick_largest_child_group(gt, ids)
Arguments
gt

Gtable of the replayed drawing (optional)

ids

Grob ids


BaseRCandlestickLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRCandlestickLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Conditional Density Plot Layer Processor

Description

Reads cdplot() as the 100% stacked area chart it draws.

A conditional density plot shows, for each value of a numeric x, how the levels of a factor y divide up: the bands are stacked, they fill the whole height, and their shares sum to 1 at every x. That is a normalized stacked area, which Ggplot2AreaLayerProcessor already emits for position = "fill", so a cdplot() is read as stacked_normalized_area and the two adapters describe one chart the same way – each series a list of ⁠{x, y, z}⁠ where y is the band's own share and z names the level, and one selector per band.

Before this, cdplot() had no branch in detect_layer_type(), so the switch fell through to "unknown" and the chart degraded to a static image (#216, #251).

Where the curves come from

cdplot() has a plot argument, so it can be asked for what it drew without drawing it again. It returns the boundaries between the bands: nlevels(y) - 1 functions, each approxfun() over the density grid, named for the level below the boundary.

rval <- cdplot(x, y, plot = FALSE)
names(rval)          # "c" "b"   for a factor with levels a, b, c
rval[[1]](50)        # the cumulative share at x = 50

Stacking them the way cdplot() does – rbind(0, boundaries, 1), band i running from row i to row i + 1 – gives the shares back. Measured over three hundred random charts, every column's shares sum to 1 and no band comes out negative, so the boundaries do not cross in the drawn range.

graphics::cdplot by the qualified name, not the bare one: maidr patches the name on the search path, and a bare call would record the replay as a second chart. The same line BaseRSpineplotLayerProcessor draws.

Which x values were drawn

The returned functions interpolate over the density grid, which stats::density() pads past the data by three bandwidths. cdplot() throws that padding away before drawing:

y1 <- y1[, which(x1 >= min(x) & x1 <= max(x))]
x1 <- x1[x1 >= min(x) & x1 <= max(x)]

so the announced grid is trimmed the same way – 372 of the 512 points on a measured chart. Announcing the untrimmed grid would put readings either side of the data at x values the chart has no marks at.

The grid itself is read off the first returned function rather than recomputed: approxfun() keeps its knots, and environment(f)$x is the grid cdplot() built. Recomputing density() here would have to guess bw, n, from, to and weights back out of the recorded call, and a guess that differed by one point would shift every reading.

What the replay proves

A cdplot() that returned rather than stopping has already established most of what a reader here would otherwise have to check, because cdplot() checks it first and stops. Each was measured:

The NULL checks that remain below are therefore not checking any of that. They are there so that a shape none of the above rules out cannot throw out of the pipeline, where declining to read leaves the static image the figure produces today. Where a check would only repeat one another check already makes, it is not written twice.

Super class

LayerProcessor -> BaseRCdplotLayerProcessor

Methods

Public methods

Inherited methods

BaseRCdplotLayerProcessor$process()

Build the layer

Usage
BaseRCdplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot, layout, built, gt, grob_id, panel_id, panel_ctx

Pipeline arguments

layer_info

The recorded call

Returns

List with data, selectors, type, title and axes


BaseRCdplotLayerProcessor$extract_data()

One series per band, bottom to top

Bottom to top because that is the order cdplot() draws its polygons in, and the selector list is positional against those polygons.

Usage
BaseRCdplotLayerProcessor$extract_data(layer_info)
Arguments
layer_info

The recorded call

Returns

A list of series, each a list of ⁠{x, y, z}⁠ points


BaseRCdplotLayerProcessor$drawn_bands()

The grid, the band names and their shares, or NULL

NULL rather than an empty list when the call cannot be read, so every caller degrades the same way: no data, no selectors, and the figure falls back to the static image it produces today.

Usage
BaseRCdplotLayerProcessor$drawn_bands(layer_info)
Arguments
layer_info

The recorded call

Returns

A list of x, levels and shares (a bands-by-points matrix), or NULL


BaseRCdplotLayerProcessor$replay()

Ask cdplot() what it drew, without drawing it

Usage
BaseRCdplotLayerProcessor$replay(args)
Arguments
args

The recorded call's arguments

Returns

The list of boundary functions, or NULL


BaseRCdplotLayerProcessor$drawn_grid()

The x values the bands were drawn over

Usage
BaseRCdplotLayerProcessor$drawn_grid(boundaries, args)
Arguments
boundaries

The replayed boundary functions

args

The recorded call's arguments

Returns

A numeric vector, or NULL


BaseRCdplotLayerProcessor$band_levels()

The band names, bottom to top

cdplot() names its boundaries for every level but the topmost, so the one it does not name is the one level of the response that is left. Derived that way rather than by reproducing the ylevels argument's reordering, which is cdplot()'s to define.

Declines unless exactly one level is left over. That is the one guard the step needs, and it does double duty: an ylevels naming a strict subset of the factor's levels leaves two over – measured, ylevels = c("a", "b") on a three-level factor – and unnamed boundaries would leave every level over. Either way the reading would be missing a band, and every selector after it would point at its neighbour.

An empty level name is not declined. factor(x, levels = c("b", "")) is a level like any other, setdiff() matches it like any other, and the chart draws a band for it – so it is announced with the empty name the axis shows rather than dropped.

Usage
BaseRCdplotLayerProcessor$band_levels(boundaries, args)
Arguments
boundaries

The replayed boundary functions

args

The recorded call's arguments

Returns

A character vector, or NULL


BaseRCdplotLayerProcessor$predictor()

The numeric variable on the x axis

Usage
BaseRCdplotLayerProcessor$predictor(args)
Arguments
args

The recorded call's arguments

Returns

A numeric vector, or NULL


BaseRCdplotLayerProcessor$response()

The factor whose levels the bands are

Usage
BaseRCdplotLayerProcessor$response(args)
Arguments
args

The recorded call's arguments

Returns

A factor, or NULL


BaseRCdplotLayerProcessor$variables()

The two variables the call was given, whichever form it took

cdplot() has two methods and they name their variables differently: cdplot(x, y) and cdplot(y ~ x, data). The formula method builds a model frame and takes the response from column one and the predictor from column two, which is reproduced here because the recorded call carries the formula rather than the frame.

Memoised. process() reaches this three times – through response(), through predictor() and again for the axis names – and on the formula path each ask would rebuild a model frame and re-apply the subset. The same call BaseRSpineplotLayerProcessor makes about its replayed table, for the same reason. Nothing observable changes, which is why this is written down rather than left to be rediscovered.

Usage
BaseRCdplotLayerProcessor$variables(args)
Arguments
args

The recorded call's arguments

Returns

A list of x, y and names, or NULL


BaseRCdplotLayerProcessor$default_variables()

The variables of a cdplot(x, y) call

Usage
BaseRCdplotLayerProcessor$default_variables(args)
Arguments
args

The recorded call's arguments

Returns

A list of x, y and names, or NULL


BaseRCdplotLayerProcessor$formula_variables()

The variables of a cdplot(y ~ x, data) call

subset is carried through, because cdplot.formula builds its model frame with it and everything downstream is the subset's: measured on cdplot(b ~ a, data = d, subset = a > 45), the chart draws over 46.1 to 70.9 while the whole column runs from 25.5. Read without the subset, the grid would be trimmed to the wider range and a fifth of the announced points would sit left of the leftmost mark.

Usage
BaseRCdplotLayerProcessor$formula_variables(formula, data, subset = NULL)
Arguments
formula

The recorded formula

data

The recorded data, if any

subset

The recorded subset, if any

Returns

A list of x, y and names, or NULL


BaseRCdplotLayerProcessor$extract_axis_titles()

Name the axes

The band's number is a share of the column, not a position on the drawn y axis – which carries the level names, and is the fill dimension shown positionally. So ylab names z and y says what its numbers are, the same split BaseRMosaicLayerProcessor makes for the same reason.

Usage
BaseRCdplotLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

The recorded call

Returns

An axes payload


BaseRCdplotLayerProcessor$extract_main_title()

The chart's own title, where the call gave one

Usage
BaseRCdplotLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

The recorded call

Returns

The title, or an empty string


BaseRCdplotLayerProcessor$generate_selectors()

Address each band by the polygon that drew it

cdplot() writes one polygon grob per band, in draw order, which is the bottom-to-top order the series are in. The frame it draws around the plot is a polygon too, but gridGraphics names it -box-1 rather than -polygon-N, so the anchored pattern does not collect it.

Withheld entirely when the counts disagree, for the reason every processor here gives: a partial list hands a band its neighbour's element, and a user cannot tell that apart from a correct one.

Usage
BaseRCdplotLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  n_series = 0L
)
Arguments
layer_info

The recorded call

gt

The grob tree

n_series

How many bands the data reports

Returns

A list of CSS selectors, one per band


BaseRCdplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRCdplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Contour Layer Processor

Description

Reads a base R contour() call as the contour it draws.

contour() had no processor. base_r_adapter mapped the call to the type "contour" and the factory fell through to the generic processor, so the layer shipped typed "unknown" – and the core's trace factory ends its dispatch with ⁠throw new Error("Invalid trace type: …")⁠, so the figure bound interactively and then failed to construct. #214 stopped that by typing the call "unknown" at the adapter, which degrades to a static image; this replaces the picture with the reading (#218).

The curves come from grDevices::contourLines(), which is the same computation contour() does and takes the same defaults, so nothing here is a guess about what was drawn:

contour.default(x = seq(0, 1, length.out = nrow(z)),
                y = seq(0, 1, length.out = ncol(z)),
                z, nlevels = 10, levels = pretty(zlim, nlevels),
                zlim = range(z, finite = TRUE))

The payload matches ggplot2_contour_layer_processor.R exactly – a list of curves, each a list of ⁠{x, y, level}⁠ – so the two adapters describe one chart the same way.

Super class

LayerProcessor -> BaseRContourLayerProcessor

Methods

Public methods

Inherited methods

BaseRContourLayerProcessor$process()

Build the layer

Usage
BaseRContourLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot, layout, built, gt, grob_id, panel_id, panel_ctx

Pipeline arguments

layer_info

The recorded call

Returns

List with data, selectors, type, title and axes


BaseRContourLayerProcessor$read_curves()

Read the drawn curves off the recorded call

Hands back which curves were kept as well as the curves themselves. gridGraphics draws from the same contourLines() output, so it writes one grob per curve before any filtering here – and comparing a filtered count against an unfiltered one would make the two disagree, which generate_selectors answers by withholding the whole layer's highlighting. Keeping the indices lets each announced curve take the grob that drew it, whatever was dropped.

Usage
BaseRContourLayerProcessor$read_curves(layer_info)
Arguments
layer_info

The recorded call

Returns

data (curves of ⁠{x, y, level}⁠), kept (their indices among what contourLines() returned) and total (how many that was)


BaseRContourLayerProcessor$extract_data()

The curves alone, for callers that want only the payload

Usage
BaseRContourLayerProcessor$extract_data(layer_info)
Arguments
layer_info

The recorded call

Returns

A list of curves, each a list of ⁠{x, y, level}⁠ points


BaseRContourLayerProcessor$contour_grid()

The grid and levels the call drew, or NULL when it drew none

Resolved the way contour.default resolves it, rather than by a rule of our own. Its arguments are ⁠(x, y, z, ...)⁠; a named argument claims its slot and the unnamed ones fill what is left, in order. Then, and only then:

  if (missing(z) && !missing(x) && !is.list(x)) { z <- x; x <- NULL }
  

which is what makes contour(m) a contour of m rather than a chart with m on the x axis.

Reproducing that matters because the wrapper records a partially named call: measured, contour(c(10, 20, 30), c(100, 200, 300), z) arrives as one unnamed argument plus ⁠y =⁠ and ⁠z =⁠. A rule that looked for the matrix and took the unnamed arguments before it found z already named, never looked at the unnamed one, and announced the caller's grid on the 0-1 default – every coordinate wrong, and nothing raised.

Usage
BaseRContourLayerProcessor$contour_grid(args)
Arguments
args

The recorded call's arguments

Returns

A list of x, y, z and levels, or NULL


BaseRContourLayerProcessor$default_nlevels()

How many levels the drawing function defaults to

contour.default defaults to 10 and filled.contour to 20, and the number decides the whole announced set: pretty(zlim, nlevels). The two are otherwise resolved identically, so the difference lives here rather than in a second copy of contour_grid().

Usage
BaseRContourLayerProcessor$default_nlevels()
Returns

The default nlevels for this call


BaseRContourLayerProcessor$extract_axis_titles()

Name the two axes

Only x and y. The level is not an axis here: it travels on every point of the curve it belongs to, which is where the frontend's contour trace reads it from – the same choice ggplot2_contour_layer_processor makes, so the two adapters describe one chart alike.

No default label. contour() prints the deparsed argument when the caller names nothing, and those names are gone by the time the wrapper has recorded evaluated values – so a guessed noun would be worse than none, and the axis is left to the renderer's generic.

Usage
BaseRContourLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

The recorded call

Returns

An axes payload


BaseRContourLayerProcessor$extract_main_title()

The chart's own title, where the call gave one

Usage
BaseRContourLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

The recorded call

Returns

The title, or an empty string


BaseRContourLayerProcessor$generate_selectors()

Address each curve by the element that drew it

gridGraphics writes one lines grob per curve, named ⁠graphics-plot-<group>-contour-<i>-<i>⁠, in the order contourLines() returns them – checked vertex count by vertex count, not assumed. A lines grob renders as a ⁠<polyline>⁠.

Withheld entirely when the count does not match what was announced. The frontend drops a layer whose selector list disagrees with its series count, and a partial list would hand a curve its neighbour's element – the defect #145 and #204 are both about.

Usage
BaseRContourLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  kept = integer(0),
  total = 0
)
Arguments
layer_info

The recorded call

gt

The grob tree

kept

Which of the drawn curves were announced

total

How many curves were drawn

Returns

A list of CSS selectors, one per announced curve


BaseRContourLayerProcessor$find_contour_grobs()

Every contour grob this layer drew

Usage
BaseRContourLayerProcessor$find_contour_grobs(grob, group_index)
Arguments
grob

The grob tree

group_index

Which plot on the device

Returns

A character vector of grob names


BaseRContourLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRContourLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Correlogram Layer Processor

Description

Processes the three correlogram entry points – acf(), pacf() and ccf(). Each draws one vertical spike per lag, from the zero line to the correlation at that lag, and joins nothing to anything.

Read as a lollipop layer, for the reason BaseRSpikeLayerProcessor gives: a line would say the samples are joined and that the space between two lags can be interpolated, which is the one relationship a correlogram is drawn to deny. The spikes even export under the same grob name – measured, plot(acf(v)) names them graphics-plot-1-spike-1 – so the inherited selector search needs no override.

What this adds is where the numbers come from. The recorded call holds the series, not the correlogram: measured, acf(v, lag.max = 5) records one HIGH call whose args are the 60 observations and lag.max. So the reading replays the call with plot = FALSE and takes ⁠$lag⁠ and ⁠$acf⁠ off the result, which is the same shape BaseRSpineplotLayerProcessor takes for a table it cannot read off the drawing either.

All three differ in what the lags are, and the replay answers that too rather than the reading assuming it. Measured on one 60-point series:

acf(v,  lag.max = 5)   lags  0  1  2  3  4  5
pacf(v, lag.max = 5)   lags     1  2  3  4  5
ccf(v, w, lag.max = 3) lags -3 -2 -1  0  1  2  3

acf starts at lag 0, whose correlation is 1 by construction and which the chart draws; pacf has no lag 0 at all; and a cross-correlation's lags are signed, because "x leads y" and "y leads x" are different statements. Announcing any of the three as another would misname every spike on the chart.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRSpikeLayerProcessor -> BaseRCorrelogramLayerProcessor

Methods

Public methods

Inherited methods

BaseRCorrelogramLayerProcessor$process()

Process the correlogram layer.

Usage
BaseRCorrelogramLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (kept for interface compatibility)

layout

Unused for Base R (kept for interface compatibility)

built

Unused for Base R (kept for interface compatibility)

gt

Gtable object used for selector generation (optional)

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Information about the recorded plot call

Returns

List with data, selectors, type, title and axes


BaseRCorrelogramLayerProcessor$extract_data()

Read one point per lag the correlogram draws.

The lag on the category axis and the correlation as the magnitude, in the order they are drawn – which for ccf runs from the most negative lag rightwards, so the announced order is the drawn one.

Usage
BaseRCorrelogramLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

List of x/y points, empty when the replay states nothing


BaseRCorrelogramLayerProcessor$extract_axis_titles()

Name the lag axis and the quantity drawn against it.

A correlogram writes its own axis labels, so there is nothing the caller titled to read – and the inherited X/Y fallback would name a lag after a coordinate. The value axis follows what the replay says it computed: acf(type = "covariance") draws covariances, not correlations, and calling them correlations would announce a normalisation the chart never applied.

Usage
BaseRCorrelogramLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Named list with x and y


BaseRCorrelogramLayerProcessor$extract_main_title()

Title the chart the way the drawing does.

plot.acf() writes "Series v" above an acf and "v & w" above a ccf, and the replayed object carries those as ⁠$series⁠ and ⁠$snames⁠ – but not usefully. Both are deparse(substitute(x)), and the replay hands stats the recorded values rather than the name the caller wrote, so measured they come back as the whole series pasted in: ⁠Series c(-2.0715334064552, -0.117989125730012, ...)⁠. That is worse than no title at all.

The name is in the recorded call expression instead, which is kept as the source text of the call – measured, "acf(v, lag.max = 3)". It is used only when the argument is a bare symbol: a caller who wrote acf(rnorm(60)) named nothing, and titling the chart with the expression that produced it would announce a call rather than a series.

A caller's own ⁠main =⁠ wins, which is what the inherited reading already answers.

Usage
BaseRCorrelogramLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

The title, or NULL when the caller named nothing


BaseRCorrelogramLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRCorrelogramLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Cumulative Periodogram Processor

Description

Reads cpgram() as the staircase it draws: the cumulative periodogram against frequency, held across each interval and then jumping.

It is a step, not a line. cpgram() plots with type = "s", which draws the horizontal segment first – MAIDR's "hv". A line would imply the value slides between frequencies, which is not what a cumulative sum does, and the export agrees: the grob is step-1, not lines-1.

It does NOT use spectrum()'s estimate. This is the trap the reading exists to avoid. spectrum() defaults to ⁠taper = 0.1, detrend = TRUE, demean = FALSE⁠ and smooths; cpgram() computes its own periodogram – taper, FFT, zero the first ordinate, then normalise the cumulative sum – and the two disagree. Measured, the second step of the drawn curve is 0.0801 while spectrum()'s estimate gives 0.0817: close enough to look right and wrong enough to announce wrong numbers. So the computation below is cpgram()'s own, and it reproduces the traced plot.xy call exactly.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRCpgramLayerProcessor

Methods

Public methods

Inherited methods

BaseRCpgramLayerProcessor$process()

Emit the cumulative periodogram as a step

Usage
BaseRCpgramLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

Unused; the selector is built rather than searched for.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A step layer, or NULL when nothing was read


BaseRCpgramLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRCpgramLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Dodged Bar Layer Processor

Description

Processes Base R dodged bar plot layers with proper ordering to match backend logic

Super class

LayerProcessor -> BaseRDodgedBarLayerProcessor

Methods

Public methods

Inherited methods

BaseRDodgedBarLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRDodgedBarLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRDodgedBarLayerProcessor$extract_data()

One series per row of the recorded height matrix

Usage
BaseRDodgedBarLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of series


BaseRDodgedBarLayerProcessor$generate_selectors()

The selector for the bars, scoped to this layer's plot group

Usage
BaseRDodgedBarLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

Returns

List of selectors


BaseRDodgedBarLayerProcessor$find_rect_grobs()

Find the rect grobs drawn by the recorded call at call_index

Usage
BaseRDodgedBarLayerProcessor$find_rect_grobs(grob, call_index)
Arguments
grob

The grob tree to search

call_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

Character vector of grob names


BaseRDodgedBarLayerProcessor$generate_selectors_from_grob()

Build this layer's selector from the grob tree

Usage
BaseRDodgedBarLayerProcessor$generate_selectors_from_grob(grob, call_index)
Arguments
grob

The grob tree to search

call_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

A selector string, or an empty string when no grob matches


BaseRDodgedBarLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

Same shape as the stacked processor: barplot(beside = TRUE) writes no title, its points carry the column category on x and the bar height on y, and the group each bar belongs to travels with the point as z rather than as a named axis.

Usage
BaseRDodgedBarLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRDodgedBarLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRDodgedBarLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRDodgedBarLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRDodgedBarLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Dot Chart Layer Processor

Description

Processes Base R dotchart() layers – a Cleveland dot plot: one value per category, marked with a dot on a horizontal guide line, with the categories running down the page.

Read as a dot layer, which the core builds on BarTrace: a bar chart's reading with a different mark. The guide lines and the category labels are frame rather than data – gridGraphics draws them as ⁠-abline-h-⁠ and ⁠-mtext-left-⁠, and only the dots carry a value.

orientation is "horz", which is not a detail: dotchart() puts the categories on the vertical axis and the value along the horizontal one, and the core reads a horz layer's magnitude from x and its category from y. Without the key the layer defaults to vertical and reads the category name where the magnitude belongs – no number to pitch, and the announcement inverted (#184, #480).

The dots are emitted in the order dotchart() was handed them, which is bottom-up on the drawn chart – base R puts the first element at the bottom. That is the arrangement barplot(horiz = TRUE) already ships for the same reason, so the two horizontal base R charts read from the same end.

Selectors come from the points grob, which is the one thing a dotchart shares with a scatter: graphics-plot-N-points-1 holds one ⁠<use>⁠ per dot. So this inherits BaseRPointLayerProcessor and replaces what is read out of the call rather than how the marks are addressed.

Super classes

LayerProcessor -> BaseRPointLayerProcessor -> BaseRDotchartLayerProcessor

Methods

Public methods

Inherited methods

BaseRDotchartLayerProcessor$process()

Process the dot chart layer.

Usage
BaseRDotchartLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (kept for interface compatibility)

layout

Unused for Base R (kept for interface compatibility)

built

Unused for Base R (kept for interface compatibility)

gt

Gtable object used for selector generation (optional)

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Information about the recorded plot call

Returns

List with data, selectors, type, title, axes and orientation


BaseRDotchartLayerProcessor$extract_data()

Read the dots out of the recorded dotchart() call.

dotchart(x, labels = NULL, ...) names its dots from labels when the caller gives them and from names(x) otherwise, which is what the chart draws down its left margin. A vector with neither is drawn against blank labels, and is emitted here against its positions so a reader still has something to navigate by.

x and y carry the magnitude and the category respectively, which is the arrangement a horz layer means – see the class note.

Usage
BaseRDotchartLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

List of x/y points, empty when there is nothing to read


BaseRDotchartLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRDotchartLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Filled Contour Layer Processor

Description

Reads a base R filled.contour() call as the contour it draws.

filled.contour draws the same level curves contour() does and fills the bands between them, so it is read the same way: one curve per level, from grDevices::contourLines(), which is the computation the drawing itself runs. One chart with two spellings gets one reading.

Two things differ from contour(), and only two:

The level default. contour.default takes nlevels = 10 and filled.contour takes nlevels = 20, and the number decides the whole announced set through pretty(zlim, nlevels). Everything else about resolving the call – the ⁠(x, y, z, ...)⁠ slots, the if (missing(z)) z <- x fallback, the list(x =, y =, z =) unpacking, the zlim default – is identical in both, so this class overrides the number and inherits the rest.

The chart cannot be highlighted, and the inherited code already knows it. contour() writes one lines grob per curve, which is what generate_selectors() pairs against. filled.contour writes one polygon grob for the entire field, and gridSVG exports it as a flat run of pieces. Measured on a 6x5 grid drawn at the 17 default levels:

grobs written    graphics-plot-2-filled-contour-1   (one)
SVG polygons     graphics-plot-2-filled-contour-1.1.1 .. .1.160
curves announced 40

160 pieces against 40 curves, and against 17 levels: the polygons are the grid's cells cut by the level crossings, not the bands and not the curves. Nothing pairs, so nothing is emitted – the inherited generate_selectors() finds no -contour-N-N grob, its count check fails, and it withholds the list, which is the same answer it gives a contour() whose grobs and curves disagree. A layer with no selectors is announced, sonified and navigated; it is the visual highlight alone that is missing, and that is the established degradation here (#89) rather than a reason to ship a picture.

Note also that the field is drawn in the second plot region: the call lays out a colour key as graphics-plot-1 and the field as graphics-plot-2. Nothing here depends on that, because nothing here addresses a grob, but a later attempt to highlight this chart will.

py-maidr declines the equivalent call. Its reason – recorded in maidr/patch/contour.py – is that contourf hands back the filled paths and "an outline of one runs along two different level curves", which is a statement about deriving curves from what was drawn. It does not apply here: R hands over contourLines(), so the curves announced are the level curves themselves rather than an inference from the fill, and every one of them is on the page as the boundary between two bands.

Super classes

LayerProcessor -> BaseRContourLayerProcessor -> BaseRFilledContourLayerProcessor

Methods

Public methods

Inherited methods

BaseRFilledContourLayerProcessor$default_nlevels()

How many levels filled.contour() defaults to

Twice contour()'s, and the only number that separates the two.

Usage
BaseRFilledContourLayerProcessor$default_nlevels()
Returns

20


BaseRFilledContourLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRFilledContourLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Fourfold Display Layer Processor

Description

Processes Base R fourfoldplot() layers drawn with std = "ind.max" or std = "all.max" – a 2x2 contingency table drawn as four quarter-circles, one per cell, sized so the wedge's AREA is proportional to that cell's count.

Read as a heat layer, for the same reason assocplot() is: the drawing is a 2x2 grid of one number per cell, and row-then-column is how a reader navigates a contingency table. Measured on fourfoldplot(as.table(matrix(c(10, 40, 90, 160), 2)), std = "ind.max"), the radii and the counts they recover:

grob                       r        quadrant      r^2 * max(count)  cell
graphics-plot-1-polygon-1  0.250000 upper LEFT     10               [1, 1]
graphics-plot-1-polygon-2  0.500000 lower LEFT     40               [2, 1]
graphics-plot-1-polygon-3  0.750000 upper RIGHT    90               [1, 2]
graphics-plot-1-polygon-4  1.000000 lower RIGHT   160               [2, 2]

exactly, so radius / sqrt(count) is one constant (0.0790569415 here, with a relative spread of 1.755e-16). The table's FIRST dimension runs vertically and its SECOND horizontally – the transpose of assocplot()'s arrangement, and measured from the label grobs, which put names(dimnames(x))[1] at the top and bottom of the drawing and names(dimnames(x))[2] at the left and right.

Nothing is inferred from the drawing. fourfoldplot() is handed the table, so the recorded call carries every number the trace wants; the grobs are consulted only to decide whether the drawing on the page is the one the recorded call describes.

What this deliberately does not do

It is not a mosaic and it is not a bar. A mosaic's tiles tile a whole and carry proportions of it; these are four independent quarter-discs separated by space (default 0.2) that tile nothing. A bar trace would imply a value axis with a zero baseline and one categorical axis, while measured the radii are on a square-root scale and the quadrants are laid out in two dimensions.

The confidence arcs are dropped. Measured, a conf.level draws 8 unfilled arcs (polygon-5 .. polygon-12), arcs 4 + j and 8 + j being the lower and upper bound of cell c(tab)[j]. A heat layer has nowhere to carry an interval, so a reader is told the counts and not that the chart also draws a band around each. Unlike qqplot's band this does not justify declining: the arcs decorate numbers that ARE fully carried.

The odds ratio is never announced. There is no MAIDR trace for one scalar. Under ind.max/all.max a reader gets four counts and can compute it.

Where the two gates live, and what the second one cannot do

#268 asks that a chart whose geometry disagrees LOSE the reading rather than get a wrong one. That is not achievable here and this does not pretend it is. Verified against BaseRPlotOrchestrator$initialize(), which runs detect_layers(), resolve_fallback_scope(), create_layer_processors() and process_layers() at lines 122-125: the picture-versus-chart decision is frozen by line 123, which reads private$.layers[[i]]$type through unsupported_layer_flags(). That field is written only by the two private$.layers[[layer_counter]] <- list(type = ...) assignments at lines 147 and 169, both from detect_layer_type(), and is never rewritten from a processor result; line 124 does not even build a processor for a layer already typed "unknown" (line 219). A processor that answered type = "unknown" would ship that string with has_unsupported_layers() still FALSE – the #214 failure BaseRProcessorFactory$get_supported_types() records, where the figure binds and then fails to construct.

So the split is:

What the geometry gate can and cannot see

Measured, and weaker than it looks:

What it does catch is a grob tree that is not this call's: measured, fourfoldplot(UCBAdmissions, std = "ind.max") draws 78 polygons, and an asymmetric table's margins drawing checked against its own counts gives 7.616e-01.

Super class

LayerProcessor -> BaseRFourfoldLayerProcessor

Methods

Public methods

Inherited methods

BaseRFourfoldLayerProcessor$process()

Process the fourfold display layer.

Usage
BaseRFourfoldLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (kept for interface compatibility)

layout

Unused for Base R (kept for interface compatibility)

built

Unused for Base R (kept for interface compatibility)

gt

Gtable object used for selector generation (optional)

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Information about the recorded plot call

Returns

List with data, selectors, type, title and axes


BaseRFourfoldLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRFourfoldLayerProcessor$needs_reordering()
Returns

FALSE


BaseRFourfoldLayerProcessor$recorded_table()

The 2x2 table of counts the call was handed, when it is one.

Guarded by fourfold_decline_reason(), the same predicate detect_layer_type() asks, so dispatch and extraction cannot disagree about which calls are readable.

Used for the COUNTS and the shape only. The level names and the axis names come from drawn_dimnames() instead, which is not the same thing – see there.

Usage
BaseRFourfoldLayerProcessor$recorded_table(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

A 2x2 table, or NULL


BaseRFourfoldLayerProcessor$drawn_dimnames()

The dimnames fourfoldplot() itself labels the chart with.

NOT dimnames(recorded_two_way_table(...)), and the difference is the whole point. recorded_two_way_table() repairs dimnames through as.table(); graphics::fourfoldplot repairs them from dimnames(x) as handed, after reshaping a 2-D x to ⁠2 x 2 x 1⁠. Its own source:

if (length(dim(x)) == 2L)
    x <- if (is.null(dimnames(x))) array(x, c(dim(x), 1L))
         else array(x, c(dim(x), 1L), c(dimnames(x), list(NULL)))
dnx <- dimnames(x)
if (is.null(dnx)) dnx <- vector("list", 3L)
for (i in which(sapply(dnx, is.null))) dnx[[i]] <- LETTERS[seq_len(dim(x)[i])]
if (is.null(names(dnx))) i <- 1L:3L else i <- which(is.null(names(dnx)))
if (any(i)) names(dnx)[i] <- c("Row", "Col", "Strata")[i]

reproduced here rather than approximated. For an ftable the two disagree completely: dimnames(ftable(tb)) is NULL, so the chart is labelled Row: A, Col: A, Row: B, Col: B – measured off the text grobs – while as.table(ftable(tb)) reconstructs Treatment/Outcome and Drug/Placebo/Cured/Not. Reading the levels off as.table() would announce six strings that appear nowhere on the page, which is #268's own failure displaced from the numbers onto the labels.

The which(is.null(names(dnx))) line is upstream's, quirk included: is.null() returns one logical, so that expression is always integer(0) and the Row/Col default fires only when names(dimnames(x)) is entirely NULL. A PARTIALLY named dimnames is therefore never repaired, and measured, names(dimnames(t)) <- c("Answer", "") makes the chart literally draw ": hi" and ": lo". extract_axis_titles() substitutes "Col" there rather than announce an empty label – deliberately better than the chart, and the one place this reading is not identical to it. test-base-r-fourfoldplot.R pins the chart's wrong answer as well as ours.

Usage
BaseRFourfoldLayerProcessor$drawn_dimnames(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

A length-3 named list of dimnames, or NULL


BaseRFourfoldLayerProcessor$extract_data()

Read the 2x2 grid of counts out of the recorded call.

Rows of the emitted grid are the table's FIRST dimension, top to bottom, and its columns the SECOND, left to right – measured from the drawing, where polygon-1 (cell ⁠[1, 1]⁠) is the upper-left quadrant and the top and bottom labels carry names(dimnames(x))[1]. That is the transpose of what assocplot() does with the same argument, and it is not a convention chosen here.

The level names come from drawn_dimnames(), so they are the strings the chart puts beside each quadrant.

Usage
BaseRFourfoldLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

List with points, x and y, empty when there is no readable table


BaseRFourfoldLayerProcessor$levels_of()

The level names of one table dimension, as drawn.

drawn_dimnames() is NULL only when the recorded first argument has no 2-D dim() at all, which a readable call cannot have; the fallback is there so a malformed layer_info in a test yields level names rather than an error.

Usage
BaseRFourfoldLayerProcessor$levels_of(named, i, fallback)
Arguments
named

The list drawn_dimnames() returned, or NULL

i

Which dimension, 1 or 2

fallback

Level names to use when there are none drawn

Returns

A character vector of level names


BaseRFourfoldLayerProcessor$extract_axis_titles()

Name the axes the way the chart places the dimensions.

x is the SECOND dimension, because measured the columns run left to right; y is the first, because the rows run top to bottom. z names what the numbers are rather than a dimension of the table – a reader told "Outcome" for the value would be given a level name where a number is.

"Count" is the contingency-table convention and not a claim that the values are whole numbers: measured, fourfoldplot() draws non-integer cells happily (⁠1.5, 2.5, 3.5, 4.5⁠ gives radii ⁠0.5774, 0.7454, 0.8819, 1.0000⁠ and the ratio check passes).

No recorded_axis_label() call, unlike the assocplot processor. Measured, names(formals(graphics::fourfoldplot)) is exactly ⁠x, color, conf.level, std, margin, space, main, mfrow, mfcol⁠ – no xlab, no ylab, no ... – and fourfoldplot(tab, xlab = "X") stops with "unused argument". Reading a label that the function rejects would be dead code asserting an argument that cannot exist.

Usage
BaseRFourfoldLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Canonical axes list


BaseRFourfoldLayerProcessor$extract_main_title()

The title the call was given, if any.

Usage
BaseRFourfoldLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Character scalar, empty when the author wrote no title


BaseRFourfoldLayerProcessor$wedge_names()

The four wedge grobs of one fourfold panel, or NULL.

Discriminated by polygon count and vertex count, NOT by gp$fill. Fill looks like the exact discriminator and is not: measured, fourfoldplot(tb, std = "ind.max", color = "steelblue") leaves two quadrants with fill = NA, because fourfoldplot() does not validate color's length and its draw calls index it as color[1 + (d > 1)] / color[2 - (d > 1)]. A fill-keyed search finds two wedges there and the layer would ship with no selectors at all, for a chart that is perfectly readable.

What is measured to be stable:

Usage
BaseRFourfoldLayerProcessor$wedge_names(gt, plot_index)
Arguments
gt

Grob tree to search

plot_index

The plot (panel) index the grob names carry

Returns

The four wedge grob names, or NULL when this is not one panel


BaseRFourfoldLayerProcessor$radii_agree()

Whether the wedges on the page are the counts in the call.

radius / sqrt(count) constant across the four quadrants, to a relative spread of 1e-8. Measured headroom on the worst-conditioned tables that fourfoldplot() will draw at all – c(1e-9, 1, 1, 1) (2.220e-16), c(1, 2, 3, 1e18) (2.068e-16) and the non-integer c(1.5, 2.5, 3.5, 4.5) (1.178e-16) – is eight orders below the threshold, while an asymmetric table's margins drawing checked against its counts gives 7.616e-01. (c(1, 1e15, 1, 1) is not in that list because radii_agree() is never asked about it – not because it errors. Measured, fourfoldplot() returns normally there with a "NaNs produced" warning and emits ZERO polygon grobs under ind.max and all.max, so wedge_names() refuses it on the polygon count first. Under the default margins the same table does draw 13 polygons – radii ⁠0.00017782794, 0.99999998, 0.99999998, 0.00017782794⁠ – but no margins call reaches a processor.)

A zero count makes radius / sqrt(count) 0/0, so those cells are held to radius == 0 instead – measured, a zero cell still emits a full 501-vertex polygon at the origin, so the grob is there and addressable.

The check is vacuous when fewer than two cells are non-zero: one non-zero cell makes the relative deviation identically 0 whatever the radius is. Measured, ⁠0, 0, 0, 160⁠ under ind.max draws radii ⁠0, 0, 0, 1⁠ and would pass an unguarded check against any table with the same three zeros. Two non-zero cells are required before the ratio is treated as evidence, so such a chart announces its counts and highlights nothing.

Usage
BaseRFourfoldLayerProcessor$radii_agree(gt, names, counts)
Arguments
gt

Grob tree to search

names

The four wedge grob names, in drawing order

counts

The four recorded counts, in c(tab) order

Returns

TRUE when the drawing carries the recorded counts


BaseRFourfoldLayerProcessor$generate_selectors()

Address the quadrant the chart drew each count into.

Each quadrant is its own polygon grob, so each needs its own selector – the pie's situation, not the barplot's. Two orderings compose here, and they run in opposite directions:

Grid row 1 is therefore the BOTTOM wedges: ⁠[[polygon-2, polygon-4], [polygon-1, polygon-3]]⁠. The wrong answer is to emit it index-aligned with points, top row first – it reads correctly and highlights the vertically mirrored quadrant on every cell of every fourfold plot: ⁠row 0, col 0⁠ then announces Placebo / 40 while lighting the wedge drawn for Drug / 10. test-base-r-fourfoldplot.R pins the bottom-first order against that. image()/heatmap() is the precedent – measured on image(matrix(1:6, 2)), points[[1]] is the top row while selectors[[1]] is rect:nth-child(1), the lowest rect on the page – and NEWS.md records fixing the same mirror there.

Returns list() when the drawing and the recorded call disagree. The numbers are still announced – they are what the call was handed – and nothing highlights, rather than a highlight landing on another panel's quadrant.

Usage
BaseRFourfoldLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  extracted_data = NULL
)
Arguments
layer_info

Information about the recorded plot call

gt

Grob tree to search

extracted_data

Unused; the counts are re-read from the call

Returns

A 2x2 nested list of selectors, or an empty list


BaseRFourfoldLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRFourfoldLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Heatmap Layer Processor

Description

Processes Base R heatmap layers using the heatmap() function

Super class

LayerProcessor -> BaseRHeatmapLayerProcessor

Methods

Public methods

Inherited methods

BaseRHeatmapLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRHeatmapLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

grob_id

Unused; present for the processor interface

panel_id

Unused; present for the processor interface

panel_ctx

Unused; present for the processor interface

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRHeatmapLayerProcessor$extract_data()

One row per cell of the recorded matrix, in drawn order

Usage
BaseRHeatmapLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of rows


BaseRHeatmapLayerProcessor$compute_heatmap_ordering()

Reproduce the row/column ordering heatmap() draws with

Usage
BaseRHeatmapLayerProcessor$compute_heatmap_ordering(args)
Arguments
args

Recorded heatmap() arguments

Returns

List with rowInd/colInd, or NULL if unavailable


BaseRHeatmapLayerProcessor$generate_selectors()

Selectors for the image tiles, one per cell

Usage
BaseRHeatmapLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  extracted_data = NULL
)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

extracted_data

The data already extracted for this layer (optional)

Returns

List of selectors


BaseRHeatmapLayerProcessor$find_image_rect_grobs()

Find the image-rect grobs drawn by the plot group at group_index

Usage
BaseRHeatmapLayerProcessor$find_image_rect_grobs(grob, group_index)
Arguments
grob

The grob tree to search

group_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

Character vector of grob names


BaseRHeatmapLayerProcessor$generate_selectors_from_grob()

Build this layer's selector from the grob tree

Usage
BaseRHeatmapLayerProcessor$generate_selectors_from_grob(
  grob,
  group_index = NULL
)
Arguments
grob

The grob tree to search

group_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

A selector string, or an empty string when no grob matches


BaseRHeatmapLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

heatmap() lays the matrix out one way round only – its columns run along x and its rows up y – so those two words are facts about the call. image() is not the same picture: it draws a coordinate grid, and image(x, y, z) puts the caller's own coordinates on those axes, so naming them after a matrix would be a guess. It gets no default.

Usage
BaseRHeatmapLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRHeatmapLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRHeatmapLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRHeatmapLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRHeatmapLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Histogram Layer Processor

Description

Processes Base R histogram plot layers using verified data extraction and selector generation logic.

Super class

LayerProcessor -> BaseRHistogramLayerProcessor

Methods

Public methods

Inherited methods

BaseRHistogramLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRHistogramLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRHistogramLayerProcessor$extract_data()

One point per bin, from the histogram recomputed from the recorded call

Usage
BaseRHistogramLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of points


BaseRHistogramLayerProcessor$recompute_histogram()

Recompute the plotted histogram from the recorded call

Usage
BaseRHistogramLayerProcessor$recompute_histogram(args)
Arguments
args

Recorded argument list

Returns

A "histogram" object, or NULL when the call recorded no data


BaseRHistogramLayerProcessor$is_frequency()

Is this a frequency histogram rather than a density one?

The plotted y-axis shows counts only for frequency histograms; with freq = FALSE or probability = TRUE it shows densities. hist()'s own default is freq = TRUE only for equidistant breaks.

Usage
BaseRHistogramLayerProcessor$is_frequency(args, hist_obj = NULL)
Arguments
args

Recorded argument list

hist_obj

The recomputed histogram, or NULL when there is none

Returns

Logical


BaseRHistogramLayerProcessor$generate_selectors()

The selector for the bins, scoped to this layer's plot group

Usage
BaseRHistogramLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

Returns

List of selectors


BaseRHistogramLayerProcessor$find_rect_grobs()

Find the rect grobs drawn by the recorded call at call_index

Usage
BaseRHistogramLayerProcessor$find_rect_grobs(grob, call_index)
Arguments
grob

The grob tree to search

call_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

Character vector of grob names


BaseRHistogramLayerProcessor$generate_selectors_from_grob()

Build this layer's selector from the grob tree

Usage
BaseRHistogramLayerProcessor$generate_selectors_from_grob(
  grob,
  call_index = NULL
)
Arguments
grob

The grob tree to search

call_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

A selector string, or an empty string when no grob matches


BaseRHistogramLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

hist() derives both titles inside the call and so records neither: the x title is deparse(substitute(x)), which is gone by the time the evaluated arguments reach us, and the y title is "Frequency" or "Density" depending on what the bars measure. The y default therefore repeats hist()'s own choice – resolved by the same rule that decides which values extract_data() emits, so the noun always names the number being announced – while x says only what the axis certainly holds: the bins.

Usage
BaseRHistogramLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRHistogramLayerProcessor$frequency_label()

The title hist() itself would print above the counted axis

Usage
BaseRHistogramLayerProcessor$frequency_label(args)
Arguments
args

Recorded argument list

Returns

"Frequency" or "Density"


BaseRHistogramLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRHistogramLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRHistogramLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRHistogramLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Interaction Plot Layer Processor

Description

Reads interaction.plot() as the set of lines it draws: one series per level of the trace factor, running across the levels of the x factor at fun(response) for each cell.

stats::interaction.plot computes that grid itself and hands it straight to matplot:

cells <- tapply(response, list(x.factor, trace.factor), fun)
matplot(xvals, cells, ..., type = type, ...)

which is the shape BaseRLineLayerProcessor already reads for matplot – one series per column, each point carrying its column name as z. So the whole reading is recomputing cells and handing it over; nothing about extracting a multi-series line is new here.

Recomputed rather than read back off the drawing, for the reason the correlograms recompute theirs (#276): a cell mean is not on the page in any form a grob carries, and the recorded arguments hold everything needed to get it exactly as the function did.

type is not consulted. It varies the marks – "l" draws lines, "p" points, "b"/"o"/"c" both – but every variant draws the same cell means in the same series, and reading a type = "p" chart as loose points would lose the trace grouping that makes it an interaction plot.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRInteractionLayerProcessor

Methods

Public methods

Inherited methods

BaseRInteractionLayerProcessor$extract_data()

One series per trace level, read from the grid of cell means

Usage
BaseRInteractionLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of series


BaseRInteractionLayerProcessor$extract_axis_titles()

The three labels the drawing writes, each explicit argument first.

All three of interaction.plot's own defaults are deparse1(substitute(...)) of an argument, so they name the expression the caller wrote. The wrapper records evaluated values, by which point substitute() is long gone – a factor's levels are not its name – so the expressions come off the recorded call text instead, the way the correlograms recover their series names (#276).

Usage
BaseRInteractionLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information for the recorded call

Returns

An axes list from build_axes()


BaseRInteractionLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRInteractionLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Lag Plot Processor

Description

Reads lag.plot() as the grid of scatters it draws: one panel per series and lag, the series plotted against a shifted copy of itself.

A grid, not a layer. Like pairs(), this call lays out its own panels and hands nothing to the device's layout calls, so the grid has to come from the reading. It answers multi_panel = TRUE and places each layer at its own cell.

What a panel pairs. Measured by tracing graphics::plot.xy through a real call – the panel is plot(lag(X, k), X), and lag() shifts the time base back, so the pair at time t is

x = X[t + k]      the later reading, across
y = X[t]          the earlier one, up

over every t where both indices land inside the series. A negative lag works out of the same expression and was measured too: set.lags = -1 gives ⁠x = X[1..n-1]⁠ against ⁠y = X[2..n]⁠, and set.lags = 0 gives the series against itself.

The panels are numbered in draw order. The nested loop runs series outermost and lag innermost, and par(mfrow) fills row by row – measured against a grid.echo() export, a two-column matrix at lags = 2 writes graphics-plot-1 through graphics-plot-4 for ⁠(a,1) (a,2) (b,1) (b,2)⁠. So panel k sits at row (k - 1) %/% ncols + 1, column (k - 1) %% ncols + 1.

A panel's marks are symbols or labels, and labels decides which. lag.plot() writes the time index at each pair rather than a symbol when labels is true, and labels defaults to do.lines, which defaults to n <= 150 – so the default chart is the labelled one. Measured, the four combinations give:

labels  do.lines   grobs in a panel
FALSE   FALSE      points
FALSE   TRUE       points, lines
TRUE    FALSE      text
TRUE    TRUE       text, brokenline

So do.lines only adds the joining line and labels alone decides the mark, which is why the two are read separately rather than through the default that ties them together.

Both marks can be outlined, and the export is what says so. Measured on a real save_html() of a labelled call, the text grob comes out as one group per pair, in data order:

<g id="graphics-plot-1-text-1.1">
  <g id="graphics-plot-1-text-1.1.1" transform="translate(254.32, …)">
  <g id="graphics-plot-1-text-1.1.2" transform="translate(221.70, …)">

which is the same shape a points grob has, with use elements replaced by nested gs. So the selector differs only in the grob name and the child it reaches for.

Super classes

LayerProcessor -> BaseRPointLayerProcessor -> BaseRLagLayerProcessor

Methods

Public methods

Inherited methods

BaseRLagLayerProcessor$process()

Emit one point layer per drawn panel

Usage
BaseRLagLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

Unused; the selectors are built rather than searched for.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A multi-panel result, or NULL when nothing was read


BaseRLagLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRLagLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Line Plot Layer Processor

Description

Processes Base R line plot layers based on recorded plot calls

Super class

LayerProcessor -> BaseRLineLayerProcessor

Methods

Public methods

Inherited methods

BaseRLineLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRLineLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

grob_id

Unused; present for the processor interface

panel_id

Unused; present for the processor interface

panel_ctx

Unused; present for the processor interface

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRLineLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRLineLayerProcessor$needs_reordering()
Returns

FALSE


BaseRLineLayerProcessor$extract_data()

One series per line: a vector, each column of a matrix, a time series, or the endpoints of abline()

Usage
BaseRLineLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of series


BaseRLineLayerProcessor$get_axis_labels()

Get custom axis labels from axis() LOW-level calls

Usage
BaseRLineLayerProcessor$get_axis_labels(layer_info, axis_side = 1)
Arguments
layer_info

Layer information containing group data

axis_side

Which axis (1=bottom/x, 2=left/y, 3=top, 4=right)

Returns

Character vector of labels or NULL if not found


BaseRLineLayerProcessor$extract_single_line_data()

The points of one line, pairing each x with its y

Usage
BaseRLineLayerProcessor$extract_single_line_data(x, y, x_labels = NULL)
Arguments
x

x positions

y

y values

x_labels

Category labels to announce in place of the x positions (optional)

Returns

List holding one series


BaseRLineLayerProcessor$extract_multiline_data()

One series per column of y_matrix, named after the columns

Usage
BaseRLineLayerProcessor$extract_multiline_data(x, y_matrix, x_labels = NULL)
Arguments
x

x positions

y_matrix

One column of y values per series

x_labels

Category labels to announce in place of the x positions (optional)

Returns

List of series


BaseRLineLayerProcessor$extract_axis_titles()

The axis titles, taken from the HIGH-level call for an overlay such as abline()

Usage
BaseRLineLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Canonical axes list


BaseRLineLayerProcessor$extract_abline_data()

The endpoints of an abline() call across the axis the HIGH-level call set up

Usage
BaseRLineLayerProcessor$extract_abline_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List holding one two-point series, or empty


BaseRLineLayerProcessor$get_x_range_from_group()

The x extent of the group's HIGH-level call, as the axis was drawn

Usage
BaseRLineLayerProcessor$get_x_range_from_group(group)
Arguments
group

The recorded plot group holding the HIGH-level call

Returns

Numeric vector of two, or NULL


BaseRLineLayerProcessor$get_y_range_from_group()

The y extent of the group's HIGH-level call, as the axis was drawn

Usage
BaseRLineLayerProcessor$get_y_range_from_group(group)
Arguments
group

The recorded plot group holding the HIGH-level call

Returns

Numeric vector of two, or NULL


BaseRLineLayerProcessor$axis_extent()

The extent of an axis the way plot.default() sets it: an explicit xlim/ylim, or the finite data extended by 4 % each way, which is what abline() draws its clipped line across.

Usage
BaseRLineLayerProcessor$axis_extent(limits, data)
Arguments
limits

An explicit xlim/ylim, or NULL

data

The plotted values on that axis

Returns

Numeric vector of two, or NULL when nothing finite was plotted


BaseRLineLayerProcessor$extract_main_title()

The main title, taken from the HIGH-level call for abline()

Usage
BaseRLineLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRLineLayerProcessor$generate_selectors()

One selector per polyline, in series order

Usage
BaseRLineLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

Returns

List of selectors


BaseRLineLayerProcessor$find_lines_grobs()

Find every grob of the given family drawn by the plot group at group_index

Usage
BaseRLineLayerProcessor$find_lines_grobs(
  grob,
  group_index,
  grob_type = "lines"
)
Arguments
grob

The grob tree to search

group_index

Index of the recorded plot group, which numbers the panel's grobs

grob_type

The grob family to match: "lines", "abline", "segments", "spike" or "step"

Returns

Character vector of grob names


BaseRLineLayerProcessor$selector_grob_type()

Which family of grob names this layer's selectors are drawn from: "abline", "lines", "segments", "spike" or "step". Overridden by subclasses whose geometry lands under a different grob name (see BaseRStepLayerProcessor).

Usage
BaseRLineLayerProcessor$selector_grob_type(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

"abline" or "lines"


BaseRLineLayerProcessor$generate_selectors_from_grob()

One selector per matching polyline, sorted by the grob number

Usage
BaseRLineLayerProcessor$generate_selectors_from_grob(
  grob,
  group_index,
  layer_info
)
Arguments
grob

The grob tree to search

group_index

Index of the recorded plot group, which numbers the panel's grobs

layer_info

Layer information with the recorded call

Returns

List of selectors


BaseRLineLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRLineLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Mosaic Plot Layer Processor

Description

Processes Base R mosaicplot() layers – a two-way contingency table drawn as tiles, where a column's width encodes that category's share of all observations and a tile's height its conditional proportion within the column.

Read as a mosaic layer, which exists for exactly this shape. Read as a stacked_bar it would lose the width entirely, and the width is half the table: the conditional proportions would arrive without the group sizes they were computed from, so a category of six observations and one of six hundred would read identically.

Nothing here is inferred from the drawing. mosaicplot() is handed the table itself, so the recorded call carries every number the trace wants – the counts, the margins they imply, and the level names from dimnames().

Super class

LayerProcessor -> BaseRMosaicLayerProcessor

Methods

Public methods

Inherited methods

BaseRMosaicLayerProcessor$process()

Process the mosaic layer.

Usage
BaseRMosaicLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (kept for interface compatibility)

layout

Unused for Base R (kept for interface compatibility)

built

Unused for Base R (kept for interface compatibility)

gt

Gtable object used for selector generation (optional)

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Information about the recorded plot call

Returns

List with data, selectors, type, title and axes


BaseRMosaicLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRMosaicLayerProcessor$needs_reordering()
Returns

FALSE


BaseRMosaicLayerProcessor$extract_data()

Read the table out of the recorded mosaicplot() call.

The emitted shape is the segmented one the stacked bar processor already produces – data[[fill]][[category]] – because the core builds MosaicTrace on SegmentedTrace and navigates it category-then-series exactly as a stack. mosaicplot() splits along the first dimension into columns and each column along the second, so the first dimension is the category and the second the fill.

Each cell carries four numbers rather than one:

A column that observed nothing has no conditional proportions to report – dividing would give NaN for every cell – so its cells carry a proportion of 0 alongside their true count of 0. That is what the chart draws: a column of zero width and no tiles.

Usage
BaseRMosaicLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Nested list of points, empty when there is no table to read


BaseRMosaicLayerProcessor$recorded_table()

The two-way table the call was handed, when it is one.

Only a two-dimensional table is read. mosaicplot() accepts three and more, splitting recursively, and a mosaic layer has one category axis and one fill – so a deeper table has nowhere to put its later dimensions and is declined rather than flattened into a cross-classification the chart does not claim.

Usage
BaseRMosaicLayerProcessor$recorded_table(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

A 2-D table, or NULL


BaseRMosaicLayerProcessor$extract_axis_titles()

Name the axes from the table's own dimension names.

mosaicplot() labels its axes with names(dimnames(x)) unless the author overrides them – "Hair" and "Eye" for HairEyeColor[, , 1] – so those are the two words a reader should be given for the two dimensions.

Which grammar axis each lands on is not the chart's own arrangement. The second dimension is what mosaicplot() draws up the y axis, but a segmented layer's y holds the magnitude and its z the fill, so the second dimension is named on z and y says what its numbers are. ylab follows the dimension it names rather than the axis it shares a letter with, for the same reason.

Usage
BaseRMosaicLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Canonical axes list


BaseRMosaicLayerProcessor$extract_main_title()

The title the call was given, if any.

mosaicplot() writes a default main of its own from the deparsed data expression, but the recorded call carries only what the author passed, and a title invented from a variable name is not something a reader can act on.

Usage
BaseRMosaicLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

Character scalar, empty when the author wrote no title


BaseRMosaicLayerProcessor$generate_selectors()

Address the tiles the chart drew.

Measured on a rendered mosaicplot(HairEyeColor[, , 1]): gridGraphics draws one -polygon-N grob per cell and nothing else as a polygon – 16 grobs for a 4x4 table, with no frame among them. Their geometry says the order: polygon-1 to -4 share the leftmost column's x extent, -5 to -8 the next one's, and within a column the grob number runs down the fill levels in the table's own order. So the tile for row f of column c is the (c - 1) * fills + fth grob.

The grobs are found rather than named by formula, and a short list is declined: a guessed id resolves to nothing at best and to another panel's tiles at worst, and a mosaic with no highlight still reads – the outcome #145 established for a layer with nothing to point at.

Usage
BaseRMosaicLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Information about the recorded plot call

gt

Gtable object to search

Returns

List of selectors, one per cell, or empty when unresolvable


BaseRMosaicLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRMosaicLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Scatterplot Matrix Processor

Description

Reads pairs() as the grid of scatters it draws: one panel per ordered pair of columns, the column across plotted against the column down.

A grid, not a layer. Every other base R processor answers with one layer, or several layers in one cell. A scatterplot matrix is ⁠n x n⁠ panels, which is the figure's shape rather than a layer's, so this one answers with multi_panel = TRUE and places each layer at its own cell. That is the shape the same chart already gets elsewhere: sns.pairplot in py-maidr, and a plotly splom since xability/py-maidr#667.

The grid cannot come from the recording. Measured, pairs() sets its own par(mfrow) internally and restores it, so nothing lands in the device's layout calls – get_layout_calls() answers zero – and detect_panel_configuration() sees a single panel (#272). The grid is the reading's own, derived from the number of columns.

The panels are numbered column-major. Measured against a real grid.echo() export of a three-column matrix, gridGraphics writes nine graphics-plot-N groups – one per cell, diagonal included – and pairing them with a panel that recorded what it was handed gives

k    cell     drawn
1    (1,1)    text        the variable's name
2    (2,1)    points      x = column 1, y = column 2
3    (3,1)    points      x = column 1, y = column 3
4    (1,2)    points      x = column 2, y = column 1
5    (2,2)    text
6    (3,2)    points      x = column 2, y = column 3
7    (1,3)    points      x = column 3, y = column 1
8    (2,3)    points      x = column 3, y = column 2
9    (3,3)    text

– that is k = (col - 1) * n + row, and the panel at ⁠(row, col)⁠ plots column col horizontally against column row vertically.

The diagonal has no layer. A diagonal cell draws the variable's name and nothing else, which the orchestrator already has an answer for: a cell with no layers becomes a valid empty subplot. Giving it a histogram instead would announce a chart pairs() does not draw – its default diag.panel draws nothing at all.

Super classes

LayerProcessor -> BaseRPointLayerProcessor -> BaseRPairsLayerProcessor

Methods

Public methods

Inherited methods

BaseRPairsLayerProcessor$process()

Emit one point layer per off-diagonal panel

Usage
BaseRPairsLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

Unused; the selectors are built rather than searched for.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A multi-panel result, or NULL when nothing was read


BaseRPairsLayerProcessor$extract_columns()

The columns pairs() drew, with the names it wrote

Read from the recorded call rather than re-derived. A formula call is resolved from the frame the recording kept (#254) rather than from the formula, whose variables may since have been rebound; the ordinary spelling hands over a data frame or a matrix, which is recorded by value and needs nothing.

An unnamed matrix is labelled the way pairs.default labels it – measured against a real export, the diagonals of a two-column unnamed matrix read "var 1" and "var 2".

A column that is not numeric is not a case to handle: pairs() itself stops with "non-numeric argument to 'pairs'" before anything is recorded.

Usage
BaseRPairsLayerProcessor$extract_columns(layer_info)
Arguments
layer_info

Layer information with the recorded call.

Returns

A named list of numeric vectors, empty when nothing resolves


BaseRPairsLayerProcessor$column_names()

The names pairs() writes down its diagonal

Usage
BaseRPairsLayerProcessor$column_names(handed, count)
Arguments
handed

The recorded data frame or matrix.

count

How many columns it has.

Returns

One name per column


BaseRPairsLayerProcessor$panel_layer()

One panel's points, axes and selector

A pair with a missing coordinate is dropped rather than announced. Measured, pairs() hands its panel the raw columns including NA, and points() then draws nothing for that pair – so announcing it would offer a sample the chart does not draw (#170).

Usage
BaseRPairsLayerProcessor$panel_layer(columns, row, col, title)
Arguments
columns

The named columns.

row

Which column runs up the panel.

col

Which column runs across it.

title

The figure's own title.

Returns

A layer, or NULL when the panel drew no points


BaseRPairsLayerProcessor$panel_selectors()

The marks one panel was drawn into

Built rather than searched for, from the column-major numbering in the class docs. find_graphics_plot_grob() answers with the first points grob of the plot, and a scatterplot matrix draws one per cell, so a search would give every panel the first cell's marks.

Numbered from one because a call that declares its own grid is the only thing on the page: pairs() takes over the device's layout, and the orchestrator reads a grid from a single call only.

Usage
BaseRPairsLayerProcessor$panel_selectors(row, col, count)
Arguments
row

Which column runs up the panel.

col

Which column runs across it.

count

How many columns there are.

Returns

A one-element list of selectors


BaseRPairsLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRPairsLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Patch Architecture

Description

Modular system for patching Base R plotting functions with chain of responsibility pattern

Methods

Public methods


BaseRPatcher$can_patch()

Abstract: whether this patcher applies to the call

Usage
BaseRPatcher$can_patch(function_name, args)
Arguments
function_name

Name of the plotting function

args

Recorded argument list

Returns

Logical


BaseRPatcher$apply_patch()

Abstract: the argument list with this patcher's change applied

Usage
BaseRPatcher$apply_patch(function_name, args)
Arguments
function_name

Name of the plotting function

args

Recorded argument list

Returns

Argument list


BaseRPatcher$get_name()

Get the patcher name for debugging

Usage
BaseRPatcher$get_name()

BaseRPatcher$clone()

The objects of this class are cloneable with this method.

Usage
BaseRPatcher$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Pie Chart Layer Processor

Description

Processes Base R pie() layers based on recorded plot calls. A pie layer is 1-D and flat: one point per slice, carrying the slice label as x and the slice magnitude as y. Percentages are derived by the frontend from those magnitudes, so none are emitted here.

Super class

LayerProcessor -> BaseRPieLayerProcessor

Methods

Public methods

Inherited methods

BaseRPieLayerProcessor$process()

Process the pie layer

Usage
BaseRPieLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (NULL)

layout

Layout information

built

Unused for Base R (NULL)

gt

Grob tree used for selector generation

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Layer information (contains the recorded plot call)

Returns

List with data, selectors, type, title and axes


BaseRPieLayerProcessor$extract_dial()

Where the ring begins and which way it runs

The frontend walks a pie clockwise – Right steps to the next slice the way a clock hand goes, the audio pans each slice to where it sits and p names its clock position – all from where the layer says the first slice begins, measured in degrees clockwise from 12 o'clock. pie() draws the other way round by default: clockwise = FALSE, from init.angle degrees counterclockwise from 3 o'clock, so its default of 0 is the frontend's 90 and a clockwise = TRUE pie's default of 90 is the frontend's 0. The direction is declared so the frontend can turn the walk round; the wedges stay in recorded-call order, which is what the selectors are index-aligned to.

Both keys are left out at the frontend's own defaults – a clockwise ring from the top – which is also what every layer declared before the keys existed.

Usage
BaseRPieLayerProcessor$extract_dial(layer_info)
Arguments
layer_info

Layer information

Returns

Named list holding startAngle and/or direction, possibly empty


BaseRPieLayerProcessor$needs_reordering()

Pie slices are emitted in drawing order (see extract_data)

Usage
BaseRPieLayerProcessor$needs_reordering()
Returns

FALSE


BaseRPieLayerProcessor$extract_data()

Extract one point per slice from the recorded call

Usage
BaseRPieLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information

Returns

Flat list of ⁠list(x = <label>, y = <value>)⁠ points


BaseRPieLayerProcessor$resolve_slice_labels()

Resolve the per-slice labels the way pie() does

labels defaults to names(x), and falls back to the slice position when the input is unnamed. pie(labels = NA) draws neither label nor leader line, but the wedges are still there and still navigable, so those slices are announced by position rather than as "NA".

Usage
BaseRPieLayerProcessor$resolve_slice_labels(values, args)
Arguments
values

The recorded x argument (names still attached)

args

Recorded argument list from the pie() call

Returns

Character vector with one label per slice


BaseRPieLayerProcessor$generate_selectors()

Generate one selector per wedge, index-aligned to the data

Usage
BaseRPieLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  extracted_data = NULL
)
Arguments
layer_info

Layer information

gt

Grob tree to search

extracted_data

Points from extract_data(), used for the count

Returns

List of CSS selector strings, one per slice


BaseRPieLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

x names what the slice labels mean, y what their magnitudes measure. pie() records neither unless the author wrote one, and a pie always holds labelled categories against their magnitudes, so the defaults say that much rather than leaving the axes nameless.

Usage
BaseRPieLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRPieLayerProcessor$extract_main_title()

Extract the main title for this layer

Usage
BaseRPieLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information

Returns

Character scalar


BaseRPieLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRPieLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Plot Orchestrator Class

Description

This class orchestrates the detection and processing of multiple layers in Base R plots. It analyzes each recorded plot call individually and combines the results into a comprehensive interactive plot.

Methods

Public methods


BaseRPlotOrchestrator$new()

Create an orchestrator for the calls recorded on a device

Usage
BaseRPlotOrchestrator$new(device_id = grDevices::dev.cur())
Arguments
device_id

Graphics device ID


BaseRPlotOrchestrator$detect_layers()

Turn each recorded plot group into layer entries: one for its HIGH-level call and one per LOW-level overlay

Usage
BaseRPlotOrchestrator$detect_layers()

BaseRPlotOrchestrator$analyze_single_layer()

Describe one recorded call as a layer entry with its detected type

Usage
BaseRPlotOrchestrator$analyze_single_layer(
  plot_call,
  layer_index,
  group = NULL
)
Arguments
plot_call

The recorded call

layer_index

Index of the layer

group

The recorded plot group holding the HIGH-level call

Returns

Layer information list


BaseRPlotOrchestrator$create_layer_processors()

Create a processor for every layer of a known type

Usage
BaseRPlotOrchestrator$create_layer_processors()

BaseRPlotOrchestrator$create_layer_processor()

Create the processor for one layer

Usage
BaseRPlotOrchestrator$create_layer_processor(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

A layer processor, or NULL for an unknown type


BaseRPlotOrchestrator$create_unified_layer_processor()

Unified layer processor creation - used by all plot types

Usage
BaseRPlotOrchestrator$create_unified_layer_processor(layer_info)
Arguments
layer_info

Layer information

Returns

Layer processor instance


BaseRPlotOrchestrator$process_layers()

Run every layer processor and combine the results

Usage
BaseRPlotOrchestrator$process_layers()

BaseRPlotOrchestrator$extract_format_config_from_axis_calls()

Extract Format Configuration from axis() Calls

Scans logged axis() calls for format config stored by the axis wrapper. The wrapper stores .maidr_format_config when labels is a scales:: function.

Usage
BaseRPlotOrchestrator$extract_format_config_from_axis_calls()
Returns

A list with x and/or y format configurations, or NULL


BaseRPlotOrchestrator$extract_layout()

Read the figure-level title, subtitle and axis labels from the recorded HIGH-level calls

Usage
BaseRPlotOrchestrator$extract_layout()
Returns

List


BaseRPlotOrchestrator$combine_layer_results()

Combine the per-layer results into the subplot grid

Usage
BaseRPlotOrchestrator$combine_layer_results(layer_results)
Arguments
layer_results

List of per-layer results, one per processor


BaseRPlotOrchestrator$generate_maidr_data()

Assemble the MAIDR data object for the figure

Usage
BaseRPlotOrchestrator$generate_maidr_data()
Returns

List with an id and the subplots


BaseRPlotOrchestrator$get_layout()

The figure-level layout read by extract_layout()

Usage
BaseRPlotOrchestrator$get_layout()
Returns

List


BaseRPlotOrchestrator$get_combined_data()

The combined per-layer data

Usage
BaseRPlotOrchestrator$get_combined_data()
Returns

List


BaseRPlotOrchestrator$get_layer_processors()

The processors created for the layers

Usage
BaseRPlotOrchestrator$get_layer_processors()
Returns

List


BaseRPlotOrchestrator$get_layers()

The detected layer entries

Usage
BaseRPlotOrchestrator$get_layers()
Returns

List


BaseRPlotOrchestrator$get_plot_calls()

The recorded plot calls

Usage
BaseRPlotOrchestrator$get_plot_calls()
Returns

List


BaseRPlotOrchestrator$get_gtable()

The gtable of the replayed drawing, built once and cached

Usage
BaseRPlotOrchestrator$get_gtable()
Returns

A gtable, or NULL when nothing was recorded


BaseRPlotOrchestrator$get_grob_for_layer()

The grob a layer's processor searches for its selectors

Usage
BaseRPlotOrchestrator$get_grob_for_layer(layer_index)
Arguments
layer_index

Index of the layer

Returns

A grob, or NULL


BaseRPlotOrchestrator$unsupported_layer_flags()

Flag each detected layer maidr cannot process

Decorations carry no data of their own; leaving them out of the interactive output loses nothing. Data-bearing LOW-level overlays (polygon, rect, segments, ...) with no processor would silently disappear from the accessible output, so they count as unsupported.

Usage
BaseRPlotOrchestrator$unsupported_layer_flags()
Returns

Logical vector, one entry per detected layer


BaseRPlotOrchestrator$has_unsupported_layers()

Check if any HIGH-level layers are unsupported (unknown type)

Usage
BaseRPlotOrchestrator$has_unsupported_layers()
Returns

Logical indicating if there are unsupported layers


BaseRPlotOrchestrator$unsupported_group_indices()

Plot groups holding a layer maidr cannot process

Usage
BaseRPlotOrchestrator$unsupported_group_indices()
Returns

Integer vector of plot-group indices, in ascending order


BaseRPlotOrchestrator$resolve_fallback_scope()

Work out how far an unsupported layer reaches

An unsupported LOW-level overlay sits on top of a chart maidr does understand, so it only makes the panel that owns it undescribable. In a multi-panel figure the other panels are drawn from their own calls and stay fully accessible, so the fallback is scoped to the affected panels. It widens to the whole figure when there is nothing left to scope to: a single-panel figure, a figure whose every visible panel is affected, an unsupported call that belongs to no panel of the exported page, or an unsupported HIGH-level call.

Usage
BaseRPlotOrchestrator$resolve_fallback_scope()
Returns

Invisible NULL; the scope is cached on the orchestrator


BaseRPlotOrchestrator$is_group_scoped_out()

Check whether a plot group is scoped out of the payload

Usage
BaseRPlotOrchestrator$is_group_scoped_out(group_index)
Arguments
group_index

Plot-group index to test

Returns

TRUE when the group's panel falls back on its own


BaseRPlotOrchestrator$fallback_panels()

Panels rendered without accessible data

Usage
BaseRPlotOrchestrator$fallback_panels()
Returns

Integer vector of 1-based panel numbers, empty when the whole figure renders normally or falls back as a whole


BaseRPlotOrchestrator$should_fallback()

Determine if the plot should fall back to image rendering

Usage
BaseRPlotOrchestrator$should_fallback()
Returns

Logical indicating if fallback should be used


BaseRPlotOrchestrator$clone()

The objects of this class are cloneable with this method.

Usage
BaseRPlotOrchestrator$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Point/Scatter Plot Layer Processor

Description

Processes Base R scatter plot layers based on recorded plot calls

Super class

LayerProcessor -> BaseRPointLayerProcessor

Methods

Public methods

Inherited methods

BaseRPointLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRPointLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

grob_id

Unused; present for the processor interface

panel_id

Unused; present for the processor interface

panel_ctx

Unused; present for the processor interface

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRPointLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRPointLayerProcessor$needs_reordering()
Returns

FALSE


BaseRPointLayerProcessor$extract_data()

One point per observation, from the recorded x and y or from the formula's model frame

Usage
BaseRPointLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of points


BaseRPointLayerProcessor$resolve_coordinates()

The x and y a recorded call plots, resolved as plot() resolves them.

plot(y ~ x, data = d) carries a formula rather than two vectors, and its coordinates are the two columns of the model frame the recording kept (#254). Read from resolve_xy_args() alone, the formula is a language object and the layer came out with no points at all – an interactive chart with nothing in it.

Usage
BaseRPointLayerProcessor$resolve_coordinates(plot_call)
Arguments
plot_call

The recorded call

Returns

A list with x and y, either of which may be NULL


BaseRPointLayerProcessor$formula_variables()

The two numeric variables of a recorded formula call, or NULL.

Only a numeric pair is a scatter: plot(y ~ f) on a factor draws a box plot through plot.factor(), and a frame with more than one predictor draws something else again. Both are left as they were.

Usage
BaseRPointLayerProcessor$formula_variables(plot_call)
Arguments
plot_call

The recorded call

Returns

A list with x, y, x_name, y_name, or NULL


BaseRPointLayerProcessor$extract_axis_titles()

Extract axis information from Base R plot call

Returns per-axis objects with an optional label and optional grid navigation fields (min, max, tickStep). Grid fields are derived from xlim/ylim args or data range, and tick intervals via pretty(). Every field is included only when extraction succeeds, and an axis that ends up with none of them is left out of the payload entirely.

Usage
BaseRPointLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information with recorded plot call

Returns

Canonical axes list


BaseRPointLayerProcessor$extract_base_r_axis_grid_info()

Extract grid navigation info for a Base R axis

Computes min, max from xlim/ylim or data range, and tickStep from pretty() tick positions. Returns NULL if extraction fails.

Usage
BaseRPointLayerProcessor$extract_base_r_axis_grid_info(data, lim = NULL)
Arguments
data

Numeric vector of data values

lim

Optional axis limits (xlim or ylim)

Returns

List with min, max, tickStep or NULL


BaseRPointLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRPointLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRPointLayerProcessor$generate_selectors()

The selector for the points, scoped to this layer's plot group

Usage
BaseRPointLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

Returns

List of selectors


BaseRPointLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRPointLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Processor Factory

Description

Factory for creating Base R-specific processors. This factory creates processors for Base R plot types based on recorded plot calls.

Format

An R6 class inheriting from ProcessorFactory

Super class

ProcessorFactory -> BaseRProcessorFactory

Methods

Public methods

Inherited methods

BaseRProcessorFactory$new()

Initialize the Base R processor factory

Usage
BaseRProcessorFactory$new()

BaseRProcessorFactory$create_processor()

Create a processor for a specific plot type

Usage
BaseRProcessorFactory$create_processor(plot_type, layer_info)
Arguments
plot_type

The type of plot (e.g., "bar", "line", "point")

layer_info

Information about the layer (contains plot call and metadata)

Returns

Processor instance for the specified plot type


BaseRProcessorFactory$get_supported_types()

Get list of supported plot types

Usage
BaseRProcessorFactory$get_supported_types()
Returns

Character vector of supported plot types


BaseRProcessorFactory$get_system_name()

Get the system name

Usage
BaseRProcessorFactory$get_system_name()
Returns

System name string


BaseRProcessorFactory$is_processor_available()

Check if a specific processor class is available

Usage
BaseRProcessorFactory$is_processor_available(processor_class_name)
Arguments
processor_class_name

Name of the processor class

Returns

TRUE if available, FALSE otherwise


BaseRProcessorFactory$get_available_processors()

Get available processor classes

Enumerated from create_processor() rather than listed here, so the answer cannot drift away from what the factory actually dispatches to (#200).

Usage
BaseRProcessorFactory$get_available_processors()
Returns

Character vector of available processor class names


BaseRProcessorFactory$try_create_processor()

Create a processor with error handling

Usage
BaseRProcessorFactory$try_create_processor(plot_type, layer_info)
Arguments
plot_type

The type of plot

layer_info

The layer information

Returns

Processor instance or NULL if creation fails


BaseRProcessorFactory$clone()

The objects of this class are cloneable with this method.

Usage
BaseRProcessorFactory$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Q-Q Plot Layer Processor

Description

Reads qqnorm() and qqplot() as the scatter of quantile pairs they draw.

A Q-Q plot is a scatter, and BaseRPointLayerProcessor already knows how to emit one – the selector, the title and the grid all carry over unchanged, and qqnorm() exports its marks under graphics-plot-N-points-1, the same grob the point processor looks for. What does not carry over is the data.

The base R processors read a call's recorded arguments, not the drawn grob, and a Q-Q plot's arguments are not its coordinates. qqnorm(y) is handed one sample and draws it against theoretical quantiles it computes; qqplot(x, y) is handed two samples of possibly different lengths and draws one interpolated pair per point of the shorter. Read as an ordinary scatter, both would announce numbers the chart does not draw – for qqnorm the raw sample on an axis of standard deviations, which is the one reading a Q-Q plot most needs not to have, because the whole point of the chart is the comparison between the two.

So the coordinates are not re-derived here. stats computes them and both functions will hand them over without drawing: plot.it = FALSE returns exactly the pairs the plotted call would have drawn. Forwarding the recorded arguments rather than picking them apart is what makes the awkward cases free. Measured on eight values against five:

qqnorm(x, plot.it = FALSE)$x    theoretical quantiles, in the
                                caller's order, not sorted
qqnorm(x, datax = TRUE, ...)    the same pair, swapped
qqplot(x, y, plot.it = FALSE)   both length 5; x interpolated to the
                                shorter sample's quantiles

datax is the clearest of them: it swaps which axis holds the sample, and forwarding it means nothing here has to know that.

The pairs come back in the order the call drew them, which is the order the points grob lays its marks down in, so the selector list keeps pairing positionally. Nothing is sorted.

Super classes

LayerProcessor -> BaseRPointLayerProcessor -> BaseRQqLayerProcessor

Methods

Public methods

Inherited methods

BaseRQqLayerProcessor$extract_data()

Extract the quantile pairs the call drew

Usage
BaseRQqLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of points, each a list of x and y


BaseRQqLayerProcessor$quantile_pairs()

Ask stats for the pairs the call drew

Returns NULL when the computation raises or does not come back as a usable pair of equal-length numeric vectors, which leaves the layer empty and the chart on the fallback rather than shipping half of it.

Usage
BaseRQqLayerProcessor$quantile_pairs(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List with x and y, or NULL


BaseRQqLayerProcessor$extract_axis_titles()

Axis labels and grid for a Q-Q plot

qqnorm() writes "Theoretical Quantiles" against "Sample Quantiles" whenever the caller does not, and datax = TRUE swaps them along with the axes – both are constants in the function's own signature, so they are the labels the chart really carries rather than a guess.

qqplot() has no such defaults: its are deparse1(substitute(x)), the caller's expression, which is gone by the time the wrapper has recorded evaluated values. So a qqplot() the caller did not label is left unlabelled for the renderer's generic, on the same reasoning the point processor already states – a guessed noun is worse than none.

The grid is computed from the drawn pairs, not from the recorded arguments: those are the samples, and on qqnorm one of the two axes is not a sample at all.

Usage
BaseRQqLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Canonical axes list


BaseRQqLayerProcessor$default_axis_labels()

The labels the call writes when the caller does not

Usage
BaseRQqLayerProcessor$default_axis_labels(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List with x and y, either a string or NULL


BaseRQqLayerProcessor$extract_main_title()

The title the call writes when the caller does not

qqnorm()'s main defaults to "Normal Q-Q Plot" and it is drawn, so announcing it is reporting the chart rather than inventing a name for it. qqplot() has no default title and gets none.

Usage
BaseRQqLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

The title string


BaseRQqLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRQqLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Q-Q Reference Line Layer Processor

Description

Reads qqline() as the reference line it draws.

qqnorm() and qqplot() became readable in #251, and qqline() – how nearly every Q-Q plot in the wild is finished – was listed in LOW with no reading at all, purely so a chart carrying one would decline rather than come out as a scatter with a drawn mark silently missing from it. That was the lower of the two claims; this is the reading (#252).

Why it is recorded at all

stats::qqline() ends in abline(int, slope, ...), and that call is reached from inside the stats namespace, where maidr's search-path wrapper never sees it. Measured: before qqline was listed, ⁠qqnorm(x); qqline(x)⁠ recorded exactly one call and the reference line left no trace.

Where the endpoints come from

Not from the drawn grob and not re-derived. stats::qqline's body is four lines, and the line it draws is the one through two points:

y <- quantile(y, probs, names = FALSE, type = qtype, na.rm = TRUE)
x <- distribution(probs)

with probs defaulting to c(0.25, 0.75) and distribution to qnorm. This class asks stats for the same two anchors, from the call's own arguments, so a qqline() written with a non-default probs, qtype or distribution is read from what it was given rather than from the defaults. datax = TRUE swaps which of the two the slope is taken over, and qqline() takes its own copy of that argument rather than inheriting the plot's – so a qqline(datax = TRUE) over a qqnorm(datax = FALSE) is expressible, and is read from the qqline call.

Why the x range is not the parent's

BaseRLineLayerProcessor$extract_abline_data() takes its x range from get_x_range_from_group(), which reads the group's HIGH call's first argument as the x data. On a qqnorm group that argument is the sample, not the theoretical quantiles the chart puts on x – so inheriting it would stretch the line across the wrong interval, which is the class of mistake the Q-Q reading exists to avoid. The range comes from the drawn pairs instead, which BaseRQqLayerProcessor already computes from stats' own output.

Highlighting needs nothing new: ⁠qqnorm(x); qqline(x)⁠ and ⁠plot(x, y); abline(0, 1)⁠ export the same grob, graphics-plot-1-abline-ab-1, so the parent's abline selector reaches it unchanged.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRQqlineLayerProcessor

Methods

Public methods

Inherited methods

BaseRQqlineLayerProcessor$extract_data()

Extract the two endpoints the reference line is drawn between

Usage
BaseRQqlineLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of one series, each point a list of x and y


BaseRQqlineLayerProcessor$reference_line()

The intercept and slope stats::qqline computes

Reproduces the four lines of stats::qqline's own body against the recorded arguments, defaults included. Returns NULL when the call did not carry a sample this can read, or when the anchors come back degenerate – two equal quantiles give a slope of Inf or NaN, and a line through them is not a line the chart drew.

Usage
BaseRQqlineLayerProcessor$reference_line(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List with intercept and slope, or NULL


BaseRQqlineLayerProcessor$qq_x_range()

The x interval the chart drew, taken from the Q-Q pairs

The group's HIGH call is the qqnorm() or qqplot() this line sits on, and its recorded first argument is a sample rather than either drawn coordinate. So the range is read off the pairs stats computes, which is what the chart put on the x axis.

Usage
BaseRQqlineLayerProcessor$qq_x_range(layer_info)
Arguments
layer_info

Layer information carrying the group

Returns

Numeric of length two, or NULL


BaseRQqlineLayerProcessor$selector_grob_type()

Which grob family this layer's selectors are drawn from

qqline() ends in abline(), so the mark it leaves is an abline's: ⁠qqnorm(x); qqline(x)⁠ and ⁠plot(x, y); abline(0, 1)⁠ export the same graphics-plot-1-abline-ab-1. The parent keys this off the recorded function name, which here is qqline rather than abline, so without the override the selector would look under lines and find nothing – announcing the line correctly and highlighting nothing, which is the shape of defect #145 was about.

Usage
BaseRQqlineLayerProcessor$selector_grob_type(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

The grob family name


BaseRQqlineLayerProcessor$named_or_first()

A named argument, or the first positional one

Usage
BaseRQqlineLayerProcessor$named_or_first(args, name)
Arguments
args

The recorded arguments

name

The formal's name

Returns

The value, or NULL


BaseRQqlineLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRQqlineLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Smooth/Density Layer Processor

Description

Processes Base R smooth curves including:

Super class

LayerProcessor -> BaseRSmoothLayerProcessor

Methods

Public methods

Inherited methods

BaseRSmoothLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRSmoothLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

grob_id

Unused; present for the processor interface

panel_id

Unused; present for the processor interface

panel_ctx

Unused; present for the processor interface

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRSmoothLayerProcessor$extract_data()

One point per fitted value of the smooth, density or curve

Usage
BaseRSmoothLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of points


BaseRSmoothLayerProcessor$generate_selectors()

The selector for the curve's polyline

Usage
BaseRSmoothLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

Returns

List of selectors


BaseRSmoothLayerProcessor$find_polyline_grobs()

Find the lines container grob for this layer

Usage
BaseRSmoothLayerProcessor$find_polyline_grobs(grob, call_index = NULL)
Arguments
grob

The grob tree to search

call_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

Grob name, or NULL


BaseRSmoothLayerProcessor$generate_selectors_from_grob()

Build this layer's selector from the grob tree

Usage
BaseRSmoothLayerProcessor$generate_selectors_from_grob(grob, call_index = NULL)
Arguments
grob

The grob tree to search

call_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

A selector string, or an empty string when no grob matches


BaseRSmoothLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

The x axis holds whatever variable was smoothed, which the recorded arguments no longer name, so it carries no default. The y axis does when the curve came from density(): that estimate is a density, and plot.density() prints exactly that word.

Usage
BaseRSmoothLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRSmoothLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRSmoothLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRSmoothLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRSmoothLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Spectral Density Processor

Description

Reads spectrum() as the line it draws: the estimated spectral density against frequency.

The values are the raw spec, not its logarithm. plot.spec draws on a log y axis by default, but it puts the log on the axis and hands plot.xy the untransformed values – traced, the first call's y equals spectrum(v, plot = FALSE)$spec exactly. So the numbers a reader hears are the numbers the chart is scaled from, and taking a logarithm here would announce a series the caller never computed.

The caller's arguments are forwarded. spans, taper, detrend and the rest change the estimate, so recomputing with defaults would announce a different curve from the one drawn. The recorded call's arguments are passed through with plot = FALSE added.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRSpectrumLayerProcessor

Methods

Public methods

Inherited methods

BaseRSpectrumLayerProcessor$process()

Emit the spectral density as a line

Usage
BaseRSpectrumLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

Unused; the selector is built rather than searched for.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A line layer, or NULL when nothing was read


BaseRSpectrumLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRSpectrumLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Spike Plot Layer Processor

Description

Processes Base R spike layers – plot(x, y, type = "h") and the lines() equivalent. type = "h" draws a vertical line from the baseline to each value and joins nothing to anything: the samples stand side by side rather than in a series.

Read as a lollipop layer, which the core builds on BarTrace: one value per position, with no claim about the space between two of them. The marker head a lollipop conventionally carries is the only difference from what base R draws here, and it is not something a reader hears.

Announced as a line before this existed, which is the reading a spike chart most needs not to have – a line says the samples are joined and that the space between them can be interpolated, and that is the one relationship the chart is drawn to deny (#239).

Data extraction, axis titles and the main title are a line layer's, so this inherits BaseRLineLayerProcessor; what it adds is the layer type, the flat point list a bar-shaped trace wants, and the grob family the spikes are actually named after.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRSpikeLayerProcessor

Methods

Public methods

Inherited methods

BaseRSpikeLayerProcessor$process()

Process the spike layer.

Usage
BaseRSpikeLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (kept for interface compatibility)

layout

Unused for Base R (kept for interface compatibility)

built

Unused for Base R (kept for interface compatibility)

gt

Gtable object used for selector generation (optional)

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Information about the recorded plot call

Returns

List with data, selectors, type, title and axes


BaseRSpikeLayerProcessor$extract_data()

Read the spikes as one flat list of points.

A line layer's data is a list of series, because several lines can share one layer. A lollipop is read as a bar is, and a bar layer's data is one point per position – so the single series the inherited extraction produces is unwrapped here rather than shipped one level too deep, where the frontend would read the whole chart as a single point.

Only the first series is taken. plot(type = "h") and lines(type = "h") each draw exactly one, and matplot – the call that draws several – has its own dispatch that never reaches here.

Usage
BaseRSpikeLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

List of x/y points, empty when there is nothing to read


BaseRSpikeLayerProcessor$selector_grob_type()

Draw a spike layer's selectors from the spike grobs.

gridGraphics names a grob after what drew it, so spikes land under graphics-plot-N-spike-M – never under the ⁠-lines-⁠ name the inherited search looks for. Measured on plot(1:6, y, type = "h"), one polyline per spike sits under that grob, in data order:

<polyline id="graphics-plot-1-spike-1.1.1" points="74.4,... "/>
<polyline id="graphics-plot-1-spike-1.1.2" points="151.2,..."/>
...

so the one selector this yields resolves to one element per point, which is what a bar-shaped trace pairs with its data.

Usage
BaseRSpikeLayerProcessor$selector_grob_type(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

"spike"


BaseRSpikeLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRSpikeLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Spine Plot Layer Processor

Description

Reads spineplot() as the two-way contingency table it draws.

A spine plot is a mosaic of one categorical axis against another: one column per level of x, its width that level's share of all observations, split vertically by y's conditional proportions inside it. That is the shape BaseRMosaicLayerProcessor was written for in #247 – "the column widths encode data too" – so a spine plot is read as a mosaic layer, and this class changes only the two things that differ.

Where the table comes from. mosaicplot() is handed its table, so the recorded call carries it. spineplot() is handed the two variables and builds the table itself, and it has no plot argument to be asked for the table without drawing – cdplot() has one, spineplot() does not. But it returns what it drew:

spineplot(table(f, g))
#       g
# f      no yes
#   low  11  12
#   mid   7  14
#   high 12   4

so the call is replayed on a throwaway device and the return value taken. graphics::spineplot by the qualified name, not the bare one: maidr patches the name on the search path, and the bare call would record the replay as a second chart. Reproducing the binning by hand instead would be a re-derivation – spineplot cuts a numeric x by its own rule – and the point of every reading in this package is that the library is asked rather than imitated.

Where the tiles are. mosaicplot() writes one polygon grob per cell. spineplot() writes one rect grob for the whole panel, and gridSVG exports it as one ⁠<rect>⁠ element per tile. Measured on a 3x2 table:

graphics-plot-1-rect-1.1.1   x  59.04   w 152.86   h 118.71
graphics-plot-1-rect-1.1.2   x  59.04   w 152.86   h 108.81
graphics-plot-1-rect-1.1.3   x 219.88   w 139.57   h 151.68
graphics-plot-1-rect-1.1.4   x 219.88   w 139.57   h  75.84
graphics-plot-1-rect-1.1.5   x 367.42   w 106.34   h  56.88
graphics-plot-1-rect-1.1.6   x 367.42   w 106.34   h 170.64

Six elements for six cells, sharing an x within a column, and the widths 152.86 : 139.57 : 106.34 in the marginals' own ratio 23 : 21 : 16. The order is column-major, and within a column the last fill level is drawn first: the heights pair 118.71 : 108.81 with 12 : 11, which is yes above no. That is the opposite of the mosaic's ascending order, which is why the index is computed here rather than inherited.

A zero cell still draws. The hazard this shape invites is the one xability/maidr#1002 found elsewhere: a count of zero skipped rather than drawn, shifting every later tile's index by one. Measured on a table with a genuine zero in it, spineplot draws the tile anyway with height="0", so the positional pairing holds. There is a test for exactly that.

Super classes

LayerProcessor -> BaseRMosaicLayerProcessor -> BaseRSpineplotLayerProcessor

Methods

Public methods

Inherited methods

BaseRSpineplotLayerProcessor$recorded_table()

The table spineplot() drew, by replaying the call

Memoised, because process() asks for it three times – once for the data, once for the axis names and once for the selectors – and each ask would otherwise open a device and draw the chart again.

Usage
BaseRSpineplotLayerProcessor$recorded_table(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

A 2-D table, or NULL when the call cannot be replayed


BaseRSpineplotLayerProcessor$generate_selectors()

Address the tile each cell was drawn into

One rect grob holds the whole panel, so the tiles are its sub-elements rather than grobs of their own and are addressed by their exported ids. The grob is found rather than named by formula, and a panel that does not hold exactly one is declined: a guessed id resolves to nothing at best and to another panel's tiles at worst, and a spine plot with no highlight still reads (#145).

The index runs column-major with the fill levels descending inside a column, which is the order measured off the drawing and recorded in the class note. The emitted list is in the payload's own order – all categories of the first fill, then the second – so the two line up.

Usage
BaseRSpineplotLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Information about the recorded plot call

gt

Gtable object to search

Returns

List of selectors, one per cell, or empty when unresolvable


BaseRSpineplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRSpineplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Stacked Bar Layer Processor

Description

Processes Base R stacked bar plot layers intercepted via the patching system. Assumes sorting by x (columns) and then z (rows) has already been applied by the SortingPatcher.

Super class

LayerProcessor -> BaseRStackedBarLayerProcessor

Methods

Public methods

Inherited methods

BaseRStackedBarLayerProcessor$process()

Process the layer: read its data, selectors, axis titles and main title from the recorded call

Usage
BaseRStackedBarLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

grob_id

Unused; present for the processor interface

panel_id

Unused; present for the processor interface

panel_ctx

Unused; present for the processor interface

layer_info

Layer information with the recorded call

Returns

List describing the layer for the MAIDR payload


BaseRStackedBarLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRStackedBarLayerProcessor$needs_reordering()
Returns

FALSE


BaseRStackedBarLayerProcessor$extract_data()

One series per row of the recorded height matrix

Usage
BaseRStackedBarLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

List of series


BaseRStackedBarLayerProcessor$extract_axis_titles()

Extract the axis titles for this layer

A stacked barplot() records no title unless the author wrote one, and its points always carry the column category on x and the segment height on y, so the defaults name those two. The stack's own dimension is already announced per point as z; nothing in the call names the variable those groups came from, so no z title is claimed.

Usage
BaseRStackedBarLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information

Returns

Canonical axes list


BaseRStackedBarLayerProcessor$extract_main_title()

The main title of the recorded call, or an empty string

Usage
BaseRStackedBarLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Character string


BaseRStackedBarLayerProcessor$generate_selectors()

The selector for the segments, scoped to this layer's plot group

Usage
BaseRStackedBarLayerProcessor$generate_selectors(
  layer_info,
  gt = NULL,
  extracted_data = NULL
)
Arguments
layer_info

Layer information with the recorded call

gt

Gtable of the replayed drawing (optional)

extracted_data

The data already extracted for this layer (optional)

Returns

List of selectors


BaseRStackedBarLayerProcessor$find_rect_groups()

Find every rect group drawn by the plot group at call_index

Usage
BaseRStackedBarLayerProcessor$find_rect_groups(grob, call_index)
Arguments
grob

The grob tree to search

call_index

Index of the recorded plot group, which numbers the panel's grobs

Returns

Character vector of grob names


BaseRStackedBarLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRStackedBarLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Star Plot Processor

Description

Reads stars() as the radar it draws: one closed outline per observation, with a spoke for each variable.

It is a multi-line layer. MAIDR's radar trace is navigated as one – "each spoke a column and each series a row" – so the whole reading is handing over the matrix with its axes the other way round from the way stars() takes it. stars(m) draws a glyph per row, so the rows are the series and the columns are the spokes, and BaseRLineLayerProcessor's extract_multiline_data() wants series in columns. Hence the transpose, which is the only rearranging here.

The values are the caller's, not the drawing's. stars() scales every column to ⁠[0, 1]⁠ before drawing, so the radii on the page are shares of each column's range rather than the readings themselves. Announcing those would tell a reader that observation 1 scores 0 on a variable it merely has the smallest value of. The recorded matrix carries what the caller measured, and that is what a reader is told.

It is read without an outline, deliberately. The marks are there and the pairing is known – measured by giving each observation its own col.stars and reading every polygon's fill, observation k owns polygons ⁠2k - 1⁠ and ⁠2k⁠, both filled its colour, plus a segments-k carrying one segment per variable:

polygon-1  #111199   polygon-2  #111199   segments-1   observation 1
polygon-3  #229922   polygon-4  #229922   segments-2   observation 2
...

What is not established is the selector those grobs export to. A selector is only worth emitting once it has been resolved against a real export, and a real export cannot be had until the chart stops falling back to a picture – which is what this reading is for. So the outline is left for a follow-up that can measure it, rather than guessed from the grob names. Reading without one is what gauge already does upstream when the marks and the cursor cannot be paired with confidence.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRStarsLayerProcessor

Methods

Public methods

Inherited methods

BaseRStarsLayerProcessor$process()

Emit one radar series per observation

Usage
BaseRStarsLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

Unused; this reading emits no selectors.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A radar layer, or NULL when nothing was read


BaseRStarsLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRStarsLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Step Plot Layer Processor

Description

Processes Base R stairstep layers – plot(x, y, type = "s"), plot(x, y, type = "S") and the lines() equivalents. A step chart is piecewise constant: the value is held across an interval and then jumps, rather than being interpolated between samples the way a line implies.

Data extraction, axis titles, the main title and polyline selector generation are identical to a line layer, so this class inherits BaseRLineLayerProcessor and adds only the step-specific reporting: the layer type and the stepDirection convention the call requested.

One data point is emitted per data sample, never one per stairstep vertex – the MAIDR frontend maps the rendered polyline's corner vertices back onto the samples itself.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRStepLayerProcessor

Methods

Public methods

Inherited methods

BaseRStepLayerProcessor$process()

Process the step layer.

Usage
BaseRStepLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused for Base R (kept for interface compatibility)

layout

Unused for Base R (kept for interface compatibility)

built

Unused for Base R (kept for interface compatibility)

gt

Gtable object used for selector generation (optional)

grob_id

Unused for Base R

panel_id

Unused for Base R

panel_ctx

Unused for Base R

layer_info

Information about the recorded plot call

Returns

List with data, selectors, type, title, axes and stepDirection


BaseRStepLayerProcessor$extract_step_direction()

Read the step convention the recorded call requested.

type = "s" draws the horizontal segment first (MAIDR's "hv") and type = "S" draws the vertical segment first ("vh"). The two are not interchangeable, so an unrecognised or absent type yields NULL and the caller omits stepDirection rather than guessing.

Usage
BaseRStepLayerProcessor$extract_step_direction(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

"hv", "vh", or NULL


BaseRStepLayerProcessor$selector_grob_type()

Draw a step layer's selectors from the stairstep grobs.

gridGraphics names a grob after the type letter that drew it, so a stairstep lands under graphics-plot-N-step-M (type = "s") or graphics-plot-N-Step-M (type = "S") – never under the ⁠-lines-⁠ name the inherited line search looks for. Without this override a Base R step layer emits zero selectors, and the frontend's ⁠selectors.length === series count⁠ precondition then drops the layer's highlighting entirely.

Usage
BaseRStepLayerProcessor$selector_grob_type(layer_info)
Arguments
layer_info

Information about the recorded plot call

Returns

"step"


BaseRStepLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRStepLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Strip Chart Layer Processor

Description

Reads stripchart() as the one-dimensional scatter it draws: every observation as its own mark, laid along a value axis at its group's position.

One layer per group. That is the shape of the reading and it is forced by the drawing rather than chosen for tidiness: measured on two groups, gridGraphics exports

graphics-plot-1-points-1
graphics-plot-1-points-2

– one points grob per group – and find_graphics_plot_grob() answers with the first match, so a single layer would announce every observation and highlight only the first group's. It is also the reading the same chart already gets in py-maidr, whose stripplot and swarmplot split into one named layer per category.

The groups are not re-derived. stripchart forms them in two places and both are read rather than reimplemented:

stripchart.default   groups <- if (is.list(x)) x
                               else if (is.numeric(x)) list(x)
stripchart.formula   split(mf[[response]], mf[-response])
                     after stats::model.frame

so a list keeps its own names, a bare vector is one unnamed group, and a formula is split by split() itself.

A formula with no ⁠data =⁠ is read too, which is worth stating because the opposite is the obvious guess. A formula carries the environment it was written in, and the recorded call holds the formula, so that environment is still reachable when the chart is read; model.frame() resolves the variables from it exactly as stripchart.formula did when it drew them. Measured on stripchart(len ~ supp) with the variables local to a function, global, and in a closure whose call had returned – all three read back the drawn groups and values. This is also what BaseRBoxplotLayerProcessor already does with its data = args[["data"]].

What that inherits is the hazard of a late lookup: rebinding len between the drawing and the rendering makes the payload announce the new values. Measured, and filed as #254 – it belongs to every recorded formula in this package rather than to this processor, and refusing the ordinary spelling to dodge it would trade a chart that reads exactly for a picture.

The position stays a number. ScatterPoint.x is typed number in the grammar and ScatterTrace does arithmetic on it, so the group's name travels beside its position in yLabel – or xLabel on a vertical chart – exactly as the ggplot2 point processor does since #178.

method = "jitter" is not a reading problem here, and that is worth saying because it is one for geom_jitter() (#174). A stripchart jitters along the group axis only; the value axis is untouched, so every number announced is the observation itself. What is displaced is the position whose name is already carried as a label.

Super classes

LayerProcessor -> BaseRPointLayerProcessor -> BaseRStripchartLayerProcessor

Methods

Public methods

Inherited methods

BaseRStripchartLayerProcessor$process()

Emit one point layer per drawn group

Usage
BaseRStripchartLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

The grob tree, for the selectors.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A multi-layer result, or NULL when nothing was read


BaseRStripchartLayerProcessor$extract_groups()

The groups stripchart() itself would form

Read from the recorded call rather than re-derived, per the two spellings in the class docs. Returns an empty list for anything this cannot resolve exactly, which leaves the chart on the static fallback.

Usage
BaseRStripchartLayerProcessor$extract_groups(layer_info)
Arguments
layer_info

Layer information with the recorded call.

Returns

A named list of numeric vectors, one per group


BaseRStripchartLayerProcessor$draws_horizontally()

Which visual axis the observations run along

stripchart() draws horizontally unless told otherwise, which is the opposite of boxplot()'s default and worth reading from the call rather than assuming.

Usage
BaseRStripchartLayerProcessor$draws_horizontally(layer_info)
Arguments
layer_info

Layer information with the recorded call.

Returns

TRUE when the values run left to right


BaseRStripchartLayerProcessor$extract_axis_titles()

Name the value axis and the group axis

Overrides the inherited scatter helper, which reads the recorded x as a pair of coordinates and so gets a stripchart wrong in both directions at once. Measured on stripchart(c(3.1, 4.2, 5.0, 2.2, 6.9)), that helper hands the bare vector to xy.coords(), which reads it as y and indexes x over 1:5:

announced   x  1 .. 5        y  2 .. 7
drawn       x  2.2 .. 6.9    y  one group, at 1

– the value range offered on the group axis, and a bare index on the value axis. A stripchart is one categorical axis against one measured axis, the same shape boxplot() and barplot() draw, so it is named the same way and by the same helper; vertical = TRUE swaps which visual axis holds which, exactly as horizontal = TRUE does there.

No range is emitted for either axis. The group axis has none to give – its positions are names – and the value axis is left to the renderer's own generic, which is where every other grouped base R chart leaves it.

Usage
BaseRStripchartLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information with the recorded call.

Returns

Canonical axes list


BaseRStripchartLayerProcessor$split_by_formula()

Split a formula's response by its grouping columns

stripchart.formula does exactly this, through stats::model.frame and split, so both are called rather than imitated. data is passed through exactly as recorded – a data frame, a plain list, or NULL – because model.frame() accepts all three, and NULL is its own default for "resolve from the formula's environment", which is what the drawing did. Coercing it first bought nothing: measured, as.data.frame(NULL) and NULL produce the same split. This is the spelling BaseRBoxplotLayerProcessor already uses.

A one-sided formula needs no guard of its own. stripchart(~ len) does not draw at all – stripchart.formula stops with "formula missing or incorrect" – so it cannot be recorded, and reached directly it gives response == 0, whose frame[[0]] the tryCatch below already turns into the decline.

Usage
BaseRStripchartLayerProcessor$split_by_formula(formula, data, frame = NULL)
Arguments
formula

The recorded formula.

data

The recorded data argument, or NULL.

frame

The model frame kept when the call was recorded, or NULL for a call recorded before that existed.

Returns

A named list of numeric vectors, or NULL


BaseRStripchartLayerProcessor$group_names()

The names stripchart() writes down the group axis

group.names first, then the list's own names, then the positions – the order stripchart.default resolves them in. A group.names of the wrong length is ignored here because it is ignored there.

Usage
BaseRStripchartLayerProcessor$group_names(args, groups)
Arguments
args

The recorded argument list.

groups

The groups already formed.

Returns

One name per group


BaseRStripchartLayerProcessor$group_positions()

Where along the group axis each group was drawn

at when the caller gave one of the right length, and 1:n otherwise, which is what stripchart.default falls back to.

Usage
BaseRStripchartLayerProcessor$group_positions(layer_info, count)
Arguments
layer_info

Layer information with the recorded call.

count

How many groups there are.

Returns

One numeric position per group


BaseRStripchartLayerProcessor$group_points()

One group's observations, as points

The value goes on the value axis and the position on the group axis, and the group's name travels beside the position as a label rather than in place of it – ScatterPoint.x is typed number and ScatterTrace does arithmetic on it (#178).

Usage
BaseRStripchartLayerProcessor$group_points(values, label, position, horizontal)
Arguments
values

The group's observations.

label

The group's name.

position

Where the group sits on its axis.

horizontal

TRUE when the values run left to right.

Returns

A list of points


BaseRStripchartLayerProcessor$group_selectors()

The marks one group was drawn into

Built rather than searched for. find_graphics_plot_grob() answers with the first points grob of the plot, and a stripchart draws one per group, so a search would give every layer the first group's marks. The names are graphics-plot-{plot}-points-{group}, measured against a real gridSVG export, and every emitted selector was then resolved in Chromium against a rendering of a three-group chart: 5, 4 and 2 elements, one per observation.

Usage
BaseRStripchartLayerProcessor$group_selectors(layer_info, index)
Arguments
layer_info

Layer information with the recorded call.

index

Which group this is, from 1.

Returns

A one-element list of selectors


BaseRStripchartLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRStripchartLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Seasonal Subseries Layer Processor

Description

Reads monthplot() as the set of lines it draws: one series per cycle position – month, quarter, whatever the frequency makes it – running over that position's own subseries.

stats::monthplot.default draws exactly that, one lines() call per position:

for (i in 1L:f) {
  sub <- phase == i
  lines((y[sub] - min(y)) * scale - 0.45 + i, x[sub], type = type, ...)
}

The x coordinate there is a slot offset, not a reading: every subseries is squeezed into its own 0.9-wide band so twelve of them fit side by side on one axis. What the offset is computed from is times, and that is the reading – the cycle each observation falls in. So each series comes out as its own subseries over times, carrying its position's label as z, which is the shape BaseRLineLayerProcessor already reads for matplot.

Recomputed from the recorded arguments rather than read back off the drawing, for the reason the correlograms recompute theirs (#276): the slot offsets are on the page and the times are not, and the offsets are the half that carries no meaning.

What is not read

base draws one horizontal segment per position at base(x[phase == i]) – the position's mean, by default – and those segments are not emitted. There is no shape in the grammar for a per-series reference level, and a series of its own would be wrong: the twelve means run across the cycle positions, while every series here runs across the cycles, so putting them in one layer would put two x domains in it. Left to a maintainer with the grammar to change.

type does not change the reading, for the reason interaction.plot's does not change its own (#278): "l" and "h" draw the same subseries with different marks, and reading the spikes as loose values would lose the grouping that makes the chart a subseries plot.

It does change where the marks are, though. "h" is not handed to lines() the way a type usually is – monthplot branches and calls segments() instead – so the grobs land under ⁠-segments-⁠ and the inherited search for ⁠-lines-⁠ finds nothing. A layer with no selectors is dropped by the frontend's ⁠selectors.length === series count⁠ precondition, so the chart would read correctly and highlight nothing at all. See selector_grob_type() and generate_selectors() below.

A monthly series whose labels this reads rather than the caller writes is named with month.abb rather than with monthplot's own initials – see the note beside ts_labels() for why the initials do not survive being announced.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRSubseriesLayerProcessor

Methods

Public methods

Inherited methods

BaseRSubseriesLayerProcessor$extract_data()

One series per cycle position, over that position's own subseries.

Built point by point rather than through extract_multiline_data(), which takes a matrix and so would need the short positions padded. A series of 48 monthly observations gives every month four cycles and pads nothing; 50 gives January and February five and the other ten four, and the padding would put two points on the chart that monthplot never drew.

Usage
BaseRSubseriesLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information for the recorded call

Returns

A list of series, each a list of x/y/z points


BaseRSubseriesLayerProcessor$extract_axis_titles()

The labels the drawing writes.

monthplot's own ylab default is deparse1(substitute(x)), so it names the expression the caller wrote. The wrapper records evaluated values, by which point substitute() is gone, so the expression comes off the recorded call text instead, the way the correlograms recover their series names (#276).

There is no default for xlab: monthplot blanks it unless the caller passes one, because the axis it writes carries the cycle labels and not a quantity. An unset label is left unset rather than invented.

Usage
BaseRSubseriesLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

Layer information for the recorded call

Returns

An axes list from build_axes()


BaseRSubseriesLayerProcessor$generate_selectors()

The selectors, with the base line's grob left out of them.

monthplot draws the base reference segments in one segments() call before the loop, so on a type = "h" chart the first ⁠-segments-⁠ grob is the twelve means and the rest are the twelve subseries. Handed over as they are, every series would be outlined on the position before it.

A count that is not exactly one more than the series drops the selectors rather than guessing which grob is which: outlining the wrong spikes is worse than outlining none.

base = NULL would remove the extra grob, and cannot arrive here: monthplot(x, type = "h", base = NULL) raises ⁠object 'means' not found⁠ inside stats – measured – because the "h" branch reads a means the base guard never computed. A call that raised is never recorded.

Usage
BaseRSubseriesLayerProcessor$generate_selectors(layer_info, gt = NULL)
Arguments
layer_info

Layer information for the recorded call

gt

The gtable the drawing was exported to

Returns

A list of CSS selectors, one per series


BaseRSubseriesLayerProcessor$selector_grob_type()

Which family of grob names this layer's selectors come from.

⁠-segments-⁠ when the spikes were drawn, ⁠-lines-⁠ otherwise. Public because the parent's is: generate_selectors_from_grob() reaches it through ⁠self$⁠, which finds nothing private.

Usage
BaseRSubseriesLayerProcessor$selector_grob_type(layer_info)
Arguments
layer_info

Layer information for the recorded call

Returns

"segments" or "lines"


BaseRSubseriesLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRSubseriesLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Term Plot Processor

Description

Reads termplot() as the partial-effect curves it draws: one panel per term, the term's contribution to the fit plotted against its own carrier.

A grid, not a layer. Like pairs() and lag.plot(), one termplot() call draws several panels, and the orchestrator's ordinary multipanel path cannot split them: combine_layer_results() maps a layer to a cell by its group index, and one recorded call is one group, so every curve would land in the same cell. So this answers multi_panel = TRUE and places each curve at its own cell.

The panels are pages, not terms. termplot() sets no layout of its own – unlike pairs(), which calls par(mfrow) itself – so the caller's par(mfrow) decides how many terms share a page, and R starts a new page when it runs out of cells. Only the last page is exported. Measured on a three-term lm:

no mfrow      (k = 1)   graphics-plot-1                 1 panel
mfrow c(1,2)  (k = 2)   graphics-plot-1                 1 panel
mfrow c(1,3)  (k = 3)   graphics-plot-1 .. -3           3 panels
mfrow c(2,2)  (k = 4)   graphics-plot-1 .. -3           3 panels

So with n terms and k cells the page carries the last ((n - 1) %% k) + 1 of them, which is the rule compute_panel_slots() already applies to whole plot groups – the same arithmetic, one level down. A reading that announced all n terms would name curves that are not on the page.

The par call is recorded as LAYOUT rather than as a layer, so it does not reach the processor with the rest of the call. It is read off the device the call was recorded on, through the same detect_panel_configuration() the orchestrator uses, so the two cannot disagree about the grid.

What a panel draws. The curve is the term's fitted contribution, predict(model, type = "terms")[, term], against that term's carrier from the model frame, in increasing carrier order – which is the order termplot() sorts them into before drawing. With partial.resid = TRUE it adds the partial residuals, contribution + residuals(model), as points beside the curve; measured, that is a second grob in the same panel:

termplot(fit)                     panel k: lines-1
termplot(fit, partial.resid = T)  panel k: lines-1 and points-1

The points are left for a follow-up rather than emitted as a second layer: they are a different reading of the same panel, and the curve is the thing termplot() exists to draw.

A factor term is declined. termplot() draws it as a step function over the levels, which is neither this line nor a bar, and reading it as a line would announce a slope between levels that have no order. It is left out of the grid rather than given a wrong shape.

Super classes

LayerProcessor -> BaseRLineLayerProcessor -> BaseRTermplotLayerProcessor

Methods

Public methods

Inherited methods

BaseRTermplotLayerProcessor$process()

Emit one line layer per drawn panel

Usage
BaseRTermplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

Unused; the selectors are built rather than searched for.

grob_id

Unused; present for the processor interface.

panel_id

Unused; present for the processor interface.

panel_ctx

Unused; present for the processor interface.

layer_info

Layer information with the recorded call.

Returns

A multi-panel result, or NULL when nothing was read


BaseRTermplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRTermplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Unknown Layer Processor

Description

Processes unknown Base R layer types as a fallback

Super class

LayerProcessor -> BaseRUnknownLayerProcessor

Methods

Public methods

Inherited methods

BaseRUnknownLayerProcessor$process()

Describe a layer nothing is known about

Usage
BaseRUnknownLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface

layout

Unused; present for the processor interface

built

Unused; present for the processor interface

gt

Gtable of the replayed drawing, searched for selectors (optional)

grob_id

Unused; present for the processor interface

panel_id

Unused; present for the processor interface

panel_ctx

Unused; present for the processor interface

layer_info

Layer information with the recorded call

Returns

List with no data and no selectors


BaseRUnknownLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing; a Base R layer is read from the recorded call and never is

Usage
BaseRUnknownLayerProcessor$needs_reordering()
Returns

FALSE


BaseRUnknownLayerProcessor$extract_data()

Nothing: an unknown layer has no data to announce

Usage
BaseRUnknownLayerProcessor$extract_data(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Empty list


BaseRUnknownLayerProcessor$generate_selectors()

Nothing: an unknown layer has no elements to address

Usage
BaseRUnknownLayerProcessor$generate_selectors(layer_info)
Arguments
layer_info

Layer information with the recorded call

Returns

Empty list


BaseRUnknownLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRUnknownLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Base R Violin Layer Processor

Description

Reads a vioplot::vioplot() call as the violin_box + violin_kde layer pair, matching what the ggplot2 adapter produces for geom_violin(). Which plotting system a user chose should not decide whether their chart is accessible.

vioplot() returns its box summary but not the density curve it drew, so both are recovered by replaying the call vioplot makes internally — see compute_vioplot_stats(), which records why that is a transcription rather than an approximation.

Super class

LayerProcessor -> BaseRViolinLayerProcessor

Methods

Public methods

Inherited methods

BaseRViolinLayerProcessor$process()

Read a recorded vioplot() call as two maidr layers

Usage
BaseRViolinLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  layer_info = NULL
)
Arguments
plot

Unused; present for the processor interface.

layout

Unused; present for the processor interface.

built

Unused; present for the processor interface.

gt

The grob tree of the rendered plot.

layer_info

The recorded plot call and its metadata.

Returns

A multi-layer result, or NULL when nothing can be read.


BaseRViolinLayerProcessor$extract_violins()

The recorded call's violins, each with its statistics

Usage
BaseRViolinLayerProcessor$extract_violins(layer_info)
Arguments
layer_info

The recorded plot call and its metadata.

Returns

A list of list(label =, stats =), one per drawn violin.


BaseRViolinLayerProcessor$build_box_data()

One box summary per violin

Usage
BaseRViolinLayerProcessor$build_box_data(violins)
Arguments
violins

As returned by extract_violins().

Returns

A list of box points.


BaseRViolinLayerProcessor$build_kde_data()

One density curve per violin

Usage
BaseRViolinLayerProcessor$build_kde_data(violins)
Arguments
violins

As returned by extract_violins().

Returns

A list of point lists, one per violin.


BaseRViolinLayerProcessor$build_kde_selectors()

Selectors addressing each violin's outline

Usage
BaseRViolinLayerProcessor$build_kde_selectors(violins, gt, plot_index)
Arguments
violins

As returned by extract_violins().

gt

The grob tree.

plot_index

Which recorded plot these grobs belong to.

Returns

A list of selector strings, or an empty list.


BaseRViolinLayerProcessor$build_box_selectors()

BoxSelector objects addressing each violin's box parts

vioplot draws the whisker, the quartile box and the median as separate grobs, so unlike a plotly violin – where the whole box is one path and every section has to share it – each section can point at what it actually is.

Usage
BaseRViolinLayerProcessor$build_box_selectors(violins, gt, plot_index)
Arguments
violins

As returned by extract_violins().

gt

The grob tree.

plot_index

Which recorded plot these grobs belong to.

Returns

A list of BoxSelector lists, or an empty list.


BaseRViolinLayerProcessor$grob_ids()

Grob names of one kind, in drawing order

Usage
BaseRViolinLayerProcessor$grob_ids(gt, plot_index, kind)
Arguments
gt

The grob tree.

plot_index

Which recorded plot these grobs belong to.

kind

One of the kinds in .maidr_vioplot_grob_kinds.

Returns

A character vector of grob names.


BaseRViolinLayerProcessor$plot_index()

Which recorded plot this layer belongs to

Usage
BaseRViolinLayerProcessor$plot_index(layer_info)
Arguments
layer_info

The recorded plot call and its metadata.

Returns

An integer index.


BaseRViolinLayerProcessor$extract_axis_titles()

Axis titles for the violin's two axes

Usage
BaseRViolinLayerProcessor$extract_axis_titles(layer_info)
Arguments
layer_info

The recorded plot call and its metadata.

Returns

An axes list.


BaseRViolinLayerProcessor$extract_main_title()

The plot's main title

Usage
BaseRViolinLayerProcessor$extract_main_title(layer_info)
Arguments
layer_info

The recorded plot call and its metadata.

Returns

A character scalar.


BaseRViolinLayerProcessor$determine_orientation()

Whether the violins run up the page or across it

Usage
BaseRViolinLayerProcessor$determine_orientation(layer_info)
Arguments
layer_info

The recorded plot call and its metadata.

Returns

"vert" or "horz".


BaseRViolinLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
BaseRViolinLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


The std choices graphics::fourfoldplot() offers

Description

Written out rather than read from formals(graphics::fourfoldplot)$std, which is an unevaluated call of length 4 (measured: class() is "call", and formals(...)$std[[1]] is the symbol c) and would have to be eval()ed on every dispatch. That the literal still matches upstream is asserted against the real formals() in tests/testthat/test-base-r-fourfoldplot.R, the answer the qqplot branch gives to the same exposure.

Usage

FOURFOLD_STD_CHOICES

A path geom that admits a threshold aesthetic

Description

ggplot2::GeomPath with one optional aesthetic added, so that a threshold mapped to a maidr_roc() layer survives ggplot_build() as a column of the built data rather than being dropped with ⁠Ignoring unknown aesthetics⁠. Nothing about the drawing changes; the aesthetic reaches no grob.

Usage

GeomRoc

Format

A ggproto object inheriting from ggplot2::GeomPath


ggplot2 System Adapter

Description

Adapter for the ggplot2 plotting system. This adapter wraps the existing ggplot2 functionality to work with the new extensible architecture.

Format

An R6 class inheriting from SystemAdapter

Super class

SystemAdapter -> Ggplot2Adapter

Methods

Public methods


Ggplot2Adapter$new()

Initialize the ggplot2 adapter

Usage
Ggplot2Adapter$new()

Ggplot2Adapter$can_handle()

Check if this adapter can handle a plot object

Usage
Ggplot2Adapter$can_handle(plot_object)
Arguments
plot_object

The plot object to check

Returns

TRUE if this adapter can handle the object, FALSE otherwise


Ggplot2Adapter$detect_layer_type()

Detect the type of a single layer

Usage
Ggplot2Adapter$detect_layer_type(layer, plot_object)
Arguments
layer

The ggplot2 layer object to analyze

plot_object

The parent plot object (for context)

Returns

String indicating the layer type (e.g., "bar", "line", "point")


Ggplot2Adapter$is_pie_coord()

Check if a bar layer is drawn as pie wedges

coord_radial() produces a CoordRadial that does NOT inherit CoordPolar, so both class names have to be tested. theta decides what the angle encodes: only theta = "y" maps a bar's height onto the angle, which is a pie. theta = "x" keeps the height on the radius, which is a coxcomb/rose - still a bar chart, just bent.

The coordinate system alone is not enough: a polar bar layer is a pie only when it draws ONE ring. geom_col(aes(x = category)) under coord_polar("y") draws one concentric ring per x category - a bullseye - and a pie payload has no room for that second dimension, so such a layer keeps the bar classification it has always had.

Usage
Ggplot2Adapter$is_pie_coord(plot_object, layer = NULL)
Arguments
plot_object

The ggplot2 plot object

layer

The layer being classified, or NULL for the plot's first

Returns

TRUE when the layer is drawn as a pie, FALSE otherwise


Ggplot2Adapter$draws_single_ring()

Check if a layer occupies a single position on x

The ring count has to come off the BUILT data: a mapping expression cannot say how many levels it has, and by build time ggplot2 has already resolved every constant form - the literal "", a one-level factor, a column holding one repeated value - to the same single x position. Each facet panel is its own pie, so constancy is asked of each panel separately. A build that fails answers FALSE, leaving the layer classified the way it was before pie support.

Usage
Ggplot2Adapter$draws_single_ring(plot_object, layer = NULL)
Arguments
plot_object

The ggplot2 plot object

layer

The layer being classified, or NULL for the plot's first

Returns

TRUE when no panel holds more than one x position


Ggplot2Adapter$find_layer_index()

Locate a layer among its plot's layers

Usage
Ggplot2Adapter$find_layer_index(plot_object, layer = NULL)
Arguments
plot_object

The ggplot2 plot object

layer

The layer to locate, or NULL for the plot's first

Returns

Integer index into the plot's layers, or NULL when absent


Ggplot2Adapter$create_orchestrator()

Create an orchestrator for this system (ggplot2)

Usage
Ggplot2Adapter$create_orchestrator(plot_object)
Arguments
plot_object

The ggplot2 plot object to process

Returns

PlotOrchestrator instance


Ggplot2Adapter$get_system_name()

Get the system name

Usage
Ggplot2Adapter$get_system_name()
Returns

System name string


Ggplot2Adapter$get_adapter()

Get a reference to this adapter (for use by orchestrator)

Usage
Ggplot2Adapter$get_adapter()
Returns

Self reference


Ggplot2Adapter$has_facets()

Check if plot has facets

Usage
Ggplot2Adapter$has_facets(plot_object)
Arguments
plot_object

The ggplot2 plot object

Returns

TRUE if plot has facets, FALSE otherwise


Ggplot2Adapter$is_patchwork()

Check if plot is a patchwork plot

Usage
Ggplot2Adapter$is_patchwork(plot_object)
Arguments
plot_object

The ggplot2 plot object

Returns

TRUE if plot is patchwork, FALSE otherwise


Ggplot2Adapter$ribbon_is_area()

Whether a ribbon fills from a baseline rather than spanning two curves.

geom_ribbon(aes(ymin = 0, ymax = y)) is an area chart: the magnitude is the height of the fill, measured from a baseline the reader can assume. geom_ribbon(aes(ymin = lo, ymax = hi)) draws the gap, and its content is the distance between two edges rather than the height of either – read as an area it would announce hi as a magnitude and drop lo entirely.

The same distinction the Python binding draws for fill_between(), and drawn the same way: only an identically-zero lower edge is an area.

Reads the built data rather than the mapping, because ymin may be a constant, a column, or a computed aesthetic, and only the built frame has resolved which. A layer that cannot be built is treated as a band, which is the reading that loses nothing: an area announced as an interval still carries both edges.

Usage
Ggplot2Adapter$ribbon_is_area(layer, plot_object)
Arguments
layer

The ggplot2 layer

plot_object

The parent plot object

Returns

TRUE when the ribbon is an area chart


Ggplot2Adapter$segments_span_lanes()

Check whether a segment layer draws intervals in lanes

Asked of the built data for the reason ribbon_is_area() is: a mapping expression cannot say whether the two ends of a segment agree, and by build time ggplot2 has resolved every spelling of the lane – a factor, a character column, a repeated constant – to the position it drew at.

The whole layer is asked at once rather than each row, which is the rule xability/maidr#1100 settled for the same reading: one geom_segment() call can hold spans and edges together, and reading three spans out of four segments would announce a gantt quietly missing a quarter of its chart.

Usage
Ggplot2Adapter$segments_span_lanes(layer, plot_object)
Arguments
layer

The layer being classified

plot_object

The ggplot2 plot object

Returns

TRUE when the layer's segments lay intervals in lanes


Ggplot2Adapter$rect_spans_lanes()

Check whether a declared rect layer draws intervals in lanes

Asked through the same predicate the processor will use, so the two cannot disagree about what a schedule is: rect_gantt_frame() renames the declared layer's bounds into the four columns segment_lane_axis() already reads, and the landed test decides.

The degenerate case comes free rather than needing a rule of its own. Measured: a declared layer whose rectangles are all zero-width normalises to level on both axes, segment_lane_axis() returns NULL, and the layer is refused instead of being announced as a schedule of zero-length work – which is the rule this file already applies to geom_segment().

Nothing else is asked of the rectangles. A guard on their shape is the structural rule the eight-chart table above the GeomRect branch of detect_layer_type() falsified, and a veto on a layer the author explicitly declared is near-useless anyway: measured, a declared monotone waterfall partitions on y and would pass one.

Usage
Ggplot2Adapter$rect_spans_lanes(layer, plot_object)
Arguments
layer

The layer being classified

plot_object

The ggplot2 plot object

Returns

TRUE when the layer's rectangles lay intervals in lanes


Ggplot2Adapter$unread_layer_type()

The answer for a layer no branch above claimed

"unknown" is what makes has_unsupported_layers() true and drops the whole plot to a static image. That is right for a layer carrying marks nothing describes: a filled geom_polygon() is drawn, and a reader told the chart was complete would be told wrong.

It is not right for a layer that drew nothing. Then there is no mark, so there is nothing the reader is missing, and the chart pays everything to protect them from an absence. Measured with save_html() on thirty points:

geom_point()                                interactive   50,406 bytes
geom_point() + geom_point(data = d[0, ])    interactive   51,313 bytes
geom_point() + geom_polygon(data = d[0, ])  base64 image  27,368 bytes
geom_point() + geom_polygon()               base64 image  31,848 bytes

Rows two and three are the same chart in every way a reader could tell – thirty points and a layer of nothing – and only one of them was interactive, because its empty layer happened to be of a kind this function names. Row four is the case the fallback exists for, and it keeps falling back.

The case this turns up in is not contrived: a missing Suggests package. geom_quantile() without quantreg warns, computes no rows and draws nothing; ggplot2 carries on and r-maidr turned the whole figure into a picture, with no second warning connecting the two (#227).

A plot made only of such layers still falls back, for the reason #176 gives: has_unsupported_layers() is true when every layer is "skip" as well, so "nothing unsupported" cannot quietly come to mean "nothing at all".

Nothing here decides which geoms are readable. A geom_polygon() with data in it is still "unknown" and still costs its chart exactly what it costs today.

Usage
Ggplot2Adapter$unread_layer_type(layer, plot_object)
Arguments
layer

The layer being classified

plot_object

The ggplot2 plot object

Returns

"skip" when the layer drew no rows, "unknown" otherwise


Ggplot2Adapter$layer_drew_nothing()

Whether a layer put no mark on the page at all

A layer's rows can vanish in its input, in a filter, in an aggregate over no groups, or – the case #227 was found through – in a stat that could not run because a Suggests package is absent. All four arrive here identically: ggplot_build() reports zero rows for that layer while ggplot2 warns, draws the rest of the chart and carries on.

Asked by unread_layer_type(), which turns it into "skip" rather than "unknown", and by the quantile branch of detect_layer_type(), which uses it to keep from claiming a curve that was never drawn (#229). Kept as its own method for that second caller: a rule two branches ask is a rule, not a fall-through.

Declines whenever the build cannot answer – it raised, or gave this layer no frame. Absent is not empty: a build that said nothing about what a layer drew must not have that read as "it drew nothing", which would wave a mark through as harmless.

Usage
Ggplot2Adapter$layer_drew_nothing(layer, plot_object)
Arguments
layer

The layer being asked about

plot_object

The ggplot2 plot object

Returns

TRUE when the layer built no rows


Ggplot2Adapter$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2Adapter$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


ggplot2 Area Layer Processor

Description

Processes geom_area() into MAIDR's area, stacked_area and stacked_normalized_area layers.

Before this processor existed these layers fell through to Ggplot2UnknownLayerProcessor, so an area chart carried no data at all.

Why not read it as a line

In a stacked area chart the band's top edge is a cumulative total while the band's height is the series' own value. A line layer announces one number per point with nothing to say which of those two it is, so the type exists to keep them apart: MAIDR's area trace announces the series' value and reports the running total beside it.

The two traps in ggplot2's built data

y is not the value. ggplot2 stacks by computing absolute band edges, so ymin/ymax are the cumulative positions and y is the top edge. The series' own value is ymax - ymin, and MAIDR sums the series itself to reach the total. Emitting y would hand it a cumulative number to accumulate again, announcing totals that grow with the number of series rather than with the data.

Most of the rows are not data. geom_area() defaults to stat = "align", which inserts interpolation vertices so the bands stack cleanly and closes each polygon on the baseline. A four-point, two-series chart produces twenty-four rows:

       x      y  ymin   ymax group align_padding
 1999.997  0.000 0.000  0.000     1          TRUE
 2000.000  5.000 2.000  5.000     1         FALSE
 2000.003  5.009 2.003  5.009     1         FALSE   <- not a data point
 2000.997  7.991 2.997  7.991     1         FALSE   <- not a data point

Read whole, a chart of four years announces twelve points per series, including a reading of 5.009 at "year 2000.003" – a value the data does not hold at an x the chart does not have. align_padding does not identify them: it marks only the two baseline-closing vertices.

The rows that are data are those whose x the layer's own data carries, which is verified to give the same answer as stat = "identity".

Super classes

LayerProcessor -> Ggplot2LineLayerProcessor -> Ggplot2AreaLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2AreaLayerProcessor$process()

Process the area layer.

Usage
Ggplot2AreaLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

List with data, axes and type


Ggplot2AreaLayerProcessor$generate_selectors()

One selector per band, so navigation highlights a band.

geom_area draws each series as its own geom_ribbon.gTree holding a filled GRID.polygon, which is the granularity the consumer wants: AreaTrace extends the line trace, whose multi-series highlight needs one selector per series and discards a list whose length disagrees.

The polygon rather than the outline polyline each ribbon also draws: the band is the mark a reader is being pointed at, and outlining it would highlight a hairline around the shape instead of the shape.

The existing curve machinery does not transfer. Ggplot2LineLayerProcessor$curve_selectors() counts curves inside one auto-named polyline grob, and layer_polyline_grobs() deliberately skips geom-named gTrees to avoid miscounting – an area layer being exactly the shape that helper excludes.

Emits nothing rather than a short or mispaired list, for the reason every processor here gives: the caller can tell an empty selector list apart from a wrong one, and a user cannot.

Usage
Ggplot2AreaLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  panel_ctx = NULL,
  n_series = 0L
)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for panel-scoped selector generation

n_series

How many bands the data reports

Returns

A list of selectors, one per band, or an empty list


Ggplot2AreaLayerProcessor$band_polygon_names()

Names of the band polygon grobs inside a grob, in draw order, which is series order.

Matches the auto-generated GRID.polygon.N name exactly rather than by prefix, so a theme element's own polygon – named after the element, not after grid's counter – cannot be collected as a band. Anchoring both ends is what keeps this from widening the way a grepl on a bare prefix would.

Usage
Ggplot2AreaLayerProcessor$band_polygon_names(grob)
Arguments
grob

A grob to walk

Returns

Character vector of grob names


Ggplot2AreaLayerProcessor$resolve_area_type()

Decide which of the three area types this layer is.

position = "fill" rescales every column to a common height, so a band's height is its share of that column and every column totals 1 by construction. Reading that as a plain stacked area would announce the shares as if they were values and imply the columns have equal totals, which is the one thing a filled chart is drawn to deny – the same distinction stacked_normalized_bar draws for bars.

A single series has nothing stacked on it whatever its position, so it is a plain area: announcing a running total equal to the value at every point would be noise.

Usage
Ggplot2AreaLayerProcessor$resolve_area_type(plot, series)
Arguments
plot

The ggplot2 object

series

The emitted series

Returns

One of "area", "stacked_area", "stacked_normalized_area"


Ggplot2AreaLayerProcessor$extract_series()

Build the MAIDR series for this layer.

Emits each series' own value rather than the cumulative band top, because MAIDR's area trace sums the series to reach the running total and announces the two separately.

Usage
Ggplot2AreaLayerProcessor$extract_series(built, layer_data, panel_id = NULL)
Arguments
built

Built plot data

layer_data

This layer's computed rows

panel_id

Panel ID for faceted plots (optional)

Returns

A list of series, each a list of MAIDR points


Ggplot2AreaLayerProcessor$drop_alignment_vertices()

Keep only the rows the chart was given.

StatAlign inserts interpolation vertices and baseline-closing vertices, neither of which is an observation. The rows that are data are those whose x appears in the layer's own data; that filter is verified to give the same rows stat = "identity" produces.

A layer whose x values cannot be recovered keeps every row rather than losing the chart – a noisy reading being better than none – which is why this returns the input unchanged rather than empty when the lookup fails.

Usage
Ggplot2AreaLayerProcessor$drop_alignment_vertices(built, layer_data)
Arguments
built

Built plot data

layer_data

This layer's computed rows

Returns

The data-bearing rows


Ggplot2AreaLayerProcessor$source_x_values()

Read the x values the layer was given, before any stat.

Reports a discrete axis as such rather than guessing its positions: those are assigned by the scale, and reconstructing them from the data column would drift the moment a factor level appeared in one and not the other. The caller has an exact rule for that case.

"Discrete" and "could not be read" are answered differently, because the caller must do different things with them. The integer rule that is exact for a discrete axis would silently drop the fractional rows of a continuous one, and an x mapped through an expression – aes(x = year / 2), say – resolves to no column and lands here while still being continuous. Collapsing the two into one NULL is how that axis loses half its data to a rule that was never about it.

Usage
Ggplot2AreaLayerProcessor$source_x_values(built)
Arguments
built

Built plot data

Returns

list(kind = "discrete"), list(kind = "numeric", values =), or NULL when the axis could not be read at all


Ggplot2AreaLayerProcessor$band_height()

The band's own height at one row.

Falls back to y when the edges are absent, since an unstacked layer whose baseline is the axis draws a band of exactly that height.

Usage
Ggplot2AreaLayerProcessor$band_height(rows, i)
Arguments
rows

The rows of one series

i

Which row

Returns

The series' value at that row


Ggplot2AreaLayerProcessor$resolve_series_labels()

Name each series after its fill level.

Usage
Ggplot2AreaLayerProcessor$resolve_series_labels(built, rows, panel_id = NULL)
Arguments
built

Built plot data

rows

This layer's data-bearing rows

panel_id

Panel ID for faceted plots (optional)

Returns

A named list of group key to label


Ggplot2AreaLayerProcessor$fill_levels()

The fill levels, in the order ggplot2 numbered the groups.

Usage
Ggplot2AreaLayerProcessor$fill_levels(built)
Arguments
built

Built plot data

Returns

A character vector of levels, possibly empty


Ggplot2AreaLayerProcessor$attach_fill_axis()

Add the legend title as the z axis, when there is one.

Usage
Ggplot2AreaLayerProcessor$attach_fill_axis(plot, built, axes, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data

axes

The axes assembled so far

panel_id

Panel ID for faceted plots (optional)

Returns

The axes, with z added when a fill legend exists


Ggplot2AreaLayerProcessor$mapped_column()

Name the source column an aesthetic is mapped to.

aes(x = factor(year)) maps a call rather than a bare name, and the column the data actually holds is its argument – so the call is unwrapped rather than labelled, which would give "factor(year)" and match nothing.

Usage
Ggplot2AreaLayerProcessor$mapped_column(quo)
Arguments
quo

The mapped quosure, or NULL

Returns

The column name, or NULL when there is no mapping


Ggplot2AreaLayerProcessor$scalar()

Convert one coordinate to a JSON-safe scalar.

Usage
Ggplot2AreaLayerProcessor$scalar(value)
Arguments
value

A coordinate read off the built data

Returns

A number, or a string when the coordinate is not numeric


Ggplot2AreaLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2AreaLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Bar Layer Processor

Description

Processes bar plot layers with complete logic included

Super class

LayerProcessor -> Ggplot2BarLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2BarLayerProcessor$process()

Process the layer: read its bars, selectors and orientation from the built plot

Usage
Ggplot2BarLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List describing the layer for the MAIDR payload


Ggplot2BarLayerProcessor$is_flipped()

Is this layer's category axis y rather than x?

ggplot(df, aes(y = g, x = n)) + geom_col() is the ordinary spelling of a horizontal bar chart, and ggplot_build() marks it flipped_aes and swaps which computed column holds what. Everything below reads x as the category and y as the measure, so on a flipped layer it picked up exactly the wrong pair: apple/banana/cherry at 30/70/50 came out as category "30" with value 1, category "50" with value 3 and category "70" with value 2 – the labels gone, the values replaced by factor codes, and the rows resorted by the measure (#162).

coord_flip() is not this. It rotates the coordinate system and leaves flipped_aes alone, so its data layout is genuinely unflipped, and it is reported vert today. That question spans every processor.

Should it ever be answered here, the key and the point layout have to move together: "horz" and the vertical ⁠x = category, y = measure⁠ pairing is precisely the combination #184 was about, and a coord_flip() chart currently reads correctly only because both halves are left in their vertical form. That is what swap_point_axes() being driven from this same answer is for.

Usage
Ggplot2BarLayerProcessor$is_flipped(plot, built = NULL)
Arguments
plot

The ggplot2 object.

built

Its ggplot_build() result, when the caller has one.

Returns

TRUE when the category runs up the y axis.


Ggplot2BarLayerProcessor$unflip_mapping()

Exchange a plot's x and y aesthetics

Returns a copy: the mapping is only read to recover the category's column name, and the caller's plot is still wanted unswapped for selectors and axis labels.

The layer is copied too. A ggplot2 layer is a ggproto object, which is an environment, so assigning into plot$layers[[i]]$mapping wrote through to the caller's plot: after one read a horizontal geom_col(aes(y = g, x = n)) was a vertical chart, for the render and for the user. The swapped mapping goes on a child object that inherits everything else from the layer, so the layer itself is never written.

Usage
Ggplot2BarLayerProcessor$unflip_mapping(plot)
Arguments
plot

The ggplot2 object.

Returns

A copy whose plot-level and layer-level x/y mappings are swapped.


Ggplot2BarLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing, so the emitted order matches the drawn rects

Usage
Ggplot2BarLayerProcessor$needs_reordering()
Returns

TRUE


Ggplot2BarLayerProcessor$reorder_layer_data()

Reorder the plot data by category so the emitted rows match the drawn rects

Usage
Ggplot2BarLayerProcessor$reorder_layer_data(data, plot)
Arguments
data

The data frame ggplot2 will draw from

plot

The ggplot2 object

Returns

The reordered data frame


Ggplot2BarLayerProcessor$extract_data()

One point per bar, read from the built plot

Usage
Ggplot2BarLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List of points


Ggplot2BarLayerProcessor$format_x_value()

Format an x-axis value as character.

Date / POSIXct / POSIXlt values are formatted via format() so that a Date column emits ISO date strings ("2024-01-02") rather than the default scale-tick labels ("Jan 02"). All other types use as.character(). Mirrors Ggplot2CandlestickProcessor$format_x_value() so candle and bar layers from the same Date column align string-wise.

Usage
Ggplot2BarLayerProcessor$format_x_value(x)
Arguments
x

The value to format

Returns

Character vector


Ggplot2BarLayerProcessor$panel_x_is_discrete()

Does this panel draw x on a discrete scale?

Only a discrete scale numbers its built positions 1..n, which is what makes indexing the break labels with them legitimate.

Usage
Ggplot2BarLayerProcessor$panel_x_is_discrete(panel_params, x_pos, panel_labels)
Arguments
panel_params

This panel's entry from built$layout$panel_params

x_pos

Built x positions for this panel

panel_labels

This panel's x break labels, or NULL

Returns

TRUE for a discrete x scale


Ggplot2BarLayerProcessor$map_discrete_x()

Label discrete built positions with this panel's breaks.

Usage
Ggplot2BarLayerProcessor$map_discrete_x(x_pos, panel_labels)
Arguments
x_pos

Built x positions for this panel

panel_labels

This panel's x break labels, or NULL

Returns

Character vector of x labels


Ggplot2BarLayerProcessor$map_continuous_x()

Recover user-facing x values for a non-discrete scale.

Built positions on a continuous, Date or datetime scale already are the values, but a Date arrives as a day count. Matching them back to the mapped column restores the original typing so format_x_value() can emit "2024-01-02" rather than "19724". Mirrors the same recovery in Ggplot2LineLayerProcessor.

Usage
Ggplot2BarLayerProcessor$map_continuous_x(x_pos, plot, layer_index)
Arguments
x_pos

Built x positions for this panel

plot

The ggplot object

layer_index

Index of this layer within the plot

Returns

Character vector of x labels


Ggplot2BarLayerProcessor$generate_selectors()

Selectors for the layer's rects

Usage
Ggplot2BarLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List of selectors


Ggplot2BarLayerProcessor$own_rect_names()

The rect grob names under this layer's own slot, or under the whole panel

A panel holds one geom_rect.rect grob per bar layer, so a plot that overlays two geom_col()s – a total behind a highlighted part is the usual reason – has two, and a search over the panel returns both to each layer. Its selectors then address every bar in the panel: the frontend finds twice the marks it has points for and highlights nothing, on both layers. So the search is scoped to the layer's own slot (see find_layer_slot_grob()): whatever the geom drew there, a bare rect grob or a tree wrapping one, and nothing when it drew no rects here at all. The panel-wide search remains only for a panel whose layout the slot cannot be read from.

Usage
Ggplot2BarLayerProcessor$own_rect_names(panel_grob, find_rect_names)
Arguments
panel_grob

The panel grob

find_rect_names

Function collecting every rect grob name under a grob

Returns

Character vector of rect grob names


Ggplot2BarLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2BarLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Boxplot Layer Processor

Description

Processes boxplot layers (geom_boxplot) to extract statistical data and generate selectors for individual boxplot components in the SVG structure.

Super class

LayerProcessor -> Ggplot2BoxplotLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2BoxplotLayerProcessor$get_built()

Get (and cache) the built plot data

ggplot_build() is expensive; extract_data, generate_selectors, determine_orientation, and map_categories_to_names all need it, so build at most once per processor instance.

Usage
Ggplot2BoxplotLayerProcessor$get_built(plot, built = NULL)
Arguments
plot

The ggplot2 object

built

Optionally a pre-built plot to adopt

Returns

Built plot data


Ggplot2BoxplotLayerProcessor$process()

Process the boxplot layer

Usage
Ggplot2BoxplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

List with data and selectors


Ggplot2BoxplotLayerProcessor$extract_data()

Extract data from boxplot layer

Usage
Ggplot2BoxplotLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Optional facet panel to restrict extraction to

Returns

List with boxplot statistics for each category


Ggplot2BoxplotLayerProcessor$generate_selectors()

Generate selectors for boxplot elements

Usage
Ggplot2BoxplotLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  panel_ctx = NULL,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

panel_ctx

Panel context for panel-scoped selection (optional)

panel_id

Optional facet panel to restrict outlier counts to

Returns

List of selectors for each boxplot


Ggplot2BoxplotLayerProcessor$determine_orientation()

Determine if the boxplot is horizontal or vertical

Usage
Ggplot2BoxplotLayerProcessor$determine_orientation(plot)
Arguments
plot

The ggplot2 object

Returns

"horz" or "vert"


Ggplot2BoxplotLayerProcessor$map_categories_to_names()

Map numeric category codes to actual category names Uses panel_params axis labels from ggplot_build to map codes to labels

Usage
Ggplot2BoxplotLayerProcessor$map_categories_to_names(
  boxplot_data,
  plot,
  panel_id = NULL
)
Arguments
boxplot_data

List of boxplot statistics

plot

The ggplot2 object

panel_id

Optional facet panel whose scale supplies the labels

Returns

Updated boxplot data with proper category names


Ggplot2BoxplotLayerProcessor$find_panel_grob()

Find the panel grob this layer draws into

Usage
Ggplot2BoxplotLayerProcessor$find_panel_grob(gt, panel_ctx = NULL)
Arguments
gt

The gtable to search

panel_ctx

Panel context for patchwork leaves and facets; NULL for a single plot, where the panel is the cell literally named "panel"

Returns

The panel grob or NULL


Ggplot2BoxplotLayerProcessor$find_children_by_type()

Find children by type pattern

Usage
Ggplot2BoxplotLayerProcessor$find_children_by_type(grob, type_pattern)
Arguments
grob

The grob to search

type_pattern

Pattern to match

Returns

List of matching children


Ggplot2BoxplotLayerProcessor$find_outlier_container()

Find the outlier container within a boxplot

Usage
Ggplot2BoxplotLayerProcessor$find_outlier_container(gt, boxplot_id)
Arguments
gt

The gtable object

boxplot_id

The boxplot container ID

Returns

The outlier container ID or NULL


Ggplot2BoxplotLayerProcessor$find_box_container()

Find the box container within a boxplot

Usage
Ggplot2BoxplotLayerProcessor$find_box_container(gt, boxplot_id)
Arguments
gt

The gtable object

boxplot_id

The boxplot container ID

Returns

The box container ID or NULL


Ggplot2BoxplotLayerProcessor$find_whisker_container()

Find the whisker container within a boxplot

Usage
Ggplot2BoxplotLayerProcessor$find_whisker_container(gt, boxplot_id)
Arguments
gt

The gtable object

boxplot_id

The boxplot container ID

Returns

The whisker container ID or NULL


Ggplot2BoxplotLayerProcessor$find_median_container()

Find the median container within a boxplot

Usage
Ggplot2BoxplotLayerProcessor$find_median_container(gt, boxplot_id)
Arguments
gt

The gtable object

boxplot_id

The boxplot container ID

Returns

The median container ID or NULL


Ggplot2BoxplotLayerProcessor$find_child_by_pattern()

Find a child element by pattern within a container

Usage
Ggplot2BoxplotLayerProcessor$find_child_by_pattern(gt, container_id, pattern)
Arguments
gt

The gtable object

container_id

The container ID to search within

pattern

Pattern to match

Returns

The matching child ID or NULL


Ggplot2BoxplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2BoxplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Candlestick Layer Processor

Description

Processes candlestick chart layers produced by tidyquant::geom_candlestick().

tidyquant's geom_candlestick() expands into TWO ggplot layers:

  1. A GeomLinerangeBC (BC = barchart) layer drawing the high-low wicks.

  2. A GeomRectCS (CS = candlestick) layer drawing the open-close bodies.

The adapter tags the wick layer as "skip" so the orchestrator does not create a separate maidr layer for it. This processor handles only the second (body) layer, but reads back into the wick layer's grobs to produce wick CSS selectors.

Output type: "candlestick". Each data point is a CandlestickPoint with value, open, high, low, close, optional volume, computed trend (Bull / Bear / Neutral) and volatility (high - low).

Selectors are emitted as a single CandlestickSelector object whose body and wick fields are arrays of per-candle CSS selectors using ⁠:nth-of-type⁠ against the rect/line elements of the gridSVG-exported tidyquant grobs.

Super class

LayerProcessor -> Ggplot2CandlestickProcessor

Methods

Public methods

Inherited methods

Ggplot2CandlestickProcessor$process()

Process the candlestick layer

Usage
Ggplot2CandlestickProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

ggplot2 object

layout

Layout information

built

Built plot data

gt

Gtable object

grob_id

Grob ID (faceting; not yet supported for candlestick)

panel_id

Panel id (patchwork; accepted for signature parity)

panel_ctx

Panel context (faceting; not yet supported)

Returns

Maidr candlestick layer list


Ggplot2CandlestickProcessor$extract_data()

Extract OHLC data points from the plot

Usage
Ggplot2CandlestickProcessor$extract_data(plot, built = NULL)
Arguments
plot

ggplot2 object

built

Built plot data

Returns

List of CandlestickPoint dicts


Ggplot2CandlestickProcessor$generate_selectors()

Generate candlestick CSS selectors

Returns a single CandlestickSelector object with body and wick as single CSS group selectors (one per element kind, not per candle). The maidr JS layer uses these to grab all candle elements at once and then auto-derives open/close from body rect edges based on trend.

Usage
Ggplot2CandlestickProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

ggplot2 object

gt

Gtable object

grob_id

Grob ID (faceting)

panel_ctx

Panel context (faceting)

Returns

Named list with body and (optionally) wick single-string selectors, or empty list if grobs cannot be located.


Ggplot2CandlestickProcessor$extract_layer_axes()

Extract axes labels for candlestick layer

Candlestick layer mappings are typically NULL (top-level mapping carries x/open/high/low/close). The base implementation only inspects layer_mapping$x and layer_mapping$y, which yields blank labels. Here we additionally fall back to plot$mapping$x and synthesize a "Price" y-label since OHLC has no single y mapping.

Usage
Ggplot2CandlestickProcessor$extract_layer_axes(plot, layout)
Arguments
plot

ggplot2 object

layout

Layout information

Returns

list(x = list(label = ...), y = list(label = ...))


Ggplot2CandlestickProcessor$resolve_col()

Resolve a mapping quosure to a column name in data

Usage
Ggplot2CandlestickProcessor$resolve_col(mapping_expr, data)
Arguments
mapping_expr

A mapping quosure

data

The data frame to resolve the column in

Returns

The column name, or NULL when the mapping names no column of data


Ggplot2CandlestickProcessor$format_x_value()

Format an x-axis value as character

Usage
Ggplot2CandlestickProcessor$format_x_value(x)
Arguments
x

The value to format

Returns

Character vector


Ggplot2CandlestickProcessor$get_effective_mapping()

Get the effective mapping (layer mapping merged on top)

Usage
Ggplot2CandlestickProcessor$get_effective_mapping(plot)
Arguments
plot

The ggplot2 object


Ggplot2CandlestickProcessor$get_original_data()

Get original data for the layer (falls back to plot$data)

Usage
Ggplot2CandlestickProcessor$get_original_data(plot)
Arguments
plot

The ggplot2 object


Ggplot2CandlestickProcessor$count_candles()

Count candles from the original data

Usage
Ggplot2CandlestickProcessor$count_candles(plot)
Arguments
plot

The ggplot2 object

Returns

Integer, 0 when there is no data


Ggplot2CandlestickProcessor$find_panel_grob()

Find the panel grob this layer draws into

Usage
Ggplot2CandlestickProcessor$find_panel_grob(gt, panel_ctx = NULL)
Arguments
gt

Gtable object

panel_ctx

Panel context for patchwork leaves and facets; NULL for a single plot, where the panel is the cell literally named "panel"

Returns

The panel gTree, or NULL when it cannot be resolved


Ggplot2CandlestickProcessor$find_first_child_name()

Find the first descendant whose name matches pattern

Usage
Ggplot2CandlestickProcessor$find_first_child_name(grob, pattern)
Arguments
grob

The grob tree to search

pattern

Regular expression the grob name must match

Returns

The matching grob name, or NULL


Ggplot2CandlestickProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2CandlestickProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Contour Layer Processor

Description

Processes contour layers (geom_contour, geom_density_2d).

A contour is the one chart of its family whose value is a number rather than a colour: ggplot2 computes level per row, so both halves of the reading invert exactly and nothing is recovered from a fill. That is what separates it from the same chart in a renderer that keeps its magnitude only in a continuous colour, which is why xability/maidr#1084 left Observable Plot's contour unread.

The filled forms are not this chart. geom_contour_filled() and geom_density_2d_filled() draw the bands between levels, and say so in the frame: their level is a factor of intervals rather than a number. Announcing one of those outlines as a level's own curve would be right for half of its points.

Super class

LayerProcessor -> Ggplot2ContourLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2ContourLayerProcessor$process()

Process the contour layer

Usage
Ggplot2ContourLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for patchwork leaves and facets

Returns

List with data, selectors and axes


Ggplot2ContourLayerProcessor$extract_axes()

Name the two axes

Only x and y. The level is not an axis here: it travels on every point of the curve it belongs to, and the frontend's contour trace announces it from there under its own heading rather than from a third axis label.

Usage
Ggplot2ContourLayerProcessor$extract_axes(plot, built = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

Returns

An axes payload with x and y


Ggplot2ContourLayerProcessor$generate_selectors()

Address each drawn curve by its own element

GeomContour draws every curve in one polylineGrob, and gridSVG exports that as one ⁠<polyline>⁠ per piece with an id of the form ⁠<grob>.1.<n>⁠ – measured, a two-level field with two peaks gives GRID.polyline.1.1.1 through .4. So a curve is addressed by its position among the pieces, which is what contour_curves() returns.

The grob carries grid's automatic name rather than one derived from the geom, so it is found the way a line layer's is – by position among the auto-named polylines of the panel. That is the whole reason polyline_layer_position() had to learn about this type: a contour drawn beside a geom_line() sits in the same candidate list, and a count that skipped it would give both layers the other's curves.

Usage
Ggplot2ContourLayerProcessor$generate_selectors(
  gt = NULL,
  plot = NULL,
  panel_ctx = NULL,
  order = integer(0)
)
Arguments
gt

Gtable object

plot

The ggplot2 object, used to build a gtable when none is given

panel_ctx

Panel context for patchwork leaves and facets

order

The piece behind each emitted curve

Returns

A list of CSS selectors, one per curve


Ggplot2ContourLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2ContourLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Dodged Bar Layer Processor

Description

Processes dodged bar plot layers with complete logic included

Super class

LayerProcessor -> Ggplot2DodgedBarLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2DodgedBarLayerProcessor$process()

Process the layer: read its series, selectors and DOM mapping from the built plot

Usage
Ggplot2DodgedBarLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List describing the layer for the MAIDR payload


Ggplot2DodgedBarLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing, so the emitted order matches the drawn rects

Usage
Ggplot2DodgedBarLayerProcessor$needs_reordering()
Returns

TRUE


Ggplot2DodgedBarLayerProcessor$resolve_aes_values()

Resolve this layer's x/y/fill aesthetics to VALUES. rlang::as_label() produces a display string, which doubles as a column name only for bare-column mappings. Evaluating the quosure against the data also covers expression aesthetics such as aes(fill = factor(cyl)), which are idiomatic ggplot2 and which the column-name treatment turned into data[["factor(cyl)"]], i.e. NULL.

Usage
Ggplot2DodgedBarLayerProcessor$resolve_aes_values(plot, data)
Arguments
plot

A ggplot2 object

data

Data frame the aesthetics are evaluated against

Returns

List with x, y and fill vectors (any may be NULL)


Ggplot2DodgedBarLayerProcessor$reorder_layer_data()

Reorder the plot data by x and fill so each column's rects are drawn in the order the frontend walks them

Usage
Ggplot2DodgedBarLayerProcessor$reorder_layer_data(data, plot)
Arguments
data

The data frame ggplot2 will draw from

plot

The ggplot2 object

Returns

The reordered data frame


Ggplot2DodgedBarLayerProcessor$extract_data()

One series per fill level, as a rectangular grid with a 0 for every missing bar

Usage
Ggplot2DodgedBarLayerProcessor$extract_data(
  plot,
  built = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List of series


Ggplot2DodgedBarLayerProcessor$generate_selectors()

One flat selector matching every rect in the layer, which is the contract the frontend expects (see the note above)

Usage
Ggplot2DodgedBarLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List holding one selector


Ggplot2DodgedBarLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2DodgedBarLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Dot Plot Layer Processor

Description

Processes Wilkinson dot plots (geom_dotplot).

The layer emits type hist, because that is the chart: a stack of dots is a bar, and the bin and its count are what a reader navigates. The y axis a dot plot draws is not one – ggplot2's own documentation says the values on it are meaningless – so the count goes on it here.

Highlighting is not offered, and that is a real limit rather than an oversight. GeomDotplot draws the whole chart as one dotstackGrob, which gridSVG exports as one <circle> per observation: a bin of three dots has three elements and no element of its own, while the frontend's bar traces resolve exactly one element per announced value. So the bins are announced, sonified and brailled, and nothing lights up – which is the highlight-only blind spot xability/maidr#814 names, and strictly better than the static image this chart was before (#201). geom_histogram() draws the same distribution with a rect per bin and highlights.

Super class

LayerProcessor -> Ggplot2DotplotLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2DotplotLayerProcessor$process()

Process the dot plot layer

Usage
Ggplot2DotplotLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for patchwork leaves and facets

Returns

List with type, data, orientation, selectors and axes


Ggplot2DotplotLayerProcessor$bins_run_up_the_y_axis()

Which axis the values were binned along

Read from the layer's own binaxis parameter, which is what decides it: geom_dotplot(binaxis = "y") is the form drawn beside a categorical x, and its bins run up the y axis. Defaulted to "x" to match ggplot2's own default rather than guessed at from the data.

Usage
Ggplot2DotplotLayerProcessor$bins_run_up_the_y_axis(plot)
Arguments
plot

The ggplot2 object

Returns

TRUE when the bins run up the y axis


Ggplot2DotplotLayerProcessor$extract_data()

Read the bins out of the built data

Emitted in the shape Ggplot2HistogramLayerProcessor emits, so the frontend's histogram trace reads it unchanged: the bin's centre and count as x/y, and the bin's own bounds as xMin/xMax. A bar rises from zero, so yMin is 0 and yMax is the count.

Both are swapped for a layer binned up the y axis, matching what orientation = "horz" tells the frontend to expect.

Usage
Ggplot2DotplotLayerProcessor$extract_data(
  plot,
  built = NULL,
  panel_id = NULL,
  horizontal = FALSE
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

horizontal

Whether the bins run up the y axis

Returns

A list of bins, ascending


Ggplot2DotplotLayerProcessor$extract_axes()

Name the two axes

The bin axis keeps the variable's name through the package's shared labs()-then-mapping chain. The other one is named "count" here rather than read from the plot: ggplot2 labels a dot plot's count axis "count" while drawing values on it that its own documentation calls meaningless, and reading that label back would pair a real count with whatever the caller renamed the fiction to.

Usage
Ggplot2DotplotLayerProcessor$extract_axes(
  plot,
  built = NULL,
  horizontal = FALSE
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

horizontal

Whether the bins run up the y axis

Returns

An axes payload with x and y


Ggplot2DotplotLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2DotplotLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


ggplot2 Error Bar Layer Processor

Description

Processes ggplot2's uncertainty geoms – geom_errorbar(), geom_errorbarh(), geom_linerange(), geom_pointrange() and geom_crossbar() – into MAIDR's error_bar layer.

Uncertainty is usually the finding rather than the decoration: whether two group means differ is answered by whether their intervals overlap. Until this processor existed every one of these geoms fell through to Ggplot2UnknownLayerProcessor, so the interval was dropped and that comparison was unavailable to a MAIDR reader.

Reading the right pair of bounds

The trap this class exists to avoid is that ggplot2's built data carries both pairs for most of these geoms, and only one of them is the interval. A vertical geom_errorbar() computes:

  x  y  ymin ymax  xmin  xmax  flipped_aes
  1 4.2  3.8  4.6  0.55  1.45  FALSE

ymin/ymax are the interval; xmin/xmax are the cap width – how wide the little crossbars are drawn, which is a styling parameter and not data at all. Reading the wrong pair yields a chart describing the cap geometry, which is both wrong and plausible-looking.

Which pair is the interval is decided by the layer's orientation, and ggplot2 records that in two different ways depending on the geom:

Both are handled, because a layer that read only flipped_aes would treat every geom_errorbarh() as vertical and emit the cap heights as the interval.

Super classes

LayerProcessor -> Ggplot2PointLayerProcessor -> Ggplot2ErrorbarLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2ErrorbarLayerProcessor$process()

Process the error bar layer.

Usage
Ggplot2ErrorbarLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

List with data, selectors, axes, type and orientation


Ggplot2ErrorbarLayerProcessor$attach_group_axis()

Name the series axis after the legend the chart shows.

Guarded on the split rather than on the payload's shape, because data_has_series_groups() reads data[[1]][[1]]$z and an ungrouped layer's data[[1]] is a point, whose first element is an atomic category name. The grouped shape is the only one that has a z axis to name, so asking the split is both safer and the actual question.

Usage
Ggplot2ErrorbarLayerProcessor$attach_group_axis(
  plot,
  built,
  data,
  groups,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

built

Built plot data

data

The extracted layer data

groups

The layer's series split, or NULL

panel_id

Panel ID for faceted plots (optional)

Returns

The axes list, with z added when the layer is grouped


Ggplot2ErrorbarLayerProcessor$resolve_interval_groups()

Split the layer's rows into the series the chart draws.

A dodged interval chart puts one whip per group at every category, and without the split every category is announced twice with nothing saying which reading belongs to which group – so the comparison the figure exists to support, whether two groups' intervals overlap, is the one thing unavailable (#183). MAIDR's grammar gained the grouped shape, ErrorBarPoint[][] with a z per point, in xability/maidr#942.

The group each row belongs to cannot be read out of the built data: ggplot_build() replaces the grouping column with an integer group id, and on a discrete x that id is the interaction of x and the grouping aesthetic – 6 ids for 3 categories and 2 groups, measured – so it names a cell rather than a series. The aesthetic's own values are replaced too, by the palette colour they mapped to. The user's frame is what still holds the names, and it is only usable while it still has a row per built row: ggplot2 drops rows it cannot draw, and a padded lookup would name every series NA. The same guard Ggplot2StackedBarProcessor applies for the same reason.

A layer with its own data is read from that frame rather than the plot's, matching ggplot2's own precedence.

Usage
Ggplot2ErrorbarLayerProcessor$resolve_interval_groups(
  plot,
  layer_data,
  built,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

layer_data

This layer's computed rows

built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

A list of positions (each row's series, as an index into levels), levels (the series, in the order ggplot2 draws them) and order (the row indices in series order), or NULL when the layer draws a single undivided series


Ggplot2ErrorbarLayerProcessor$group_values_agree()

Check the frame's values against what the layer drew.

Row counts agreeing is not the same as rows corresponding. A stat that happens to emit as many rows as the frame has would pass the count test while pairing a reading with somebody else's group name – the worst failure available here, since every value stays correct and only the label is a lie.

What can be checked is that the pairing is consistent: ggplot2 keeps the grouping aesthetic in the built data as the value it mapped to (a palette colour, a linetype), and a correct pairing gives every row sharing that drawn value the same name. A shuffled one almost never does. Falls back to the built group id when the aesthetic itself is not in the built data, which is what an explicit aes(group = ...) leaves behind.

Usage
Ggplot2ErrorbarLayerProcessor$group_values_agree(layer_data, aes_names, values)
Arguments
layer_data

This layer's computed rows

aes_names

The winning aesthetic's spelling variants

values

The frame's grouping values, one per row

Returns

TRUE when every drawn value carries a single name


Ggplot2ErrorbarLayerProcessor$layer_built_rows()

This layer's built rows, every panel of them.

Usage
Ggplot2ErrorbarLayerProcessor$layer_built_rows(built)
Arguments
built

Built plot data

Returns

A data frame, or NULL when the layer index does not resolve


Ggplot2ErrorbarLayerProcessor$panel_row_indices()

Which of the layer's built rows this panel's rows are.

Mirrors get_layer_built_data(), including its fallback: a panel id that selects nothing leaves the whole layer in place, so the indices have to as well. Answers NULL when the two do not line up, which is the signal to decline the split rather than pair rows at random.

Usage
Ggplot2ErrorbarLayerProcessor$panel_row_indices(full, rows, panel_id = NULL)
Arguments
full

The layer's built rows, every panel of them

rows

How many rows this panel contributed

panel_id

Panel ID for faceted plots (optional)

Returns

Integer indices into full, or NULL


Ggplot2ErrorbarLayerProcessor$interval_group_frame()

The frame still carrying the grouping column's own values.

Usage
Ggplot2ErrorbarLayerProcessor$interval_group_frame(plot, rows)
Arguments
plot

The ggplot2 object

rows

How many rows the layer computed

Returns

A data frame with one row per built row, or NULL


Ggplot2ErrorbarLayerProcessor$is_horizontal_layer()

Decide whether the interval runs along x rather than y.

Reads flipped_aes when the built data carries it, and falls back to the geom class for geom_errorbarh(), which is horizontal by construction and therefore has no such column to read.

Usage
Ggplot2ErrorbarLayerProcessor$is_horizontal_layer(plot, layer_data)
Arguments
plot

The ggplot2 object

layer_data

This layer's computed rows

Returns

TRUE when the interval spans the x axis


Ggplot2ErrorbarLayerProcessor$draws_one_shape_for_every_sample()

Whether this layer draws its whole interval as one shape.

True for a ribbon, which fills a single polygon across every x. Every other geom this processor serves draws one shape per sample – a segments grob per geom_linerange() row, a polygon per geom_crossbar() box – which is what makes a per-sample selector possible at all.

class(...)[1], matching the adapter's own ribbon test: GeomArea inherits GeomRibbon and is not routed here, but an inherits() check would still be the wrong shape of question to ask.

Usage
Ggplot2ErrorbarLayerProcessor$draws_one_shape_for_every_sample(plot)
Arguments
plot

The ggplot2 object

Returns

TRUE when the layer's interval is one undivided shape


Ggplot2ErrorbarLayerProcessor$generate_selectors()

Address the drawn interval, one SVG element per sample.

ErrorBarTrace.mapToSvgElements resolves the selectors and requires the flattened result to be exactly as long as the emitted data; any other length is discarded and the layer highlights nothing (#145). So the job here is one element per sample, in the order the data was emitted – not one per bound, and not the container.

The five geoms do not draw alike, and the differences are not cosmetic. Verified against real gridSVG::grid.export() output on ggplot2 3.4.4:

Both of those last two facts are why this could not reuse the inherited generate_selectors(): it matches geom_point.points by name, which reaches none of these, and a name-prefix search reaches geom_errorbar() least of all.

Usage
Ggplot2ErrorbarLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL,
  sample_count = NULL,
  order = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (unused; the drawn grob is resolved from the panel, which is what the unnamed polyline needs)

panel_ctx

Panel context for panel-scoped selectors (optional) A grouped layer needs one selector per sample instead of one stride over all of them, because MAIDR flattens its series before pairing them against the resolved elements: the payload runs series by series while the chart draws row by row, and one stride can only ever produce the drawn order. The per-sample form addresses each mark by the id gridSVG gave it, not by position, so resolving one cannot disturb the rest – a positional list would, since resolving a selector inserts a hidden clone beside the match and shifts every later nth-child (xability/maidr#1004).

sample_count

How many points this layer emitted

order

The row indices in series order, or NULL when the layer draws a single undivided series

Returns

A list of CSS selectors, or an empty list


Ggplot2ErrorbarLayerProcessor$interval_sample_selectors()

Address one drawn mark per sample, in series order.

gridSVG gives every exported element its own id, built from the grob's name: ⁠<grob>.1.<i>⁠ where a sample is drawn as one element, and ⁠<grob>.1.<i>a⁠, ⁠<i>b⁠, ⁠<i>c⁠ where geom_errorbar() draws its cap, whisker and other cap. Measured on ggplot2 3.4.4 across all four geoms this processor serves. The whisker is the middle one, which is the same element the stride form's ⁠nth-child(3n+2)⁠ picks – so a grouped chart and an ungrouped one outline the same mark.

Restricted to the two shapes interval_selector() handles, for the same reason: an unverified element count would name a mark by an id pattern nothing has been checked against.

Usage
Ggplot2ErrorbarLayerProcessor$interval_sample_selectors(
  grob_name,
  per_sample,
  order
)
Arguments
grob_name

Name of the grob whose children are the samples

per_sample

How many elements the grob draws per sample

order

The row indices in series order

Returns

A list of CSS selectors, one per sample, or an empty list


Ggplot2ErrorbarLayerProcessor$find_interval_grob()

Find the grob whose children are the samples.

Usage
Ggplot2ErrorbarLayerProcessor$find_interval_grob(plot, gt, panel_ctx = NULL)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

The grob one of whose child elements is drawn per sample, or NULL when it cannot be resolved


Ggplot2ErrorbarLayerProcessor$find_unnamed_interval_grob()

Resolve the drawn grob of a layer ggplot2 left unnamed.

geom_errorbar() and geom_errorbarh() draw a bare GRID.polyline.N, so there is no name to match on and find_layer_grob_tree() returns NULL for them. Position is the remaining handle: ggplot2 lays a panel out as the grill, a leading zeroGrob, one child per layer in layer order, a trailing zeroGrob and the border. A layer that draws nothing still takes its slot as a zeroGrob, so the correspondence survives an empty layer beside this one.

The leading blank is found by class rather than by an absent name: a zeroGrob is named, and its name is the four characters "NULL".

It is deliberately narrow. The result has to be a polyline for a geom that is known to draw one, because a positional hit on the wrong layer would highlight another layer's marks – worse than the missing highlight this fixes, since a reader can hear nothing but cannot hear wrongness.

Usage
Ggplot2ErrorbarLayerProcessor$find_unnamed_interval_grob(
  plot,
  gt,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

The layer's polyline grob, or NULL


Ggplot2ErrorbarLayerProcessor$interval_grob_shape()

Count the samples a grob draws, and the elements each takes.

Usage
Ggplot2ErrorbarLayerProcessor$interval_grob_shape(grob)
Arguments
grob

The grob resolved for this layer

Returns

A list of samples and per_sample, or NULL when the grob is not one of the shapes this has been verified against


Ggplot2ErrorbarLayerProcessor$grob_point_groups()

Split a grob's points into one index vector per sample.

Usage
Ggplot2ErrorbarLayerProcessor$grob_point_groups(grob)
Arguments
grob

A polygon or polyline grob

Returns

A list of index vectors in drawing order, or NULL


Ggplot2ErrorbarLayerProcessor$drawn_run_count()

Count the elements one sample of a grob is drawn as.

grid breaks a polyline at a missing point, and geom_errorbar() uses that: its eight points per sample are ⁠cap, NA, whisker, NA, cap⁠, so the export carries three elements for the one bar. A run needs two points to be a line at all, and a shorter one draws nothing.

Usage
Ggplot2ErrorbarLayerProcessor$drawn_run_count(grob, index)
Arguments
grob

A polygon or polyline grob

index

The point indices belonging to one sample

Returns

How many elements that sample is drawn as


Ggplot2ErrorbarLayerProcessor$interval_selector()

Build the CSS selector for one element per sample.

gridSVG wraps each grob in a ⁠<g>⁠ named after it with a .1 suffix and writes its elements inside in drawing order, so the samples are addressable as a stride through that group's children.

Only the two strides that have been checked against an export are emitted: one element per sample, and the three a geom_errorbar() draws, of which the middle one is the whisker spanning the interval – the cap either side of it says nothing a reader is navigating to. Any other stride returns NULL and the layer goes back to highlighting nothing, which is the honest answer for a shape nobody has looked at.

Usage
Ggplot2ErrorbarLayerProcessor$interval_selector(grob_name, per_sample)
Arguments
grob_name

The drawn grob's name

per_sample

How many elements each sample is drawn as

Returns

A CSS selector, or NULL


Ggplot2ErrorbarLayerProcessor$extract_interval_data()

Build the MAIDR points for this layer.

The emitted shape names the category x and the magnitude y in both orientations, with the bounds in yMin/yMax, and lets orientation say which is on screen where. That is the shape MAIDR's ErrorBarTrace consumes: it reads the magnitude as y/yMin/yMax with no orientation branch, so emitting screen-aligned keys would leave a horizontal chart with no interval at all.

A row missing its bounds still emits its estimate. A one-sided interval is a real chart, and dropping the point for want of its other half would lose the estimate too.

A grouped layer emits one series per group instead, each point carrying its group's name as z – the shape every other grouped layer in this package already emits, and the one MAIDR's ErrorBarTrace reads as a series of series.

Usage
Ggplot2ErrorbarLayerProcessor$extract_interval_data(
  built,
  layer_data,
  is_horizontal,
  panel_id = NULL,
  groups = NULL
)
Arguments
built

Built plot data

layer_data

This layer's computed rows

is_horizontal

Whether the interval spans the x axis

panel_id

Panel ID for faceted plots (optional)

groups

The layer's series split, or NULL for a single series

Returns

A list of MAIDR interval points, or a list of such lists when the layer is grouped


Ggplot2ErrorbarLayerProcessor$resolve_estimates()

Resolve the estimate each interval is centred on.

The estimate aesthetic is optional on these geoms, and leaving it out is idiomatic rather than exotic: geom_errorbar(aes(x, ymin, ymax)) layered over a geom_col() is the standard way to draw a bar chart with error bars, and it builds with no y column at all. geom_linerange() is the same. Requiring the column dropped every such layer silently – no interval, no estimate, no error.

When it is absent the chart genuinely draws no estimate, only a span, so the centre of that span is used. That is a property of the drawn bar rather than a claim about an unobserved estimate, and it is what keeps the bounds – which are the real data here – reachable at all. It is NOT the mean for an asymmetric interval, and nothing here pretends it is: a layer that carries y always uses the value the chart drew.

Usage
Ggplot2ErrorbarLayerProcessor$resolve_estimates(
  layer_data,
  value_col,
  min_col,
  max_col
)
Arguments
layer_data

This layer's computed rows

value_col

The estimate column for this orientation

min_col

The lower bound column for this orientation

max_col

The upper bound column for this orientation

Returns

A numeric vector of estimates, or NULL when neither the estimate nor a pair of bounds is present


Ggplot2ErrorbarLayerProcessor$resolve_category_labels()

Recover the names behind a discrete category axis.

ggplot2 maps a discrete axis onto integer positions before it computes the layer, so the built data carries ⁠1, 2, 3⁠ where the chart draws ⁠control, high dose, low dose⁠. Announcing the positions would name something the reader cannot find anywhere on the chart – and the positions are assigned in the scale's order, not the data's, so they do not even read as row numbers.

The labels come from the panel's scale rather than from the data frame, which is what makes the position an index into them.

Usage
Ggplot2ErrorbarLayerProcessor$resolve_category_labels(
  built,
  layer_data,
  category_col,
  panel_id = NULL
)
Arguments
built

Built plot data

layer_data

This layer's computed rows

category_col

Which built column carries the category

panel_id

Panel ID for faceted plots (optional)

Returns

A list of labels – strings for a discrete axis, numbers for a continuous one


Ggplot2ErrorbarLayerProcessor$category_axis_labels()

Read the break labels of the category axis, when discrete.

Usage
Ggplot2ErrorbarLayerProcessor$category_axis_labels(
  built,
  category_col,
  panel_id = NULL
)
Arguments
built

Built plot data

category_col

Which built column carries the category

panel_id

Panel ID for faceted plots (optional)

Returns

A character vector of labels, or NULL on a continuous axis


Ggplot2ErrorbarLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2ErrorbarLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Gantt Layer Processor

Description

Processes geom_segment() layers that draw intervals in lanes, and maidr_gantt() layers whose author declared that their rectangles do.

A segment with the two ends of a span on one axis and a lane on the other is how ggplot2 draws a schedule, a range plot and a high-low chart. ggplot_build computes both ends and the lane exactly, so nothing is inverted from a pixel: the four columns x, xend, y and yend are the interval and the lane the caller wrote.

geom_curve() computes the same four columns and reads the same way. Its vectorised curve grob is split into one curve per row by split_vectorised_curve_grobs() before export (#195), which is what the per-interval selectors address. See the adapter's own note.

A declared rectangle layer has none of those four columns – it builds xmin, xmax, ymin and ymax – so rect_gantt_frame() renames its bounds into them before anything here runs. That is the whole of the rect path: the lanes, the ordering, the orientation and the axes are then this class answering one question rather than two implementations of it, and the processed layer comes back identical() to the geom_segment() spelling of the same schedule in everything but the grob its selectors name. Only the lane names need their own route, because a rectangle layer's lane axis is continuous and lane_names() reads levels off a discrete one.

Super class

LayerProcessor -> Ggplot2GanttLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2GanttLayerProcessor$process()

Process the gantt layer

Usage
Ggplot2GanttLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for patchwork leaves and facets

Returns

List with data, selectors, axes and orientation


Ggplot2GanttLayerProcessor$lane_names()

Name the lanes, in the order the scale lays them out

Read off the panel's own view of the scale rather than off the source column: the built data records a discrete lane as the position ggplot2 gave it (1, 2, 3), and the panel's limits are the levels in the same order, so the two line up by index. That is also what makes an undrawn level visible – scale_y_discrete(drop = FALSE) keeps it in the limits, and it is a lane holding nothing.

NULL for a continuous lane axis, which has no names to give.

Usage
Ggplot2GanttLayerProcessor$lane_names(built, lane_axis, panel_id = NULL)
Arguments
built

Built plot data

lane_axis

"y", "x", or NULL

panel_id

Panel ID for faceted plots (optional)

Returns

Character vector of lane names, or NULL


Ggplot2GanttLayerProcessor$declared_lane_axis()

Which axis this layer's author said the lanes run up

Read off the layer rather than inferred, because inference is not available: measured, both axes partition for the target schedule and for one whose tasks all take the same time, so structure can neither confirm nor contradict what the author meant. "y" when nothing was declared, which is what a geom_segment() gantt drawn the ordinary way also reads as.

Usage
Ggplot2GanttLayerProcessor$declared_lane_axis(plot)
Arguments
plot

The ggplot2 object

Returns

"y" or "x"


Ggplot2GanttLayerProcessor$name_rect_lanes()

Name a rectangle gantt's lanes from the ticks inside them

lane_names() above cannot serve, and the reason is measured: it requires view$is_discrete(), and a rectangle layer written with the author's own numeric ymin/ymax trains a continuous scale – measured, is_discrete() is FALSE and limits is the range 0.6, 3.4 rather than a list of levels. The lanes are there; the scale just has no names to lend them.

So a lane is named by the single explicit tick drawn inside it, and by its position otherwise – the rule xability/py-maidr#533 settled. Two guards come with it and both are that issue's: a band holding more than one tick is named by none of them, and a band holding none is named by its position.

Read off built$layout$panel_params, never layer_scales(): measured on the default scale the panel's view gives breaks NA, 1, 2, 3, NA while layer_scales() gives NA, 1, 1.5, 2, 2.5, 3, NA – two ticks per band, which defeats the one-tick rule in exactly the case the rule exists for. The NA padding is dropped.

Usage
Ggplot2GanttLayerProcessor$name_rect_lanes(
  grouped,
  built,
  built_data,
  lane_axis,
  panel_id = NULL
)
Arguments
grouped

The lanes as segment_lanes() grouped them

built

Built plot data

built_data

The normalised frame, carrying the bounds and the lane

lane_axis

"y", "x", or NULL

panel_id

Panel ID for faceted plots (optional)

Returns

grouped with its named lanes renamed


Ggplot2GanttLayerProcessor$lane_ticks()

The lane axis's drawn ticks, with the padding dropped

panel_params is keyed by the axis the chart draws, not the axis the data lives on, and coord_flip() swaps the two. So the panel is asked for the drawn counterpart of lane_axis rather than for lane_axis itself.

Indexing by lane_axis reads the span axis under a flip, and the wrong answer is worth writing down because it is two different wrong answers. Measured on ggplot2 3.4.4, the example schedule with coord_flip() added: with the time axis given its own named breaks the lanes came back Jan, Feb, Mar – names the chart draws along the other axis – and with the ordinary numeric time breaks 0, 5, 10, 15 every label failed label_names_its_lane() and the lanes silently lost their names altogether, on a chart drawing design, build, test. Both are pinned in tests/testthat/test-gantt-rect.R.

The breaks travel with the labels, so they stay comparable with the bands: measured under the flip, panel_params[[1]]$x gives breaks 1, 2, 3 against bands 0.6-1.4, 1.6-2.4 and 2.6-3.4, which are data-space ymin/ymax.

class()[1] is "CoordFlip" here – measured – but the test is inherits(), because it is asking whether the coord flips rather than which coord it is.

lane_names() above indexes by lane_axis too and is deliberately left alone: it requires view$is_discrete(), a rectangle layer's numeric bounds always train a continuous lane axis, and so it cannot reach a rect gantt at all. Measured, the geom_segment() spelling of the same flipped chart comes back with no lanes rather than with borrowed ones, which is the reading it has today and not this issue's to change.

Usage
Ggplot2GanttLayerProcessor$lane_ticks(built, lane_axis, panel_id = NULL)
Arguments
built

Built plot data

lane_axis

"y", "x", or NULL

panel_id

Panel ID for faceted plots (optional)

Returns

A list of breaks and labels, or NULL


Ggplot2GanttLayerProcessor$extract_axes()

Name the two axes

Usage
Ggplot2GanttLayerProcessor$extract_axes(plot, built = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

Returns

An axes payload with x and y


Ggplot2GanttLayerProcessor$generate_selectors()

Address each drawn interval by its own element

GeomSegment draws every interval in one segmentsGrob, and gridSVG exports that as one element per segment carrying an id of the form <grob>.1.<n> – measured, a four-interval chart gives GRID.segments.38.1.1 through .4, in built-data order. So an interval is addressed by the built row it came from, and the list follows the regrouping rather than the document.

Flat rather than nested, because the frontend slices it per lane using the lane lengths it already has – and withdraws highlighting outright unless the resolved count matches the interval count exactly. A partial list is therefore worse than none, so an empty list is returned when the grob cannot be found rather than a guess at its name.

Usage
Ggplot2GanttLayerProcessor$generate_selectors(
  gt = NULL,
  plot = NULL,
  panel_ctx = NULL,
  order = integer(0)
)
Arguments
gt

Gtable object

plot

The ggplot2 object, used to build a gtable when none is given

panel_ctx

Panel context for patchwork leaves and facets

order

The built-data row behind each interval, in emission order

Returns

A list of CSS selectors, one per interval


Ggplot2GanttLayerProcessor$target_geom_class()

The class of the geom this layer was drawn with

Both the grob to look for and the shape of its exported element ids follow from it, so it is asked once and answered from the plot rather than inferred from what happens to be in the panel.

Usage
Ggplot2GanttLayerProcessor$target_geom_class(plot)
Arguments
plot

The ggplot2 object

Returns

The geom's class name, or NULL when the layer cannot be found


Ggplot2GanttLayerProcessor$segments_grob_class()

Which grid grob class a segment-family geom draws

Usage
Ggplot2GanttLayerProcessor$segments_grob_class(geom)
Arguments
geom

The layer's geom object

Details

geom_curve() draws a curve grob and everything else in the family – geom_segment(), and geom_spoke() which is a GeomSegment subclass – draws segments. Asked of the geom rather than assumed from the layer type, because it decides both which grobs find_segments_name() gathers and which layers it counts itself among, and those two have to be the same population.

Returns

"curve" or "segments"


Ggplot2GanttLayerProcessor$find_segments_name()

Find the name of the grob holding this layer's segments

The base class's find_layer_grob_tree() cannot serve here, and the reason is worth recording: it matches a grob whose name begins with the geom's own prefix, and ggplot2 does not give a segment layer one. The grob arrives with grid's automatic name – measured, GRID.segments.38 – so there is no geom_segment. to match and the lookup returns NULL, which is a layer that announces every interval and highlights none of them.

The disambiguation rule is the same one that helper applies, keyed on the grob's class instead: the nth segment layer of the plot draws the nth segments grob of the panel. Two geom_segment() layers would otherwise both resolve to the first one's elements, and the second would highlight the first's intervals while announcing its own.

The number in that automatic name is grid's global counter and is not stable between sessions, which is exactly why it is read off the gtable being exported rather than reconstructed.

Usage
Ggplot2GanttLayerProcessor$find_segments_name(plot, gt, panel_ctx = NULL)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for patchwork leaves and facets

Returns

The grob name, or NULL when it cannot be resolved


Ggplot2GanttLayerProcessor$find_rect_name()

Find the name of the grob holding a rect layer's bars

The opposite way round from find_segments_name(), and for a measured reason: a rectangle layer is given a geom-prefixed grob name, and the grob class is useless because the theme draws rects too. Collecting every rect-class grob of a lone rect chart gave, in tree order,

  plot.background..rect.33  panel.background..rect.6  geom_rect.rect.2
  

so position 1 is the plot background and the reader would have the whole page highlighted for their first task. The name prefix ^geom_rect\. matches the drawn bars and none of the theme's rects.

Every number in a grob name here is grid's global counter, which find_segments_name() above already records as not stable between sessions – the same chart measured second in a session numbers higher. What was measured is the tree order and the rect counts; each listing below is from its own fresh session.

The counter is the other half. ggplot2 names a drawn grob after the geom whose draw_panel() made it, and GeomRect$ draw_panel() hard-codes geom_rect, so geom_tile(), geom_bar() and geom_col() all emit geom_rect.rect.* grobs while geom_grob_prefix() calls them geom_tile/geom_bar/geom_col. Measured, geom_col(5 bars) + geom_rect(4 rects) draws two of them – geom_rect.rect.2 holding the 5 bars and geom_rect.rect.4 holding the 4 bands – and the base class's find_layer_grob_tree() hands the rect layer the first, the column chart's bars. Counting the target among inherits(geom, "GeomRect") layers makes the counted population the drawn population and resolves it to the second; measured the same for geom_tile(9) + geom_rect(4), which draws 9 then 4 under the same two names.

inherits() rather than a written-out list of class names, so that a rect subclass this package has never heard of is counted as what it draws. Measured, GeomTile, GeomBar and GeomCol all inherit GeomRect; GeomRaster does not, and draws GRID.rastergrob.* rather than a rect, so the two populations agree on it as well. GeomRectCS, the candlestick body, inherits it too – measured against tidyquant 1.0.12, where inherits(GeomRectCS, "GeomRect") is TRUE and class(GeomRectCS)[1] is "GeomRectCS".

Scoped to this lookup. The same miscount reaches any bar, heat or candlestick layer sharing a panel with another rect-drawn geom through find_layer_grob_tree(); that is a defect this change did not introduce and does not widen.

Usage
Ggplot2GanttLayerProcessor$find_rect_name(
  plot,
  gt,
  panel_ctx = NULL,
  target = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for patchwork leaves and facets

target

This layer's index among the plot's layers

Returns

The grob name, or NULL when it cannot be resolved


Ggplot2GanttLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2GanttLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Heatmap Layer Processor

Description

Processes heatmap layers (geom_tile) with generic data and grob reordering

Super class

LayerProcessor -> Ggplot2HeatmapLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2HeatmapLayerProcessor$process()

Process the layer: read its tiles, selectors and axis names from the built plot

Usage
Ggplot2HeatmapLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List describing the layer for the MAIDR payload


Ggplot2HeatmapLayerProcessor$needs_reordering()

Whether the plot data must be reordered before drawing, so the emitted order matches the drawn tiles

Usage
Ggplot2HeatmapLayerProcessor$needs_reordering()
Returns

TRUE


Ggplot2HeatmapLayerProcessor$binned_selector_grid()

One selector per drawn bin of a binned grid, NA for an empty one

ggplot2 draws the bins in the order the built data lists them, so the k-th rect under the layer's group is the k-th built row; cells holds that row number per cell, in the emitted row order.

Usage
Ggplot2HeatmapLayerProcessor$binned_selector_grid(selector, cells)
Arguments
selector

The layer's ⁠<group> > rect⁠ selector

cells

Per-row lists of built row numbers, NA where no bin was drawn

Returns

A per-cell selector grid


Ggplot2HeatmapLayerProcessor$reorder_layer_data()

Reorder the plot data row-wise so the emitted cells match the drawn tiles

Usage
Ggplot2HeatmapLayerProcessor$reorder_layer_data(data, plot)
Arguments
data

The data frame ggplot2 will draw from

plot

The ggplot2 object

Returns

The reordered data frame


Ggplot2HeatmapLayerProcessor$is_binned_layer()

Report whether this layer's grid was computed by a stat.

geom_bin_2d() is GeomTile + StatBin2d, so it arrives here classified as a heatmap – correctly, since a rectangular bin grid is one. What differs is where the grid comes from: a geom_tile() heatmap is handed one in plot$data, and a binned one has its computed for it.

Matched on the stat rather than on the presence of a count column, because a tidy heatmap whose value column happens to be named count is not a binned layer and must not take that path. Reached through get_own_layer(), which already answers "is there a layer at my index?" – a second bounds check here would be a second place for the answer to change.

Usage
Ggplot2HeatmapLayerProcessor$is_binned_layer(plot)
Arguments
plot

The ggplot object

Returns

TRUE when the layer's stat computes a 2D bin grid


Ggplot2HeatmapLayerProcessor$extract_binned_data()

Read a computed 2D bin grid out of the built data.

The built data is the grid: one row per drawn tile, carrying the bin's count and its edges. Only the bins that hold something are present, so the full rectangle is rebuilt from the distinct positions and the empty cells left missing rather than scored zero – an empty bin genuinely counted nothing, but the frontend reads a zero as "no rect here" for highlighting, and every cell of a heatmap has one.

Axis labels are the bin's coordinate range, not its index: "a count of 4" means nothing without "between -2.2 and -1.1", and the range is what a sighted reader gets from the axis (#136).

Usage
Ggplot2HeatmapLayerProcessor$extract_binned_data(built_data)
Arguments
built_data

This layer's computed data, already panel-filtered

Returns

The same shape extract_data returns for a tidy heatmap


Ggplot2HeatmapLayerProcessor$format_bin_edge()

Render one bin edge as a short, readable number.

Bin edges are floating point and print at full precision by default – "-1.1076174999999999 to 1.1076180000000001e-07" is an announcement nobody can hold in their head. Rounded to a few significant figures, and trimmed, so the label reads as a coordinate rather than as a machine number.

Usage
Ggplot2HeatmapLayerProcessor$format_bin_edge(value)
Arguments
value

A single numeric bin edge or centre

Returns

A length-1 character string


Ggplot2HeatmapLayerProcessor$bin_labels()

Label each bin by the range it covers.

xmin/xmax are computed alongside the count, so the range costs nothing to report and is the only thing that makes the count meaningful. Falls back to the bin centre when the edges are absent, which is still a coordinate rather than an index.

Usage
Ggplot2HeatmapLayerProcessor$bin_labels(built_data, positions, axis)
Arguments
built_data

This layer's computed data

positions

The distinct bin centres, sorted

axis

"x" or "y"

Returns

Character labels, one per position, in the same order


Ggplot2HeatmapLayerProcessor$extract_data()

One row per tile plus the fill label, read from the built plot

Usage
Ggplot2HeatmapLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List


Ggplot2HeatmapLayerProcessor$generate_selectors()

Selectors for the tiles, scoped to the panel

Usage
Ggplot2HeatmapLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List of selectors


Ggplot2HeatmapLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2HeatmapLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Hexbin Layer Processor

Description

Processes hexagonal binning layers (geom_hex, stat_binhex).

A hexbin is the standard answer to an overplotted scatter: bin the points into hexagons and encode the count as fill. Read as a lattice of counted cells that is a heatmap, and the navigation, braille and pitch all transfer – but the rows are staggered, so it is a layer type of its own rather than a heatmap with different cells. See hexbin_lattice() for what that costs.

Super class

LayerProcessor -> Ggplot2HexbinLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2HexbinLayerProcessor$process()

Process the hexbin layer

Usage
Ggplot2HexbinLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for patchwork leaves and facets

Returns

List with data, selectors and axes


Ggplot2HexbinLayerProcessor$extract_data()

Read the drawn lattice out of the built data

The built data is the lattice: one row per drawn hexagon, carrying its centre and its count. Nothing is reconstructed from the source columns, which are the raw observations and say nothing about where the stat placed the bins.

Usage
Ggplot2HexbinLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

The list hexbin_lattice() returns


Ggplot2HexbinLayerProcessor$extract_axes()

Name the three axes

The colour axis is the count of points that fell in the bin, which is what stat_binhex() computes and what the fill encodes. Named here rather than read from the legend title, which says "count" for the default and would say after_stat(density) for a chart that is still counting into the same cells.

x and y go through positional_axis_label(), which is the package's shared "labs() override, then the layer's own mapping, then the plot's" chain, falling back to the aesthetic name rather than to nothing.

Usage
Ggplot2HexbinLayerProcessor$extract_axes(plot, built = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

Returns

An axes payload with x, y and z


Ggplot2HexbinLayerProcessor$generate_selectors()

Address each drawn hexagon by its own element

gridSVG exports the layer's single multi-polygon grob as one <polygon> per hexagon, in built-data order, each carrying an id of the form <grob>.1.<n>. So a bin is addressed by the built row it came from, and the emitted list follows the regrouping rather than the document.

The frontend withdraws highlighting outright unless the resolved element count matches the bin count exactly, so a partial list is worse than none – an empty list is returned when the grob cannot be found rather than a guess at its name.

Usage
Ggplot2HexbinLayerProcessor$generate_selectors(
  gt = NULL,
  plot = NULL,
  panel_ctx = NULL,
  order = integer(0)
)
Arguments
gt

Gtable object

plot

The ggplot2 object, used to build a gtable when none is given

panel_ctx

Panel context for patchwork leaves and facets

order

The built-data row behind each bin, in emission order

Returns

A list of CSS selectors, one per bin


Ggplot2HexbinLayerProcessor$find_hex_polygon_name()

Find the name of the grob holding the hexagons

GeomHex draws every hexagon in one polygonGrob, so there is a single name to find rather than one per bin. Searched depth-first because the layer's grobs sit inside a gTree of their own – and the caller passes that tree rather than the panel, so a first match is this layer's rather than some other hexbin's.

Usage
Ggplot2HexbinLayerProcessor$find_hex_polygon_name(grob)
Arguments
grob

The layer's grob tree to search

Returns

The grob name, or NULL when the layer drew nothing


Ggplot2HexbinLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2HexbinLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Histogram Layer Processor

Description

Processes histogram plot layers with complete logic included

Super class

LayerProcessor -> Ggplot2HistogramLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2HistogramLayerProcessor$process()

Process the layer: read its bins, selectors and orientation from the built plot

Usage
Ggplot2HistogramLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List describing the layer for the MAIDR payload


Ggplot2HistogramLayerProcessor$determine_orientation()

Which axis this histogram's bins run along

ggplot_build() fills a flipped layer's ymin/ymax with the bin bounds and its x with the count, and extract_data above passes both through as they come – so the emitted data is already transposed correctly. What was missing is this key saying so.

Without it the frontend defaults to vertical and reads the bin range from xMin/xMax, which on a flipped layer hold the count bounds. A geom_histogram() drawn with aes(y = v) was announced with a bin range of "0 to 5" – counts – where the data runs -2.42 to -1.10, and with every bin centre offered as a value. Every number real, every one on the wrong axis, and nothing erroring (#163).

Read from flipped_aes, which ggplot2 sets on the built layer, the way the boxplot and violin processors already do. coord_flip() is a different question and deliberately not answered here: it leaves flipped_aes alone and rotates only the coordinate system, so the data layout this key describes is genuinely unflipped. Treating it as horizontal would swap a pair that is already the right way round.

Usage
Ggplot2HistogramLayerProcessor$determine_orientation(plot, built = NULL)
Arguments
plot

The ggplot2 object.

built

Its ggplot_build() result, when the caller already has one.

Returns

"horz" or "vert".


Ggplot2HistogramLayerProcessor$extract_data()

One point per bin, read from this layer's own built data

Usage
Ggplot2HistogramLayerProcessor$extract_data(
  plot,
  built = NULL,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List of points


Ggplot2HistogramLayerProcessor$generate_selectors()

Selectors for the bins, scoped to the panel

Usage
Ggplot2HistogramLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List of selectors


Ggplot2HistogramLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2HistogramLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Final Line Layer Processor - Uses Actual SVG Structure

Description

Processes line plot layers using the actual gridSVG structure discovered:

Super class

LayerProcessor -> Ggplot2LineLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2LineLayerProcessor$process()

Process the line layer with actual SVG structure

Usage
Ggplot2LineLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List with data and selectors


Ggplot2LineLayerProcessor$attach_group_axis()

Add the legend title as the z axis label for a multi-series line layer.

Shared with the smooth layer processor via attach_series_group_axis(); see R/series_group_utils.R.

Usage
Ggplot2LineLayerProcessor$attach_group_axis(plot, built, data, axes)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

data

The extracted layer data

axes

Axes built so far

Returns

The axes list, with z added when the layer is grouped


Ggplot2LineLayerProcessor$has_series_groups()

Report whether extracted data is split into named series.

Usage
Ggplot2LineLayerProcessor$has_series_groups(data)
Arguments
data

The extracted layer data

Returns

TRUE when there is more than one series and points carry z


Ggplot2LineLayerProcessor$resolve_group_mapping()

Resolve the aesthetic that splits this layer into series.

A line has no fill, so only the colour aesthetic is probed.

Usage
Ggplot2LineLayerProcessor$resolve_group_mapping(plot)
Arguments
plot

The ggplot2 object

Returns

list with aes (aesthetic spelling variants, or NULL when nothing is mapped) and column (the mapped column name, or "group" as a fallback)


Ggplot2LineLayerProcessor$extract_layer_axes()

Extract axes labels for line layers, with a special case for moving-average geoms (e.g. tidyquant::geom_ma).

By default the parent LayerProcessor$extract_layer_axes() reads the y-label from the layer's aesthetic mapping. For a moving-average overlay typically written as geom_ma(aes(y = close), ma_fun = SMA, ...), this yields the literal input-column name "close", which is misleading: the value being plotted (and announced during navigation) is the moving average of close, not close itself. We detect GeomMA (the class of tidyquant's geom_ma layer) and override the y-label accordingly. Plain geom_line / geom_smooth overlays are untouched.

Usage
Ggplot2LineLayerProcessor$extract_layer_axes(plot, layout)
Arguments
plot

The ggplot2 object

layout

Layout information

Returns

list(x = list(label = ...), y = list(label = ...))


Ggplot2LineLayerProcessor$extract_data()

Extract data from line layer (single or multiline)

Usage
Ggplot2LineLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List of arrays, each containing series data points


Ggplot2LineLayerProcessor$attach_discrete_y_names()

Give every point the name of its discrete y level.

A factor y aesthetic reaches the payload as ggplot2's internal level code, so without this a reader hears "5" where the axis says "Awake". y deliberately stays numeric – it drives sonification, braille and the min/max range – and the name rides alongside as label.

A continuous y is returned untouched, so no label is emitted and the frontend announces the number, which is the right reading there.

Usage
Ggplot2LineLayerProcessor$attach_discrete_y_names(
  series_data,
  plot,
  built,
  panel_id = NULL
)
Arguments
series_data

List of series produced by the extractors above

plot

The ggplot2 object

built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

The series list, labelled when y is discrete


Ggplot2LineLayerProcessor$normalize_point_values()

Coerce every point's y to a plain number.

A discrete y aesthetic – the ordinal level of a hypnogram, and the reason this processor exists – makes ggplot_build() return y as a mapped_discrete vector. That class carries no asJSON method, so emitting it verbatim aborts payload serialisation with "No method asJSON S3 class: mapped_discrete". Stripping the class here keeps y a bare number, which is what the wire contract asks for and what drives sonification, braille and the min/max range.

Usage
Ggplot2LineLayerProcessor$normalize_point_values(series_data)
Arguments
series_data

List of series produced by the line extractor

Returns

The series list with numeric y values


Ggplot2LineLayerProcessor$panel_axis_labels()

Read one panel's own axis labels.

The labels ggplot2 drew on a panel's axis, which is what a sighted reader sees, and which both discrete recoveries below start from: the y level names in build_level_lookup(), and the x values in recover_x_values(). Both need the same three things to hold before the labels can be trusted – present, non-empty, no NA – so the check lives here rather than being spelled out at each call site.

The tryCatch is not incidental: get_labels() is one of the accessors whose spelling has moved between ggplot2 versions, the same reason get_x_transformation() exists. Guarding it in one place leaves the next version bump one site to fix instead of two that can drift.

Indexes with ⁠[[axis]]⁠ rather than ⁠$x⁠ / ⁠$y⁠: $ on a list falls back to partial matching, so a panel_params without an x but with an x.range would silently hand back the range. Exact matching returns NULL there, which is the honest answer.

Usage
Ggplot2LineLayerProcessor$panel_axis_labels(built, panel_index, axis)
Arguments
built

Built plot data

panel_index

Index into built$layout$panel_params

axis

Either "x" or "y"

Returns

Character vector of labels, or NULL when the panel has no such scale or its labels are unusable


Ggplot2LineLayerProcessor$build_level_lookup()

Build a numeric-level to level-name lookup for the y aesthetic.

For a factor (or character) y aesthetic, ggplot_build() replaces the level with its numeric position, so built$data$y is a level code and the name has to be recovered from somewhere else.

It is recovered from the panel's own y scale – the very labels ggplot2 draws on the axis – which makes the announcement agree with what a sighted reader sees, and sidesteps two traps:

Returns NULL for a plain continuous y, in which case no label is emitted and the frontend announces the numeric value.

Usage
Ggplot2LineLayerProcessor$build_level_lookup(plot, built, panel_id = NULL)
Arguments
plot

The ggplot2 object (unused; kept for call-site symmetry)

built

Built plot data

panel_id

Panel ID for faceted plots (optional) – each panel carries its own scale under scales = "free_y"

Returns

Named character vector keyed by the built y value, or NULL


Ggplot2LineLayerProcessor$attach_level_labels()

Attach the ordinal level name to every point of every series. Points whose y has no entry in the lookup are left untouched, so the frontend falls back to the numeric announcement for them.

Usage
Ggplot2LineLayerProcessor$attach_level_labels(series_data, lookup)
Arguments
series_data

List of series produced by the line extractor

lookup

Named character vector keyed by the built y value

Returns

The series list with label attached where known


Ggplot2LineLayerProcessor$get_layer()

The ggplot2 layer this processor is responsible for.

Usage
Ggplot2LineLayerProcessor$get_layer(plot)
Arguments
plot

The ggplot2 object

Returns

The layer, or NULL when the index does not resolve


Ggplot2LineLayerProcessor$get_x_transformation()

The transformation a panel's x scale applies to positions.

ggplot_build() stores x positions in transformed space, so under scale_x_log10() the data value 100 is recorded as 2. Recovering the value the axis actually shows needs the scale's own transformation. ggplot2 >= 3.5 exposes it through get_transformation(); earlier versions keep it in the scale's trans field.

Usage
Ggplot2LineLayerProcessor$get_x_transformation(built, panel_index)
Arguments
built

Built plot data

panel_index

Index into built$layout$panel_params

Returns

A scales transform object, or NULL when none is available


Ggplot2LineLayerProcessor$transform_x_values()

Project raw x values into the space ggplot_build() stores positions in.

Going forwards (raw -> transformed) rather than inverting the built positions keeps the comparison exact: it repeats the very computation ggplot2 performed, so no floating-point drift is introduced. Values the transformation cannot represent (a non-positive number under a log scale, say) become NA and simply fail to match.

Usage
Ggplot2LineLayerProcessor$transform_x_values(values, transformation)
Arguments
values

Raw values taken from the plot's data

transformation

Transform object from get_x_transformation(), or NULL

Returns

Numeric vector the same length as values


Ggplot2LineLayerProcessor$format_x_value()

Format an x-axis value for the payload.

A plain number stays a number, so a line over a numeric column carries the same x a geom_point() over it does. Date / POSIXct / POSIXlt values are formatted via format() so that a Date column emits ISO date strings (e.g. "2024-01-02") rather than the underlying numeric days-since-epoch representation produced by ggplot_build(), which keeps them aligned string-wise with Ggplot2BarLayerProcessor$format_x_value(). Anything else – a category label – is a string. See line_x_value().

Usage
Ggplot2LineLayerProcessor$format_x_value(x)
Arguments
x

The value to format

Returns

A number, or a string


Ggplot2LineLayerProcessor$extract_multiline_data()

Extract data for multiple line series

Usage
Ggplot2LineLayerProcessor$extract_multiline_data(
  layer_data,
  plot,
  recovered_x = NULL
)
Arguments
layer_data

The built layer data

plot

The original ggplot2 object

recovered_x

x values recovered from the built column by recover_x_values(), aligned to layer_data's rows. NULL leaves the built value in place.

Returns

List of arrays, each containing series data


Ggplot2LineLayerProcessor$extract_single_line_data()

Extract data for single line (backward compatibility)

Usage
Ggplot2LineLayerProcessor$extract_single_line_data(
  layer_data,
  plot = NULL,
  recovered_x = NULL
)
Arguments
layer_data

The built layer data

plot

The original ggplot2 object. Unread since x recovery moved upstream; kept for signature parity with extract_multiline_data() and for existing call sites.

recovered_x

x values recovered from the built column by recover_x_values(), aligned to layer_data's rows. NULL leaves the built value in place.

Returns

List containing single series data


Ggplot2LineLayerProcessor$recover_x_values()

The x values to announce, recovered from the BUILT column.

ggplot_build() stores x in the scale's own space – a level code for a discrete scale, days-since-epoch for a Date – so something has to turn it back into what the axis shows. The obvious route, reading the caller's column, cannot be indexed by the built row number: GeomLine$setup_data() sorts the built data by (PANEL, group, x), which is the documented difference between geom_line() and geom_path(), while the caller's column keeps its own order. Pairing the two by position hands every point another point's x.

Recovering from the built value instead is order-proof by construction, and per scale type it needs:

Usage
Ggplot2LineLayerProcessor$recover_x_values(layer_data, built, panel_id = NULL)
Arguments
layer_data

The built layer data for this layer

built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

A vector aligned to layer_data's rows, or NULL to leave the built value alone


Ggplot2LineLayerProcessor$get_group_column()

Get the grouping column name from plot mappings

Usage
Ggplot2LineLayerProcessor$get_group_column(plot)
Arguments
plot

The ggplot2 object

Returns

Name of the grouping column


Ggplot2LineLayerProcessor$generate_selectors()

One selector per series this line layer draws.

The panel-wide polyline list conflates two different things: a grouped geom_line() draws ALL of its curves as ONE polylineGrob whose id splits it (gridSVG then emits GRID.polyline.N.1.1, .1.2, ... per curve), while a second polyline-producing layer such as geom_smooth() adds further grobs of its own. Indexing that flat list by this layer's position among line layers therefore returned one selector for a three-curve layer as soon as a smooth sat beside it, and the frontend's multiline trace refuses a selector list whose length does not equal the series count – so the layer lost highlighting entirely rather than mis-aiming it.

This resolves THIS layer's own grob first and then enumerates the curves inside it, emitting one selector per curve. When the curves cannot be lined up with the series, no selector is emitted: a caller can tell an absent selector apart from a wrong one, a user cannot.

Usage
Ggplot2LineLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL,
  built = NULL,
  n_series = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation

built

Built plot data (optional)

n_series

Number of series extract_data() produced, or NULL to derive it from the built layer data

Returns

List of selectors for each series


Ggplot2LineLayerProcessor$series_count()

Number of series this layer draws in the given panel.

Never throws: selector generation has to degrade gracefully for inputs extract_data() would reject.

Usage
Ggplot2LineLayerProcessor$series_count(plot, built = NULL, panel_ctx = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_ctx

Panel context for panel-scoped selector generation

Returns

Number of series, at least 1


Ggplot2LineLayerProcessor$curve_selectors()

Selectors for the curves inside this layer's own grob.

Usage
Ggplot2LineLayerProcessor$curve_selectors(plot, panel_grob, n_series)
Arguments
plot

The ggplot2 object

panel_grob

The panel's grob tree

n_series

Number of series the layer produced

Returns

List of selectors, or NULL when the grob does not line up with the series


Ggplot2LineLayerProcessor$polyline_curve_count()

Number of separate curves a polyline grob draws.

polylineGrob() splits one grob into several drawn lines via id / id.lengths; gridSVG renders each as its own SVG element suffixed .1.<k>.

Usage
Ggplot2LineLayerProcessor$polyline_curve_count(grob)
Arguments
grob

A polyline grob

Returns

Integer count, at least 1


Ggplot2LineLayerProcessor$generate_multiline_selectors()

Generate selectors for multiline plots using actual structure

Usage
Ggplot2LineLayerProcessor$generate_multiline_selectors(base_id, num_series)
Arguments
base_id

The base ID from the grob (e.g., "61")

num_series

Number of series

Returns

List of selectors


Ggplot2LineLayerProcessor$generate_single_line_selector()

Generate selector for single line plot

Usage
Ggplot2LineLayerProcessor$generate_single_line_selector(base_id)
Arguments
base_id

The base ID from the grob

Returns

List with single selector


Ggplot2LineLayerProcessor$line_layer_position()

Position of this layer among the polyline-producing layers.

Delegates to polyline_layer_position(), which counts every layer type that renders an auto-named polyline: "line", "step" and "contour". layer_polyline_grobs() skips only the layers that name their grob tree after their geom, and none of these three do. GeomContour defines no draw_panel() of its own and so draws through GeomPath's – a bare polylineGrob – while GeomStep stairsteps its data and then calls the same method; either sits in that candidate list exactly as a geom_line() does. Counting only "line" layers would therefore index the wrong polyline for every layer of a plot that combines them.

Usage
Ggplot2LineLayerProcessor$line_layer_position(plot)
Arguments
plot

The ggplot2 object

Returns

The 1-based position, or NULL if registry-based detection fails


Ggplot2LineLayerProcessor$find_main_polyline_grob()

Find the main polyline grob (GRID.polyline.XX)

Usage
Ggplot2LineLayerProcessor$find_main_polyline_grob(gt)
Arguments
gt

The gtable to search

Returns

The main polyline grob or NULL


Ggplot2LineLayerProcessor$needs_reordering()

Check if layer needs reordering

Usage
Ggplot2LineLayerProcessor$needs_reordering()
Returns

FALSE (line plots typically don't need reordering)


Ggplot2LineLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2LineLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Pie Layer Processor

Description

Processes the ggplot2 idiom for a pie chart: a geom_col() / geom_bar() layer drawn in polar coordinates with theta mapped to y, so the stack's segments become wedges. The payload is 1-D and flat – one point per wedge, x the slice label and y its magnitude. The percentage MAIDR announces is derived from those values by the frontend, so this layer deliberately does not emit one.

Multi-ring "bullseye" polar bars are out of scope. geom_col() with a non-constant x under coord_polar("y") draws one concentric ring per x category, and a flat list of wedges cannot carry that second dimension – wedges from different rings would collapse onto the same label. Those layers never reach this processor: Ggplot2Adapter$is_pie_coord() declines them, and they stay bar / stacked / dodged as before.

Super class

LayerProcessor -> Ggplot2PieLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2PieLayerProcessor$process()

Process the pie layer

Usage
Ggplot2PieLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

List with data, selectors, title, axes and type


Ggplot2PieLayerProcessor$extract_dial()

Where the ring begins and which way the emitted wedges run

The frontend walks a pie clockwise – Right steps to the next slice the way a clock hand goes, the audio pans each slice to where it sits and p names its clock position – all from where the layer says the first slice begins, in degrees clockwise from 12 o'clock. The layer says two things about the wedges it emits: where the ring begins and which way round the dial the emitted order runs.

Where the ring begins, and which way the coord runs round it, are read off the coord by pie_coord_ring(): coord_polar() keeps a start applied in its direction, coord_radial() an arc already turned round by its reverse. Either maps the whole theta scale onto the arc, so a full ring ends where it began and the same edge serves whichever way the wedges are walked. A partial coord_radial() arc, or its default theta expansion, draws less than the full circle; the frontend's pie has no key for that, so the ring's nominal edge is declared and the wedges are read as filling it.

Which way the emitted order runs is NOT simply the coord's direction. position_stack() stacks the first group on top by default, so the built rows – and the wedges, and the selectors index-aligned to them – run from the top of the stack down: the first emitted wedge is the one that ENDS the ring, and the order goes back against the coord's direction, and position_stack(reverse = TRUE) builds them the other way up. So the direction is read off the built rows themselves: emitted order running down the stack is the coord's direction reversed, running up it is the coord's direction.

Both keys are left out at the frontend's own defaults – a clockwise ring from the top – which is also what every layer declared before the keys existed.

Usage
Ggplot2PieLayerProcessor$extract_dial(plot, built, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data

panel_id

Optional facet panel to restrict extraction to

Returns

Named list holding startAngle and/or direction, possibly empty


Ggplot2PieLayerProcessor$extract_data()

Extract one point per wedge

The magnitude is the segment's own extent, not the stacked y: ymax - ymin is what the wedge actually subtends, and it is the one expression that works for both geom_col() (stat identity) and geom_bar() (stat count).

The extent is unsigned, though, and a negative datum is stacked below the baseline: ggplot2 builds v = -40 as ymin = -40, ymax = 0, so the extent is 40 and the sign is gone. Reporting that would announce a slice the author entered as -40 as 40, and compute its share against a total that swallowed it – confidently wrong, and indistinguishable from real data.

So the sign is restored from which side of the baseline the segment sits on. The renderer treats a negative slice as a gap, announcing it as missing rather than letting it corrupt every other slice's percentage; laundering it here would leave that defence nothing to catch. Whether a producer should reject such a value outright is a separate question – see xability/maidr#771 – but no answer to it is served by destroying the sign first.

Usage
Ggplot2PieLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Optional facet panel to restrict extraction to

Returns

List of list(x, y) points, one per wedge


Ggplot2PieLayerProcessor$panel_built_data()

Rows of this layer's built data, optionally one panel's

Usage
Ggplot2PieLayerProcessor$panel_built_data(built, panel_id = NULL)
Arguments
built

Built plot data

panel_id

Optional facet panel to restrict the rows to

Returns

data.frame of built rows for this layer


Ggplot2PieLayerProcessor$resolve_slice_mapping()

Resolve the aesthetic whose categories name the wedges

Fill is probed before x because the idiomatic pie maps x to the literal "" and carries the categories on fill. A layer whose built rows do not each sit in their own group is not split by any aesthetic (every row shares group -1 or 1), so no aesthetic names its wedges.

Usage
Ggplot2PieLayerProcessor$resolve_slice_mapping(plot, built_data)
Arguments
plot

The ggplot2 object

built_data

This layer's built rows

Returns

list with aes (aesthetic name, or NULL) and column (the mapped column name)


Ggplot2PieLayerProcessor$resolve_slice_labels()

Name each wedge after the category it draws

ggplot_build() has already replaced the grouping column with integer group ids, assigned in the sorted order of that column's values – the same order the scale reports its labels in. Indexing the labels BY the id, rather than by position among the ids present, is what stops a facet panel that is missing a category from shifting every remaining wedge's label by one. Wedges the scale cannot name fall back to their position.

Usage
Ggplot2PieLayerProcessor$resolve_slice_labels(plot, built, built_data)
Arguments
plot

The ggplot2 object

built

Built plot data

built_data

This layer's built rows

Returns

Character vector, one label per wedge


Ggplot2PieLayerProcessor$slice_categories()

Categories of the aesthetic that splits the wedges

The scale is asked first, because a mapping written as an expression – aes(fill = factor(cyl)) – has no column to read. A discrete POSITION scale keeps its labels in panel_params instead, and coord_polar() publishes none of those under x, so the mapped column is the fallback. Both list the categories in the same sorted order the group ids were assigned in.

Usage
Ggplot2PieLayerProcessor$slice_categories(plot, built, slice)
Arguments
plot

The ggplot2 object

built

Built plot data

slice

Slice mapping from resolve_slice_mapping()

Returns

Character vector of categories, or NULL when neither source has any


Ggplot2PieLayerProcessor$scale_labels()

Break labels of the scale backing an aesthetic

Usage
Ggplot2PieLayerProcessor$scale_labels(built, aes_name)
Arguments
built

Built plot data

aes_name

Aesthetic whose scale to read

Returns

Character vector of labels, or NULL when the scale has none


Ggplot2PieLayerProcessor$extract_pie_axes()

Build the canonical axes for a pie layer

x names what the wedge labels mean and y what their magnitudes measure. Since the labels come off the slice aesthetic, its legend title is the x label – resolved the same way the stacked bar layer resolves its z label. The y label is taken from the layout, which reads the BUILT plot's labels and so already carries a stat-derived name such as "count".

Usage
Ggplot2PieLayerProcessor$extract_pie_axes(plot, layout, built, panel_id = NULL)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data

panel_id

Optional facet panel to restrict extraction to

Returns

Canonical axes list with x and y


Ggplot2PieLayerProcessor$generate_selectors()

Generate the wedge selector for this layer

In polar coordinates the whole layer is ONE polygonGrob named geom_rect.polygon.<N> whose sub-polygons are grouped by id – not the geom_rect.rect.<N> a cartesian bar layer draws. gridSVG exports it as <g id="geom_rect.polygon.<N>.1"> with one <polygon> child per wedge, emitted in built-row order, so a single descendant selector resolves to the N elements in slice order.

Usage
Ggplot2PieLayerProcessor$generate_selectors(plot, gt = NULL, panel_ctx = NULL)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

List holding one selector, or an empty list


Ggplot2PieLayerProcessor$layer_slot_grob()

The grob slot belonging to this layer

ggplot2 lays a panel out as the grill, a leading zeroGrob, one child per layer in layer order, a trailing zeroGrob and the axis tree. So the slot is the handle: layer k owns the kth child after the leading blank, whether or not it drew anything.

It matters because a panel can hold more than one polar geom_rect layer – two geom_col()s under coord_polar() is an ordinary way to draw a ring over a pie – and a search that takes the first container hands every layer the first layer's wedges. Those selectors resolve, and the payload looks healthy, and the outline is on the wrong marks.

LayerProcessor$find_layer_grob_tree() cannot be reused for this: it matches on the geom's own class, and a geom_col() layer is GeomCol while the grob it draws is named after geom_rect. Counting containers instead of slots does not work either – a geom_text() label layer occupies a slot and draws no container, so the counts stop lining up and both rings of an annotated pie lose their selectors.

Usage
Ggplot2PieLayerProcessor$layer_slot_grob(panel)
Arguments
panel

The panel grob, or NULL

Returns

This layer's grob, or NULL when the slot cannot be established


Ggplot2PieLayerProcessor$sole_wedge_container()

The one wedge container in a tree, when there is exactly one

The fallback for when the slot lookup cannot resolve – a panel shape with no leading blank, or a caller handing over a gtable rather than a panel. Correct whenever the search finds a single container, which is every chart that is only a pie; ambiguous otherwise, and ambiguous means no selector for the reason generate_selectors() gives.

Usage
Ggplot2PieLayerProcessor$sole_wedge_container(roots)
Arguments
roots

Grobs to search

Returns

Grob name, or NULL


Ggplot2PieLayerProcessor$collect_polygon_grobs()

Every wedge container in a grob tree, in drawing order

One entry per layer that drew wedges. A match is not descended into: the container is the whole layer's wedges, and its children are the individual ones.

Usage
Ggplot2PieLayerProcessor$collect_polygon_grobs(grob)
Arguments
grob

Grob to search

Returns

Character vector of grob names, possibly empty


Ggplot2PieLayerProcessor$find_own_polygon_grob()

Whether this grob is itself a wedge container

ggplot2 does not draw a polar bar layer the same way across versions, and the difference is not cosmetic. Verified against real gridSVG::grid.export() output:

Either way the answer is a container whose <polygon> descendants are the wedges in slice order, so the caller's descendant selector resolves against both without knowing which it got.

The polar grill draws a polygon of its own under coord_radial(), named GRID.polygon.<N>; neither branch carries a name that matches it. The gTree branch also requires a polygon to be there: a geom_rect layer that drew none has nothing to point at.

Usage
Ggplot2PieLayerProcessor$find_own_polygon_grob(grob)
Arguments
grob

Grob to test

Returns

Grob name, or NULL


Ggplot2PieLayerProcessor$holds_polygon()

Whether a grob tree draws at least one polygon.

Usage
Ggplot2PieLayerProcessor$holds_polygon(grob)
Arguments
grob

Grob to search

Returns

TRUE when the tree holds a polygon grob


Ggplot2PieLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2PieLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Plot Orchestrator Class

Description

This class orchestrates the detection and processing of multiple layers in a ggplot2 object. It analyzes each layer individually and combines the results into a comprehensive interactive plot.

Methods

Public methods


Ggplot2PlotOrchestrator$new()

Create an orchestrator for a ggplot2 object

Usage
Ggplot2PlotOrchestrator$new(plot)
Arguments
plot

The ggplot2 object


Ggplot2PlotOrchestrator$detect_layers()

Turn each layer of the plot into a layer entry with its detected type

Usage
Ggplot2PlotOrchestrator$detect_layers()

Ggplot2PlotOrchestrator$skip_layers_that_drew_nothing()

Retag every layer that drew no rows as "skip".

A layer can be typed perfectly well and still have nothing in it – a data = filtered to nothing, a stat that dropped every row, a facet arrangement in which one layer's data is empty, a Suggests package absent so the stat could not run. It then reaches the schema as a layer a reader can walk into and find nothing in. Measured on ten points, the second layer drawn from d[0, ]:

geom_point()   point(0)      an empty layer of points
geom_col()     bar(0)        an empty layer of bars
geom_line()    line(1x0)     one series, holding nothing
geom_smooth()  smooth(1x0)   one series, holding nothing

Asked here rather than in detect_layer_type() because emptiness is not a fact about what kind of chart a layer is, and because the classifier runs per layer: one ggplot_build() for the whole pass costs a chart ~37 ms once, where asking per layer would multiply it. "skip" rather than a fourth answer, because that is the tag the rest of the orchestrator already understands – including the #176 guard, so a chart whose only layer is empty falls back to an image rather than announcing itself as interactive with nothing in it.

A build that cannot answer changes nothing. That is the same posture layer_drew_nothing() takes: a plot that will not build is a bigger problem than this, and it is about to be met by whatever else needs the build.

Usage
Ggplot2PlotOrchestrator$skip_layers_that_drew_nothing()
Returns

NULL, invisibly. Rewrites private$.layers in place.


Ggplot2PlotOrchestrator$analyze_single_layer()

Describe one ggplot2 layer as a layer entry with its detected type

Usage
Ggplot2PlotOrchestrator$analyze_single_layer(layer, layer_index)
Arguments
layer

A ggplot2 layer object

layer_index

Index of the layer

Returns

Layer information list


Ggplot2PlotOrchestrator$determine_layer_type()

The layer type the adapter detects for one layer of the plot

Usage
Ggplot2PlotOrchestrator$determine_layer_type(plot, layer_index)
Arguments
plot

The ggplot2 object

layer_index

Index of the layer

Returns

Character string


Ggplot2PlotOrchestrator$create_layer_processors()

Create a processor for every layer of a known type

Usage
Ggplot2PlotOrchestrator$create_layer_processors()

Ggplot2PlotOrchestrator$create_layer_processor()

Create the processor for one layer

Usage
Ggplot2PlotOrchestrator$create_layer_processor(layer_info)
Arguments
layer_info

Layer information

Returns

A layer processor, or NULL for an unknown type


Ggplot2PlotOrchestrator$create_unified_layer_processor()

Unified layer processor creation - used by all plot types

Usage
Ggplot2PlotOrchestrator$create_unified_layer_processor(layer_info)
Arguments
layer_info

Layer information

Returns

Layer processor instance


Ggplot2PlotOrchestrator$process_layers()

Run every layer processor and combine the results

Usage
Ggplot2PlotOrchestrator$process_layers()

Ggplot2PlotOrchestrator$extract_layout()

Read the figure-level title, subtitle, caption and axis labels from the built plot

Usage
Ggplot2PlotOrchestrator$extract_layout(built = NULL)
Arguments
built

Built plot data (optional)

Returns

List


Ggplot2PlotOrchestrator$combine_layer_results()

Combine the per-layer results into the subplot grid

Usage
Ggplot2PlotOrchestrator$combine_layer_results(layer_results)
Arguments
layer_results

List of per-layer results, one per processor


Ggplot2PlotOrchestrator$generate_maidr_data()

Assemble the MAIDR data object for the figure

Usage
Ggplot2PlotOrchestrator$generate_maidr_data()
Returns

List with an id and the subplots


Ggplot2PlotOrchestrator$get_gtable()

The gtable the plot was drawn to

Usage
Ggplot2PlotOrchestrator$get_gtable()
Returns

A gtable, or NULL before the layers are processed


Ggplot2PlotOrchestrator$get_layout()

The figure-level layout read by extract_layout()

Usage
Ggplot2PlotOrchestrator$get_layout()
Returns

List


Ggplot2PlotOrchestrator$get_combined_data()

The combined per-layer data

Usage
Ggplot2PlotOrchestrator$get_combined_data()
Returns

List


Ggplot2PlotOrchestrator$get_layer_processors()

The processors created for the layers

Usage
Ggplot2PlotOrchestrator$get_layer_processors()
Returns

List


Ggplot2PlotOrchestrator$get_layers()

The detected layer entries

Usage
Ggplot2PlotOrchestrator$get_layers()
Returns

List


Ggplot2PlotOrchestrator$is_patchwork_plot()

Check if the plot is a patchwork composition

Usage
Ggplot2PlotOrchestrator$is_patchwork_plot()
Returns

Logical indicating if the plot is a patchwork plot


Ggplot2PlotOrchestrator$is_faceted_plot()

Check if the plot is faceted

Usage
Ggplot2PlotOrchestrator$is_faceted_plot()
Returns

Logical indicating if the plot is faceted


Ggplot2PlotOrchestrator$process_faceted_plot()

Process a faceted plot using utility functions

Usage
Ggplot2PlotOrchestrator$process_faceted_plot()
Returns

NULL (sets internal state)


Ggplot2PlotOrchestrator$process_patchwork_plot()

Process a patchwork multipanel plot using utility functions

Usage
Ggplot2PlotOrchestrator$process_patchwork_plot()
Returns

NULL (sets internal state)


Ggplot2PlotOrchestrator$has_unsupported_layers()

Check if any layers are unsupported (unknown type)

Usage
Ggplot2PlotOrchestrator$has_unsupported_layers()
Returns

Logical indicating if there are unsupported layers


Ggplot2PlotOrchestrator$should_fallback()

Determine if the plot should fall back to image rendering

Usage
Ggplot2PlotOrchestrator$should_fallback()
Returns

Logical indicating if fallback should be used


Ggplot2PlotOrchestrator$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2PlotOrchestrator$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Point Layer Processor

Description

Processes scatter plot layers (geom_point) to extract point data and generate selectors for individual points in the SVG structure.

Super class

LayerProcessor -> Ggplot2PointLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2PointLayerProcessor$process()

Process the point layer

Usage
Ggplot2PointLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List with data and selectors


Ggplot2PointLayerProcessor$extract_axes_labels()

Extract axis information from the plot

Returns per-axis objects with label and optional grid navigation fields (min, max, tickStep). Grid fields are only included when they can be successfully extracted from the built plot scales.

Usage
Ggplot2PointLayerProcessor$extract_axes_labels(
  plot,
  built = NULL,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List with x and y per-axis objects


Ggplot2PointLayerProcessor$extract_axis_grid_info()

Extract grid navigation info (min, max, tickStep) for a single axis

Delegates to axis_grid_info(), which is where the reading now lives: the rug processor needs the same answer for the axis its ticks stand on, and one grid rule read two ways is how the two would drift. Kept as a method so this class's own callers are unchanged.

Usage
Ggplot2PointLayerProcessor$extract_axis_grid_info(
  built,
  axis = "x",
  panel_id = NULL
)
Arguments
built

Built plot data

axis

Character, either "x" or "y"

panel_id

Panel index for faceted plots (optional, defaults to 1)

Returns

List with min, max, tickStep or NULL if extraction fails


Ggplot2PointLayerProcessor$extract_data()

Extract data from point layer

Usage
Ggplot2PointLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List with points array and color information


Ggplot2PointLayerProcessor$generate_selectors()

Generate selectors for point elements

Usage
Ggplot2PointLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List of selectors


Ggplot2PointLayerProcessor$find_panel_grob()

Find the panel grob this layer draws into

Usage
Ggplot2PointLayerProcessor$find_panel_grob(gt, panel_ctx = NULL)
Arguments
gt

The gtable to search

panel_ctx

Panel context for patchwork leaves and facets; NULL for a single plot, where the panel is the cell literally named "panel"

Returns

The panel grob or NULL


Ggplot2PointLayerProcessor$find_children_by_type()

Find children by type pattern

Usage
Ggplot2PointLayerProcessor$find_children_by_type(grob, type_pattern)
Arguments
grob

The grob to search

type_pattern

Pattern to match

Returns

List of matching children


Ggplot2PointLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2PointLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Polygon Layer Processor

Description

Reads geom_polygon() as the closed path it draws.

A polygon is geom_path() with its ends joined and its interior filled. GeomPath has been dispatched to "line" since before this file existed, so reading a polygon the same way decides nothing new about what a series of vertices means – it makes two spellings of one mark behave alike, the argument GeomSpoke was routed through GeomSegment on (#225).

What it was costing until then is the whole chart. "unknown" is what makes has_unsupported_layers() true and drops the plot to a static image (#176). Measured on ggplot2 3.4.4, thirty points, save_html():

geom_point()                     interactive SVG   52,708 bytes
geom_point() + geom_polygon()    base64 image      30,913 bytes
geom_polygon() alone             base64 image      18,353 bytes

Skipping it instead was declined in #225 and the reason is worth keeping here: every geom skipped today carries no observations – geom_blank() draws nothing, a reference line is a constant, a text label repeats a value already in the payload – so skipping loses a reader nothing they could have navigated. A polygon's vertices are rows the author supplied. Skipping one that is the data would drop it silently, which is worse than the honest picture, because the reader is not told anything is missing.

The closing vertex is not emitted

Four rows draw a quadrilateral with four corners and five edges. The fifth edge is the closure, and it adds no observation – which is ggplot2's own reading as well: GeomPolygon$draw_panel() hands grid the munched rows unchanged under a linear coord, and the drawn element holds exactly as many points as the layer has rows. Straight off the exported SVG for x = c(1, 3, 3, 1), y = c(1, 1, 3, 3):

<polygon points="49.84,53.12 171.93,53.12 171.93,265.22 49.84,265.22"/>

So a series and its drawn shape are the same length, in the same order, and a reader who navigates to the end has been told every vertex once.

Addressing

Unlike a line, a polygon layer names its grob after its geom, so it needs no draw-order search of anonymous GRID.polyline.N grobs. gridSVG turns one grob into one element per group, which is the granularity the multi-series trace wants:

<g id="geom_polygon.polygon.57.1">
  <polygon id="geom_polygon.polygon.57.1.1"/>   <- group 1
  <polygon id="geom_polygon.polygon.57.1.2"/>   <- group 2

aes(subgroup =) – a shape with a hole in it – is drawn as a pathgrob rather than a polygon, and gridSVG still emits one ⁠<path>⁠ per pathId, which is still the group. Measured on two groups of two subgroups: geom_polygon.pathgrob.42.1.1 and ...1.2, each holding both of its rings in one d. So both spellings address the same way and only the element differs, which is why the search below matches either.

Super classes

LayerProcessor -> Ggplot2LineLayerProcessor -> Ggplot2PolygonLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2PolygonLayerProcessor$process()

Process the polygon layer as a closed path

The reading is the line processor's: one series per group, in the built data's row order, named from whatever aesthetic splits the layer. Only the type and the selectors are this class's own.

Usage
Ggplot2PolygonLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for patchwork leaves and facets

Returns

List with data, selectors, title, axes and type


Ggplot2PolygonLayerProcessor$resolve_group_mapping()

Resolve the aesthetic that splits this layer into series

A line probes colour alone, because a line has no fill. A polygon has both, and fill is the one it is usually split by – so fill is probed first, then the outline colour, then group itself.

group is the addition, and it is worth its line. A polygon is drawn one shape per group, and aes(group = g) with nothing else mapped is the plainest way to write two shapes – it is how ggplot2's own documentation writes them. Without it both series fall back to "Series 1" and "Series 2" while the chart's own data says "a" and "b". ggplot2 records the column under labels$group exactly as it records a legend title, so the z label comes out as "g" and a reader hears "g is a" rather than "Group is Series 1".

Usage
Ggplot2PolygonLayerProcessor$resolve_group_mapping(plot)
Arguments
plot

The ggplot2 object

Returns

list with aes (aesthetic spelling variants, or NULL when nothing is mapped) and column (the mapped column name, or "group" as a fallback)


Ggplot2PolygonLayerProcessor$attach_group_axis()

Add the grouping column's name as the z axis label

Usage
Ggplot2PolygonLayerProcessor$attach_group_axis(plot, built, data, axes)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

data

The extracted layer data

axes

Axes built so far

Returns

The axes list, with z added when the layer is grouped


Ggplot2PolygonLayerProcessor$group_aes()

The aesthetics a polygon layer can be split by, in order

Usage
Ggplot2PolygonLayerProcessor$group_aes()
Returns

List of aesthetic-name vectors, each holding the spelling variants of one aesthetic


Ggplot2PolygonLayerProcessor$curve_selectors()

Selectors for this layer's polygons, one per series

Declines when the drawn shape count and the emitted series count disagree, which is what the line processor does and for the same reason: the frontend's multiline trace drops the whole layer's highlight unless ⁠selectors.length === data.length⁠, so a mismatched list would outline the wrong shape rather than none.

Usage
Ggplot2PolygonLayerProcessor$curve_selectors(plot, panel_grob, n_series)
Arguments
plot

The ggplot2 object

panel_grob

The panel's grob tree

n_series

Number of series the layer emitted

Returns

List of CSS selectors, or NULL


Ggplot2PolygonLayerProcessor$polygon_shape_count()

How many shapes one polygon grob draws

id separates a polygon grob's locations into shapes; a pathgrob uses id for its subgroups and pathId for the group, so the group is what has to be counted there – a two-group, two-subgroup layer carries ⁠id = 1,1,1,1,2,2,2,2,1,...⁠ and ⁠pathId = 1,1,1,1,1,1,1,1,2,...⁠, and reading id would answer two for a chart drawing two paths of two rings each only by coincidence.

There is no "it has no ids" case to answer for. GeomPolygon$draw_panel() always passes one – id = munched$group for a polygon, pathId = munched$group for a path – so a grob without one is not one of these, and length(unique(NULL)) answering zero declines it, which is the right answer for a grob this should not be addressing.

Usage
Ggplot2PolygonLayerProcessor$polygon_shape_count(grob)
Arguments
grob

A polygon or pathgrob grob

Returns

The number of shapes drawn


Ggplot2PolygonLayerProcessor$find_layer_polygon_grob()

The polygon grob ggplot2 drew for THIS layer

Usage
Ggplot2PolygonLayerProcessor$find_layer_polygon_grob(
  plot,
  panel_grob,
  target = NULL
)
Arguments
plot

The ggplot2 object

panel_grob

The panel's grob tree

target

Index of the layer to find; defaults to this one's

Returns

The matching grob, or NULL


Ggplot2PolygonLayerProcessor$layer_polygon_grobs()

Panel polygons that a polygon layer could have drawn

The skip list is not defensive. geom_boxplot() draws each box's crossbar through GeomPolygon, so a boxplot contributes grobs named exactly like a polygon layer's own – and they are drawn first. Measured on three boxes beside one polygon:

geom_boxplot.gTree.30
  geom_boxplot.gTree.10 -> geom_crossbar.gTree.9 -> geom_polygon.polygon.7
  geom_boxplot.gTree.20 -> geom_crossbar.gTree.19 -> geom_polygon.polygon.17
  geom_boxplot.gTree.28 -> geom_crossbar.gTree.27 -> geom_polygon.polygon.25
geom_polygon.polygon.32                                  <- the layer's own

Taking the first match would outline a box. Every one of those sits inside a tree named after the geom that owns it, so refusing to descend into another layer's tree leaves exactly the layer's own – which is how layer_polyline_grobs() scopes the same search for the geoms that draw anonymous polylines.

Usage
Ggplot2PolygonLayerProcessor$layer_polygon_grobs(
  plot,
  panel_grob,
  target = NULL
)
Arguments
plot

The ggplot2 object

panel_grob

The panel's grob tree

target

Index of the layer whose polygons are wanted

Returns

List of grobs in draw order


Ggplot2PolygonLayerProcessor$polygon_layer_position()

This layer's position among the plot's polygon layers

Two geom_polygon() calls draw two grobs in layer order, so the second layer wants the second match.

Usage
Ggplot2PolygonLayerProcessor$polygon_layer_position(plot, target)
Arguments
plot

The ggplot2 object

target

Index of the layer of interest

Returns

The 1-based position, or NULL when the layer is not one


Ggplot2PolygonLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2PolygonLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


ggplot2 Processor Factory

Description

Factory for creating ggplot2-specific processors. This factory uses the existing ggplot2 layer processors and wraps them in the new unified interface.

Format

An R6 class inheriting from ProcessorFactory

Super class

ProcessorFactory -> Ggplot2ProcessorFactory

Methods

Public methods

Inherited methods

Ggplot2ProcessorFactory$new()

Initialize the ggplot2 processor factory

Usage
Ggplot2ProcessorFactory$new()

Ggplot2ProcessorFactory$create_processor()

Create a processor for a specific plot type

Usage
Ggplot2ProcessorFactory$create_processor(plot_type, layer_info)
Arguments
plot_type

The type of plot (e.g., "bar", "line", "point")

layer_info

Information about the layer (contains plot object and metadata)

Returns

Processor instance for the specified plot type


Ggplot2ProcessorFactory$get_supported_types()

Get list of supported plot types

Usage
Ggplot2ProcessorFactory$get_supported_types()
Returns

Character vector of supported plot types


Ggplot2ProcessorFactory$get_system_name()

Get the system name

Usage
Ggplot2ProcessorFactory$get_system_name()
Returns

System name string


Ggplot2ProcessorFactory$is_processor_available()

Check if a specific processor class is available

Usage
Ggplot2ProcessorFactory$is_processor_available(processor_class_name)
Arguments
processor_class_name

Name of the processor class

Returns

TRUE if available, FALSE otherwise


Ggplot2ProcessorFactory$get_available_processors()

Get available processor classes

Enumerated from create_processor() rather than listed here, so the answer cannot drift away from what the factory actually dispatches to (#200).

Usage
Ggplot2ProcessorFactory$get_available_processors()
Returns

Character vector of available processor class names


Ggplot2ProcessorFactory$try_create_processor()

Create a processor with error handling

Usage
Ggplot2ProcessorFactory$try_create_processor(plot_type, plot_object)
Arguments
plot_type

The type of plot

plot_object

The plot object

Returns

Processor instance or NULL if creation fails


Ggplot2ProcessorFactory$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2ProcessorFactory$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


ROC Curve Layer Processor

Description

Reads a receiver operating characteristic curve – a classifier's true positive rate against its false positive rate, one point per decision threshold, one curve per classifier – as the roc trace.

Structurally a multi-series line, so the line processor does the work: the series split, the x recovery through the scale, the selectors per drawn polyline. What this adds is what the trace reads that a line does not.

Emitted with type = "roc", which the core has read since maidr 4.9.0.

Super classes

LayerProcessor -> Ggplot2LineLayerProcessor -> Ggplot2RocLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2RocLayerProcessor$process()

Process the ROC layer

Usage
Ggplot2RocLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

List with data, selectors, title, axes and type


Ggplot2RocLayerProcessor$as_rates()

Hand the rates back as numbers, inverting x when asked

The line processor stringifies x on the way out, because a line's x may be a date or a category. A rate is neither, and the core does arithmetic on it.

Usage
Ggplot2RocLayerProcessor$as_rates(data, inverted = FALSE)
Arguments
data

The series list the line processor emitted

inverted

Whether x is specificity, to be read as 1 - x

Returns

The series list with numeric rates


Ggplot2RocLayerProcessor$attach_thresholds()

Attach each point's decision threshold, when the layer carries one

A threshold column survives the build only for a maidr_roc() layer, whose geom names the aesthetic. The line processor drops the rows whose y is NA and splits the rest by group in the group's own order, so the same filter and split here put the thresholds back beside the points they were scored at. A series whose count disagrees – a row the line processor dropped for a reason other than NA y – gets no thresholds rather than the wrong ones.

Usage
Ggplot2RocLayerProcessor$attach_thresholds(data, built, panel_id = NULL)
Arguments
data

The series list

built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

The series list, with threshold on each point that has one


Ggplot2RocLayerProcessor$attach_areas()

Attach the declared area to the first point of each curve

maidr_roc(auc = ) names one area per curve. A named vector is matched to the series by their names; an unnamed one is taken in series order when it has one entry per series. Anything else is left out rather than guessed, and the core measures the area from the points instead.

Usage
Ggplot2RocLayerProcessor$attach_areas(data, layer)
Arguments
data

The series list

layer

The layer being read

Returns

The series list, with auc on each curve's first point


Ggplot2RocLayerProcessor$rows_read()

The built rows the line processor emitted points for

The same panel filter and NA-y filter extract_data() applies, so that a column read off these rows lines up with the emitted points.

Usage
Ggplot2RocLayerProcessor$rows_read(built, panel_id = NULL)
Arguments
built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

The rows, or NULL when the layer built none


Ggplot2RocLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2RocLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Rug Layer Processor

Description

Reads geom_rug() as the observations it marks.

A rug draws one short tick per row against the edge of the panel. What it states is the raw data – which is exactly what the density curve or histogram it usually accompanies does not. Until #222 the layer reached Ggplot2UnknownLayerProcessor: a rug-only chart emitted one empty layer and a rug beside a scatter added an empty one a reader could land on and find nothing in.

Read as points, which is the reading py-maidr settled on for seaborn.rugplot (xability/py-maidr#250). length is one number for the whole layer, so a tick's length is decoration and only its position is data – the same argument the event-plot reading makes about its ticks. The coordinate across the tick is emitted as a constant rather than as the tick's own base, because that base is a fraction of the panel and would read as data at whatever scale the other axis happens to use.

One layer per axis

geom_rug() defaults to sides = "bl", so on a chart with both aesthetics mapped it marks both – and the built data carries both columns. So this emits up to two layers: the x observations and the y observations.

Not one per drawn grob. sides = "trbl" draws the same x observations at top and bottom, and emitting that twice would have a reader navigate the same numbers under two names. Measured on four rows:

sides="b"      GRID.segments.1  : x (n=4)
sides="l"      GRID.segments.41 : y (n=4)
sides="bl"     GRID.segments.78 : x (n=4)   GRID.segments.79 : y (n=4)
sides="trbl"   .116: x   .117: x   .118: y   .119: y

A side is drawn only where the matching aesthetic exists – sides = "bl" with only aes(x = v) gives one grob – so the layers follow the built data's columns rather than a parse of the sides string.

Super class

LayerProcessor -> Ggplot2RugLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2RugLayerProcessor$process()

Process the rug layer

Usage
Ggplot2RugLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for patchwork leaves and facets

Returns

A single layer, or a multi_layer result carrying two


Ggplot2RugLayerProcessor$layer_rows()

This layer's rows of the built data

Not LayerProcessor$get_layer_built_data(), and the difference is the point rather than an oversight. That method falls back to all panels' rows when the panel-scoped subset comes back empty, and a rug is a chart where an empty subset is real: a facet_grid() cell that no row falls in draws no ticks. Measured on a grid with two populated cells of two ticks each –

panel 1 -> layer_rows: 2   get_layer_built_data: 2
panel 2 -> layer_rows: 0   get_layer_built_data: 4
panel 3 -> layer_rows: 0   get_layer_built_data: 4
panel 4 -> layer_rows: 2   get_layer_built_data: 2

– so the inherited helper would have the two empty panels each announce all four observations, drawn in the other two. An empty subset is returned as it is, and process() reads it as the "no layer" it is.

Written down because the two look interchangeable and are not: swapping this for the inherited helper is a one-line simplification that reintroduces the bug silently. test-ggplot2-rug.R pins the panel that draws nothing.

Usage
Ggplot2RugLayerProcessor$layer_rows(built, panel_id = NULL)
Arguments
built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

A data frame, or NULL


Ggplot2RugLayerProcessor$marked_axes()

Which axes this rug actually marks

Both halves are needed, and measuring showed why. The built data's columns are not enough on their own: geom_rug(sides = "b") on aes(v, w) carries a y column and draws no left rug, and reading the columns alone emitted a y layer for observations the chart never marked – a whole layer invented out of an aesthetic that was mapped for the scatter underneath.

sides is not enough either, in the other direction: it defaults to "bl" on every rug, and one drawn over aes(x = v) alone has no y to mark. So a side counts only where both agree, which is exactly what the drawing does – measured, sides = "bl" gives two grobs with both aesthetics mapped and one with only x.

Usage
Ggplot2RugLayerProcessor$marked_axes(rows, layer)
Arguments
rows

This layer's rows of the built data

layer

This layer, for its sides

Returns

A character vector, a subset of c("x", "y"), in that order


Ggplot2RugLayerProcessor$axis_layer()

One axis' observations as a point layer

Usage
Ggplot2RugLayerProcessor$axis_layer(
  plot,
  layout,
  rows,
  axis,
  gt,
  panel_ctx,
  built = NULL,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

rows

This layer's rows of the built data

axis

"x" or "y"

gt

Gtable object

panel_ctx

Panel context for patchwork leaves and facets

built

Built plot data, for the axis bounds

panel_id

Panel ID for faceted plots (optional)

Returns

A layer list


Ggplot2RugLayerProcessor$axis_labels()

Name the axes, calling the strip the ticks sit in what it is

The axis carrying the observations keeps the chart's own label. The one across the ticks is renamed even where the caller labelled it: a rug under a density curve has a real "density" label on that axis, and every entry this layer emits sits at 0 rather than at any density. Both carry bounds as well, and that is what makes the layer reachable in grid mode – the only mode where a point layer renders braille at all. Measured against maidr's ScatterTrace: with the labels alone the braille state comes back empty, and a rug is then the one chart with no braille surface reachable by any keystroke. With them, four observations at 1, 2, 3 and 9 over a 0-10 axis give values [[2, 1, 0, 1]] – the observation count per cell, which is the clustering a rug is drawn to show and the one thing its audio cannot carry, every tick sitting at the same place on the axis pitch is mapped from (xability/maidr#1132).

The observation axis takes the chart's own bounds, through the same axis_grid_info() the point processor reads, and is declined on the same grounds. The axis across the ticks is supplied whole as 0 to 1 in one step: a rug is one row deep by construction, and a finer step buys a second row of zeroes – measured, tickStep 0.5 gives ⁠[[2, 1, 0, 1], [0, 0, 0, 0]]⁠.

Additive only: grid mode is entered deliberately, and the ordinary reading is untouched.

Usage
Ggplot2RugLayerProcessor$axis_labels(
  layout,
  axis,
  built = NULL,
  panel_id = NULL
)
Arguments
layout

Layout information

axis

"x" or "y"

built

Built plot data, for the observation axis' bounds

panel_id

Panel ID for faceted plots (optional)

Returns

An axes list


Ggplot2RugLayerProcessor$generate_selectors()

Address each tick by the element it was drawn as

geom_rug() draws one segmentsGrob per side, and gridSVG exports that as one element per segment carrying an id of the form ⁠<grob>.1.<n>⁠ – the shape #194 measured for geom_segment(), whose GRID.segments.38.1.1 through .4 follow built-data order.

The grob cannot be found by name: ggplot2 gives a rug layer no geom prefix, so it arrives as grid's automatic GRID.segments.N, whose number is a global counter and not stable between sessions. It is located by class and by position instead, and read off the gtable being exported rather than reconstructed.

Where a rug draws one axis twice – sides = "tb", or "trbl" – the first grob for that axis is addressed. The two are copies of one observation, so highlighting one of them is partial; highlighting neither is worse, and #145 settled that a selector list which does not match the point count is withdrawn wholesale rather than applied in part. An empty list is returned when the grob cannot be resolved, for that same reason: a guess at its name is worse than no highlighting.

Usage
Ggplot2RugLayerProcessor$generate_selectors(
  plot,
  gt,
  axis,
  count,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object

axis

"x" or "y"

count

How many ticks this layer emits

panel_ctx

Panel context for patchwork leaves and facets

Returns

A list of CSS selectors, one per tick


Ggplot2RugLayerProcessor$find_segments_name()

The grob holding this layer's ticks for one axis

geom_rug() wraps its grobs in a gTree of its own – measured, a sides = "bl" rug gives GRID.gTree.3 holding GRID.segments.1 and GRID.segments.2, and two rug layers give two such trees in layer order. So one layer's grobs arrive as a block already, and there is nothing to slice.

That wrapping is also what tells a rug from its neighbours. geom_segment() draws a bare segments grob directly under the panel:

rug + segment    GRID.segments.117            <- the segment layer
                 GRID.gTree.120
                   GRID.segments.118          <- the rug, x
                   GRID.segments.119          <- the rug, y

So a candidate is a directly-held gTree with grid's automatic name whose children are every one a segments grob, and the nth of those belongs to the nth rug layer. Matching on the name is necessary as well as the class: a layer ggplot2 does name arrives with its geom's prefix, and one of those is not a rug whatever it holds.

Which axis a grob stands on it answers itself: a tick standing on x is held constant in y, so y0 is a single recycled value while x0 carries one entry per observation. The same property py-maidr's read_rug uses, and it needs no reference to sides.

Where a rug draws one axis twice – sides = "tb", or "trbl" – the first grob for that axis wins. The two are copies of one observation, so highlighting one of them is partial; highlighting neither is worse, and #145 settled that a selector list which does not match the point count is withdrawn wholesale rather than in part.

Usage
Ggplot2RugLayerProcessor$find_segments_name(plot, gt, axis, panel_ctx = NULL)
Arguments
plot

The ggplot2 object

gt

Gtable object

axis

"x" or "y"

panel_ctx

Panel context for patchwork leaves and facets

Returns

The grob name, or NULL when it cannot be resolved


Ggplot2RugLayerProcessor$position_among_rugs()

This layer's place among the plot's rug layers

Counted among its own kind, so a second geom_rug() reaches its own grobs rather than the first one's – the rule Ggplot2GanttLayerProcessor$find_segments_name() applies, for the same reason.

Usage
Ggplot2RugLayerProcessor$position_among_rugs(plot)
Arguments
plot

The ggplot2 object

Returns

The 1-based position, or NULL when the layer cannot be found


Ggplot2RugLayerProcessor$rug_trees()

The panel's rug wrappers, in drawing order

Usage
Ggplot2RugLayerProcessor$rug_trees(gt, panel_ctx = NULL)
Arguments
gt

Gtable object

panel_ctx

Panel context for patchwork leaves and facets

Returns

A list of gTrees


Ggplot2RugLayerProcessor$wraps_a_rug()

Whether a grob is one rug layer's wrapper

Usage
Ggplot2RugLayerProcessor$wraps_a_rug(node)
Arguments
node

A grob

Details

The name test is a guard rather than a live branch, and is kept as one deliberately. Measured across every geom that draws segments – boxplot, violin, errorbar, crossbar, pointrange, linerange, step, a reference line – none produces a gTree whose children are all segments, so today the class test alone decides and dropping the name test changes no reading. test-ggplot2-rug.R pins that measurement, so the ggplot2 release that ends it turns a test red rather than leaving this silently load-bearing.

Returns

TRUE when it is a GRID.gTree of nothing but segments


Ggplot2RugLayerProcessor$axis_of()

Which axis one segments grob's ticks stand on

Usage
Ggplot2RugLayerProcessor$axis_of(grob)
Arguments
grob

A segments grob

Returns

"x", "y", or NA when it says neither


Ggplot2RugLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2RugLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Smooth Layer Processor

Description

Processes smooth plot layers with complete logic included

Super class

LayerProcessor -> Ggplot2SmoothLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2SmoothLayerProcessor$process()

Process the layer: read its curves, selectors and axes from the built plot

Usage
Ggplot2SmoothLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List describing the layer for the MAIDR payload


Ggplot2SmoothLayerProcessor$layer_computes_interval()

Whether this layer's ymin/ymax are an interval at all.

Asked of the stat, never of the columns, because the columns lie. This processor serves StatSmooth and StatDensity alike, and both emit ymin/ymax while meaning different things: StatSmooth computes the confidence bounds around the fit, but StatDensity sets ymin = 0 and ymax = density – the extent of the fill, not an uncertainty. Measured on a three-group geom_density(): all 1536 rows carry finite bounds running from zero to the curve.

Reading those as an interval would announce every density curve as having a confidence band from zero, which is a claim about the data rather than a missing feature – the worse of the two failures, and the one this guard exists to prevent.

Usage
Ggplot2SmoothLayerProcessor$layer_computes_interval(plot)
Arguments
plot

The ggplot2 object

Returns

TRUE when the layer's stat computes an interval


Ggplot2SmoothLayerProcessor$group_aes()

Grouping aesthetics that split this layer into curves.

geom_smooth() and geom_density() both render a fill, so aes(fill = g) splits them into one curve per group just as aes(colour = g) does. The line processor probes colour only, because a line has no fill and reading one from an unrelated layer's mapping would invent a legend the plot never draws.

Ggplot2Adapter types a layer as smooth for GeomSmooth or for any layer whose stat is StatDensity. A default geom_area() uses StatAlign and so never arrives here, but geom_area(stat = "density") does, and splits per group like the others.

Usage
Ggplot2SmoothLayerProcessor$group_aes()
Returns

List of aesthetic-name vectors, in precedence order


Ggplot2SmoothLayerProcessor$attach_group_axis()

Add the legend title as the z axis label when the layer is split into per-group curves.

Shared with the line layer processor via attach_series_group_axis(); see R/series_group_utils.R.

Usage
Ggplot2SmoothLayerProcessor$attach_group_axis(plot, built, data, axes)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

data

The extracted layer data

axes

Axes built so far

Returns

The axes list, with z added when the layer is grouped


Ggplot2SmoothLayerProcessor$resolve_target_layer()

Resolve which layer of the plot this processor describes.

Prefers this processor's OWN layer: picking the first line-like layer would extract another layer's data in multi-layer plots (e.g. geom_line + geom_smooth).

Usage
Ggplot2SmoothLayerProcessor$resolve_target_layer(plot)
Arguments
plot

The ggplot2 object

Returns

Index into plot$layers


Ggplot2SmoothLayerProcessor$layer_built_data()

Built data for this layer, restricted to one facet panel.

Usage
Ggplot2SmoothLayerProcessor$layer_built_data(
  plot,
  built = NULL,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

A data frame of built rows


Ggplot2SmoothLayerProcessor$series_group_ids()

Distinct group ids when the layer draws more than one curve.

Usage
Ggplot2SmoothLayerProcessor$series_group_ids(built_data)
Arguments
built_data

Built rows for this layer

Returns

Sorted group ids, or an empty vector for a single-curve layer


Ggplot2SmoothLayerProcessor$extract_data()

Extract one series per drawn curve.

ggplot2 draws a mapped smooth as one curve per group, so the payload has to be split the same way: concatenating the groups into a single series would walk a reader off the end of one curve into the start of the next with nothing announced in between.

Usage
Ggplot2SmoothLayerProcessor$extract_data(plot, built = NULL, panel_id = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List of series, each a list of points


Ggplot2SmoothLayerProcessor$curve_points()

Turn built rows into MAIDR points.

Usage
Ggplot2SmoothLayerProcessor$curve_points(rows, z = NULL)
Arguments
rows

Built rows for one curve

z

Series name, or NULL for a single-curve layer

Returns

List of points


Ggplot2SmoothLayerProcessor$attach_interval()

Keep this layer's ymin/ymax only when they are an uncertainty, and put them in the space the reader is shown.

The columns are dropped rather than ignored, so curve_points() can key on their presence instead of re-asking the stat per sample.

Usage
Ggplot2SmoothLayerProcessor$attach_interval(
  built_data,
  plot,
  built = NULL,
  panel_id = NULL
)
Arguments
built_data

This layer's built rows

plot

The ggplot2 object

built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

built_data, with the interval untransformed or removed


Ggplot2SmoothLayerProcessor$generate_selectors()

The selector for the polyline this layer drew, or none when it cannot be found (see the note above)

Usage
Ggplot2SmoothLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  panel_ctx = NULL,
  built = NULL,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

List of selectors


Ggplot2SmoothLayerProcessor$own_curve_grob()

The polyline grob ggplot2 drew for THIS layer's curve.

A curve layer leaves its grob in one of two shapes, and which one decides how it can be found again:

Usage
Ggplot2SmoothLayerProcessor$own_curve_grob(plot, gt, panel_ctx = NULL)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for panel-scoped selector generation

Returns

The grob, or NULL when this layer's curve cannot be identified


Ggplot2SmoothLayerProcessor$series_group_count()

Number of curves this layer draws in the given panel.

Never throws: selector generation has to degrade to the single-curve path for inputs extract_data() would reject.

Usage
Ggplot2SmoothLayerProcessor$series_group_count(
  plot,
  built = NULL,
  panel_id = NULL
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

Returns

Number of groups, or 0 when the layer draws a single curve


Ggplot2SmoothLayerProcessor$grouped_curve_selectors()

One selector per curve for a layer split into groups.

ggplot2 draws a grouped smooth one group at a time, so the layer's grob tree holds an equal run of children per group: a bare polyline for se = FALSE, a ribbon gTree followed by a polyline for se = TRUE, and a ribbon gTree alone for geom_density(). Chunking the children by group and taking the last polyline in each chunk applies the same "the curve is the last polyline drawn" rule the single-curve path uses, once per group instead of once per layer.

Usage
Ggplot2SmoothLayerProcessor$grouped_curve_selectors(
  plot,
  gt,
  panel_ctx,
  n_series
)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for panel-scoped selector generation

n_series

Number of curves the layer draws

Returns

List of selectors, or NULL when the grob tree does not line up


Ggplot2SmoothLayerProcessor$polyline_grob_names()

Names of the curve polyline grobs inside a grob, in draw order. Panel grid lines are excluded: they are named after the theme element (panel.grid.major.x..polyline.N), not GRID.polyline.N.

Usage
Ggplot2SmoothLayerProcessor$polyline_grob_names(grob)
Arguments
grob

A grob to walk

Returns

Character vector of grob names


Ggplot2SmoothLayerProcessor$find_layer_grob_tree()

Find the grob tree ggplot2 drew for this layer.

Defers to the base walk and supplies the one thing this processor does differently: which layer to look for. resolve_target_layer() may answer a layer other than this processor's own, which is why the target cannot simply be the layer index the base class would use.

Usage
Ggplot2SmoothLayerProcessor$find_layer_grob_tree(
  plot,
  gt,
  panel_ctx = NULL,
  target = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for panel-scoped selector generation

target

Index of the layer to find; resolved when absent

Returns

The matching grob, or NULL


Ggplot2SmoothLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2SmoothLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Stacked Bar Layer Processor

Description

Processes stacked bar plot layers with complete logic included

Super class

LayerProcessor -> Ggplot2StackedBarProcessor

Methods

Public methods

Inherited methods

Ggplot2StackedBarProcessor$process()

Process the layer: read its series, selectors and the fill legend title from the built plot

Usage
Ggplot2StackedBarProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List describing the layer for the MAIDR payload


Ggplot2StackedBarProcessor$needs_reordering()

Whether the plot data must be reordered before drawing, so the emitted order matches the drawn rects

Usage
Ggplot2StackedBarProcessor$needs_reordering()
Returns

TRUE


Ggplot2StackedBarProcessor$reorder_layer_data()

Reorder the plot data by category and fill so the emitted rows match the drawn rects

Usage
Ggplot2StackedBarProcessor$reorder_layer_data(data, plot)
Arguments
data

The data frame ggplot2 will draw from

plot

The ggplot2 object

Returns

The reordered data frame


Ggplot2StackedBarProcessor$extract_plot_columns()

The column names the plot maps to x, y and fill

Usage
Ggplot2StackedBarProcessor$extract_plot_columns(plot)
Arguments
plot

The ggplot2 object

Returns

List with category_col, value_col and fill_col


Ggplot2StackedBarProcessor$extract_data()

One series per fill level, restricted to the panel's rows under faceting

Usage
Ggplot2StackedBarProcessor$extract_data(
  plot,
  built = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List of series


Ggplot2StackedBarProcessor$generate_selectors()

One flat selector matching every rect in the layer, which is the contract the frontend expects (see the note above)

Usage
Ggplot2StackedBarProcessor$generate_selectors(
  plot,
  gt = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List holding one selector


Ggplot2StackedBarProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2StackedBarProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


ggplot2 Step Layer Processor

Description

Processes geom_step() layers. A step chart is piecewise constant: the value is held across an interval and then jumps, rather than being interpolated between samples the way a line implies. The canonical case is a hypnogram – an ordinal sleep stage (Awake / REM / N1 / N2 / N3) against time.

Everything about extracting x/y and locating the rendered polyline is the same as for a line, so this class inherits Ggplot2LineLayerProcessor and adds only what a step layer has that a line layer does not:

One data point is emitted per data sample, never one per stairstep vertex. ggplot2 expands the stairsteps inside GeomStep$draw_panel(), so the rendered polyline carries ⁠2n - 1⁠ vertices (hv / vh) or ⁠2n⁠ (mid) for n samples; the MAIDR frontend's StepTrace maps those vertices back onto the samples. Emitting vertex-level data to "match" the polyline would double every level and misreport transitions and run lengths.

Super classes

LayerProcessor -> Ggplot2LineLayerProcessor -> Ggplot2StepLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2StepLayerProcessor$process()

Process the step layer.

Usage
Ggplot2StepLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selectors (optional)

Returns

List with data, selectors, title, axes, type and stepDirection


Ggplot2StepLayerProcessor$in_drawn_order()

Put this layer's built rows into the order they are drawn in, dropping any row that is not a point.

Both halves come from one fact: GeomStep does not draw the rows it is handed. GeomStep$draw_panel() calls ggplot2:::stairstep(), whose first act is data[order(data$x), ] – so the drawn staircase is the sorted rows, whatever order the stat returned them in. Sorting here recovers what is on screen rather than imposing a new order, and an already-sorted layer – which is every geom_step() written by hand – is unchanged.

StatEcdf is why this is needed at all. It returns its rows in input order and pads them with -Inf / Inf for the two ends of the staircase. Measured on n = 20: 22 rows, two of them infinite, unsorted. Those two rows are not observations – there is no x to announce for them – and an infinity in the payload is worse than a dropped point in both bindings: jsonlite writes it as the string "-Inf", and json.dumps on the Python side writes a bare -Infinity that JSON.parse rejects outright (xability/py-maidr#427).

Ordered within PANEL and group, not globally, because draw_panel() is called once per panel per group – a grouped ECDF is several staircases, and a global sort would interleave them into one series that walks backwards at every seam.

The filter asks about x alone, and deliberately. A row with a real x and a missing y is a different thing – it has a position and no reading – and the line processor this class inherits already decides those, for its own reason: it keeps the rows ggplot2 draws through, dropping leading and trailing NA-y rows and announcing an interior one as missing (line_drawn_span()). Repeating that here would be a second filter with a second rationale over the same rows. Raised in review on #169; the Python binding draws the same x-only line, and for the same reason (xability/py-maidr#430).

Left alone when x is not numeric: the finiteness test is meaningless there and is.finite() on a character vector is FALSE throughout, which would delete every row.

Usage
Ggplot2StepLayerProcessor$in_drawn_order(built)
Arguments
built

Built plot data

Returns

built, with this layer's frame reordered and filtered


Ggplot2StepLayerProcessor$extract_step_direction()

Read the step convention this layer was drawn with.

geom_step(direction = ) is a formal of GeomStep$draw_panel(), so ggplot2 files it under layer$geom_params$direction rather than layer$aes_params or the layer's mapping. The three accepted values ("hv", "vh", "mid") are exactly MAIDR's, so they pass through unchanged. "hv" is both ggplot2's and MAIDR's default.

Usage
Ggplot2StepLayerProcessor$extract_step_direction(plot)
Arguments
plot

The ggplot2 object

Returns

One of "hv", "vh", "mid" (defaulting to "hv"), or NULL when the layer cannot be located at all.


Ggplot2StepLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2StepLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Unknown Layer Processor

Description

Handles unsupported layer types gracefully by returning empty data

Super class

LayerProcessor -> Ggplot2UnknownLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2UnknownLayerProcessor$process()

Describe a layer nothing is known about

Usage
Ggplot2UnknownLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List with no data and no selectors


Ggplot2UnknownLayerProcessor$extract_data()

Nothing: an unknown layer has no data to announce

Usage
Ggplot2UnknownLayerProcessor$extract_data(plot, built = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

Returns

Empty list


Ggplot2UnknownLayerProcessor$generate_selectors()

Nothing: an unknown layer has no elements to address

Usage
Ggplot2UnknownLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

Empty list


Ggplot2UnknownLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2UnknownLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Violin Layer Processor

Description

Processes violin layers (geom_violin) to extract density curve (KDE) data and box-summary statistics, producing two maidr layers: violin_kde and violin_box.

The processor injects a thin geom_boxplot(width = 0.1) into the plot before rendering so that the SVG contains visible box elements whose CSS selectors can drive the violin_box highlight in the maidr frontend.

Super class

LayerProcessor -> Ggplot2ViolinLayerProcessor

Methods

Public methods

Inherited methods

Ggplot2ViolinLayerProcessor$needs_augmentation()

Violin needs to inject a boxplot layer

Usage
Ggplot2ViolinLayerProcessor$needs_augmentation()

Ggplot2ViolinLayerProcessor$augment_plot()

Inject geom_boxplot into the plot for visual box + selectors

Usage
Ggplot2ViolinLayerProcessor$augment_plot(plot)
Arguments
plot

ggplot2 object

Returns

Augmented ggplot2 object with boxplot layer added


Ggplot2ViolinLayerProcessor$process()

Process the violin layer

Returns a list with multi_layer = TRUE and two maidr layers: violin_box (with BoxSelector objects) and violin_kde.

Usage
Ggplot2ViolinLayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object (already augmented with boxplot)

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_id

Panel ID for faceted plots (optional)

panel_ctx

Panel context for faceted plots (optional)

Returns

List with multi_layer flag and layers, or NULL for facet panels


Ggplot2ViolinLayerProcessor$extract_box_data()

Extract box-summary statistics per violin group

Computes min, Q1, median, Q3, max from the original data (since geom_violin only stores the KDE curve, not quartiles).

Usage
Ggplot2ViolinLayerProcessor$extract_box_data(plot, built)
Arguments
plot

The ggplot2 object

built

Built plot data

Returns

List of BoxPoint objects (one per violin)


Ggplot2ViolinLayerProcessor$extract_kde_data()

Extract KDE density-curve data per violin group

Uses ggplot2's built violin data (violinwidth, x, y, width columns) to compute left/right violin edges, applies RDP simplification to ~30 points per violin, and includes the width field needed by the maidr frontend. The svg_x/svg_y coordinates are injected later by create_enhanced_svg() after the grid device is drawn.

Usage
Ggplot2ViolinLayerProcessor$extract_kde_data(plot, built, max_kde_points = 30L)
Arguments
plot

The ggplot2 object

built

Built plot data

max_kde_points

Maximum number of output points per violin (default 30)

Returns

List of lists (ViolinKdePoint[][])


Ggplot2ViolinLayerProcessor$simplify_violin_kde()

Simplify a single violin's KDE curve using RDP

Uses ggplot2's built violin data columns (y, violinwidth, x, width) to compute the left/right edges, then applies RDP simplification.

Usage
Ggplot2ViolinLayerProcessor$simplify_violin_kde(
  rows,
  cat_label,
  is_horizontal,
  max_points = 30L
)
Arguments
rows

data.frame of built violin data for one group

cat_label

Character label for this violin category

is_horizontal

Logical, TRUE for horizontal violins

max_points

Maximum number of output points

Returns

List of ViolinKdePoint dicts with data_left_x/data_right_x/data_y


Ggplot2ViolinLayerProcessor$extract_data()

Not used directly - required by base class interface

Usage
Ggplot2ViolinLayerProcessor$extract_data(plot, built = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)


Ggplot2ViolinLayerProcessor$generate_selectors()

Generate CSS selectors for violin polygons (for violin_kde layer)

Usage
Ggplot2ViolinLayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object

grob_id

Grob ID (for faceted plots)

panel_ctx

Panel context (for faceted plots)

Returns

List of CSS selector strings (one per violin)


Ggplot2ViolinLayerProcessor$generate_box_selectors()

Generate BoxSelector objects for the injected boxplot grobs

Walks the gtable to find geom_boxplot grobs and produces a BoxSelector list (one per violin) with CSS selectors for min, iq, q2, max, lowerOutliers, upperOutliers.

Usage
Ggplot2ViolinLayerProcessor$generate_box_selectors(
  plot,
  gt,
  built,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object (augmented with boxplot)

gt

Gtable object

built

Built plot data

panel_ctx

Panel context (for patchwork leaves)

Returns

List of BoxSelector objects


Ggplot2ViolinLayerProcessor$determine_orientation()

Determine orientation from built data

Usage
Ggplot2ViolinLayerProcessor$determine_orientation(built)
Arguments
built

Built plot data (optional)


Ggplot2ViolinLayerProcessor$get_effective_mapping()

The violin layer's mapping merged with the plot's

Built in ggplot2's own order – the layer's own aesthetics first, then whatever only the plot maps – because the order is not cosmetic. ggplot2 numbers group from the interaction of a layer's discrete columns taken in the order the mapping produced them, so a mapping assembled the other way round gives the injected box different group ids than the violin, and every lookup keyed on group then crosses the two layers.

Usage
Ggplot2ViolinLayerProcessor$get_effective_mapping(plot)
Arguments
plot

The ggplot2 object

Returns

Named list of quosures, one per mapped aesthetic


Ggplot2ViolinLayerProcessor$discrete_axis_labels()

Break labels of whichever panel axis holds the categories

The categorical axis is the discrete one, which is not always the axis the data is keyed on: coord_flip() leaves the data x-major while moving the category labels to the y axis, so reading the axis off flipped_aes alone returns the value axis' breaks and every violin is labelled with a number.

Usage
Ggplot2ViolinLayerProcessor$discrete_axis_labels(built)
Arguments
built

Built plot data

Returns

Character vector of labels, or NULL when neither axis is discrete (a continuous category axis carries its value directly)


Ggplot2ViolinLayerProcessor$fill_levels_by_colour()

Map each mapped fill colour back to the level it came from

Dodging splits one category into several violins that differ only by fill, and they all round to the same category position. Without the level, they are announced under one repeated name and cannot be told apart.

Usage
Ggplot2ViolinLayerProcessor$fill_levels_by_colour(built)
Arguments
built

Built plot data

Returns

Named character vector (colour -> level), or NULL when the plot has no fill scale


Ggplot2ViolinLayerProcessor$group_labels()

Announceable label for each drawn violin

Usage
Ggplot2ViolinLayerProcessor$group_labels(
  built,
  layer_data,
  groups,
  is_horizontal
)
Arguments
built

Built plot data

layer_data

Built data for the violin layer

groups

The layer's group ids, in emission order

is_horizontal

Whether the value axis is x

Returns

Character vector of labels, one per group


Ggplot2ViolinLayerProcessor$boxplot_stats()

Box statistics ggplot2 itself computed for each group

The processor injects a geom_boxplot() so the SVG has box elements to highlight; that layer's stat_boxplot output is also the authoritative source for the numbers to announce. Reading it keyed by group avoids re-deriving quartiles from the original data via a rounded axis position and a string match on the break label – a round trip that silently mislabels dodged violins and finds nothing at all under coord_flip().

Usage
Ggplot2ViolinLayerProcessor$boxplot_stats(plot, built)
Arguments
plot

The ggplot2 object

built

Built plot data

Returns

data.frame of the boxplot layer's built data, or NULL


Ggplot2ViolinLayerProcessor$find_boxplot_layer_index()

Find the boxplot layer index in the augmented plot

Usage
Ggplot2ViolinLayerProcessor$find_boxplot_layer_index(plot)
Arguments
plot

The ggplot2 object

Returns

Integer index of the boxplot layer, or NULL


Ggplot2ViolinLayerProcessor$find_panel_grob()

Find the panel grob this layer draws into

Usage
Ggplot2ViolinLayerProcessor$find_panel_grob(gt, panel_ctx = NULL)
Arguments
gt

Gtable object

panel_ctx

Panel context for patchwork leaves; NULL for a single plot, where the panel is the cell literally named "panel"

Returns

The panel gTree, or NULL when it cannot be resolved


Ggplot2ViolinLayerProcessor$find_grob_ids()

Recursively find all grob IDs matching a pattern

Usage
Ggplot2ViolinLayerProcessor$find_grob_ids(grob, pattern)
Arguments
grob

Grob tree to search

pattern

Regular expression matched against grob names

Returns

Character vector of unique matching grob names


Ggplot2ViolinLayerProcessor$find_direct_children()

Find direct children of a named parent matching a pattern

Usage
Ggplot2ViolinLayerProcessor$find_direct_children(grob, parent_id, pattern)
Arguments
grob

Grob tree to search

parent_id

Name of the parent grob

pattern

Regular expression matched against child names

Returns

Character vector of matching child names


Ggplot2ViolinLayerProcessor$find_grob_by_id()

Find a grob by its name (recursive)

Usage
Ggplot2ViolinLayerProcessor$find_grob_by_id(grob, target_id)
Arguments
grob

Grob tree to search

target_id

Name of the grob to find

Returns

The matching grob, or NULL


Ggplot2ViolinLayerProcessor$find_desc_by_pattern()

Find the first descendant matching a pattern under a named parent

Usage
Ggplot2ViolinLayerProcessor$find_desc_by_pattern(grob, parent_id, pattern)
Arguments
grob

Grob tree to search

parent_id

Name of the parent grob

pattern

Regular expression matched against descendant names

Returns

The first matching name, or NULL


Ggplot2ViolinLayerProcessor$find_all_desc_by_pattern()

Find all descendants matching a pattern under a named parent

Usage
Ggplot2ViolinLayerProcessor$find_all_desc_by_pattern(grob, parent_id, pattern)
Arguments
grob

Grob tree to search

parent_id

Name of the parent grob

pattern

Regular expression matched against descendant names

Returns

Character vector of matching descendant names


Ggplot2ViolinLayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
Ggplot2ViolinLayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


The aesthetics that split an uncertainty layer into series

Description

Probed in order, and each entry holds the spelling variants of ONE aesthetic, which is the contract resolve_series_group_mapping() documents. colour first because it is what a dodged interval chart is almost always split by; group last, because an author who writes aes(group = g) and nothing else has still said what the series are.

Usage

INTERVAL_GROUP_AES

Position classes that displace a point from where its data puts it

Description

PositionJitterdodge does not inherit from PositionJitter – both descend straight from Position – so it has to be named rather than caught by inheritance. Its dodge is removed along with its jitter, which is correct for the same reason: the offset that separates hue groups is drawn geometry, and the group itself is carried as an aesthetic, so nothing is lost by putting the point back on its category.

Usage

JITTER_POSITION_CLASSES

Abstract Layer Processor Interface

Description

This is the abstract base class for all layer processors. It defines the interface that all layer processors must implement.

Public fields

layer_info

Information about the layer

Methods

Public methods


LayerProcessor$new()

Initialize the layer processor

Usage
LayerProcessor$new(layer_info)
Arguments
layer_info

Information about the layer


LayerProcessor$process()

Process the layer (MUST be implemented by subclasses)

Usage
LayerProcessor$process(
  plot,
  layout,
  built = NULL,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

layout

Layout information

built

Built plot data (optional)

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List with data and selectors


LayerProcessor$extract_data()

Extract data from the layer (MUST be implemented by subclasses)

Usage
LayerProcessor$extract_data(plot, built = NULL)
Arguments
plot

The ggplot2 object

built

Built plot data (optional)

Returns

Extracted data


LayerProcessor$generate_selectors()

Generate selectors for the layer (MUST be implemented by subclasses)

Usage
LayerProcessor$generate_selectors(
  plot,
  gt = NULL,
  grob_id = NULL,
  panel_ctx = NULL
)
Arguments
plot

The ggplot2 object

gt

Gtable object (optional)

grob_id

Grob ID for faceted plots (optional)

panel_ctx

Panel context for panel-scoped selector generation (optional)

Returns

List of selectors


LayerProcessor$get_layer_built_data()

Read this layer's rows out of the built plot.

Scoped to one panel when asked. A faceted plot puts every panel's rows in one frame, so a layer that took all of them would describe the whole facet grid as one series.

Usage
LayerProcessor$get_layer_built_data(built, panel_id = NULL)
Arguments
built

Built plot data

panel_id

Panel ID for faceted plots (optional)

Returns

A data frame of computed aesthetics, or NULL


LayerProcessor$resolve_panel_index()

Resolve which entry of built$layout$panel_params describes a facet panel.

Panel parameters are stored in the row order of built$layout$layout, so panel n is entry n. An unfaceted plot (or an unusable id) resolves to the only panel there is.

Shared: the line processor asked this first and the gantt processor asks it to name its lanes, and two readings disagreeing about which panel they are describing is not a difference worth having.

Usage
LayerProcessor$resolve_panel_index(built, panel_id = NULL)
Arguments
built

Built plot data

panel_id

Panel id for faceted plots (optional)

Returns

Integer index guaranteed to be in range


LayerProcessor$get_own_layer()

Resolve the plot layer this processor was built for.

Every processor knows its index and several need the layer itself – for its geom, its position or its own data – so the lookup lives here rather than being copied into each. Answers NULL rather than erroring when the index does not resolve, since a caller that cannot find its layer has a reading to fall back on and no crash to justify.

Usage
LayerProcessor$get_own_layer(plot)
Arguments
plot

The ggplot2 object

Returns

The layer, or NULL when the index does not resolve


LayerProcessor$find_layer_grob_tree()

Find the grob tree ggplot2 drew for this layer.

ggplot2 names a layer's grob after its geom (geom_smooth.gTree.5), so the tree is located by that prefix and, when the plot repeats the geom, by this layer's position among the layers sharing it. Scoping to the layer's own tree is what keeps a sibling layer's grobs out of whatever the caller counts inside it.

Lives here rather than on one processor because two now need it and the walk is the same walk – the argument that moved get_own_layer() and get_layer_built_data() here before it. What differs between callers is only which layer they are looking for, and that is the target argument; the smooth processor passes a resolved index because it may describe a layer other than its own, and everything else takes the default.

Usage
LayerProcessor$find_layer_grob_tree(plot, gt, panel_ctx = NULL, target = NULL)
Arguments
plot

The ggplot2 object

gt

Gtable object

panel_ctx

Panel context for panel-scoped selector generation

target

Index of the layer to find; defaults to this one's

Returns

The matching grob, or NULL


LayerProcessor$find_layer_polyline_grob()

The polyline grob ggplot2 drew for THIS layer.

Shared rather than owned by the line processor: GeomPath, GeomStep and GeomContour all draw through a bare polylineGrob and so all land in the same auto-named candidate list, which is exactly why one answer to "which of them is mine" has to serve all three.

Usage
LayerProcessor$find_layer_polyline_grob(plot, panel_grob, target = NULL)
Arguments
plot

The ggplot2 object

panel_grob

The panel's grob tree

target

Index of the layer to find; defaults to this one's

Returns

The matching grob, or NULL


LayerProcessor$layer_polyline_grobs()

Panel polylines that a line layer could have drawn.

GeomPath$draw_panel() returns a bare polylineGrob, so a line layer's grob carries grid's auto-generated GRID.polyline.N name with no geom prefix to match on – only its draw-order position identifies it. Layers that DO name their grob tree after their geom (geom_smooth.gTree.N) are skipped whole via geom_grob_prefix(), the same helper the smooth processor uses to scope itself to its own tree; without that, the smooth's three curves are counted as line-layer polylines and shift every position by three. Panel grid lines are named after the theme element (panel.grid.major.x..polyline.N) and so never match.

Usage
LayerProcessor$layer_polyline_grobs(plot, panel_grob, target = NULL)
Arguments
plot

The ggplot2 object

panel_grob

The panel's grob tree

target

Index of the layer whose polylines are wanted

Returns

List of grobs in draw order


LayerProcessor$other_geom_grob_prefixes()

Grob-name prefixes belonging to the plot's OTHER geoms.

This layer's own prefix is excluded so that a second layer sharing the geom (two geom_line() calls) is still walked.

Usage
LayerProcessor$other_geom_grob_prefixes(plot, target = NULL)
Arguments
plot

The ggplot2 object

target

Index of the layer whose prefix is the own one

Returns

Character vector of prefixes, possibly empty


LayerProcessor$needs_reordering()

Check if this layer needs reordering (OPTIONAL - default: FALSE)

Usage
LayerProcessor$needs_reordering()
Returns

Logical indicating if reordering is needed


LayerProcessor$reorder_layer_data()

Reorder layer data (OPTIONAL - default: no-op)

Usage
LayerProcessor$reorder_layer_data(data, plot)
Arguments
data

data.frame effective for this layer

plot

full ggplot object (for mappings)

Returns

Reordered data


LayerProcessor$augment_plot()

Augment the plot before building (OPTIONAL - default: no-op)

Called by the orchestrator before ggplot_build/ggplotGrob. Allows a processor to inject additional geom layers (e.g., a boxplot inside a violin) so they appear in the SVG and can be targeted by selectors.

Usage
LayerProcessor$augment_plot(plot)
Arguments
plot

The ggplot2 object to augment

Returns

The (possibly augmented) ggplot2 object


LayerProcessor$needs_augmentation()

Check if this processor needs to augment the plot

Usage
LayerProcessor$needs_augmentation()
Returns

Logical


LayerProcessor$get_layer_index()

Get layer index

Usage
LayerProcessor$get_layer_index()
Returns

Layer index


LayerProcessor$is_flipped_layer()

Is this layer drawn with its category axis running up y?

ggplot(df, aes(y = g, x = n)) + geom_col() is the ordinary spelling of a horizontal bar chart. ggplot_build() marks such a layer flipped_aes and swaps which computed column holds what, so a processor that reads x as the category and y as the measure picks up exactly the wrong pair unless it asks first (#162, #184, #186).

Lives here rather than on one processor because three of them need the same answer, and because a processor that never asks it goes wrong silently: both columns hold plausible values.

coord_flip() is a different thing and answers FALSE here. It rotates the coordinate system and leaves flipped_aes alone, so the data layout is genuinely unflipped.

Usage
LayerProcessor$is_flipped_layer(built = NULL)
Arguments
built

A ggplot_build() result, or NULL when the caller has none – in which case the layer is treated as unflipped, since there is nothing to read the flag from.

Returns

TRUE when the category runs up the y axis.


LayerProcessor$unflip_columns()

Exchange a built layer's paired x and y columns

Swapping the pairs up front lets every branch downstream stay written against one arrangement rather than each learning to ask which way round it is – and a branch that forgot to ask would go wrong silently, since both columns hold plausible numbers.

Usage
LayerProcessor$unflip_columns(built_data)
Arguments
built_data

One layer's built data.

Returns

The same frame with its x and y pairs exchanged.


LayerProcessor$is_horizontal_call()

Was this base R call drawn with horiz = TRUE?

The base R counterpart of is_flipped_layer(): barplot() takes the orientation as an argument rather than marking the built layer, so the answer is read back off the captured call.

Usage
LayerProcessor$is_horizontal_call(layer_info = NULL)
Arguments
layer_info

The captured layer information, or NULL.

Returns

TRUE when the bars run across the page.


LayerProcessor$unflip_panel_params()

Exchange a panel's x and y scales

So the break labels a processor reads come from the axis the categories are actually drawn on. Both the scale objects and the flattened x.labels/y.labels of older ggplot2 are swapped, since readers fall back from one to the other.

Usage
LayerProcessor$unflip_panel_params(panel_params)
Arguments
panel_params

One entry of built$layout$panel_params.

Returns

The same list with its x and y entries exchanged.


LayerProcessor$swap_point_axes()

Put a horizontal layer's category and measure in the fields the bar grammar reads them from.

Processors emit ⁠x = category, y = measure⁠, which is the vertical arrangement. A horizontal bar is read the other way round: MAIDR takes x as the magnitude and y as the category when orientation is "horz", so the pair has to be exchanged on the way out. Left unexchanged, the core looks for a number and finds a category name – no magnitude to pitch, and an announcement that pairs the category axis with the measure and the measure axis with the category name (#184).

Only the bar family wants this, which is why it is a step a processor opts into rather than something the orchestrator applies to every horizontal layer. An error bar keeps its category in x at both orientations and lets orientation swap only which axis labels the reading is announced against; a box carries quantiles and has no axis assignment to exchange at all.

Usage
LayerProcessor$swap_point_axes(data_points)
Arguments
data_points

Points in ⁠x = category, y = measure⁠ form. A nested list – one series per element, as a grouped bar layer emits – is handled as well as a flat one.

Returns

The same points with x and y exchanged.


LayerProcessor$set_last_result()

Store the last processed result (used by orchestrator)

Usage
LayerProcessor$set_last_result(result)
Arguments
result

The result to store


LayerProcessor$get_last_result()

Get the last processed result

Usage
LayerProcessor$get_last_result()
Returns

The last result


LayerProcessor$extract_layer_axes()

Extract axes labels for this specific layer

Returns axes in the canonical per-axis object schema: list(x = list(label = "..."), y = list(label = "...")).

Bare strings, top-level format/min/max/tickStep/ fill/level, and any non-{x,y,z} keys are NOT permitted.

Usage
LayerProcessor$extract_layer_axes(plot, layout)
Arguments
plot

The ggplot object

layout

Global layout with fallback axes

Returns

Named list with x and y AxisConfig objects


LayerProcessor$clone()

The objects of this class are cloneable with this method.

Usage
LayerProcessor$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


How long a cached internet probe stays trusted, in seconds

Description

Five minutes: long enough that knitting a document with dozens of plots still probes at most once or twice, short enough that connectivity that changed under a long-lived session is picked up while the user is still looking at it.

Usage

MAIDR_INTERNET_CACHE_TTL

MAIDR JavaScript library version bundled with this package

Description

MAIDR JavaScript library version bundled with this package

Usage

MAIDR_VERSION

Plot System Registry

Description

Central registry for managing different plotting systems and their adapters. This registry allows dynamic registration and discovery of plotting systems and their associated adapters and processor factories.

Format

An R6 class

Methods

Public methods


PlotSystemRegistry$register_system()

Registered plotting systems

System adapters

Processor factories

Register a new plotting system

Usage
PlotSystemRegistry$register_system(system_name, adapter, processor_factory)
Arguments
system_name

Name of the plotting system (e.g., "ggplot2", "base_r")

adapter

Adapter instance for this system

processor_factory

Processor factory instance for this system


PlotSystemRegistry$detect_system()

Detect which system can handle a plot object

Usage
PlotSystemRegistry$detect_system(plot_object)
Arguments
plot_object

The plot object to check

Returns

System name if found, NULL otherwise


PlotSystemRegistry$get_adapter()

Get the adapter for a specific system

Usage
PlotSystemRegistry$get_adapter(system_name)
Arguments
system_name

Name of the system

Returns

Adapter instance


PlotSystemRegistry$get_processor_factory()

Get the processor factory for a specific system

Usage
PlotSystemRegistry$get_processor_factory(system_name)
Arguments
system_name

Name of the system

Returns

Processor factory instance


PlotSystemRegistry$get_adapter_for_plot()

Get the adapter for a plot object (auto-detect system)

Usage
PlotSystemRegistry$get_adapter_for_plot(plot_object)
Arguments
plot_object

The plot object

Returns

Adapter instance


PlotSystemRegistry$get_processor_factory_for_plot()

Get the processor factory for a plot object (auto-detect system)

Usage
PlotSystemRegistry$get_processor_factory_for_plot(plot_object)
Arguments
plot_object

The plot object

Returns

Processor factory instance


PlotSystemRegistry$list_systems()

List all registered systems

Usage
PlotSystemRegistry$list_systems()
Returns

Character vector of registered system names


PlotSystemRegistry$is_system_registered()

Check if a system is registered

Usage
PlotSystemRegistry$is_system_registered(system_name)
Arguments
system_name

Name of the system

Returns

TRUE if registered, FALSE otherwise


PlotSystemRegistry$unregister_system()

Unregister a system

Usage
PlotSystemRegistry$unregister_system(system_name)
Arguments
system_name

Name of the system to unregister


PlotSystemRegistry$clone()

The objects of this class are cloneable with this method.

Usage
PlotSystemRegistry$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Processor Factory Base Class

Description

Abstract base class for creating processors specific to different plotting systems. Each plotting system should have its own factory implementation that creates the appropriate processors for different plot types.

Format

An R6 class

Methods

Public methods


ProcessorFactory$create_processor()

Abstract method to create a processor for a specific plot type

Usage
ProcessorFactory$create_processor(plot_type, plot_object)
Arguments
plot_type

The type of plot (e.g., "bar", "line", "point")

plot_object

The plot object to process

Returns

Processor instance for the specified plot type


ProcessorFactory$get_supported_types()

Abstract method to get list of supported plot types

Usage
ProcessorFactory$get_supported_types()
Returns

Character vector of supported plot types


ProcessorFactory$supports_plot_type()

Check if a plot type is supported by this factory

Usage
ProcessorFactory$supports_plot_type(plot_type)
Arguments
plot_type

The plot type to check

Returns

TRUE if supported, FALSE otherwise


ProcessorFactory$get_system_name()

Get system name (should be overridden by subclasses)

Usage
ProcessorFactory$get_system_name()
Returns

System name string


ProcessorFactory$clone()

The objects of this class are cloneable with this method.

Usage
ProcessorFactory$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


What the axis across a rug's ticks is called

Description

Every entry a rug layer emits sits at the same place on that axis, so whatever the panel calls it measures nothing here. Named rather than left blank, and named the same thing py-maidr names it, so the two bindings announce one chart the same way.

Usage

RUG_AXIS_LABEL

Layer types whose frontend model reads selectors as ONE selector for every mark

Description

In maidr.js 4.x the shape of selectors is a contract, not a convenience. A plain string is handed to document.querySelectorAll() and the matches are aligned to the layer's points in document order. An ARRAY means something else for these types: one selector per data point for bar and hist (src/model/bar.ts), a per-series grid for the segmented bars and mosaic (src/model/segmented.ts), and nothing at all for point and pie, whose models read ⁠layer.selectors as string⁠ (src/model/scatter.ts, src/model/pie.ts). dot and lollipop are built by the bar model; heat reads a string or a per-cell grid (src/model/heatmap.ts). maidr.js 3.x read a one-element array as the string it held, which is why every processor here that returns list(selector) was fine until the bundle moved to 4.0 (#316).

Usage

SINGLE_SELECTOR_LAYER_TYPES

Details

Types NOT listed keep whatever shape their processor built, because their models want the array: one selector per series for line, smooth, step, area, roc and violin_kde, one per level for contour, one per box for box and violin_box, one per tick for a rug, and error_bar, gantt, word_cloud and candlestick accept either.


System Adapter Base Class

Description

Abstract base class for adapting different plotting systems to the unified maidr interface. Each plotting system (ggplot2, base R, lattice, etc.) should have its own adapter implementation.

Format

An R6 class

Public fields

system_name

Name of the plotting system

Methods

Public methods


SystemAdapter$new()

Initialize the adapter

Usage
SystemAdapter$new(system_name)
Arguments
system_name

Name of the plotting system


SystemAdapter$can_handle()

Abstract method to check if this adapter can handle a plot object

Usage
SystemAdapter$can_handle(plot_object)
Arguments
plot_object

The plot object to check

Returns

TRUE if this adapter can handle the object, FALSE otherwise


SystemAdapter$create_orchestrator()

Abstract method to create an orchestrator for this system

Usage
SystemAdapter$create_orchestrator(plot_object)
Arguments
plot_object

The plot object to process

Returns

Orchestrator instance specific to this system


SystemAdapter$clone()

The objects of this class are cloneable with this method.

Usage
SystemAdapter$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


The Suggests packages whose plotting entry point maidr wraps

Description

Named by package, valued by the function. Each is wrapped into maidr's own namespace when the package loads (the onLoad hooks below), so a bare call reaches the wrapper only while package:maidr sits ahead of the package on the search path. Attached after maidr, the package masks the wrapper and its calls go unrecorded; see package_masks_maidr().

Usage

WRAPPED_SUGGESTS

Convert Accuracy to Decimal Places

Description

Converts the scales package accuracy parameter to a decimal count. For example, accuracy = 0.01 becomes decimals = 2.

Usage

accuracy_to_decimals(accuracy)

Arguments

accuracy

The accuracy value (e.g., 0.01 for 2 decimal places)

Value

Integer number of decimal places


Add maidr-data to SVG using proper XML manipulation

Description

Add maidr-data to SVG using proper XML manipulation

Usage

add_maidr_data_to_svg(svg_content, maidr_data)

Arguments

svg_content

Character vector of SVG lines

maidr_data

The maidr-data structure

Value

Modified SVG content


Reposition chartSeries date-range bracket header to prevent clipping

Description

quantmod::chartSeries() renders a bracketed date-range header (e.g. "[2024-01-12/2024-01-15]") via base R title() with par(adj=1). For short timeseries the text width exceeds the available right margin and the closing bracket is clipped at the SVG viewBox edge. This is upstream quantmod issue #129 (open since 2016, no fix). See chartSeries.chob.R lines 205-209 for the hardcoded placement.

Usage

adjust_chartseries_bracket(svg_content, maidr_data)

Arguments

svg_content

Character vector of SVG lines

maidr_data

The maidr-data structure (read-only; used to detect candlestick layers)

Details

Earlier we tried CSS overflow: visible, but that re-exposes chartSeries' intentionally negative-y volume ⁠<rect>⁠s which rely on the SVG root's default overflow: hidden for clipping. Further canvas enlargement is impractical: the header is anchored at ~91% of canvas width regardless of total width, so even 24-inch canvases would still leave the header riding the right edge.

This helper performs surgical SVG post-processing on the exported gridSVG output: it locates the bracket text element by content pattern, switches its text-anchor to end, and snaps its x coordinate to 95% of the viewBox width. The text then right-aligns within the viewBox regardless of header length.

Safety: this is a no-op when maidr_data contains no candlestick layers (ggplot candlestick / non-candlestick plots), when xml2 is unavailable, when the SVG fails to parse, when the viewBox cannot be read, or when no element matches the bracket pattern.

Value

Modified SVG content (character vector). If any guard fails, returns svg_content unchanged.


Document-level implementation of adjust_chartseries_bracket()

Description

Mutates svg_doc in place.

Usage

adjust_chartseries_bracket_doc(svg_doc)

Arguments

svg_doc

Parsed SVG document (xml2)

Value

TRUE if the document was modified


Say, as a package is attached, that it now masks a maidr wrapper

Description

Run from the attach hooks below. By then the package sits ahead of maidr on the search path, so this is the moment to tell the user that bare calls to its entry point will no longer be recorded. Silent when the package does not mask, and under maidr.startup_message = FALSE.

Usage

announce_masking(package)

Arguments

package

Name of the package, as in WRAPPED_SUGGESTS.


Apply modular patches to barplot arguments

Description

Apply modular patches to barplot arguments

Usage

apply_barplot_patches(args)

Arguments

args

List of arguments passed to barplot

Value

Modified arguments with applied patches


Apply sorting logic to barplot arguments for consistent visual ordering

Description

Apply sorting logic to barplot arguments for consistent visual ordering

Usage

apply_barplot_sorting(args)

Arguments

args

List of arguments passed to barplot

Value

Modified arguments with sorted matrix data


Normalize a single axis value into AxisConfig shape

Description

Accepts legacy or partial inputs (bare string label, already-wrapped list, or NULL) and returns either NULL (when nothing to emit) or a named list conforming to the AxisConfig schema.

Usage

as_axis_config(value)

Arguments

value

Raw axis input (string, list, or NULL)

Value

A named list (AxisConfig) or NULL


Return a withVisible() result with the visibility it recorded

Description

The wrappers used to return everything invisibly, so par("mar") printed nothing once maidr was attached and hist(x, plot = FALSE) had to be wrapped in print() to be seen.

Usage

as_drawn(result)

Arguments

result

A list from withVisible()

Value

The value, invisibly when the original call made it so


Attach a format object to a specific axis

Description

Mutates a single axis's format field. Creates the axis slot (with label = default_label) if it does not exist. No-ops when format_obj is NULL.

Usage

attach_axis_format(axes, which, format_obj, default_label = NULL)

Arguments

axes

Canonical axes list

which

Axis key: one of "x", "y", "z"

format_obj

AxisFormat list (or NULL)

default_label

Label to use if the axis slot is being created. NULL (the default) creates the slot without one, so attaching a format to an axis whose processor had no title to give does not put an empty label back in front of the renderer's generic.

Value

The mutated axes list


Add the legend title as the z axis label for a grouped layer

Description

A grouped layer emits a per-series z value (the group's name), and MAIDR announces it as "<z label> is <z value>". Without a z label the frontend falls back to the generic word "Group", losing the legend title the plot actually shows. Single-series layers emit no z value at all, so they get no z label either.

Usage

attach_series_group_axis(
  axes,
  plot,
  built,
  data,
  layer_index = NULL,
  aes_groups = list(c("colour", "color"))
)

Arguments

axes

Axes built so far

plot

The ggplot2 object

built

Built plot data (optional)

data

The extracted layer data

layer_index

Index of the layer being described

aes_groups

Grouping aesthetics to probe, as documented on resolve_series_group_mapping()

Value

The axes list, with z added when the layer is grouped


Apply processor plot augmentation to a single leaf ggplot

Description

Some processors need extra geoms in the rendered SVG to hang selectors on – violin injects a thin geom_boxplot() so the box-summary layer has something to highlight. The single-plot path does this in Ggplot2PlotOrchestrator$process_layers(); leaves of a patchwork need the same treatment before the composition is rendered.

Usage

augment_leaf_plot(leaf_plot)

Arguments

leaf_plot

A ggplot object

Value

The augmented ggplot (the input unchanged when nothing augments)


Augment every leaf of a patchwork composition

Description

Mirrors extract_patchwork_leaves()'s traversal so the augmented tree has the same leaf order. The result must be used for BOTH patchwork::patchworkGrob() and process_patchwork_plot_data(): grob names come from a global counter, so selectors computed against one build cannot resolve against another.

Usage

augment_patchwork_leaves(node)

Arguments

node

Patchwork node or ggplot object

Value

The same structure with each leaf augmented


Canonical Axes Schema Helpers

Description

Utilities for constructing and validating the canonical per-axis axes object emitted by the MAIDR payload. Only x, y, and z keys are permitted at the top level of axes; each maps to an AxisConfig list with optional label (string), min, max, tickStep (numbers), and format (an AxisFormat list). The legacy flat form (bare string labels and top-level format/min/max/tickStep/fill/level) has been removed with no deprecation path.


Grid navigation bounds for one axis

Description

min, max and tickStep for the axis named, read off the built plot's panel parameters, or NULL when any of the three cannot be determined – which leaves the axis with its label and no grid, the graceful degradation #158 settled on.

Usage

axis_grid_info(built, axis = "x", panel_id = NULL)

Arguments

built

Built plot data

axis

Character, either "x" or "y"

panel_id

Panel index for faceted plots (optional, defaults to 1)

Details

Lifted out of Ggplot2PointLayerProcessor, which still calls it, when the rug processor came to need the same answer for the axis its ticks stand on (#222). Two readings of one grid rule is how the two would drift.

Value

List with min, max, tickStep, or NULL


Functions maidr masks on attach

Description

library(maidr) puts maidr's own copies of the Base R plotting functions ahead of the originals on the search path, and R says so with its "The following objects are masked" notice. Each copy is a wrapper: it records the call so that show() and save_html() can render an accessible chart, then calls the original and returns what the original returns. With interception off (maidr_off(), or options(maidr.base_r = FALSE)) the wrappers pass straight through.

Usage

barplot(...)

plot(...)

hist(...)

boxplot(...)

image(...)

heatmap(...)

contour(...)

matplot(...)

curve(...)

dotchart(...)

stripchart(...)

stem(...)

pie(...)

mosaicplot(...)

assocplot(...)

pairs(...)

coplot(...)

persp(...)

sunflowerplot(...)

fourfoldplot(...)

spineplot(...)

cdplot(...)

qqnorm(...)

qqplot(...)

qqline(...)

filled.contour(...)

acf(...)

pacf(...)

ccf(...)

cpgram(...)

spectrum(...)

monthplot(...)

termplot(...)

lag.plot(...)

biplot(...)

interaction.plot(...)

bxp(...)

stars(...)

vioplot(...)

wordcloud(...)

chartSeries(...)

lines(...)

points(...)

text(...)

mtext(...)

abline(...)

segments(...)

arrows(...)

polygon(...)

rect(...)

symbols(...)

legend(...)

axis(side, at = NULL, labels = TRUE, ...)

title(...)

grid(...)

par(...)

layout(...)

## S3 method for class 'screen'
split(...)

Arguments

...

Arguments passed to the original graphics function.

side, at, labels

axis()'s own arguments, which its wrapper names so that the tick labels a chart is given can be recorded; passed on to graphics::axis() unchanged.

Details

What is masked

From graphics: the high-level plotting functions barplot(), plot(), hist(), boxplot(), image(), contour(), matplot(), curve(), dotchart(), stripchart(), stem(), pie(), mosaicplot(), assocplot(), pairs(), coplot(), persp(), sunflowerplot(), fourfoldplot(), spineplot(), cdplot(), filled.contour(), bxp() and stars(); the low-level additions lines(), points(), text(), mtext(), abline(), segments(), arrows(), polygon(), rect(), symbols(), legend(), axis(), title() and grid(); and the layout functions par(), layout() and split.screen().

From stats: heatmap(), qqnorm(), qqplot(), qqline(), acf(), pacf(), ccf(), cpgram(), spectrum(), monthplot(), termplot(), lag.plot(), biplot() and interaction.plot().

plot() is also masked from base, where its generic has lived since R 4.0, and show() from methods: the S4 display generic, which maidr's show() hands back any object that is not a plot.

vioplot::vioplot(), wordcloud::wordcloud() and quantmod::chartSeries() are wrapped as well, once their package is loaded.

show() and methods::show()

maidr's show() takes a ggplot2 object or, with no argument, the last recorded Base R chart. Anything else it is given goes to methods::show(), so show(x) on an S4 object prints as it did before maidr was attached. In a script or a package, where what is masked depends on what else is attached, call maidr::show() and methods::show() by name.

Attach order for vioplot, wordcloud and quantmod

These three are wrapped into maidr's namespace when their package loads, so a bare call reaches the wrapper only while package:maidr sits ahead of the package on the search path. Attach them before maidr:

library(vioplot)
library(maidr)

Attached after it, the package masks the wrapper, a bare vioplot(), wordcloud() or chartSeries() draws without being recorded, and show() reports that no Base R plot was detected. maidr says so at the moment the package is attached and again in that error. The other way round it is maidr::vioplot(), maidr::wordcloud() or maidr::chartSeries(), called explicitly.

Calling an original directly

The wrappers add nothing to the drawing and return what the original returns, so there is rarely a reason to go around them. graphics::barplot() does, and draws a chart maidr does not record.

Value

Same as the original Base R function (invisibly when applicable).

See Also

show() and save_html(); maidr_on() and maidr_off() for turning interception on and off; ?"maidr-options".


Base R Axis-Title Defaults

Description

Base R's high-level plotting functions derive their axis titles inside the call (hist() names the y axis "Frequency", boxplot.formula() reads both titles off the formula) instead of recording them, and barplot() and pie() draw no title at all. Either way the recorded call carries no ⁠xlab=⁠/⁠ylab=⁠, so a processor that only reads those arguments announces a nameless axis.

Details

What a chart can honestly put there is a property of the chart type rather than of the data, so the shapes shared by several processors are resolved here once.


Canonical axes for a categorical Base R chart

Description

barplot(), boxplot() and pie() all plot one categorical axis against one measured axis, and none of them writes a title unless the author does. Naming those axes for what they hold – "Category" against "Value" – says what the numbers mean, where the renderer's positional "X"/"Y" fallback only says where they sit, and it claims nothing beyond the shape of the call. py-maidr's pie chart defaults to the same two words.

Usage

base_r_categorical_axes(args, horizontal = FALSE)

Arguments

args

Recorded argument list, or NULL

horizontal

TRUE when the chart draws its value axis horizontally, which swaps which visual axis holds the categories

Value

Canonical axes list


Map a Base R type argument onto a MAIDR step direction

Description

type = "s" draws the horizontal segment first, which is MAIDR's "hv"; type = "S" draws the vertical segment first, which is "vh". Any other value returns NULL so the caller can omit stepDirection rather than assert a convention the call never asked for.

Usage

base_r_step_direction(plot_type)

Arguments

plot_type

The type argument recorded from the plot call.

Value

"hv", "vh", or NULL.


Box-family emission order

Description

Which end of a horizontal box or violin chart a reader starts at is not something the input schema states, so the two MAIDR bindings disagreed about it: r-maidr emitted its categories bottom to top and py-maidr top to bottom, and the same chart was navigated from opposite ends depending on which one drew it (#187).


Build a canonical axes object

Description

Convenience constructor for a per-axis axes list. Drops NULL and empty axes, so a caller that can say nothing about an axis simply omits the key and leaves the generic to the renderer.

Usage

build_axes(x = NULL, y = NULL, z = NULL)

Arguments

x

Label string or AxisConfig list for the x axis (or NULL)

y

Label string or AxisConfig list for the y axis (or NULL)

z

Label string or AxisConfig list for the z axis (or NULL)

Value

A canonical axes list with only non-NULL axes set. Named even when empty, so it serializes as the JSON object {} rather than as ⁠[]⁠.


Build a single AxisConfig

Description

Drops the fields that are absent, so an axis only carries what its caller could establish. Returns a named empty list when nothing could be: passed to build_axes(), that drops the axis key entirely.

Usage

build_axis_config(label = NULL, min = NULL, max = NULL, tickStep = NULL)

Arguments

label

Axis label, or NULL

min

Axis minimum, or NULL

max

Axis maximum, or NULL

tickStep

Distance between ticks, or NULL

Value

A named list (AxisConfig), possibly empty


Build Format Config from Detected Type and Parameters

Description

Build Format Config from Detected Type and Parameters

Usage

build_format_config(format_type, prefix, suffix, accuracy, digits)

Arguments

format_type

Detected format type

prefix

Prefix from closure

suffix

Suffix from closure

accuracy

Accuracy from closure

digits

Digits from closure

Value

Format configuration list


Build the Interactive SVG, or Answer NULL When It Cannot Be Built

Description

should_fallback() answers whether the recorded layers are ones maidr can read. It cannot answer whether the plot can be exported, because that is the exporter's question and the exporter is not consulted until the export runs. When maidr exported through gridSVG, two base R charts failed there on plots that pass the gate – matplot() with "non-numeric argument to binary operator" and symbols() with gridSVG's own "We shouldn't be here!" assertion, both raised inside grid.export() rather than by anything this package computes. The svglite export draws both, but an export can still throw.

Usage

build_interactive_svg(orchestrator, ...)

Arguments

orchestrator

The orchestrator for the plot being rendered.

...

Passed through to create_enhanced_svg().

Details

Left to propagate, those kill the save outright: the caller gets neither the interactive chart nor the static image, and an error naming a package they never called. The lower claim the package makes about a recorded plot is that it is at worst a picture (#216), and an export that throws is no more a reason to break that than a layer it cannot classify.

The whole build is guarded rather than the export alone. From the caller's side the gtable, the data and the SVG are one step – producing the interactive chart – and which of the three threw does not change what they should be given instead.

maidr_set_fallback(enabled = FALSE) is the caller asking for the failure rather than the picture, so the error is re-raised untouched there.

Value

The SVG content, or NULL when the build failed and fallback is enabled.


Assemble the exported document

Description

Assemble the exported document

Usage

build_svg_document(walk, svg, w, h)

Arguments

walk

The walk state.

svg

svglite's page.

w, h

Page size in px.

Value

Character vector of SVG lines.


Delegate to the stored original knitr plot hook

Description

Falls back to knitr's markdown hook only when no original was stored.

Usage

call_original_plot_hook(x, options)

Arguments

x

The plot file path from knitr

options

Chunk options

Value

The hook's output


Cancel a pending auto-show callback

Description

Removes by NAME rather than by the index addTaskCallback() returned: that index is a position in R's callback list, and any other package adding or removing a callback in the meantime shifts it. Removing a stale index silently deletes an unrelated package's callback.

Usage

cancel_auto_show()

The name at one position, or NULL

Description

A point drawn off an integer is still in that integer's category. position_dodge shifts a point sideways to make room for a sibling series and position_jitter scatters it, and both displacements stay within the category's own slot – so the tick it was moved from is the category it is in, not a category it is being falsely assigned to. Measured on a dodged scatter over two groups, x arrives as 0.875, 1.125, 1.875, 2.125, and an exact match names none of the 24 points.

Usage

category_at(position, labels)

Arguments

position

A drawn coordinate

labels

The map from discrete_axis_labels()

Details

Rounding is what recovers them, which is the answer the Python binding reached for the same situation: "a point drawn off one is a group a dodge shifted aside to make room for its neighbour – still that group, and still named by the tick it was moved from."

Bounded at half a tick, which is what keeps it honest. ggplot2 keeps both displacements inside the slot – a dodge divides the category's width, and position_jitter's default reaches 40% of the resolution – so anything further out is not a displaced member of that category and is left unnamed. The is_discrete() gate above does the rest: on a continuous axis there are no names to round onto, so a measurement cannot be renamed after whichever tick it happens to fall nearest.

Value

The name, or NULL when there is none


The technical-analysis indicators a recorded chartSeries() call draws

Description

quantmod::chartSeries() draws the indicators named by its TA argument, which defaults to "addVo()", and splits a single string on TAsep (";" by default), so TA = "addVo();addSMA()" draws two. An explicit TA = NULL, FALSE, NA or "" draws none.

Usage

chartseries_ta_calls(args)

Arguments

args

The recorded arguments of the chartSeries() call

Value

A character vector of indicator calls, such as "addVo()"; character(0) when none is drawn, or NA when TA is not a character vector (an evaluated indicator object), which cannot be read.


Classify a Base R Function

Description

Determines the classification level of a Base R plotting function.

Usage

classify_function(function_name)

Arguments

function_name

Name of the function to classify

Value

Character string: "HIGH", "LOW", "LAYOUT", or "UNKNOWN"


Clean MAIDR internal arguments from args list

Description

Removes internal arguments (starting with .maidr_) from an args list before passing to original functions during replay.

Usage

clean_maidr_args(args)

Arguments

args

List of arguments

Value

Cleaned args list without .maidr_* entries


Clear All Device Storage

Description

Clears storage for all devices.

Usage

clear_all_device_storage()

Value

NULL (invisible)


Clear Device Storage

Description

Clears all stored plot calls for a specific device.

Usage

clear_device_storage(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

NULL (invisible)


Clear recorded plot calls

Description

Clear recorded plot calls

Usage

clear_plot_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID (defaults to current device)

Value

NULL (invisible)


Clip chartSeries' lower-panel rects to their plot region

Description

quantmod::chartSeries() draws its volume panel (addVo()) as bars from zero, in a panel whose y range starts near the smallest volume, and leaves R to clip them to the plot region, as base graphics do by default. The tree gridGraphics::grid.echo() rebuilds from that drawing places those bars in ⁠graphics-plot-<N>⁠, which does not clip, rather than in the ⁠graphics-plot-<N>-clip⁠ viewport it builds beside it, so every bar ran on past the panel's lower border into the date labels.

Usage

clip_chartseries_panel_rects(grob)

Arguments

grob

A grob, gTree, gList, or gtable (or NULL)

Details

Each rect of a panel below the first is moved into that panel's -clip viewport, when the tree has one. The rect keeps its name, so its element id, and the selectors built from it, are unchanged; only the viewport groups around it are named after the clipping viewport, and the export clips them as R clipped the drawing. The price panel is left alone: its candles lie within its range.

Value

The same tree with lower-panel rects in their clipping viewport


Close and clean up the MAIDR temp device

Description

Close and clean up the MAIDR temp device

Usage

close_maidr_temp_device()

Value

NULL (invisible)


Collapse multiple "line" layer entries in a single panel into one multi-series line layer entry. Other layers are left untouched.

Description

The first line layer's id, title, and axes are preserved; data and selectors are concatenated across all line layers.

Usage

collapse_lines_to_multiseries(panel)

Arguments

panel

A processed panel list with $id and $layers

Value

Panel with line layers merged


Collect all candlestick layers in a maidr_data structure

Description

Collect all candlestick layers in a maidr_data structure

Usage

collect_candlestick_layers(maidr_data)

Every grob name in a tree, depth first

Description

Shared with the box plot processor's own walk, which looks for the same graphics-plot-N-kind-M names in the same tree shape.

Usage

collect_grob_names(g)

Arguments

g

A grob, gList, gTree or gtable.

Value

A character vector of names.


Collect every panel cell of a gtable, descending into nested gtables

Description

Nested layouts like (p1 | p2) / p3 place the inner row's panels inside a CHILD gtable ("patchwork-table-N"), so scanning only the top-level layout drops them. Panels are collected in DISCOVERY order, which follows patchwork's plot-addition order and therefore matches extract_patchwork_leaves().

Usage

collect_gtable_panels(
  gt,
  t_path = integer(0),
  l_path = integer(0),
  vp_prefix = character(0),
  wrapper = NULL
)

Arguments

gt

Gtable object

t_path

Accumulated top positions of the enclosing cells

l_path

Accumulated left positions of the enclosing cells

vp_prefix

Accumulated viewport names of the enclosing cells

Details

Each entry also carries the grid viewport path needed to navigate to that panel after the gtable has been drawn. gtable names a cell's viewport ⁠<name>.<t>-<r>-<b>-<l>⁠ and wraps a nested gtable's children in a "layout" viewport, so the path down to a nested panel is c("<child>.t-r-b-l", "layout", "<panel>.t-r-b-l"). Panel names are NOT unique across a nested composition (both halves of a 2x2 contain a "panel-1"), which is why callers address panels by position in this list rather than by name.

Value

List of panel entries (name, grob, t, l, t_key, l_key, vp_path)


The full paths of the viewports a grob tree defines

Description

The full paths of the viewports a grob tree defines

Usage

collect_viewport_paths(grob)

Arguments

grob

A grob, gTree, gList, or gtable

Value

Character vector of "a::b::c" paths, from every childrenvp


Combine data from multiple layers in facet processing

Description

Combine data from multiple layers in facet processing

Usage

combine_facet_layer_data(layer_results)

Arguments

layer_results

List of layer processing results

Value

Combined data


Combine selectors from multiple layers in facet processing

Description

Combine selectors from multiple layers in facet processing

Usage

combine_facet_layer_selectors(layer_results)

Arguments

layer_results

List of layer processing results

Value

Combined selectors


Compute Panel Slot for Each Plot Group

Description

Maps plot groups to panel slots (1-based, in drawing order) for a multi-panel configuration:

Usage

compute_panel_slots(plot_groups, panel_config)

Arguments

plot_groups

List of plot groups from group_device_calls()

panel_config

Panel configuration from detect_panel_configuration()

Value

Integer vector (one entry per group): panel slot or NA


Compute one violin's statistics the way vioplot does

Description

Compute one violin's statistics the way vioplot does

Usage

compute_vioplot_stats(data, h = NULL, range = .maidr_vioplot_default_range)

Arguments

data

Numeric vector for one violin.

h

Bandwidth, when the caller passed one. NULL lets sm.density choose, which is vioplot's default.

range

Whisker reach in interquartile ranges, as vioplot's range.

Value

A list with min, q1, median, q3, max, positions, density and bandwidth, or NULL when there is nothing to describe.


Group a contour layer's rows into the curves it drew

Description

A contour draws a scalar field as curves of constant value, and ggplot_build() has already done the hard half: level is a number on every row, and piece separates the curves. A field with two peaks crosses a level twice and arrives as two pieces, so nothing has to be split apart here – which is the one place this reading is easier than the matplotlib one, where both islands come back in a single compound path (xability/py-maidr#540).

Usage

contour_curves(built_data)

Arguments

built_data

A layer's computed data, carrying x, y, level and piece, one row per vertex

Details

Pieces are emitted in ascending piece, which is ascending level and then draw order within a level, and the returned order is that sequence of piece identifiers – what the selectors are built from, so the highlight follows the grouping rather than relying on the document happening to agree.

Value

A list with data (one curve per piece, each a list of x, y and level) and order (the piece behind each emitted curve)


How many gtable panels one patchwork leaf occupies

Description

One for an ordinary leaf; one per facet cell for a faceted one; none for a leaf patchwork has wrapped. Used to walk find_patchwork_panels() in step with the leaf list – a leaf that contributes no panel must not consume one, or every leaf after it is described over somebody else's panel.

Usage

count_leaf_panels(leaf_plot)

Arguments

leaf_plot

A leaf of a patchwork composition

Value

Integer count, 0 for a wrapped leaf


Create enhanced wrapper for axis to capture scales:: format config

Description

This wrapper intercepts axis() calls and checks if the labels argument is a scales:: label function (closure). If so, it extracts the format configuration before applying the function to get the actual labels.

Usage

create_axis_wrapper(original_function)

Arguments

original_function

Original axis function

Value

Enhanced wrapped function


Create enhanced wrapper for barplot with sorting logic

Description

Create enhanced wrapper for barplot with sorting logic

Usage

create_barplot_wrapper(original_function)

Arguments

original_function

Original barplot function

Value

Enhanced wrapped function


Create enhanced SVG with maidr data

Description

Create enhanced SVG with maidr data

Usage

create_enhanced_svg(gt, maidr_data, ...)

Arguments

gt

A gtable object

maidr_data

The maidr-data structure

...

Additional arguments

Value

Character vector of SVG content


Create Fallback HTML Content

Description

Creates HTML content with the fallback image, styled to fit in iframes.

Usage

create_fallback_html(
  plot = NULL,
  shiny = FALSE,
  format = get_fallback_format(),
  width = 7,
  height = 5
)

Arguments

plot

A ggplot2 object or NULL for Base R plots

shiny

If TRUE, returns just the image tag for Shiny/knitr use

format

Image format. Defaults to the maidr.fallback_format option, which maidr_set_fallback() sets.

width

Image width in inches

height

Image height in inches

Value

HTML content string or htmltools object


Create iframe HTML tag for fallback static image

Description

Creates an iframe element whose srcdoc carries a static image. Used when plots contain unsupported layers and fall back to PNG rendering. Unlike create_maidr_iframe, this does not include MAIDR.js dependencies.

Usage

create_fallback_iframe(
  html_content,
  width = "100%",
  height = "450px",
  plot_id = NULL
)

Arguments

html_content

Character string of HTML content (with img tag)

width

Width of the iframe (default: "100%")

height

Height of the iframe (default: "450px")

plot_id

Unique identifier for the plot

Value

Character string of iframe HTML


Create Fallback Image for Unsupported Plots

Description

Renders a plot as a standard PNG image when MAIDR cannot process it. This is used as a fallback for unsupported plot types or layers.

Usage

create_fallback_image(
  plot = NULL,
  format = "png",
  width = 7,
  height = 5,
  res = 150
)

Arguments

plot

A ggplot2 object or NULL for Base R plots

format

Image format: "png" (default), "svg", or "jpeg"

width

Image width in inches (default: 7)

height

Image height in inches (default: 5)

res

Resolution in DPI for PNG/JPEG (default: 150)

Value

Base64-encoded image data URI string


Create a function wrapper

Description

Create a function wrapper

Usage

create_function_wrapper(function_name, original_function)

Arguments

function_name

Name of the function

original_function

Original function to wrap

Value

Wrapped function


Create HTML document with dependencies

Description

Create HTML document with dependencies

Usage

create_html_document(svg_content, use_cdn = NULL)

Arguments

svg_content

Character vector of SVG content

use_cdn

Logical. If TRUE, use CDN. If FALSE, use bundled files. If NULL (default), auto-detect based on internet availability.

Value

An htmltools HTML document object


Create inline image HTML for non-iframe rendering

Description

Creates a simple img tag for fallback/non-HTML output. Used when we don't need iframe isolation (unsupported plots in HTML, or any plot in PDF/EPUB output).

Usage

create_inline_image(plot = NULL, width = "100%", height = "auto")

Arguments

plot

A ggplot object or NULL for Base R

width

Width for the image container

height

Height for the image container

Value

Character string of HTML with img tag


Wrap a chart in its iframe for a knitted document

Description

Online, the frame loads maidr.js from the CDN, and the document is given its own copy of the bundle (maidr_page_bundle_dependency()) for the frame to fall back on. The frame's document sits in a srcdoc attribute, where R Markdown's self_contained and Quarto's embed-resources cannot reach its ⁠<script src>⁠; the copy is what they embed instead, once per document however many charts it has, so a self-contained document's charts work offline. Offline at render time, each frame carries the bundle inline, as before.

Usage

create_knitr_iframe(content)

Arguments

content

The chart's SVG content, from create_maidr_html()

Value

Character string of iframe HTML


Create HTML document with maidr enhancements using the orchestrator

Description

Create HTML document with maidr enhancements using the orchestrator

Usage

create_maidr_html(
  plot,
  use_cdn = NULL,
  shiny = FALSE,
  orchestrator = NULL,
  ...
)

Arguments

plot

A ggplot2 object

use_cdn

Logical. If TRUE, use CDN. If FALSE or NULL (default), use bundled files; see maidr_html_dependencies().

shiny

If TRUE, returns just the SVG content instead of full HTML document

orchestrator

Optional pre-created orchestrator to reuse (avoids double creation)

...

Additional arguments passed to internal functions

Value

An htmltools HTML document object or SVG content


Create iframe HTML tag for isolated MAIDR plot

Description

Creates an iframe element whose srcdoc carries the complete MAIDR plot. This isolates each plot in its own document/JavaScript context while leaving it same-origin with the page, which is what Web Bluetooth and Web Serial — and so the tactile display — require.

Usage

create_maidr_iframe(
  svg_content,
  width = "100%",
  height = "450px",
  plot_id = NULL,
  use_cdn = NULL,
  page_fallback = FALSE
)

Arguments

svg_content

Character vector of SVG content with maidr-data attribute

width

Width of the iframe (default: "100%")

height

Height of the iframe (default: "450px")

plot_id

Unique identifier for the plot

use_cdn

Logical. If TRUE, use CDN. If FALSE, use bundled files. If NULL (default), auto-detect based on internet availability.

page_fallback

Logical. Passed to create_standalone_html().

Value

Character string of iframe HTML


Create MAIDR Widget for knitr (Internal)

Description

Internal function to create a MAIDR widget from either ggplot or Base R plots.

Usage

create_maidr_widget_internal(plot = NULL)

Arguments

plot

A ggplot object or NULL for Base R

Value

An htmlwidget object


Create a wrapper for functions taking unevaluated expressions

Description

Used for functions like curve() whose arguments must stay lazy. The recorded args are the unevaluated call expressions together with the caller environment, so replay evaluates them exactly as the user's call did.

Usage

create_nse_wrapper(function_name, original_function)

Arguments

function_name

Name of the function

original_function

Original function to wrap

Value

Wrapped function


Create self-contained HTML for iframe embedding

Description

Generates a complete standalone HTML document with MAIDR.js that can be embedded in an iframe for isolation. Each iframe gets its own JavaScript context, avoiding MAIDR.js singleton pattern issues with multiple plots.

Usage

create_standalone_html(svg_content, use_cdn = NULL, page_fallback = FALSE)

Arguments

svg_content

Character vector of SVG content with maidr-data attribute

use_cdn

Logical. If TRUE, use CDN. If FALSE, use bundled files. If NULL (default), auto-detect based on internet availability.

page_fallback

Logical. When the CDN is used, fall back to the copy of the bundle the embedding page carries if the CDN load fails. Set by the knitr paths, which add that copy to the document with maidr_page_bundle_dependency(), and by maidr_widget(), which declares the same dependency on the widget.

Value

Character string of complete HTML document


Reproduce the axis labels curve() derives for itself

Description

curve() computes its default labels internally and does not return them: the x label is xname ("x" unless the caller overrides it) and the y label is the deparsed expression, with a bare function name rewritten as fname(xname). Reading them off the recorded call keeps the announced axes matching the drawn ones; without them a visibly labelled plot would be announced with two empty axis titles.

Usage

curve_default_labels(recorded_args)

Arguments

recorded_args

Recorded (unevaluated) argument list of the call

Details

An explicit xlab/ylab in the call wins over these defaults; the line processor applies that precedence.

Value

List with x and y label strings


Keep the points curve() itself evaluated

Description

curve() returns, invisibly, the exact x/y vectors it just drew. Keeping them means the accessible data is read back from the user's own call – evaluated once, at the moment it was made, in the frame that made it.

Usage

curve_recorded_values(recorded_args, result)

Arguments

recorded_args

Recorded (unevaluated) argument list of the call

result

The value curve() returned

Details

The alternative – re-deriving the points when the figure is emitted – means running user code a second time, later, in a rebuilt frame. That is the failure mode #59 fixed for for loops, where every panel replayed the last iteration's bindings; snapshot_call_env() narrows the window but cannot close it, and the recorded call alone is not enough anyway: from/to arrive unevaluated too (to = 2 * pi is recorded as a call), so emit time would have to redo curve()'s own seq/log/xname handling on top of evaluating the expression. Reading back what was drawn is neither. The SVG still comes from replaying the call, so a deliberately non-deterministic expression can draw a second, different curve – that is true of every recorded call and is not made worse here.

The values are stored under a .maidr_ name, so clean_maidr_args() drops them before the call is replayed.

Value

A list with x, y and labels, or NULL when the returned value is not a usable pair of coordinate vectors


Report whether extracted layer data is split into named series

Description

Report whether extracted layer data is split into named series

Usage

data_has_series_groups(data)

Arguments

data

The extracted layer data (a list of series)

Value

TRUE when there is more than one series and points carry z


Detect Multi-panel Configuration

Description

Analyzes layout calls to determine multi-panel configuration.

Usage

detect_panel_configuration(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

Panel configuration list or NULL


Detect Format Type from scales Closure Parameters

Description

Detect Format Type from scales Closure Parameters

Usage

detect_scales_format_type(prefix, suffix, digits, scale, accuracy)

Arguments

prefix

Prefix string from closure

suffix

Suffix string from closure

digits

Digits parameter (only in label_scientific)

scale

Scale parameter

accuracy

Accuracy parameter

Value

Format type string or NULL


Positions to category names for one axis of one panel

Description

Positions to category names for one axis of one panel

Usage

discrete_axis_labels(built, axis = "x", panel_id = NULL)

Arguments

built

The built ggplot2 object

axis

"x" or "y"

panel_id

The panel to read, defaulting to the first

Value

A named character vector keyed by position (as character), or NULL when the axis is continuous. Empty names are dropped, so a scale with a blank label yields the number rather than a blank announcement.


Distinct values of an aesthetic, in the order ggplot2 draws them

Description

A factor follows its own level order, minus the levels nothing was drawn for; anything else sorts in its own type's order. Sorting the values AS TEXT reorders the columns twice over: against a factor whose levels are not alphabetical, and against a number, where it puts 10 before 2.

Usage

discrete_level_order(values)

Arguments

values

A vector of aesthetic values

Details

The missing category comes last, which is where ggplot2 puts it for a character column, a numeric one and an ordinary factor alike.

Value

Character vector of the observed levels, in drawn order, with NA_character_ last when the aesthetic has a missing value


Resolve the definition R dispatched a recorded call to

Description

hist is the motivating case from #98: the generic is hist(x, ...), so matching against it leaves a positional breaks inside the dots. The method carries the formals that matter, and picking it by the first argument's class is the same choice UseMethod() made when the call ran.

Usage

dispatched_definition(function_name, definition, args)

Arguments

function_name

Name of the recorded function

definition

The original (unwrapped) function that was called

args

Recorded argument list of evaluated values

Value

A function to match against, or NULL when none can be resolved


The processor classes a factory's create_processor() dispatches to

Description

Read off the method rather than kept beside it. The list a factory used to return was written out by hand and consulted by nothing, so it drifted freely: by the time #200 was filed it was missing four of the twenty ggplot2 processors and one of the fourteen base R ones, and nothing in the package or its tests could tell. A second list is a second thing to keep true; asking the dispatch itself is one thing that cannot disagree with itself.

Usage

dispatched_processor_classes(generator, prefix)

Arguments

generator

The factory's R6ClassGenerator.

prefix

The class-name prefix its processors share.

Details

deparse(body(...)) is used rather than reading ⁠R/⁠, because an installed package has no ⁠R/⁠ to read – the sources are gone by then and only the parsed function survives.

Value

Character vector of class names, in the order first dispatched.


Display HTML document directly

Description

Display HTML document directly

Usage

display_html(html_doc)

Arguments

html_doc

An htmltools HTML document object


Display HTML file in browser

Description

Display HTML file in browser

Usage

display_html_file(file)

Arguments

file

HTML file path


Collapse a dot stack into the bins it counts

Description

A Wilkinson dot plot is a histogram drawn one dot per observation: the values are binned, and each bin's dots are stacked so the stack's height is the bin's count. ggplot_build() says so directly – it returns one row per observation carrying that observation's bin centre, the bin width, and the bin's count – so the histogram is read off rather than reconstructed:

Usage

dotplot_bins(built_data, horizontal = FALSE)

Arguments

built_data

A layer's computed data, carrying the bin centre on x or y plus binwidth and count

horizontal

TRUE when the bins run up the y axis

Details

   y x binwidth count countidx
1  0 1        1     3        1
2  0 1        1     3        2
3  0 1        1     3        3
4  0 2        1     2        1

Three rows for the bin at 1, each saying count = 3. Collapsing on the centre gives four bins of 3, 2, 1 and 4.

The count is taken from the count column rather than by counting rows. Measured, the two agree everywhere ggplot2 will build a dot plot – aes(weight = w) expands a bin to one row per weighted unit, and a fractional weight is refused outright ("weight must be nonnegative integers") – so this is not a correction, it is a preference for the stat's own answer over a count of the rows that happen to represent it.

Bin bounds come from the centre and the width rather than from xmin/xmax, which name the bin only in one of the two orientations: measured, binaxis = "y" puts the panel's whole range in ymin/ymax and the dot's own width in xmin/xmax, so neither pair is the bin there.

Value

A list of bins, each with centre, half and count, ascending; empty when the layer drew nothing readable


Drop selectors entries that carry no selector

Description

A layer that resolved no highlight target must say so by OMITTING the key, never by sending an empty list. The frontend hands layer.selectors straight to document.querySelectorAll(), and an empty array stringifies to "", which is a SyntaxError – thrown inside the trace constructor, so the whole figure fails to initialise: no announcement, no sonification, no braille, no keyboard entry, on a chart that still looks fine. An absent key is falsy and takes the frontend's own "no selectors" path instead.

Usage

drop_empty_selectors(node)

Arguments

node

A maidr-data node (list, or a leaf)

Details

Applied here rather than in each processor because every payload passes through this one point, and list() is the honest return value for a processor whose grob lookup found nothing.

Value

The node with empty selectors entries removed


Embed volume y-values from a bar layer into the candlestick layer's data.

Description

Strategy:

  1. If both layers have the same number of points, embed positionally. This is the canonical case: patchwork stacks two panels driven by the same date column, so the i-th candle and the i-th bar refer to the same trading day even if the bar layer's x is formatted differently from the candle's value.

  2. Otherwise, fall back to string-matching the candle's value field against the bar layer's x field.

Usage

embed_volume_into_candle_data(candle_layer, bar_layer)

Ensure a device is open before plotting (suppress default window)

Description

Ensure a device is open before plotting (suppress default window)

Usage

ensure_maidr_device()

Value

The current device ID after ensuring one is open


Escape a document so it can travel in an HTML attribute

Description

The result is pure ASCII: quotes, angle brackets and ampersands become entities, and every character outside ASCII becomes a numeric character reference (⁠&#xD55C;⁠). The browser decodes those while it parses the attribute, so the frame's document holds the original characters, and nothing between here and the page has a byte left to garble.

Usage

escape_for_attribute(html)

Arguments

html

Character string holding a complete HTML document

Details

That last part is the point. The escaped document goes on through knitr's output, an htmlwidget's JSON and pandoc, and under a C locale – a container, a CI runner, plenty of servers – more than one of those treats an unmarked string as native-encoded and rewrites each non-ASCII byte as the text ⁠<e2>⁠, ⁠<80>⁠, ⁠<a6>⁠. Leaving the bytes unmarked, which is what this did before, kept them intact only as far as this function: the ellipsis in the inlined maidr.js arrived in the page as ⁠<e2><80><a6>⁠, the script no longer parsed, and a chart rendered offline was a plain picture. ASCII has no encoding to get wrong.

Byte-wise, and deliberately not htmltools::htmlEscape(), which works in characters and so garbles the same way under a C locale. useBytes = TRUE keeps the substitutions off the encoding: every byte they replace is ASCII and every byte of a multi-byte UTF-8 sequence is not, so none can land inside one. Ampersands go first, or the ones the later replacements introduce would be escaped a second time; the character references come last for the same reason.

A string marked latin1 is converted to UTF-8 first; an unmarked one is taken to be UTF-8, which is what the SVG serialisation and the bundle produce, because enc2utf8() on it would assume the native encoding and garble it under a C locale. Unmarked bytes that are not valid UTF-8 are left as they are, since there is no telling what they spell.

Quotes and angle brackets are enough for a double-quoted attribute: an apostrophe cannot end one, and a newline inside one is legal and preserved, so both are left alone.

Value

The same document, escaped for use as an attribute value


Export the grid scene on the current (svglite) device

Description

Must be called with the chart already drawn on a device opened by open_svg_device(). Closes that device.

Usage

export_svg_scene(svg_string, width, height)

Arguments

svg_string

The function open_svg_device() returned.

width, height

Page size in inches.

Value

Character vector of SVG lines.


Extract Format Configuration for a Single Axis

Description

Extracts formatting configuration by inspecting the closure environment of scales label functions (e.g., scales::label_dollar, scales::label_percent).

Usage

extract_axis_format(built, axis = "x")

Arguments

built

A built ggplot2 object

axis

Either "x" or "y"

Value

Format configuration list or NULL


Extract a label from a possibly-wrapped axis value

Description

Accepts a bare string, an AxisConfig list with a label field, or NULL. Returns a character scalar.

Usage

extract_axis_label(value, default = "")

Arguments

value

Raw axis input

default

Default label when no value is present

Value

Character scalar label


Extract the body ⁠<g>⁠ id from a candlestick body selector

Description

Input form: "#geom_rect\.rect\.57\.1 rect" Output: "geom_rect.rect.57.1"

Usage

extract_body_grob_id(body_selector)

Extract Format Configuration from Built Plot

Description

Extracts MAIDR format configuration from the scale objects in a built ggplot2 plot. This looks for the maidr_format attribute attached by the maidr label functions.

Usage

extract_format_config(built)

Arguments

built

A built ggplot2 object from ggplot2::ggplot_build()

Value

A list with x and/or y format configurations, or NULL if no format config is found


Extract Format Configuration from scales Package Closure

Description

Inspects the closure environment of a scales label function to extract formatting parameters. This allows users to use scales:: functions directly without needing maidr:: wrappers.

Usage

extract_from_scales_closure(label_func)

Arguments

label_func

A label function (e.g., from scales::label_dollar)

Value

Format configuration list or NULL


Extract layout from a single leaf ggplot

Description

Mirrors the single-plot path in Ggplot2PlotOrchestrator: labels are read from the BUILT plot first. ggplot2 v4 resolves derived labels only while building – the stat-computed "count" of geom_bar(), and the mapped column names – so an unbuilt labels holds nothing but the explicit labs() overrides.

Usage

extract_leaf_plot_layout(leaf_plot, leaf_built = NULL)

Arguments

leaf_plot

The ggplot object

leaf_built

The leaf's built plot, or NULL when it is unavailable

Value

Layout with title and axes


Recursively extract leaf ggplots in patchwork addition order

Description

The order matches panel discovery order in find_patchwork_panels() (patches first, then the plot carried by the patchwork object itself), which is how leaves are paired with panels.

Usage

extract_patchwork_leaves(node)

Arguments

node

Patchwork node or ggplot object

Value

List of leaf ggplot objects


Extract the trailing numeric index used to scope grouped open/close element ids. For an id like "geom_rect.rect.57.1", returns "57".

Description

Extract the trailing numeric index used to scope grouped open/close element ids. For an id like "geom_rect.rect.57.1", returns "57".

Usage

extract_rect_index_from_id(grob_id)

Split a vioplot call's arguments into one sample per violin

Description

vioplot() takes its groups the way boxplot() does: as separate vectors (vioplot(a, b, c)), as a single list or data frame, or as a formula. Only the first two are read here; a formula call is left for the caller to decline, because resolving it needs the environment the call was made in and reconstructing that would be guesswork.

Usage

extract_vioplot_samples(args)

Arguments

args

The recorded call's arguments.

Value

A named list of numeric vectors, one per violin, or an empty list.


Rows of a layer's own data that belong to one facet panel

Description

The obvious values == group is wrong the moment the facet column holds an NA: == answers NA for that row, and [ turns an NA index into a fabricated all-NA row. One missing facet value therefore injects junk rows into EVERY panel's subset, not only the panel the NA belongs to.

Usage

facet_group_rows(values, group)

Arguments

values

The facet column of the layer's data

group

The panel's own facet group, possibly NA

Details

NA is a panel, not an absence. ggplot2 lays out a real panel for it and draws "NA" on its strip, so the matching rows have to be selected for that panel rather than dropped everywhere. %in% handles the ordinary levels (it scores an NA value as FALSE instead of NA), and is.na() picks out the NA panel's own rows. A facet column that literally contains the string "NA" stays distinct from a missing value: as.character() leaves the former as "NA" and the latter as NA_character_.

Value

A logical vector, one element per row, never NA


Find children matching a type pattern

Description

Find children matching a type pattern

Usage

find_children_by_type(parent_grob, pattern)

Arguments

parent_grob

The parent grob to search

pattern

The pattern to match in grob names

Value

Vector of matching child names


Find grob by element type pattern

Description

Searches recursively through a grob tree to find a grob whose name matches the pattern: ⁠graphics-plot-<number>-<element_type>-<number>⁠

Usage

find_graphics_plot_grob(grob, element_type, plot_index = NULL)

Arguments

grob

The grob tree to search (typically from ggplotify::as.grob())

element_type

The element type to search for (e.g., "rect", "lines", "points")

plot_index

Optional plot index to match (for multipanel layouts)

Value

The name of the first matching grob, or NULL if not found


Every grob of one plot drawn by a given element type

Description

find_graphics_plot_grob() answers with the first match, which is what a chart drawing its whole layer into one grob wants. A chart drawing one grob per datum – a pie's wedges, a mosaic's tiles – needs all of them, in the order gridGraphics numbered them.

Usage

find_graphics_plot_grobs(grob, element_type, plot_index)

Arguments

grob

The grob tree to search

element_type

The element type, e.g. "polygon"

plot_index

The plot (panel) index to match

Details

Sorted by the trailing grob number rather than by tree order or lexicographically: ⁠density =⁠ shading interleaves a segments grob between a pie's wedges so tree order is not contiguous, and a lexicographic sort would put -polygon-10 before -polygon-2.

Value

Character vector of grob names, ascending by grob number


Resolve the panel grob a layer belongs to

Description

Without a panel context this keeps the single-plot behaviour: the cell literally named "panel". With one, the panel is addressed by panel_ctx$panel_index into collect_gtable_panels(), whose order matches find_patchwork_panels(). Name matching is only a fallback because patchwork reuses panel names across nesting levels.

Usage

find_gtable_panel_grob(gt, panel_ctx = NULL)

Arguments

gt

Gtable object

panel_ctx

Panel context (panel_index, panel_name, ...), or NULL

Value

The panel gTree, or NULL when it cannot be resolved


The grob a ggplot2 layer drew, found by its slot in the panel

Description

ggplot2 lays a panel out as grill, a zeroGrob, then one grob per layer in layer order, then the panel's border – so this layer's grob is the one index places after that first blank. A search by grob name cannot tell two layers of the same geom apart: two geom_col()s in a panel are both geom_rect.rect.N, and a search that collects every match hands each layer the other's bars as well as its own.

Usage

find_layer_slot_grob(panel, index)

Arguments

panel

The panel grob, or NULL

index

The layer's index in the plot, or NULL

Details

LayerProcessor$find_layer_grob_tree() matches on the geom's own class, and a geom_col() layer is GeomCol while the grob it draws is named after geom_rect, so it does not serve here. Counting containers instead of slots does not either – a geom_text() layer occupies a slot and draws no container, so the counts stop lining up.

Value

The grob in the layer's slot, or NULL when the slot cannot be established


Find the original function in loaded namespaces

Description

Find the original function in loaded namespaces

Usage

find_original_function(function_name)

Arguments

function_name

Name of the function to find

Value

Original function or NULL if not found


Discover panels via gtable layout rows named ⁠^panel-<num>⁠ or ⁠^panel-<row>-<col>⁠ Returns a data.frame with panel_index, name, t, l, row, col

Description

Discover panels via gtable layout rows named ⁠^panel-<num>⁠ or ⁠^panel-<row>-<col>⁠ Returns a data.frame with panel_index, name, t, l, row, col

Usage

find_patchwork_panels(gtable)

Arguments

gtable

Gtable object

Value

Data frame with panel information


Collapse a layer's selector list into the one string its type is read as

Description

Every processor builds selectors with list() or lapply(), so even a single CSS selector reaches jsonlite::toJSON() as a list of one, and auto_unbox = TRUE cannot unbox a list: the payload says ⁠"selectors": ["#geom_rect\\.rect\\.2\\.1 rect"]⁠. maidr.js 4.x reads that array as one selector per data point, resolves it to one element for seven bars, and declines the whole layer – navigation and speech keep working while nothing on the chart ever changes colour (#316). Measured in headless Chromium against the bundled 4.9.0: ggplot2 bar, point, histogram, dodged, stacked and pie, and Base R barplot(), hist(), plot() and pie(), all announced their values and drew no highlight; rewriting only the JSON to a string restored every one of them.

Usage

flatten_single_selectors(node)

Arguments

node

A maidr-data node (list, or a leaf)

Details

For a layer whose type is in SINGLE_SELECTOR_LAYER_TYPES, a flat character vector or list of strings becomes one string. Several entries are joined with ", ": a selector list, which querySelectorAll() resolves in document order – the same order the layer's points are in, since a processor that names several containers (Base R pie() draws one polygon grob per wedge) walks them in drawing order. Nested lists (a per-cell grid), named objects (BoxSelector) and anything not made of strings are left exactly as they are, and so is every other layer type.

Applied where drop_empty_selectors() is, and for the same reason: every payload passes through this one point, the processors are honest about what they found, and the shape the frontend reads is decided once, against the bundle actually shipped, where a future bundle bump has one place to look.

Value

The node with single-selector layers carrying a string


Move a rect's anchor so its extent can be stated positively

Description

Only rects anchored at the low edge are touched. A centred or high-anchored rect with a negative extent covers a different span, and moving its anchor would move the rectangle rather than restate it – so those are left as they are, warning and all, rather than silently redrawn somewhere else.

Usage

flip_negative_extent(position, extent, anchor)

Arguments

position

The rect's x or y, as a unit

extent

The rect's width or height, as a unit

anchor

Where the rect is anchored on this axis, from rect_anchor(): 0 is the low edge

Value

List with the restated position and extent


Format Extraction Utilities

Description

Utility functions for extracting and converting axis format configuration from ggplot2 scale objects to MAIDR format specifications.


Describe a Panel-scoped Fallback

Description

Builds the warning text used when only some panels of a multi-panel figure lose their accessible data. Naming the panels matters: the rest of the figure still sonifies and navigates, so the user needs to know which panel went quiet rather than assuming the whole figure did.

Usage

format_panel_fallback_warning(panels)

Arguments

panels

Integer vector of 1-based panel numbers, in drawing order

Value

A single warning string


Sum a table over the variables a formula names

Description

The branch mosaicplot.formula() takes when data is already a table: the formula selects which margins to keep, and ~ . keeps all of them.

Usage

formula_margin_table(formula, data)

Arguments

formula

The recorded formula

data

The recorded table

Value

A table, or NULL


The table mosaicplot.formula() would build from a recorded formula call

Description

mosaicplot(~ Hair + Eye, data = df) is a common calling style, and the table it draws is recoverable rather than invented – so it is built the way mosaicplot.formula() builds it, by the same two branches, rather than by a rule of our own that would agree with it only sometimes.

Usage

formula_two_way_table(args)

Arguments

args

Recorded argument list, or NULL

Details

Which branch, and why it matters

Read from graphics:::mosaicplot.formula rather than assumed. A data that is a table (or has more than two dimensions) is summed over the formula's terms; anything else goes through model.frame() and is counted by row:

if (inherits(edata, "ftable") || inherits(edata, "table") ||
    length(dim(edata)) > 2) {
  data <- marginSums(as.table(data), varnames)
  mosaicplot(data, ...)
} else {
  mf <- eval(model.frame(formula, data, subset, na.action))
  mosaicplot(table(mf), ...)
}

The distinction is not cosmetic, and #248 proposed the other reading. A data frame of pre-counted cells – as.data.frame(HairEyeColor[, , 1]), sixteen rows and a Freq column – is counted by row like any other, so the chart draws sixteen equal cells:

table(model.frame(~ Hair + Eye, df))    xtabs(Freq ~ Hair + Eye, df)
        Brown Blue Hazel Green                  Brown Blue Hazel Green
  Black     1    1     1     1            Black    32   11    10     3
  Brown     1    1     1     1            Brown    53   50    25    15
  Red       1    1     1     1            Red      10   10     7     7
  Blond     1    1     1     1            Blond     3   30     5     8

The left table is what mosaicplot() draws; the right is the one it would draw if handed HairEyeColor[, , 1] directly. Reading the right one would announce numbers the chart does not show, which is the one thing this processor exists not to do. There is no Freq convention to match: mosaicplot.formula() has none.

What is declined

A recorded subset. mosaicplot.formula() passes it into model.frame(), so honouring it means reproducing an argument whose recorded form is not the expression model.frame() is given – and ignoring it would read rows the chart left out. Declined rather than guessed, which leaves the figure exactly the picture it is today.

A recorded na.action is not passed through, and does not need to be: table() drops a missing level whatever reached it, so the three actions a caller can name all draw the same chart. Measured on a frame with an NA in each column, na.omit, na.pass and na.exclude gave one table:

     b
a     p q
  x   2 1
  y   1 0

stats::na.omit is named here anyway, because that is what mosaicplot.formula() defaults to and matching it costs nothing.

Value

A table, or NULL when the call is not a readable formula call


Which measured refusal a fourfoldplot() call runs into, if any

Description

Returns NULL when the call is read, and otherwise the reason warn_fourfoldplot_declined() explains. Kept beside is_two_way_table() and called from the processor as well as from dispatch, so the two cannot disagree about which calls are readable.

Usage

fourfold_decline_reason(args)

Arguments

args

The arguments recorded from the fourfoldplot() call.

Details

The three gates, in the order they are asked:

  1. std. Only "ind.max" and "all.max" are read. Measured on ⁠c(tab) = 10, 40, 90, 160⁠, std = "ind.max" draws radii ⁠0.25, 0.50, 0.75, 1.00⁠ and r^2 * max(count) recovers ⁠10, 40, 90, 160⁠ exactly, so the wedge AREA is the count. Under the default "margins" the same table draws ⁠0.632456, 0.774597, 0.774597, 0.632456⁠ – r1 == r4, r2 == r3, four radii carrying one number.

  2. Shape. Exactly two dimensions, both of extent 2. Written as length(dims) == 2L && all(dims == 2L) and NOT as identical(dims, c(2L, 2L)), because dim() may carry the dimension names and identical() compares them – so the exact-comparison spelling can decline a table for a reason that has nothing to do with what the chart draws. Measured, dim(as.table(ftable(tb))) is c(Treatment = 2L, Outcome = 2L) on R 4.3.3 and unnamed on R 4.6.1, so which tables that spelling would have dropped varies by R version. The spelling used here does not vary, which is the point of it: whether the names survive is not a fact about the chart.

  3. Values. is.numeric() rather than as.numeric(): measured, a logical 2x2 prints TRUE/FALSE on the page as its count labels while as.numeric() would have announced 1/0 under z = "Count". Finite, non-negative and summing above zero, because the all-zero table makes stdize() return NaN four times and grid emits ZERO polygon grobs for it – measured, npoly = 0.

    all(counts >= 0) is load-bearing, not defensive, and the obvious reading of it is wrong. An NA count does stop fourfoldplot() under every std, with "missing value where TRUE/FALSE needed", so it never arrives. A NEGATIVE count stops only under the DEFAULT std = "margins", with that same message. Measured on c(-1, 2, 3, 4), std = "ind.max" and std = "all.max" both return normally with a "NaNs produced" warning and npoly = 0 – and those are exactly the two values that get past gate 1. So a negative count reaches this gate whenever it is drawable at all, its counts are finite and sum to 8, and this clause is the one that declines it. test-base-r-fourfoldplot.R pins both halves.

A 2x2xk array fails gate 2 twice over, and is asked about first only so the advisory can say "strata" rather than "not a 2x2 table": recorded_two_way_table() returns NULL for any input with three dimensions anyway. That includes ⁠2x2x1⁠, which draws exactly what the matrix spelling draws – a conservative, measured loss.

Value

NULL when the counts are readable, otherwise "std", "strata" or "table".


Whether a fourfoldplot() call draws the table rather than its odds ratio

Description

The dispatch-side wrapper over fourfold_decline_reason(), in the shape is_two_way_table() has over recorded_two_way_table().

Usage

fourfold_reads_counts(args)

Arguments

args

The arguments recorded from the fourfoldplot() call.

Value

TRUE when the four quadrants are the four counts.


Resolve a recorded std the way fourfoldplot() itself resolves it

Description

graphics::fourfoldplot runs std <- match.arg(std), so partial spellings are legal calls that draw the counts. Measured on R 4.3.3, every one of these is admitted:

Usage

fourfold_std(args)

Arguments

args

The arguments recorded from the fourfoldplot() call.

Details

  <absent> -> margins   "margins" -> margins   "m"   -> margins
  "ma"     -> margins   "ind.max" -> ind.max   "ind" -> ind.max
  "i"      -> ind.max   "in"      -> ind.max   "all.max" -> all.max
  "all"    -> all.max   "a"       -> all.max
  c("margins", "ind.max", "all.max") -> margins

So identical(args[["std"]], "ind.max") would silently decline five legal spellings. The one existing exact-comparison idiom in this package, base_r_subseries_layer_processor.R's spikes(), is complete only because monthplot's type choices are the single characters "l" and "h", where partial matching cannot produce a non-choice string. std's choices are multi-character, so the same idiom is incomplete here.

Anything match.arg() rejects resolves to "margins", which declines: measured, "IND.MAX", "x", NA_character_, character(0) and c("ind.max", "margins") all error inside match.arg(), and fourfoldplot() itself raises the identical error first, so none of them is reachable from a drawn chart. The tryCatch is there so that a reader never stop()s and takes the whole figure with it – the reason recorded_flag() gives for the same shape.

A non-character std is refused before match.arg() rather than coerced. Measured, fourfoldplot(tb, std = 1L), std = TRUE and std = factor("ind.max") all stop with "'arg' must be NULL or a character vector"; as.character(factor("ind.max")) would have been accepted here and would have read a call that upstream refuses to draw.

args[["std"]], not args$std: $ partial-matches a list, the collision recorded in recorded_main_title().

Value

One of "margins", "ind.max" or "all.max".


Generate robust CSS selector from grob name

Description

Creates a CSS selector that targets SVG elements by their ID pattern, without relying on panel structure or hardcoded values.

Usage

generate_robust_css_selector(grob_name, svg_element)

Arguments

grob_name

The name of the grob (e.g., "graphics-plot-1-rect-1")

svg_element

The SVG element type to target (e.g., "rect", "polyline")

Value

A robust CSS selector string, or NULL if grob_name is invalid


Generate robust selector for any element type

Description

Creates a robust CSS selector that works regardless of panel structure. This is the main function that layer processors should use.

Usage

generate_robust_selector(
  grob,
  element_type,
  svg_element,
  plot_index = NULL,
  max_elements = NULL
)

Arguments

grob

The grob tree to analyze

element_type

The element type to search for (e.g., "rect", "lines")

svg_element

The SVG element to target (e.g., "rect", "polyline")

plot_index

Optional plot index for multipanel layouts

max_elements

Optional limit on number of elements to target

Value

A robust CSS selector string, or NULL if element not found


Generate a unique ID for MAIDR plots

Description

Creates a unique identifier combining timestamp and counter to ensure uniqueness even when multiple plots are created within the same second.

Usage

generate_unique_id()

Value

Character string with unique ID


Grob-name prefix ggplot2 gives a geom's layer grob

Description

ggplot2 names a layer's grob after the snake-cased class of its geom, so GeomSmooth draws geom_smooth.gTree.<n> and GeomDensity draws geom_density.gTree.<n>.

Usage

geom_grob_prefix(geom)

Arguments

geom

A ggproto Geom object

Value

Character scalar prefix


Get Flat List of All Patchable Functions

Description

Returns a flat vector of all functions to patch.

Usage

get_all_function_names()

Value

Character vector of all patchable function names


Get All Patchable Functions

Description

Returns a list of all functions that should be patched, organized by class.

Usage

get_all_patchable_functions()

Value

List with HIGH, LOW, and LAYOUT function vectors


Get All Plot Groups

Description

Retrieves all plot groups for a device.

Usage

get_all_plot_groups(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

List of plot groups


Get Current Plot Index

Description

Returns the current active plot index for a device.

Usage

get_current_plot_index(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

Current plot index (integer)


Get Plot Calls from Device Storage

Description

Retrieves all plot calls for a specific device.

Usage

get_device_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

List of plot call entries


Filter Device Calls by Classification

Description

Retrieves plot calls of a specific classification level.

Usage

get_device_calls_by_class(
  device_id = grDevices::dev.cur(),
  class_level = "HIGH"
)

Arguments

device_id

Graphics device ID

class_level

Classification level: "HIGH", "LOW", "LAYOUT"

Value

List of filtered plot call entries


Get or Initialize Device State

Description

Retrieves the state object for a specific graphics device.

Usage

get_device_state(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

Device state list


Get or Initialize Device Storage

Description

Retrieves storage for a specific graphics device, creating it if needed.

Usage

get_device_storage(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID (from grDevices::dev.cur())

Value

Device storage list


Get Device Storage Summary

Description

Returns summary information about device storage (for debugging).

Usage

get_device_storage_summary()

Value

List with device storage statistics


Get facet group information for a panel

Description

Get facet group information for a panel

Usage

get_facet_groups(panel_info, built)

Arguments

panel_info

Panel information from layout

built

Built plot data

Value

List of facet group information


Get Fallback Image Format

Description

Get Fallback Image Format

Usage

get_fallback_format()

Value

Character string of the format to use


Get All Functions of a Specific Class

Description

Returns all function names for a given classification level.

Usage

get_functions_by_class(class_level)

Arguments

class_level

Classification level: "HIGH", "LOW", or "LAYOUT"

Value

Character vector of function names


Get the global plot system registry

Description

Get the global plot system registry

Usage

get_global_registry()

Value

PlotSystemRegistry instance


Get Group Count

Description

Returns the number of plot groups for a device.

Usage

get_group_count(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

Number of groups (integer)


Get HIGH-level Calls

Description

Get HIGH-level Calls

Usage

get_high_level_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

List of HIGH-level plot calls


Get LAYOUT Calls

Description

Get LAYOUT Calls

Usage

get_layout_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

List of LAYOUT-level plot calls


Get LOW-level Calls

Description

Get LOW-level Calls

Usage

get_low_level_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

List of LOW-level plot calls


Get original (unwrapped) function by name

Description

Get original (unwrapped) function by name

Usage

get_original_function(function_name)

Arguments

function_name

Name of the function

Value

The original function


Get Panel Configuration

Description

Returns the panel configuration for a device.

Usage

get_panel_config(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

Panel configuration list


Get recorded plot calls

Description

Get recorded plot calls

Usage

get_plot_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID (defaults to current device)

Value

List of recorded plot calls


Get Plot Group by Index

Description

Retrieves a specific plot group.

Usage

get_plot_group(device_id = grDevices::dev.cur(), group_index)

Arguments

device_id

Graphics device ID

group_index

Index of the group to retrieve

Value

Plot group list or NULL if not found


Group Device Calls into Plot Units

Description

Groups all calls from a device into logical plot units. Each group contains one HIGH-level call and its associated LOW-level calls.

Usage

group_device_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

List of plot groups, each containing HIGH and LOW calls


Check if Device Has Calls

Description

Checks whether a specific device has any recorded plot calls.

Usage

has_device_calls(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

TRUE if device has calls, FALSE otherwise


Whether the density half of a violin can be computed at all

Description

sm is in vioplot's Depends, so it is attached wherever a violin can have been drawn: in any normal installation this cannot fail, and the branch is unreachable. Kept as a guard rather than an assumption only so a broken or partial installation surfaces as no violin layer rather than a bare "there is no package called 'sm'" thrown from inside a rendering path.

Usage

has_sm_package()

Value

TRUE when sm can be loaded.


Does This heatmap() Call Apply revC?

Description

heatmap() normally puts reordered row 1 at the bottom of the y axis, but its revC argument flips the drawing so row 1 lands at the top. revC is not part of the ordering heatmap() returns, and it defaults to identical(Colv, "Rowv") – which is TRUE for every symm = TRUE call, since Colv itself defaults to "Rowv" there.

Usage

heatmap_applies_revc(args)

Arguments

args

Recorded heatmap() arguments

Value

TRUE when revC applies, i.e. the drawn rows read top-down


Resolve a heatmap()'s Caller-Supplied Axis Labels

Description

heatmap() gives an explicit ⁠labRow=⁠/⁠labCol=⁠ priority over the matrix's own dimnames, and subscripts it by the same ordering it applies to the data: labRow[rowInd] %||% rownames(x) %||% (1L:nr)[rowInd]. Reading the labels off the reordered matrix therefore announced the dimnames – or, for an unnamed matrix, bare indices – while the axis showed the caller's strings.

Usage

heatmap_caller_labels(labels, ordering)

Arguments

labels

The recorded ⁠labRow=⁠ or ⁠labCol=⁠ argument, or NULL

ordering

The matching rowInd/colInd. Never NULL: when no ordering was recovered the caller passes the identity, because heatmap() applies a subscript either way

Details

A short or NA-carrying vector is passed through rather than rejected: labRow[rowInd] yields NA for the positions it cannot fill, and grid draws that as the glyphs "NA", so mirroring it keeps the announcement equal to the picture. Subscripting is also what holds the result to one label per row: a caller who supplies too many gets the surplus dropped, exactly as heatmap() drops it.

Value

Character vector in drawn order, or NULL when the caller supplied no labels for this axis


Group hexagonal bins into lattice rows

Description

A hex lattice staggers alternate rows by half a cell, which is what lets the hexagons tessellate. Read as a grid of counted cells it is a heatmap, but the stagger means a bin's column index is not its position – bin 3 of one row and bin 3 of the next sit at different x – so the frontend announces centres rather than indices and this returns the rows rather than a rectangle.

Usage

hexbin_lattice(built_data)

Arguments

built_data

A layer's computed data, carrying x, y and count, one row per drawn hexagon

Details

Rows ascend in y, because the frontend's UPWARD steps to the next row index, the same convention the heatmap follows. Within a row bins ascend in x.

Rows are ragged and are left that way. stat_binhex() emits only the bins that hold something, so the lattice is genuinely uneven; padding it would put cells on the chart that were never drawn.

The returned order is the point of the function. It is the built-data row behind each emitted bin, in emission order, and the selectors are built from it – so the highlight follows the regrouping instead of relying on the DOM happening to be in the same order. stat_binhex() does emit its rows bottom-first today, which means that reliance would pass; it would also be undetectable the moment it stopped, since a hexbin announces centres and has no index to contradict.

Value

A list with data (rows of bins, each a list of x, y and count) and order (the built-data row behind each bin, in emission order)


Initialize Base R function patching

Description

This function sets up the function patching system by wrapping Base R plotting functions (HIGH, LOW, and LAYOUT levels). It should be called before any Base R plotting commands.

Usage

initialize_base_r_patching(include_low = TRUE, include_layout = TRUE)

Arguments

include_low

Include LOW-level functions (lines, points, etc.)

include_layout

Include LAYOUT functions (par, layout, etc.)

Details

Wrappers are installed once into the package namespace during .onLoad (when the namespace is still open). Subsequent calls just activate the patching flag; wrappers check this flag to decide whether to record calls or simply pass through to the original function.

Value

NULL (invisible)


Base R System Initialization

Description

Initialize and register the Base R system with the global registry. This function sets up the Base R adapter and processor factory.

Usage

initialize_base_r_system()

Value

NULL (invisible)


ggplot2 System Initialization

Description

Initialize and register the ggplot2 system with the global registry. This function sets up the ggplot2 adapter and processor factory.

Usage

initialize_ggplot2_system()

Value

NULL (invisible)


Initialize MAIDR default options

Description

Sets default values for MAIDR options during package load. Does not override options already set by the user (e.g. in .Rprofile).

Usage

initialize_maidr_options()

Inject candlestick open/close virtual line elements into the SVG

Description

tidyquant's geom_candlestick() draws each candle's body as a single ⁠<rect>⁠. Upstream maidr JS auto-derives open and close highlight positions from the rect's bounding-box edges, but its heuristic assumes a natural SVG y-axis (y increasing downward). gridSVG exports content inside a ⁠translate(0, h) scale(1, -1)⁠ group, so y is flipped. The upstream heuristic therefore swaps open and close on every candle.

Usage

inject_candlestick_open_close(svg_content, maidr_data)

Arguments

svg_content

Character vector of SVG lines

maidr_data

The maidr-data structure (read-only; used to look up per-candle trend)

Details

We sidestep the heuristic by emitting two sibling ⁠<g>⁠ containers (one for opens, one for closes), each holding N invisible ⁠<line>⁠ elements positioned at the correct edge of the corresponding body rect (computed from the per-candle trend). The candlestick processor emits explicit selectors.open / selectors.close referencing these groups, so JS uses our placed elements directly and skips its own derivation.

Value

Modified SVG content (character vector). If parsing fails or no candlestick layers are present, returns svg_content unchanged.


Document-level implementation of inject_candlestick_open_close()

Description

Mutates svg_doc in place (xml2 documents are references).

Usage

inject_candlestick_open_close_doc(svg_doc, maidr_data)

Arguments

svg_doc

Parsed SVG document (xml2)

maidr_data

The maidr-data structure

Value

TRUE if the document was modified


Inject svg_x/svg_y coordinates into violin_kde layer data

Description

After grid.draw(gt) has been called on a PDF device, this function navigates to the panel viewport, maps data coordinates to SVG points, and injects svg_x/svg_y into each ViolinKdePoint. Temporary metadata fields (.panel_x_range, .panel_y_range, .is_horizontal, .panel_index, .panel_name, data_left_x, data_right_x, data_y) are stripped from the output.

Usage

inject_violin_kde_svg_coords(gt, maidr_data)

Arguments

gt

The gtable object (used to find the panel viewport paths)

maidr_data

The maidr-data structure (modified in place)

Details

Each violin_kde layer is mapped through the viewport of the panel it was extracted from, so a violin inside a patchwork gets its own panel's coordinates rather than the first panel's. Nested compositions repeat panel names across levels, so the layer's .panel_index (its position in collect_gtable_panels(), which matches find_patchwork_panels()) is the primary key and the name is only a fallback.

Value

Updated maidr_data with svg_x/svg_y injected


Check if Base R interception is enabled

Description

Check if Base R interception is enabled

Usage

is_base_r_enabled()

Value

TRUE if Base R interception is active


Whether an indicator call is quantmod's volume panel, addVo()

Description

Whether an indicator call is quantmod's volume panel, addVo()

Usage

is_chartseries_volume_ta(calls)

Arguments

calls

Indicator calls from chartseries_ta_calls()

Value

A logical vector, FALSE for NA


Check if Fallback is Enabled

Description

Check if Fallback is Enabled

Usage

is_fallback_enabled()

Value

Logical indicating if fallback rendering is enabled


Check if Fallback Warning is Enabled

Description

Check if Fallback Warning is Enabled

Usage

is_fallback_warning_enabled()

Value

Logical indicating if warnings should be shown


Whether one record matches the run's fields and types exactly

Description

Whether one record matches the run's fields and types exactly

Usage

is_flat_record(record, fields, types)

Arguments

record

A candidate record

fields

The run's field names, in order

types

The run's field types, in order

Value

TRUE when every field is one attribute-free value of its type


Is a recorded argument a formula, evaluated or not?

Description

A value recorded on the ordinary path is a formula object; on the NSE path the same argument is the unevaluated call to ~, which inherits() does not recognise.

Usage

is_formula_argument(value)

Arguments

value

A recorded argument

Value

TRUE for either spelling


Check if ggplot2 interception is enabled

Description

Check if ggplot2 interception is enabled

Usage

is_ggplot2_enabled()

Value

TRUE if ggplot2 interception is active


Whether a dotchart() call draws more than one group

Description

dotchart() draws a group per matrix column, or per level of groups, with a header in the left margin and every dot in one shared grob. The grouping is what the chart is drawn to show and there is nothing in a flat dot layer to carry it, so such a call is declined rather than flattened.

Usage

is_grouped_dotchart(args)

Arguments

args

The arguments recorded from the dotchart() call.

Value

TRUE when the call draws groups, otherwise FALSE.


Check if Function is HIGH-level

Description

Check if Function is HIGH-level

Usage

is_high_level_function(function_name)

Arguments

function_name

Name of the function

Value

TRUE if HIGH-level, FALSE otherwise


Check if current knitr output format is HTML

Description

Detects whether the current RMarkdown document is being rendered to HTML format (html_document, bookdown, etc.) vs non-HTML formats (pdf, epub, etc.)

Usage

is_html_output()

Value

TRUE if rendering to HTML, FALSE otherwise


Check Internal Guard Flag

Description

Checks if we're currently in internal code (to prevent recursive tracing).

Usage

is_internal_call()

Value

TRUE if internal guard is set, FALSE otherwise


Check if Function is LAYOUT-level

Description

Check if Function is LAYOUT-level

Usage

is_layout_function(function_name)

Arguments

function_name

Name of the function

Value

TRUE if LAYOUT-level, FALSE otherwise


Check if Function is LOW-level

Description

Check if Function is LOW-level

Usage

is_low_level_function(function_name)

Arguments

function_name

Name of the function

Value

TRUE if LOW-level, FALSE otherwise


Check if MAIDR interception is globally enabled

Description

Check if MAIDR interception is globally enabled

Usage

is_maidr_enabled()

Value

TRUE if the master switch is on


Check if MAIDR RMarkdown Mode is Enabled

Description

Check if MAIDR RMarkdown Mode is Enabled

Usage

is_maidr_on()

Value

Logical indicating if MAIDR mode is active


Check if the current device is the MAIDR temp device

Description

Check if the current device is the MAIDR temp device

Usage

is_maidr_temp_device()

Value

TRUE if current device is the temp device


Check if Multi-panel Layout is Active

Description

Check if Multi-panel Layout is Active

Usage

is_multipanel_active(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

TRUE if multi-panel layout is active, FALSE otherwise


Check Whether a Panel Configuration Describes a Multi-panel Grid

Description

Check Whether a Panel Configuration Describes a Multi-panel Grid

Usage

is_multipanel_config(panel_config)

Arguments

panel_config

Panel configuration from detect_panel_configuration()

Value

TRUE for a multi-panel (mfrow/mfcol/layout) grid


Check if patching is active

Description

Check if patching is active

Usage

is_patching_active()

Value

TRUE if patching is active, FALSE otherwise


Check if Base R patching is currently active

Description

Wrappers are installed once during .onLoad and remain in the namespace. This flag controls whether they record calls or act as pass-through.

Usage

is_patching_enabled()

Value

TRUE if patching is active


Whether a list is a run of flat, uniformly typed records

Description

The first record sets the field names and types every other record must match exactly.

Usage

is_record_run(node)

Arguments

node

A non-empty list

Value

TRUE when records_as_frames() may convert node


Whether the first record's fields can become data frame columns

Description

Each field becomes one column, so the names must be unique and non-empty and every type one that a column can hold a scalar of.

Usage

is_record_schema(fields, types)

Arguments

fields

The first record's names

types

The first record's field types

Value

TRUE for unique, non-empty names over scalar-capable types


Whether a Base R type argument draws spikes

Description

type = "h" draws a vertical line from the baseline to each value – "histogram-like" in plot()'s own wording – and joins nothing to anything. Read as a lollipop layer, which the core builds on BarTrace: one value per position, with no claim about the space between two of them.

Usage

is_spike_plot_type(plot_type)

Arguments

plot_type

The type argument recorded from the plot call (may be NULL when the caller did not pass one).

Details

Case-sensitive, like the step test beside it: plot() has no "H".

Value

TRUE when plot_type is "h", otherwise FALSE.


Does a Base R type argument request a stairstep?

Description

graphics::plot() and graphics::lines() draw stairsteps for type = "s" (horizontal segment first) and type = "S" (vertical segment first). The comparison is case-sensitive because those two letters mean different things.

Usage

is_step_plot_type(plot_type)

Arguments

plot_type

The type argument recorded from the plot call (may be NULL when the caller did not pass one).

Value

TRUE when plot_type is "s" or "S", otherwise FALSE.


Whether a mosaicplot() call was handed a two-way table

Description

A mosaic layer has one category axis and one fill, so it can carry a two-dimensional table and no more. mosaicplot() accepts deeper ones and splits them recursively.

Usage

is_two_way_table(args)

Arguments

args

The arguments recorded from the mosaicplot() call.

Details

The table itself is resolved by recorded_two_way_table(), which the processor also reads, so dispatch and extraction cannot disagree about which calls are readable.

Value

TRUE when the call's table has exactly two dimensions.


Is this panel a volume-only bar panel (single bar layer, no other layers)?

Description

Is this panel a volume-only bar panel (single bar layer, no other layers)?

Usage

is_volume_only_bar_panel(panel)

Has patchwork wrapped this leaf?

Description

inset_element(), free() and wrap_elements() each prepend their own class and place the plot in a gtable cell whose name is not a plain "panel-N", so panel discovery never sees it. Test for the wrapper rather than naming them, so one added later behaves the same.

Usage

is_wrapped_leaf(leaf_plot)

Arguments

leaf_plot

A leaf of a patchwork composition

Value

Logical


Look up a layer's rebuilt frame

Description

Look up a layer's rebuilt frame

Usage

jitter_cache_get(layer, layer_index)

Arguments

layer

The ggplot2 Layer the frame was computed from.

layer_index

Index of that layer within its plot.

Value

The cached data frame, or NULL on a miss.


Remember a layer's rebuilt frame

Description

Remember a layer's rebuilt frame

Usage

jitter_cache_set(layer, layer_index, data)

Arguments

layer

The ggplot2 Layer the frame was computed from.

layer_index

Index of that layer within its plot.

data

The rebuilt data frame, or NULL when the rebuild failed.

Value

Invisibly NULL.


One string from a flat list of selectors, or the input untouched

Description

One string from a flat list of selectors, or the input untouched

Usage

join_selector_list(selectors)

Arguments

selectors

A layer's selectors entry

Value

A single string when selectors is a flat, unnamed collection of non-empty strings; otherwise selectors as given


Custom knit_print Method for density Objects

Description

Suppresses the default printing of density return values in RMarkdown. The density() function is not patched (it's in stats, not graphics), so we need this method to suppress its output.

Usage

knit_print.density(x, options = list(), ...)

Arguments

x

A density object (from density())

options

Chunk options from knitr

...

Additional arguments (ignored)

Value

An invisible empty string


Custom knit_print Method for ggplot Objects

Description

Converts ggplot objects to MAIDR widgets for accessible rendering in RMarkdown. Uses iframe-based isolation to ensure each plot has its own MAIDR.js context. Automatically falls back to image rendering for unsupported plot types or non-HTML output formats (PDF, EPUB).

Usage

knit_print.ggplot(x, options = list(), ...)

Arguments

x

A ggplot object

options

Chunk options from knitr

...

Additional arguments (ignored)

Value

A knit_asis object containing the iframe HTML or inline image


Custom knit_print Method for histogram Objects

Description

Suppresses the default printing of histogram return values in RMarkdown. The plot is already rendered via the plot hook; this prevents the histogram object structure from being printed as text output.

Usage

knit_print.histogram(x, options = list(), ...)

Arguments

x

A histogram object (from hist())

options

Chunk options from knitr

...

Additional arguments (ignored)

Value

An invisible empty string


Whether an axis label names a lane or just writes its own coordinate out

Description

A continuous axis has labels too – "1", "0.5", "1,000" – but those are formatted renderings of the numbers rather than names for them, which is the distinction discrete_axis_labels() already draws for a discrete scale and xability/py-maidr#533 settled for lanes: an explicit tick names the lane it sits in, and the axis's own coordinates do not.

Usage

label_names_its_lane(label, break_value)

Arguments

label

One axis label

break_value

The break the label was drawn at

Details

So the test is "is this label a rendering of its own number", not "is there a label". Measured on ggplot2 3.4.4, bands at 0.6-1.4, 1.6-2.4, 2.6-3.4:

default scale                             1, 2, 3            -> position
breaks = 1:3, labels = c("design", ...)   design, build, test -> name
breaks = 1:3, labels = c("1", "2", "3")   1, 2, 3            -> position
breaks = 1:3                              1, 2, 3            -> position
breaks = seq(0, 4, 0.5)                   0.5, 1.0, 1.5, ... -> position

An author who writes labels = c("1", "2", "3") is deliberately naming lanes "1", "2" and "3" and gets positions instead. That is the trade py-maidr made, and it is the safe direction: a lane called "2" says less than a lane called by the position 2 it sits at.

The strip is what keeps scales::comma, scales::dollar and a padded label from reading as names – measured, "1,000" at the break 1000 and "$1" at 1 both come back FALSE. It does not save scales::percent, which renders the break 1 as "100%": measured, that reads as a name and a lane is called "100%" rather than 1. The cost is a cosmetic mis-name inside a schedule the author already declared, not a false claim, so it is recorded rather than chased.

Value

TRUE when the label is a name rather than the break written out


The name of the lane at one position on the lane axis

Description

A discrete scale lays its levels out at 1, 2, 3 and so on, so the name is the level at that index. A continuous lane axis has no names and the position itself is what a reader is told – GanttPoint$x takes a number or a string for exactly this reason.

Usage

lane_name(slot, lane_names = NULL)

Arguments

slot

One or more positions on the lane axis

lane_names

The lane names in drawn order, or NULL

Value

The lane names, or the positions unchanged


Which axis a declared schedule runs its lanes up

Description

maidr_gantt(lane_axis = ) is an argument rather than an inference, and this reads it back. "y" when the author said nothing, which is the ordinary horizontal schedule: lanes stacked up y, spans running along x. Also "y" when only the constructor survived – the lane axis cannot be recovered from a call whose argument may be a variable – which matches the default the author most likely took.

Usage

layer_declared_lane_axis(layer)

Arguments

layer

A ggplot2 layer object

Value

"y" or "x"


Does a layer's curve land in the panel's auto-named polyline population?

Description

layer_polyline_grobs() keeps every polyline that no geom-named tree claims, so the population it returns is "layers that draw a BARE polyline" – which is not the same set as "layers typed line". geom_function() is typed smooth and draws one anyway: GeomFunction inherits GeomPath$draw_panel(), which returns a polylineGrob with nothing named around it, while GeomSmooth and GeomDensity wrap theirs in geom_smooth.gTree / geom_density.gTree and are skipped whole.

Usage

layer_draws_bare_polyline(layer, type)

Arguments

layer

A ggplot2 layer.

type

The layer type the adapter detected for it.

Details

Counting only the line-ish types therefore counted a population one smaller than the one being indexed, and a geom_function() drawn before a geom_line() handed the line the function's curve to highlight (#204). Both charts read correctly the whole time, which is the highlight-only shape xability/maidr#814 names.

Value

TRUE when the layer draws a bare, auto-named polyline.


Whether a layer was drawn by annotate() rather than by a geom

Description

annotate() is ggplot2's word for decoration: a highlighted region, a label, an arrow pointing at something. Whatever geom it happens to use, the function is the author saying "this is not data".

Usage

layer_is_annotation(layer)

Arguments

layer

A ggplot2 layer object

Details

ggplot2 records which function built each layer, so this is exact rather than a guess about geometry. Measured on ggplot2 3.4.4, layer$constructor holds the matched call and its head is the function name:

geom_rect(aes(...))                       -> geom_rect
annotate("rect", xmin = 2, xmax = 3, ...) -> annotate
annotate("text", x = 2, y = 3, ...)       -> annotate

It survives disguise, which is what makes it better than the shape-based rules considered in #197. annotate() sets inherit.aes = FALSE and show.legend = FALSE, so those two look like a signature – but a geom_rect() written with both still reports geom_rect, and a rule keyed on them would call that user's data decoration.

Deliberately not a rule about what an annotation may draw. The whole point of asking the constructor is that the answer does not depend on the mark: annotate("segment") is an arrow, not a schedule, and a geometry test would have to claim or refuse it on its coordinates.

A layer with no constructor – a ggplot2 that stopped recording it, or a layer built by hand – answers FALSE and keeps whatever reading it had.

Value

TRUE when annotate() built the layer


Whether a layer's author declared it a schedule

Description

maidr_gantt() is maidr's word for "these rectangles are intervals in lanes", the way annotate() is ggplot2's word for "this is decoration". A rectangle layer carries no evidence of which it is – the eight-chart table above the GeomRect branch in detect_layer_type() is the measurement that closed every structural rule – so the function the author called is the answer rather than evidence towards it.

Usage

layer_is_declared_gantt(layer)

Arguments

layer

A ggplot2 layer object

Details

Two carriers are read, field first, because they fail in opposite directions. Measured on ggplot2 3.4.4:

maidr_gantt(m)                  head=maidr_gantt         field=gantt
maidr::maidr_gantt(m)           head=::/maidr/maidr_gantt field=gantt
one-deep user wrapper           head=maidr_gantt         field=gantt
two-deep user wrapper           head=maidr_gantt         field=gantt
do.call(maidr_gantt, list(m))   head=<coerce error>      field=gantt
geom_rect(m)                    head=geom_rect           field=NULL
annotate("rect", ...)           head=annotate            field=NULL

do.call() leaves the closure itself at constructor[[1]], where as.character() raises "cannot coerce type 'closure' to vector of type 'character'" – the identical hole layer_is_annotation() has for do.call(annotate, ...) – and the field answers there. The constructor answers for a ggplot2 that re-instantiated the layer through ggproto() and dropped a field it did not know. Both are wrapped, so a layer that answers neither is refused rather than raising.

Membership rather than equality on the head, for the reason layer_is_annotation() gives: maidr::maidr_gantt(...) heads as c("::", "maidr", "maidr_gantt").

Value

TRUE when maidr_gantt() built the layer


Whether a layer's points were displaced from their data positions

Description

Whether a layer's points were displaced from their data positions

Usage

layer_is_jittered(layer)

Arguments

layer

A ggplot2 Layer.

Value

TRUE when the layer carries a jittering position adjustment.


Whether a line layer maps the ROC's own vocabulary

Description

A ROC curve drawn as geom_line() or geom_path() carries no evidence of what it means except its column names, and two producers name them after the rates themselves: pROC::ggroc() maps specificity or 1-specificity against sensitivity, and ggplot2::autoplot() of a yardstick::roc_curve() maps 1 - specificity against sensitivity. Those names are the claim, and nothing else is read as one – a line of fpr against tpr under any other column names is declared with maidr_roc() instead, because a rule loose enough to catch it would catch charts that are not ROC curves.

Usage

layer_maps_roc_rates(layer, plot_object)

Arguments

layer

A ggplot2 layer

plot_object

The plot the layer belongs to

Value

TRUE when the layer's x and y are specificity and sensitivity


Which of a plot's layers drew no rows

Description

The one place the emptiness rule lives, so the orchestrator and the patchwork leaf path cannot disagree about it. A leaf inside a patchwork composition is classified by ggplot2_patchwork_utils.R rather than by Ggplot2PlotOrchestrator$detect_layers(), so a rule written only in the orchestrator would have left every composed chart ghosting (#232).

Usage

layers_that_drew_nothing(plot)

Arguments

plot

A ggplot object.

Details

One ggplot_build() per plot, not per layer. That is what makes this affordable at all: the same question asked inside detect_layer_type() would multiply the build by the layer count, which is why #231 applied it to one geom only.

A build that cannot answer reports nothing empty. A plot that will not build is a bigger problem than this one, and it is about to be met by whatever else needs the build.

Value

Integer indices into plot$layers, possibly empty.


Key aesthetic values so a missing one stays distinct from the string "NA"

Description

paste() stringifies NA to "NA", which collapses a missing value onto a level that is literally those two characters – so a grid keyed that way cannot tell the two apart, and a duplicate test built on it does not see the collision. Every present value is prefixed with "=", so the missing key is one nothing else can produce.

Usage

level_keys(values)

Arguments

values

A vector of aesthetic values

Details

The missing key is a non-empty string on purpose. "" would read as the obvious sentinel and is a trap: R lets "" %in% names(x) answer TRUE and then throws "subscript out of bounds" on x[[""]], because empty is how a name-less element is spelled. A lookup guarded by %in% – which is how both bar processors read their cells – would pass the guard and abort.

Value

Character vector of keys, one per value, never NA


The text ggplot2 draws for one level

Description

The missing level reads as the two characters "NA", which is what is printed on its axis tick and in its legend key, so the announcement names the same category a sighted reader is looking at. A level that is literally the string "NA" reads the same way and is a different level: two categories, two ticks, both saying "NA", which is what ggplot2 draws.

Usage

level_label(level)

Arguments

level

One level from discrete_level_order()

Value

A length-1 character string, never NA


Find the environment a name is bound in

Description

Walks the enclosing chain from env the way R's own lookup does, stopping once the global environment has been checked: names that resolve beyond it live in attached packages, which no plotting loop rebinds.

Usage

locate_binding_env(name, env)

Arguments

name

Name to look up

env

Environment to start from

Value

The environment holding name, or NULL when unbound


Log Plot Call to Device Storage

Description

Records a plot call in the device-specific storage.

Usage

log_plot_call_to_device(
  function_name,
  call_expr,
  args,
  device_id = grDevices::dev.cur(),
  call_env = NULL
)

Arguments

function_name

Name of the plotting function

call_expr

The call expression

args

List of function arguments

device_id

Graphics device ID

call_env

Optional environment for replaying unevaluated (NSE) arguments recorded in args

Value

NULL (invisible)


MAIDR Package Options

Description

Configure MAIDR interception and display behavior using R's options system.

Available Options

maidr.auto_show

Logical. Master switch for all MAIDR interception. When FALSE, all plotting functions behave as standard R. Default: TRUE.

maidr.base_r

Logical. Enable Base R plot interception. When TRUE, Base R plots are captured and displayed in the MAIDR viewer. Default: TRUE.

maidr.ggplot2

Logical. Enable ggplot2 auto-display. When TRUE, ggplot2 objects are automatically rendered in the MAIDR viewer instead of the standard graphics device. Default: TRUE.

maidr.startup_message

Logical. Show startup message when package is loaded. Default: TRUE.

maidr.dotpad_sdk_url

Character. URL of a copy of the DotPad tactile-display SDK module (DotPadSDK-3.0.3.js) that you serve yourself. maidr.js does not bundle the SDK, whose braille engine is a 14 MB liblouis build; unless told otherwise it imports the vendor's copy from jsDelivr the first time a DotPad is connected, from a document rendered with use_cdn = FALSE as much as any other. Set this to keep that path off the network too. Falls back to the environment variable MAIDR_DOTPAD_SDK_URL. Default: unset.

maidr.dotpad_asset_base_url

Character. URL of the directory holding the SDK's braille engine (liblouis.js, .wasm, .data), needed only when it is not the lib/ folder beside the module. Falls back to the environment variable MAIDR_DOTPAD_ASSET_BASE_URL. Default: unset.

maidr.dotpad_sdk_dir

Character. Directory where maidr_download_dotpad_sdk() writes the SDK and where show() and save_html() look for it when a document is rendered with use_cdn = FALSE. Falls back to the environment variable MAIDR_DOTPAD_SDK_DIR, then to a per-user cache directory. Default: unset.

maidr.cdn_version

Character. Which MAIDR.js the CDN paths load: a version such as "4.9.0" (a leading v is accepted), "bundled" for the version bundled with this package (maidr:::MAIDR_VERSION), or "latest" for jsDelivr's @latest tag. Either tag skips the version lookup described below. Anything else warns once and is ignored. Falls back to the environment variable MAIDR_CDN_VERSION. Default: unset, which loads the latest published version.

maidr.cdn_timeout

Numeric. Seconds allowed for the whole CDN version lookup, clamped to between 0.1 and 30. Falls back to the environment variable MAIDR_CDN_TIMEOUT. Default: 3.

maidr.locale_base_url

Character, or FALSE. Where maidr.js fetches a language other than English from, in the documents that load the bundled maidr.js (see the section on languages below): a directory URL to serve the locale packs yourself, or "" or FALSE to declare nothing, which keeps such a document off the network and in English. A URL set here is declared in CDN documents too. Falls back to the environment variable MAIDR_LOCALE_BASE_URL. Default: unset, which declares the packs of the bundled version on jsDelivr.

Setting Options

Options can be set in your .Rprofile to persist across sessions:

# Disable ggplot2 interception by default
options(maidr.ggplot2 = FALSE)

# Disable all interception
options(maidr.auto_show = FALSE)

# Suppress startup message
options(maidr.startup_message = FALSE)

# Serve the DotPad SDK from your own server instead of jsDelivr
options(
  maidr.dotpad_sdk_url = "https://example.org/vendor/DotPadSDK-3.0.3.js",
  maidr.dotpad_asset_base_url = "https://example.org/vendor/lib/"
)

# Serve MAIDR's language packs yourself, or never fetch one (English only)
options(maidr.locale_base_url = "https://example.org/maidr/")
options(maidr.locale_base_url = FALSE)

DotPad SDK and offline documents

The two maidr.dotpad_*_url options are written into every document this package produces (show(), save_html(), the htmlwidget, knitr and Shiny) as the globals window.MAIDR_DOTPAD_SDK_URL and window.MAIDR_DOTPAD_ASSET_BASE_URL, ahead of maidr.js, which reads them when a DotPad is connected. Nothing is written when neither is set. Without them a DotPad needs network access to jsDelivr on first connect, even from a use_cdn = FALSE document; the rest of the document works offline either way.

The other way to keep a DotPad off the network is to download the SDK once with maidr_download_dotpad_sdk(): show() and save_html() then copy it into lib/dotpad-sdk-<version>/ beside every use_cdn = FALSE document and declare the globals with that relative path. A configured URL wins over a downloaded copy. The widget, knitr and Shiny paths render their charts in srcdoc frames, where a relative path has nothing to resolve against, so they use only the URL options.

Languages other than English

MAIDR.js reads a chart in English on its own, and in Korean, Japanese, Chinese, Spanish, German, French, Italian or Hindi from a locale pack, locale-<code>.js, that it fetches from beside itself when the reader's language is chosen or detected. A document that loads MAIDR.js from the CDN finds the packs there. This package does not bundle them – they would add 0.8 MB to an installed package already over CRAN's size guideline – so every document that loads the bundled MAIDR.js, from lib/ or inlined, declares window.maidrLocaleBaseUrl ahead of it: the packs of the bundled version on jsDelivr, unless maidr.locale_base_url names another place or none. English needs no network; another language is fetched when the reader is online and stays English when they are not. A page that declares window.maidrLocaleBaseUrl itself keeps its own.

Which MAIDR.js the CDN serves

Documents that load MAIDR.js from the jsDelivr CDN – show() and save_html() with use_cdn = TRUE, and the widget, knitr and Shiny paths when they find the machine online – load the latest published MAIDR.js, as the Python binding does. Documents rendered with use_cdn = FALSE load the copy bundled with this package, and reach the network only for the two things described above: a DotPad's SDK and a language other than English.

The first CDN document in an R session asks which version is the latest: jsDelivr's data API (https://data.jsdelivr.com/v1/packages/npm/maidr/resolved?specifier=latest), then the npm registry (https://registry.npmjs.org/-/package/maidr/dist-tags) if that fails, within maidr.cdn_timeout seconds for the two together. The answer is kept for the rest of the session, and the document names that version, which never changes under the reader. When neither answers – offline, blocked, timed out – that too is kept, no error is raised, and the document names the version bundled with this package, the copy use_cdn = FALSE would serve, as the Python binding does. So does an answer older than the bundled version. The mutable maidr@latest tag, which jsDelivr caches for up to a week, is used only when maidr.cdn_version = "latest" asks for it.

Pin a version when a document has to load the same MAIDR.js wherever and whenever it is opened, or to keep the one this package was tested with:

# The version bundled with this package, no lookup
options(maidr.cdn_version = "bundled")

# A particular release
options(maidr.cdn_version = "4.9.0")

# Or from the shell, for every session
# MAIDR_CDN_VERSION=bundled Rscript render.R

The dependency carrying a MAIDR chart-library adapter

Description

The adapters are UMD bundles published beside maidr.js – highcharts.js defines window.maidrHighcharts, echarts.js defines window.maidrECharts – and are bundled in the same directory, so the local and CDN copies are always the same release as the core they run against. Only the adapter's own file is declared (all_files = FALSE): the directory also holds the multi-megabyte core, which the maidr dependency already copies.

Usage

maidr_adapter_dependency(adapter, use_cdn = FALSE)

Arguments

adapter

"highcharts" or "echarts".

use_cdn

Logical, as in maidr_htmlwidget().

Value

A single htmltools::htmlDependency()


Check a configured CDN lookup budget and clamp it

Description

Check a configured CDN lookup budget and clamp it

Usage

maidr_bound_cdn_timeout(value, source)

Arguments

value

The setting as given

source

What the setting is called, for the warning

Value

A number of seconds in ⁠[0.1, 30]⁠


Deparse a setting for a message, bounded in length

Description

Deparse a setting for a message, bounded in length

Usage

maidr_cdn_deparse(value)

Arguments

value

Any R value

Value

A single string of at most 80 characters


Script that loads maidr.js from the CDN, falling back to the page's copy

Description

Goes into a chart frame's srcdoc in place of a plain CDN ⁠<script>⁠. When the CDN load fails – offline, blocked – it looks in the parent document for the copy maidr_page_bundle_dependency() put there. A srcdoc frame shares its parent's origin, so it can read it.

Usage

maidr_cdn_loader_script(cdn_js_url)

Arguments

cdn_js_url

URL of maidr.js on the CDN

Details

The copy arrives in one of two shapes, and both are handled: a src (the ⁠_files⁠ folder, or a ⁠data:⁠ URL) or inline text, which is what pandoc turns a script into when it embeds it. maidr.js initialises itself when it runs, so nothing is called once it has loaded. KaTeX, which it fetches only when an AI response carries maths, is pointed at the page's copy through window.maidrMathStylesheetUrl, or added as a ⁠<style>⁠ when inline.

Value

Character string holding a ⁠<script>⁠ element


Fetch one resolver endpoint

Description

The only function in this package that makes the version lookup's request, so tests replace it and never reach the network.

Usage

maidr_cdn_resolver_request(url, timeout)

Arguments

url

Endpoint URL

timeout

Seconds allowed for the whole transfer

Value

The response body as a string; an error on any failure


The time budget for the CDN version lookup, in seconds

Description

The maidr.cdn_timeout option, then the MAIDR_CDN_TIMEOUT environment variable, then 3 seconds. A value that is not a positive number warns once and the default applies – 0 does not mean "skip the lookup"; that is maidr.cdn_version = "latest" or "bundled". A value outside 0.1 to 30 seconds warns once and is clamped: below the floor every lookup would time out, and above the ceiling a value meant as milliseconds would hang a render.

Usage

maidr_cdn_timeout()

Value

A number of seconds in ⁠[0.1, 30]⁠


Get the MAIDR CDN base URL

Description

The jsDelivr directory holding the maidr.js that CDN documents load. The version in it comes from maidr_cdn_version(): the latest published maidr.js, resolved once per session, unless a setting pins it.

Usage

maidr_cdn_url()

Details

No integrity (SRI) attribute goes with it. A hash can only be written for bytes known in advance, which here means the bundled version; the CDN paths now load whatever version is current, whose hash this package cannot know. The bundled copy needs none: it is shipped with the package, not fetched from a third party.

Value

CDN URL string, without a trailing slash


The maidr.js version CDN documents load

Description

In order: the maidr.cdn_version option, the MAIDR_CDN_VERSION environment variable, then the latest published version, looked up once per session by maidr_resolve_cdn_version().

Usage

maidr_cdn_version()

Details

When the lookup fails – offline, blocked, timed out, or an answer that is not a version – this returns the bundled version, MAIDR_VERSION, as py-maidr does (xability/py-maidr#295). The alternative, jsDelivr's ⁠@latest⁠ tag, is a mutable alias that jsDelivr serves with a cache lifetime of up to seven days, so degrading to it would let a browser replay a week-old build in exactly the case the lookup exists to cover. The bundled version is a real published release, its URL is immutable, and it is the copy use_cdn = FALSE would have served anyway. What it costs is that a network hiccup leaves the session on a possibly older release. ⁠@latest⁠ is still emitted when it is asked for by name, with options(maidr.cdn_version = "latest").

A resolver answer older than the bundled version is refused the same way (see maidr_is_older_than_bundled()): nothing obliges a resolver, or whatever sits between it and this session, to answer with the current release, and the bundled version is the one known to have shipped with this package.

Value

A single string: a semantic version, or "latest" when that tag was pinned


The CDN version a setting pins, if any

Description

Reads the maidr.cdn_version option, then the MAIDR_CDN_VERSION environment variable; an unset or blank option falls through to the variable. A value that is not a usable pin warns once and is ignored, so the latest version is looked up as though nothing were set: a mistyped version says "I want a particular release", not "stay off the network", and the latest is closer to that than failing the render would be.

Usage

maidr_cdn_version_pin()

Value

A semantic version, "latest", or NULL when nothing usable is set


Warn once per session for a given key

Description

A bad setting is read on every render, and one warning is enough.

Usage

maidr_cdn_warn_once(key, message)

Arguments

key

What identifies this warning

message

The warning

Value

NULL, invisibly


Compare two pre-release identifiers by semver precedence

Description

Numeric identifiers compare numerically and sort below alphanumeric ones; alphanumeric ones compare in ASCII order, whatever the locale's collation.

Usage

maidr_compare_identifier(x, y)

Arguments

x, y

Single identifiers

Value

-1, 0 or 1


Compare two pre-release identifier runs by semver precedence

Description

Compare two pre-release identifier runs by semver precedence

Usage

maidr_compare_prerelease(a, b)

Arguments

a, b

Character vectors of dot-separated identifiers

Value

-1, 0 or 1


Where maidr.js loads the DotPad SDK from

Description

maidr.js does not bundle the DotPad tactile-display SDK: its braille engine is a 14 MB liblouis build that every document would carry for the few readers who own the device. So the first time a reader connects a DotPad, maidr.js imports the SDK from the vendor's published copy on jsDelivr, pinned to a commit. That is the one path an offline document (use_cdn = FALSE) still takes to the network. The document renders, sonifies and brailles without it; only connecting a DotPad needs it, unless the page names its own copy of the SDK, or one was downloaded with maidr_download_dotpad_sdk() for save_html() to carry.

Usage

maidr_dotpad_config()

Details

maidr.js reads two globals off the page before it loads: window.MAIDR_DOTPAD_SDK_URL, the SDK ES module, and window.MAIDR_DOTPAD_ASSET_BASE_URL, the directory holding the braille engine's liblouis.js, .wasm and .data files (by default the ⁠lib/⁠ folder beside the module). This reads the R-side settings for them: the options maidr.dotpad_sdk_url and maidr.dotpad_asset_base_url, falling back to the environment variables MAIDR_DOTPAD_SDK_URL and MAIDR_DOTPAD_ASSET_BASE_URL, which carry the same names as the globals. An empty value counts as unset.

Value

A list with sdk_url and asset_base_url, each a single string or NULL when unset.

See Also

maidr-options


The DotPad SDK globals as an htmltools dependency

Description

For the paths that assemble their document from dependencies rather than a template: show(), save_html() and the knitr widget. The globals ride in the dependency's head, and the dependency is listed ahead of the maidr one so the head lands before the bundle's ⁠<script>⁠.

Usage

maidr_dotpad_config_dependency(config = maidr_dotpad_config())

Arguments

config

The settings, as returned by maidr_dotpad_config()

Value

An htmltools::htmlDependency(), or NULL when nothing is configured


⁠<script>⁠ tag that declares the DotPad SDK globals

Description

Emitted ahead of maidr.js wherever this package loads the bundle, so maidr.js finds the globals already set when it reads them. Nothing is emitted when neither setting is configured: maidr.js then falls back to the vendor's copy on jsDelivr, as documented in maidr-options.

Usage

maidr_dotpad_config_script(config = maidr_dotpad_config())

Arguments

config

The settings, as returned by maidr_dotpad_config()

Value

A character string: the tag, or "" when nothing is configured


Download one file of the SDK

Description

A seam for the tests, which replace it rather than reach the network.

Usage

maidr_dotpad_download_file(url, destfile)

Arguments

url

Where the file is

destfile

Where to write it

Details

Bounded rather than left to hang: a connection that takes more than half a minute to open, or that delivers under a kilobyte a second for a full minute, fails like any other error. A slow link still gets the 14 MB engine; a stalled one does not hold the session for good.

Value

destfile, invisibly


Why a file is not the one the manifest describes, or NULL

Description

Size first, because it is the failure with a story: the corrupt liblouis.data that motivated the pin was 7,685 bytes short, and a size says so where a digest only says "different".

Usage

maidr_dotpad_file_mismatch(path, expected)

Arguments

path

The file on disk

expected

One row of the manifest's files

Details

Then the digests base R can compute: MD5 always, and SHA-256 too from R 4.5, which added tools::sha256sum(). The manifest carries both so the stronger check is used wherever it is available without adding a dependency for the older R this package supports.

Value

A string naming the difference, or NULL when there is none


The local SDK dependency, when a document going offline should carry one

Description

Applies only to use_cdn = FALSE: that is the document whose reader has no network, and the one whose ⁠lib/⁠ folder already travels with it. A session that names its own copy by URL keeps that – either URL option, set alone or together, wins – and one that never downloaded the SDK gets exactly what it did before.

Usage

maidr_dotpad_local_dependency(use_cdn = NULL)

Arguments

use_cdn

The document's use_cdn, with NULL meaning FALSE

Details

Either option, not only the module's. Both this dependency and the URL one write the same globals, and the later head wins in the browser, so a document carrying both with only the engine's URL configured would load the module from ⁠lib/⁠ and the engine from that URL: the offline copy's worst half, and the network dependency use_cdn = FALSE exists to remove.

Value

An htmltools::htmlDependency(), or NULL


Is a manifest's file path one that stays put?

Description

Each path is joined onto the download directory to decide where a fetched file is written, so a manifest could otherwise name ../../.. and write wherever it liked. Nothing user-authored reaches this today – the manifest is committed and only a maintainer regenerates it – but the download is the one place this package writes files it did not name, and a check costs nothing.

Usage

maidr_dotpad_path_is_safe(path)

Arguments

path

One key of the manifest's files.

Value

TRUE when the path is relative and stays inside its directory.


Parse the shipped DotPad SDK manifest

Description

Split from maidr_dotpad_sdk_manifest() so the read happens once and the parsing is testable on its own. Every field is checked, because the file is not authored here: fetch-maidr-bundle.sh copies whatever dist/dotpad-sdk.json the pinned maidr.js release ships. A field that changed shape upstream is named as a bad manifest rather than surfacing later as a vapply type error.

Usage

maidr_dotpad_read_manifest(path = NULL)

Arguments

path

Where to read from. Defaults to the installed ⁠inst/⁠ copy.

Value

The manifest, in the shape maidr_dotpad_sdk_manifest() returns.


Whether a directory holds a complete copy of the SDK

Description

Complete means every file in the manifest is present at its recorded size. The digests are checked when the copy is made, not on every render, so this costs a handful of file.info() calls.

Usage

maidr_dotpad_sdk_available(dir = maidr_dotpad_sdk_dir())

Arguments

dir

Where to look

Value

TRUE or FALSE


A downloaded SDK as an htmltools dependency

Description

For the documents show() and save_html() write. htmltools copies the directory into ⁠<libdir>/dotpad-sdk-<version>/⁠ when the document is saved, and the dependency's head declares the two globals with that relative path, so the saved page finds its copy wherever the folder is moved to, as long as the two move together. libdir is what htmltools::save_html() is called with; its default is what this package uses.

Usage

maidr_dotpad_sdk_dependency(dir = maidr_dotpad_sdk_dir(), libdir = "lib")

Arguments

dir

A complete copy, as maidr_dotpad_sdk_available() reports

libdir

The libdir the document is saved with

Value

An htmltools::htmlDependency()


Where a downloaded copy of the DotPad SDK lives

Description

The option maidr.dotpad_sdk_dir, then the environment variable MAIDR_DOTPAD_SDK_DIR, then a per-user cache directory from tools::R_user_dir() (⁠~/.cache/R/maidr/dotpad-sdk/<version>⁠ on Linux, where the version is the manifest's, ⁠3.0.3⁠ today). Nothing is created by asking.

Usage

maidr_dotpad_sdk_dir()

Value

A single path


The DotPad SDK maidr.js is pinned to

Description

maidr.js loads the SDK from one commit of a repository on jsDelivr, and this is that pin, with the size and digests of every file a copy consists of. It is read from inst/dotpad-sdk.json, a copy of the dist/dotpad-sdk.json the maidr npm package ships as the single source of truth for its own pin. .github/scripts/fetch-maidr-bundle.sh refreshes the file with the bundle, so the two cannot drift once the bundled maidr.js is one that ships it. The maidr.js this package bundles (see MAIDR_VERSION) predates the file and still falls back to earlier commits of the vendor's repository when nothing on the page names a copy. That fallback is exactly what a downloaded copy or a configured URL replaces, so what a document loads is this pin either way; the bundle catches up at its next refresh (tools/update-maidr-assets.R).

Usage

maidr_dotpad_sdk_manifest()

Details

The files are served from xability/dotpad-sdk-guide, a mirror of the vendor's dotincorp/dotpad-sdk-guide. The vendor publishes SDK 3.0.3 only as a zip archive, which jsDelivr cannot serve a file out of, so the mirror carries the extracted files, byte-verified against the archive; the manifest's upstream entry records the vendor commit, the archive path and its SHA-256.

The pin matters beyond immutability. Earlier commits of the vendor's repository carry a corrupt liblouis.data: its .gitattributes said ⁠* text=auto⁠ and the file is braille-table text with no NUL byte in it, so git rewrote its line endings on commit. It is an Emscripten package addressed by absolute byte offsets, so every table after the first dropped byte was read from the wrong place and the braille line silently fell back to grade 1. The pinned files carry the intact bytes.

The liblouis build is LGPL-2.1-or-later. Its licence text and the sources of the WebAssembly wrapper are listed because the vendor's README asks anyone who redistributes the SDK to keep them beside the runtime files, which is the LGPL's relinking requirement.

Read once per session: every render path asks for the manifest several times over, and the answer cannot change while the package is loaded.

Value

A list: version, repository, commit, base_url, module, asset_dir, and files, a data frame with one row per file (path, bytes, md5, sha256).


Read one DotPad setting from an option, then an environment variable

Description

Read one DotPad setting from an option, then an environment variable

Usage

maidr_dotpad_setting(option, envvar)

Arguments

option

Name of the R option

envvar

Name of the environment variable consulted when the option is unset or empty

Value

A single non-empty string, or NULL


Download the DotPad SDK for use offline

Description

maidr.js drives a DotPad tactile display through the vendor's SDK, which it does not bundle: the braille engine inside it is a 14 MB liblouis build, and every document would carry it for the few readers who own the device. By default maidr.js imports the vendor's published copy from jsDelivr, pinned to a commit, the first time a DotPad is connected – the one path an offline document (use_cdn = FALSE) still takes to the network.

Usage

maidr_download_dotpad_sdk(
  dir = maidr_dotpad_sdk_dir(),
  force = FALSE,
  quiet = FALSE
)

Arguments

dir

Where to write. Defaults to the option maidr.dotpad_sdk_dir, the environment variable MAIDR_DOTPAD_SDK_DIR, or a per-user cache directory.

force

Refetch files that are already present and correct.

quiet

Say nothing about what was fetched.

Details

This fetches that pinned copy once – the module, the liblouis build, and the LGPL licence text and wrapper sources the vendor asks redistributors to keep beside it – verifying every file against its recorded size and digests (MD5, and SHA-256 on R 4.5 or later), and writes a manifest.json beside them naming the commit they came from. From then on show() and save_html() copy it into ⁠lib/dotpad-sdk-<version>/⁠ next to every use_cdn = FALSE document and tell maidr.js where it is, so a reader connects a DotPad without the network. A file already present and correct is left alone, so a second call costs nothing.

A page served from somewhere else – an intranet host, or a knitr document, whose charts live in srcdoc frames with no base URL for a relative path to resolve against – names its copy by URL instead, through the options maidr.dotpad_sdk_url and maidr.dotpad_asset_base_url; see maidr-options. A configured URL wins over a downloaded copy.

Value

The directory, invisibly.

See Also

maidr-options for naming a copy by URL

Examples

## Not run: 
maidr_download_dotpad_sdk() # about 14 MB, once
save_html(p, "chart.html", use_cdn = FALSE) # carries the SDK in lib/

## End(Not run)

Ask the resolvers which maidr.js version latest is

Description

Tries jsDelivr's data API, then the npm registry, within one shared time budget: each request gets whatever the budget has left, and curl enforces it as a limit on the whole transfer, name resolution included. Never raises and never warns – a failed lookup must not fail a render – so every failure, whether the network, an HTTP status, or an answer that is not JSON or not a version, is NULL.

Usage

maidr_fetch_latest_cdn_version(budget)

Arguments

budget

Seconds for the whole lookup

Value

A semantic version, or NULL


Declare that a rectangle layer draws a schedule

Description

maidr_gantt() is ggplot2::geom_rect() with one thing added: the author saying that these rectangles are intervals in lanes. A declared layer is read as a gantt – lanes named, intervals announced, every bar highlightable – where the same rectangles drawn with geom_rect() are left unread and cost the whole chart its interactivity.

Nothing about the picture changes. The declaration is carried on the layer object, not in the aesthetics, so the same xmin/xmax/ymin/ymax the author would have written to geom_rect() produce the same chart: measured on ggplot2 3.4.4, the built data is identical() to the bare geom_rect() layer's and the panel's x.range and y.range are identical too. Swapping ⁠geom_rect(⁠ for ⁠maidr_gantt(⁠ moves nothing on the page.

The gantt layer type [experimental] is one of the experimental plot types: it has not been through a user study, and its reading may change without a deprecation period. See "Experimental Plot Types" in the README.

Usage

maidr_gantt(
  mapping = NULL,
  data = NULL,
  position = "identity",
  ...,
  lane_axis = c("y", "x"),
  na.rm = FALSE,
  show.legend = NA,
  inherit.aes = TRUE
)

Arguments

mapping

Aesthetics, as for ggplot2::geom_rect(): xmin, xmax, ymin and ymax are required, and every other rectangle aesthetic (fill, colour, alpha, ...) behaves exactly as it does there.

data

The layer's data, as for ggplot2::geom_rect().

position

Position adjustment, as for ggplot2::geom_rect().

...

Other arguments passed to the layer, as for ggplot2::geom_rect() – except stat, which that function takes as a formal and this one does not accept. A declared schedule is always drawn from the author's own bounds, so the stat is fixed at "identity"; measured, a stat written here lands in params, is recognised by neither the geom nor the stat, and is dropped with the warning ⁠Ignoring unknown parameters⁠. Aesthetics and geom parameters pass through exactly as they do to ggplot2::geom_rect() – measured, a misspelled aesthetic and a misspelled parameter each raise the identical warning from both.

lane_axis

Which axis the lanes run up: "y" (the default) for the ordinary horizontal schedule – lanes stacked up y, spans running along x – or "x" for the mirror image. It selects which pair of bounds becomes the span and which becomes the lane; it is not a guess the package makes.

na.rm

If FALSE (the default), rows with missing values are removed with a warning.

show.legend

Whether this layer is included in the legends.

inherit.aes

If FALSE, the plot's default aesthetics are not inherited.

Value

A ggplot2 layer, to be added to a plot with +.

Why the author is asked

A rectangle layer carries no evidence of what it means. Five structural rules were measured against eight charts on ggplot2 3.4.4, and the best of them – bands partition on the lane axis, more than one band, more than one distinct span, minus a complete-lattice veto – scored 6 of 8 and still claimed a heatmap with one cell missing and a two-region highlight. The table is recorded above the reading itself, in R/ggplot2_adapter.R. A monotone waterfall and a one-task-per-lane schedule are the same rectangles, so there is nothing in the geometry to separate; asking the author is the only unfalsified rule.

The consequence is that this is trusted. maidr_gantt() over heatmap coordinates announces a heatmap as a schedule, and the package believes it, because any guard strong enough to catch that is the rule the measurements above ruled out.

What it costs not to declare

An undeclared geom_rect() layer reads as "unknown", which drops the whole plot to a static image with the "Plot contains unsupported elements" warning. That is unchanged by this function, deliberately: every chart already written keeps exactly the reading it has today.

Lane names

With numeric ymin/ymax the lane axis is continuous and has no level names to borrow, so a lane is named by the single explicit tick drawn inside it – scale_y_continuous(breaks = 1:3, labels = c("design", "build", "test")) – and by its position on the axis otherwise. A tick whose label is a rendering of its own number is a coordinate rather than a name: measured on the default scale the panel's labels are ⁠NA, 1, 2, 3, NA⁠, each one its own break written out, and a lane called "2" says less than a lane called by the position 2 it sits at.

See Also

save_html() and show() for rendering the declared chart

Examples

if (requireNamespace("ggplot2", quietly = TRUE)) {
  tasks <- data.frame(
    lane = c(1, 2, 3, 2),
    start = c(0, 3, 8, 12),
    end = c(3, 8, 11, 15)
  )

  schedule <- ggplot2::ggplot(tasks) +
    maidr_gantt(ggplot2::aes(
      xmin = start, xmax = end,
      ymin = lane - 0.4, ymax = lane + 0.4
    )) +
    ggplot2::scale_y_continuous(
      breaks = 1:3,
      labels = c("design", "build", "test")
    ) +
    ggplot2::labs(x = "week", y = "task")

  # The same rectangles written with `geom_rect()` draw the same chart and
  # are left unread, which costs the plot its interactivity.
  if (interactive()) {
    show(schedule)
  }
}


Get Current MAIDR Fallback Settings

Description

Retrieves the current fallback configuration for MAIDR.

Usage

maidr_get_fallback()

Value

A list with the current fallback settings:

See Also

maidr_set_fallback() to configure settings

Examples

# Get current settings
settings <- maidr_get_fallback()
print(settings)


Register JS dependencies for maidr

Description

Creates the HTML dependency for the MAIDR JavaScript bundle. Behavior is controlled by the use_cdn parameter:

Usage

maidr_html_dependencies(use_cdn = NULL)

Arguments

use_cdn

Logical. If TRUE, use CDN. If FALSE or NULL (default), use bundled files.

Details

We default to local bundled assets for deterministic rendering. Previously we auto-detected via curl::has_internet(); when internet was available the CDN path was selected, which combined with a (now-fixed) malformed nested-⁠<html>⁠ HTML scaffold caused base R chart SVGs to render squished in the upper-left of the viewport. Local assets match the ggplot path that has always rendered correctly. Users who want CDN can still pass use_cdn = TRUE explicitly.

When a DotPad SDK location is configured (see maidr-options), a maidr-dotpad-config dependency precedes the maidr one: its head declares the ⁠window.MAIDR_DOTPAD_*⁠ globals, and listing it first is what puts them ahead of the bundle's ⁠<script>⁠ in the rendered document.

A document that loads the bundled copy also gets a maidr-locale-config dependency ahead of the bundle. maidr.js fetches any language but English as a locale pack from beside itself, and this package does not bundle the packs, so its head declares window.maidrLocaleBaseUrl: the packs of the bundled version on jsDelivr, unless the session names another place or none (see maidr_locale_base_url() and maidr-options).

No stylesheet is declared. MAIDR styles its interface at runtime, and since maidr 3.75.1 the published maidr.css is a placeholder with no rules in it. The one stylesheet that does carry rules, maidr-math.css (KaTeX, for LaTeX in AI chat responses), is fetched by maidr.js itself, resolved against the URL it was loaded from – the CDN directory, or the ⁠lib/⁠ folder htmltools copies the bundle into.

Value

A list of htmlDependency objects: the maidr bundle, preceded by maidr-dotpad-config when a DotPad SDK location is configured and by maidr-locale-config when a locale pack location is declared


Make a plotly, highcharter or echarts4r htmlwidget accessible

Description

Attaches MAIDR to an interactive chart drawn by another R package, so a screen reader user can explore it with the keyboard, hear it as sonification, and read it as text and braille. The chart is read by the MAIDR JavaScript adapter for the library that draws it, once it has been drawn in the browser; nothing about it is recomputed in R.

Usage

maidr_htmlwidget(widget, use_cdn = FALSE)

Arguments

widget

An htmlwidget created by plotly, highcharter or echarts4r.

use_cdn

Logical. Where the MAIDR scripts are loaded from:

  • FALSE (default): the copy bundled with this package, which works offline and makes no network request.

  • TRUE: the jsDelivr CDN, which loads the latest published MAIDR unless maidr.cdn_version pins one; see ?"maidr-options".

Details

Supported widgets:

Which chart types each library supports is decided by its MAIDR adapter; see https://maidr.ai/ for the lists. A chart the adapter cannot read is left as it was drawn, with a warning in the browser console.

The widget keeps working everywhere an htmlwidget does: the RStudio viewer, htmlwidgets::saveWidget(), R Markdown and Quarto documents, and Shiny (plotly::renderPlotly(), highcharter::renderHighchart(), echarts4r::renderEcharts4r()), where the chart is read again each time the server re-renders it.

Applying it twice returns the widget unchanged.

Value

The widget, with MAIDR attached. Print it, return it from a Shiny render function, or save it as you would the original.

Examples

if (requireNamespace("plotly", quietly = TRUE)) {
  w <- plotly::plot_ly(
    x = c("Mon", "Tue", "Wed"), y = c(20, 14, 23), type = "bar"
  )
  w <- maidr_htmlwidget(w)
}
## Not run: 
# Pipe-friendly
library(echarts4r)
mtcars |>
  e_charts(wt) |>
  e_scatter(mpg) |>
  maidr_htmlwidget()

## End(Not run)

Which MAIDR adapter reads a widget

Description

Which MAIDR adapter reads a widget

Usage

maidr_htmlwidget_adapter(widget)

Arguments

widget

The object passed to maidr_htmlwidget().

Value

"plotly", "highcharts" or "echarts".


Parent-side listener for the messages a MAIDR iframe posts

Description

Handles two messages, both keyed to the frame that sent them by matching contentWindow against the message's source:

Usage

maidr_iframe_host_script()

Details

Focus goes to the tab stop before the frame where this page has a reachable one, which is what the browser would have done. Where it has none, focus lands on the element holding the frame — a reveal.js slide is a ⁠<section>⁠, so on a slide deck that is the slide itself.

Reachability is checked rather than assumed: reveal.js leaves the slides on either side of the current one rendered, so a chart on the previous slide is a tab stop in document order even though it is marked hidden.

So is the handoff itself. Asking an element to take focus is not the same as it taking focus — focus() on an element with no rendered box is a silent no-op, and Shiny wraps every output in a display: contents div — so the outcome is read back and the ancestors are walked until one actually holds it. An element is asked as it stands before being given tabindex="-1", so a tab stop this page already owns is never taken out of the tab order, and a tabindex added to one that still refuses is removed again.

Registered at most once per document (guarded by a window flag), no matter how many iframes embed it.

Value

Character string with a script tag


Get ⁠<style>⁠/⁠<script>⁠ tags with the bundled assets inlined

Description

Reads the bundled maidr.js/maidr-math.css once per session and caches the assembled tags.

Usage

maidr_inline_asset_tags()

Details

KaTeX is inlined rather than left to maidr.js to fetch, because these tags go into a standalone document whose script is inline: it has no URL of its own, so the runtime has nothing to resolve the stylesheet against.

Value

A named list with css_tag and js_tag strings


Check internet availability, with a time-boxed cache

Description

curl::has_internet() can block for seconds on offline machines, so the result is cached rather than probed per plot. The cache is time-boxed to MAIDR_INTERNET_CACHE_TTL seconds so a stale answer self-heals: a transient failure does not pin the rest of the session to inlining the multi-megabyte bundle, and a session that goes offline after a successful probe stops emitting documents that point at a CDN it can no longer reach.

Usage

maidr_internet_available()

Value

TRUE if internet appears available


Is a resolved version older than the bundled one?

Description

Semantic version precedence, as py-maidr's ⁠_is_older_than_bundled⁠ applies it: MAJOR.MINOR.PATCH compared numerically with numeric_version(); on a tie, a pre-release sorts below the release it precedes (⁠4.9.0-rc.1⁠ < ⁠4.9.0⁠), and two pre-releases compare identifier by identifier, numeric ones numerically and below alphanumeric ones, alphanumeric ones in ASCII order, a shorter run below a longer one it begins. Build metadata (+build.5) carries no precedence and is ignored.

Usage

maidr_is_older_than_bundled(resolved, bundled = MAIDR_VERSION)

Arguments

resolved

The version the resolver answered with

bundled

The version bundled with this package

Details

A version that cannot be compared is not called older: the resolver's answer, already checked to be a semantic version, stays in place.

Value

TRUE when resolved sorts below bundled


Is a string a semantic version?

Description

Is a string a semantic version?

Usage

maidr_is_semver(x)

Arguments

x

Value to test

Value

TRUE for a single string of at most 128 characters in semantic version form, otherwise FALSE


Encode a string as a JavaScript literal safe inside a ⁠<script>⁠ element

Description

JSON is a subset of JavaScript, so jsonlite does the quoting. It leaves / alone, though, and an HTML parser ends the surrounding ⁠<script>⁠ at the first ⁠</⁠ it sees whatever the JavaScript around it says, so that sequence is escaped too.

Usage

maidr_js_string_literal(x)

Arguments

x

A single string

Value

The quoted literal


Get paths to local MAIDR assets

Description

Returns the file paths to the locally bundled MAIDR JavaScript and KaTeX stylesheet. The stylesheet is maidr-math.css, with its base64 web fonts stripped by .github/scripts/fetch-maidr-bundle.sh to keep the installed package under CRAN's size limit; KaTeX's layout rules are intact and only the glyphs fall back to system fonts.

Usage

maidr_local_assets()

Value

A named list with 'js' and 'math_css' file paths


Where maidr.js should fetch its locale packs from

Description

Reads the maidr.locale_base_url option, then the MAIDR_LOCALE_BASE_URL environment variable (see maidr-options). A place either of them names is declared in every document. "" or FALSE declares nothing. Left unset, a document that loads the bundled maidr.js gets the packs of the bundled version on jsDelivr, and one that loads maidr.js from the CDN gets nothing, since its packs sit beside that copy.

Usage

maidr_locale_base_url(use_cdn = FALSE)

Arguments

use_cdn

Whether the document loads maidr.js from the CDN

Value

A directory URL, or NULL when nothing is to be declared


The locale pack location as an htmltools dependency

Description

For the paths that assemble their document from dependencies: show(), save_html(), the htmlwidget and knitr. htmltools writes a dependency's head after its scripts, so the location rides in a dependency of its own, listed ahead of the maidr one, as maidr_dotpad_config_dependency() does.

Usage

maidr_locale_config_dependency(use_cdn = FALSE)

Arguments

use_cdn

Whether the document loads maidr.js from the CDN

Value

An htmltools::htmlDependency(), or NULL when nothing is to be declared


The locale pack location as a ⁠<script>⁠ element

Description

For the documents assembled from a template. It keeps a location the page already declared, so an author's own tag wins whichever comes first.

Usage

maidr_locale_config_script(url = maidr_locale_base_url())

Arguments

url

As returned by maidr_locale_base_url()

Value

The element, or "" when url is NULL


Turn a CDN version setting into the version a URL names

Description

Turn a CDN version setting into the version a URL names

Usage

maidr_normalize_cdn_pin(value, source)

Arguments

value

The setting as given

source

What the setting is called, for the warning

Value

A semantic version, "latest", or NULL (with a warning, once per distinct value) when value is not usable


Disable MAIDR Plot Interception

Description

Disables automatic MAIDR rendering and restores normal plot behavior. After calling this, Base R plots display in the standard graphics window and ggplot2 objects render with the default ggplot2 method.

Usage

maidr_off()

Value

Invisible TRUE on success

See Also

maidr_on() to enable MAIDR rendering


Enable MAIDR Plot Interception

Description

Turns on the accessible rendering of ggplot2 and Base R plots, and installs the knitr hooks that an R Markdown or Quarto document needs.

Usage

maidr_on()

Details

Interception is on by default after library(maidr): printing a ggplot2 object opens it in the MAIDR viewer, and Base R plotting calls are recorded until show() is called. Calling maidr_on() yourself is needed in two places: after maidr_off(), to start again, and once in the setup chunk of an R Markdown or Quarto document, where it registers the knit_print methods and the plot hook that turn every plot the document draws into an accessible chart. library(maidr) alone installs neither.

Value

Invisible TRUE on success

See Also

maidr_off() to disable MAIDR rendering

Examples


library(maidr)

# Enable interception (on by default after library(maidr))
maidr_on()

# Now all plots render as accessible MAIDR widgets
library(ggplot2)
ggplot(mtcars, aes(x = factor(cyl))) +
  geom_bar()

barplot(table(mtcars$cyl))


MAIDR Output Container for Shiny UI

Description

Creates a Shiny output container for MAIDR widgets using htmlwidgets. This provides automatic dependency injection and robust JavaScript initialization.

Usage

maidr_output(output_id, width = "100%", height = "400px")

Arguments

output_id

The output variable to read the plot from

width

The width of the plot container (default: "100percent")

height

The height of the plot container (default: "400px")

Value

A Shiny widget output function for use in UI

Examples

if (interactive()) {
  library(shiny)
  ui <- fluidPage(maidr_output("myplot"))
}

The bundle a knitted document carries for its charts

Description

The knitr paths put each chart in a srcdoc iframe whose ⁠<script>⁠ loads maidr.js from the CDN. That document lives in an attribute, where neither pandoc's --embed-resources (R Markdown's self_contained, Quarto's embed-resources) nor anything else that rewrites a page's resources can see it, so a self-contained document still needed the network to make its charts accessible, and offline they were plain pictures.

Usage

maidr_page_bundle_dependency()

Details

This dependency gives the document one copy of the bundle that the tooling does see: linked from the ⁠_files⁠ folder, or embedded into the page when the document is self-contained. Its scripts are of a type no browser runs, so the page itself is left alone; a chart frame whose CDN load fails reads the copy from its parent instead (see maidr_cdn_loader_script()).

Value

A single htmltools::htmlDependency()


Read the version out of a resolver's answer

Description

Read the version out of a resolver's answer

Usage

maidr_parse_resolver_response(body, field)

Arguments

body

The response body, JSON

field

The top-level field that holds the version

Value

A semantic version, or NULL when the answer holds none


knitr Plot Hook for Base R Plots

Description

Intercepts Base R plot output and converts to MAIDR iframe. Uses iframe-based isolation to ensure each plot has its own MAIDR.js context. Automatically falls back to image rendering for unsupported plot types or non-HTML output formats (PDF, EPUB). This replaces knitr's default plot hook when maidr_on() is called.

Usage

maidr_plot_hook(x, options)

Arguments

x

The plot file path from knitr

options

Chunk options

Value

HTML string for the plot


MAIDR's custom print method for ggplot objects

Description

When MAIDR interception is enabled, this renders ggplot objects in the MAIDR interactive viewer. For unsupported plots, it falls back to the original ggplot2 rendering.

Usage

maidr_print_ggplot(x, newpage = is.null(vp), vp = NULL, ...)

Arguments

x

A ggplot object

newpage

Draw on a new page?

vp

Viewport to draw in

...

Additional arguments passed to the print method

Value

Invisible ggplot object


Forget the looked-up CDN version and the warnings already given

Description

For tests, and for a long-lived session that wants to pick up a release published since its first render.

Usage

maidr_reset_cdn_cache()

Value

NULL, invisibly


The latest published maidr.js version, looked up once per session

Description

The first call asks the resolvers (see maidr_fetch_latest_cdn_version()) and caches the answer, a failure included, so a machine that cannot reach them pays the time budget once rather than on every render. Reset with maidr_reset_cdn_cache().

Usage

maidr_resolve_cdn_version()

Value

A semantic version, or NULL when the lookup failed


Responsive page CSS dependency for MAIDR HTML output

Description

Returns an htmltools::htmlDependency() whose head payload injects a viewport meta tag, a minimal CSS reset, and rules that make the embedded SVG fill (or proportionally fit) the browser viewport. This is purely a presentational layer; the SVG content, selectors, viewBox, and embedded maidr-data attribute are untouched and the maidr JS frontend behaves identically. We use a dependency (rather than an inline ⁠<style>⁠ tag in the body) so the meta and style land in the document ⁠<head>⁠ produced by htmltools::save_html().

Usage

maidr_responsive_dependency()

Value

A single htmltools::htmlDependency() object


Declare that a path layer draws a ROC curve

Description

maidr_roc() is ggplot2::geom_path() with two things added: the author saying that the path is a receiver operating characteristic curve, and a threshold aesthetic for the decision threshold each point was scored at. A declared layer is read as a roc – each point announced as its false and true positive rates, its threshold and its height above the chance diagonal, the area under each curve and the best operating point in the description – where the same path drawn with geom_path() or geom_line() reads as a line, which says the rates and nothing a ROC curve is drawn to say.

Nothing about the picture changes: the geom draws exactly what geom_path() draws, and threshold reaches no mark.

The roc layer type [experimental] is one of the experimental plot types: it has not been through a user study, and its reading may change without a deprecation period. See "Experimental Plot Types" in the README.

Usage

maidr_roc(
  mapping = NULL,
  data = NULL,
  position = "identity",
  ...,
  auc = NULL,
  na.rm = FALSE,
  show.legend = NA,
  inherit.aes = TRUE
)

Arguments

mapping

Aesthetics, as for ggplot2::geom_path(): x (the false positive rate) and y (the true positive rate) are required, and threshold may name the decision threshold at each point. Every other path aesthetic (colour, linetype, group, ...) behaves exactly as it does there.

data

The layer's data, as for ggplot2::geom_path().

position

Position adjustment, as for ggplot2::geom_path().

...

Other arguments passed to the layer, as for ggplot2::geom_path() – except stat, which is fixed at "identity": a declared curve is always drawn from the author's own rates.

auc

The area under each curve as the author computed it – with pROC::auc(), yardstick::roc_auc() or by hand – announced in the description in place of the trapezoid rule over the drawn points. One number for a single curve; for several, a vector named by the groups' names, or unnamed and in the groups' sorted order. NULL (the default) measures the area from the points, which is what the trapezoid rule gives and what those functions compute for an empirical curve.

na.rm

If FALSE (the default), rows with missing values are removed with a warning.

show.legend

Whether this layer is included in the legends.

inherit.aes

If FALSE, the plot's default aesthetics are not inherited.

Value

A ggplot2 layer, to be added to a plot with +.

What is asked of the data

x is the false positive rate and y the true positive rate, both fractions of one, in the order the curve is to be walked – from (0, 0) up, as sklearn.metrics.roc_curve() returns them, or from (1, 1) down, as pROC::coords() does; the area is measured over the points sorted by x either way. Several classifiers on one chart are several groups, split by colour, linetype or group as a multi-series line is, and each is announced by its group's name.

Curves maidr reads without a declaration

Two idioms name their axes after the ROC's own vocabulary, and are read as ROC curves as they stand: pROC::ggroc(), whose geom_line() maps specificity (or 1-specificity with legacy.axes = TRUE) against sensitivity, and ggplot2::autoplot() of a yardstick::roc_curve(), whose geom_path() maps 1 - specificity against sensitivity. Where x is specificity itself – pROC's default, drawn on a reversed axis – the announced rate is 1 - specificity and the axis is named so, because the height above chance is measured against the false positive rate and a rate read off a reversed axis would put every point on the wrong side of the diagonal. Neither idiom carries thresholds or the area into the plot, so the area is measured from the points and no threshold is announced; maidr_roc() is how an author supplies both.

Until the bundled maidr.js carries the trace

The roc trace shipped in maidr.js 4.9.0. While the copy this package bundles is older (see maidr:::MAIDR_VERSION), a declared or detected curve is read as a line, so that every chart keeps rendering; the reading switches to roc with the next bundle update and no change to the chart.

What it costs not to declare

A geom_line() of rates under any other column names keeps the line reading it has today, deliberately: every chart already written keeps exactly the reading it has.

See Also

maidr_gantt(), the other per-layer declaration; save_html() and show() for rendering the declared chart

Examples

if (requireNamespace("ggplot2", quietly = TRUE)) {
  curve <- data.frame(
    fpr = c(0, 0.05, 0.1, 0.2, 0.35, 0.6, 1),
    tpr = c(0, 0.55, 0.75, 0.86, 0.93, 0.98, 1),
    cutoff = c(1, 0.8, 0.6, 0.45, 0.3, 0.15, 0)
  )

  roc <- ggplot2::ggplot(curve) +
    maidr_roc(ggplot2::aes(x = fpr, y = tpr, threshold = cutoff)) +
    ggplot2::geom_abline(linetype = "dashed") +
    ggplot2::labs(x = "False positive rate", y = "True positive rate")

  # The same path written with `geom_path()` draws the same chart and is
  # read as a line: the rates, and none of what a ROC curve is read for.
  if (interactive()) {
    show(roc)
  }
}


Configure MAIDR Fallback Behavior

Description

Configure how MAIDR handles unsupported plot types or layers. When fallback is enabled, unsupported plots are rendered as static images instead of failing or returning empty data.

Usage

maidr_set_fallback(enabled = NULL, format = NULL, warning = NULL)

Arguments

enabled

Logical. If TRUE, unsupported plots fall back to image rendering. If FALSE, unsupported layers return empty data. If NULL (default), the current setting is kept.

format

Character. Image format for fallback: "png", "svg", or "jpeg". If NULL (default), the current setting is kept.

warning

Logical. If TRUE, shows a warning message when falling back to image rendering. If NULL (default), the current setting is kept.

Value

Invisibly returns a list of the previous settings.

See Also

maidr_get_fallback() to retrieve current settings

Examples

# Save current settings and restore on exit
old_settings <- maidr_get_fallback()
on.exit(maidr_set_fallback(
  enabled = old_settings$enabled,
  format = old_settings$format,
  warning = old_settings$warning
))

# Disable fallback (unsupported plots will have empty data)
maidr_set_fallback(enabled = FALSE)

# Use SVG format for fallback images
maidr_set_fallback(format = "svg")

# Disable warning messages
maidr_set_fallback(warning = FALSE)

# Configure multiple options
maidr_set_fallback(enabled = TRUE, format = "png", warning = TRUE)


Create MAIDR htmlwidget

Description

Internal function that creates an interactive MAIDR widget from a ggplot object. This is called internally by render_maidr() and should not be called directly. Use maidr_output() and render_maidr() for Shiny integration instead.

Usage

maidr_widget(
  plot,
  use_cdn = NULL,
  width = NULL,
  height = NULL,
  element_id = NULL,
  ...
)

Arguments

plot

A ggplot object, or NULL to auto-detect recorded Base R plots

use_cdn

Logical. Controls where MAIDR.js is loaded from, matching show() and save_html():

  • TRUE: Use CDN (requires internet), loading the latest published MAIDR.js unless maidr.cdn_version pins one

  • FALSE: Use local bundled files (works offline)

  • NULL (default): Auto-detect based on internet availability

This differs from maidr_html_dependencies(), where NULL means bundled. The widget renders through create_maidr_iframe() and create_standalone_html(), which probes for connectivity, so an unset use_cdn loads from the CDN on a machine that is online.

width

The width of the widget in pixels or CSS units (default: NULL for auto-sizing)

height

The height of the widget in pixels or CSS units (default: NULL for auto-sizing)

element_id

A unique identifier for the widget (default: NULL for auto-generated)

...

Additional arguments passed to create_maidr_html()

Details

Uses iframe-based isolation to ensure MAIDR.js initializes properly. Each widget gets its own isolated JavaScript context where MAIDR.js can discover and initialize the SVG with maidr-data attribute.

Value

An htmlwidget object that can be displayed in RStudio, Shiny, or saved as HTML


MAIDR Widget Output for Shiny UI (Internal Alternative)

Description

Internal alternative Shiny UI function. This provides the same functionality as maidr_output() but is no longer recommended for direct use. Use maidr_output() and render_maidr() instead for better consistency.

Usage

maidr_widget_output(output_id, width = "100%", height = "400px")

Arguments

output_id

The output variable to read the widget from

width

The width of the widget (default: "100percent")

height

The height of the widget (default: "400px")

Value

A Shiny widget output function for use in UI


Map visual panel position to DOM panel name

Description

This function handles the mismatch between visual layout order (row-major) and DOM element generation order (column-major) in gridSVG.

Usage

map_visual_to_dom_panel(panel_info, gtable)

Arguments

panel_info

Panel information from layout

gtable

Gtable object

Details

Visual layout (row-major): 1 2 3 4

DOM order (column-major): 1 3 2 4

Value

Gtable panel name or NULL if not found


Advice shown when an attached package masks one of maidr's wrappers

Description

Shared by .onAttach, the attach hooks and the "No Base R plots detected" errors so the wording stays in one place.

Usage

mask_advice(package)

Arguments

package

Name of the package, as in WRAPPED_SUGGESTS.

Value

A single advice string.


Name a recorded call's arguments the way R matched them

Description

A wrapper declared ⁠function(...)⁠ sees only the names the user typed, so hist(x, 20) records an unnamed 20 and every processor asking for args[["breaks"]] comes up empty. Running the recorded arguments through match.call() against the definition R actually dispatched to restores the names R itself assigned, once, for every processor.

Usage

match_recorded_args(function_name, definition, args)

Arguments

function_name

Name of the recorded function

definition

The original (unwrapped) function that was called

args

Recorded argument list of evaluated values

Details

Two properties are preserved deliberately:

Value

args with the names R matched, in the recorded order


Post-process a 2D subplot grid: if the layout is candlestick over volume-only bar (2 rows x 1 col, sharing an x-axis), collapse to a single 1x1 subplot whose layers are candlestick (+ embedded volume), bar, and optional line (multi-series MAs).

Description

Post-process a 2D subplot grid: if the layout is candlestick over volume-only bar (2 rows x 1 col, sharing an x-axis), collapse to a single 1x1 subplot whose layers are candlestick (+ embedded volume), bar, and optional line (multi-series MAs).

Usage

merge_candlestick_volume_panels(grid)

Combine a list of single-line layer entries into one multi-series line entry.

Description

Each input line layer's data is a list-of-series (typically length-1 for a single GeomLine/GeomMA). We concatenate all series across all layers.

Usage

merge_line_layers(line_layers)

Details

Selector handling: each input layer now resolves to its OWN polyline grob and emits one selector per curve it drew, so the inputs no longer overlap. The dedupe-and-trim below is kept as a backstop, not as the mechanism: it used to be load-bearing, because the generator discovered all polyline grobs in the panel and handed every one of N line layers the same length-N set. What still has to hold after merging is the frontend precondition ⁠selectors.length === data.length⁠, one selector per merged series, which is why the trim below stays. There is deliberately no pad: a short list fails that precondition and the frontend drops the layer's highlight rather than aiming it at the wrong curve.


Evaluate an expression, muffling the retry's promise-restart warning

Description

When a call fails part-way through forcing an argument, that argument's promise is left interrupted. Forcing it again - which both the retry and the argument recording do - makes R warn "restarting interrupted promise evaluation". It is an artifact of retrying, not anything the user's call did, so it is muffled; every other warning passes through untouched.

Usage

muffle_promise_restart(expr)

Arguments

expr

Expression to evaluate (lazily, inside the handler)

Value

The value of expr


Message for show()/save_html()/maidr_widget() with nothing recorded

Description

Names every masking case that applies: the bare "create a plot first" wording is actively misleading there, because the user did draw a chart - it just went to the other package unrecorded.

Usage

no_base_r_plots_message()

Value

The error message string.


Replace every non-ASCII character with a numeric character reference

Description

Found and replaced as bytes. The same work done in characters, with a UTF-8 perl regex, takes seconds on a document carrying the 1.8 MB bundle, where this takes milliseconds: in bytes, a run of non-ASCII is a run of bytes from 0x80 up, and nothing has to count characters to find it.

Usage

non_ascii_to_references(x)

Arguments

x

A single string whose bytes are UTF-8, marked or not

Value

x in ASCII, or unchanged when it is already ASCII or its bytes are not valid UTF-8


Restate a rect grob's negative heights and widths as positive ones

Description

gridSVG::grid.export() warns "number of items to replace is not a multiple of replacement length" on any rect grob drawn with a negative height or width, and the warning reaches save_html(), where a plot that warns falls back to a picture.

Usage

normalise_negative_rects(grob)

Arguments

grob

A grob, gTree, gList, or gtable (or NULL)

Details

Measured on a bare rectGrob() with nothing else to it: four rects with positive heights export silently, and the same four with two heights negated warn. So this is an upstream gridSVG defect rather than anything about a particular chart – but it is reached by an ordinary one. assocplot() draws every tile from a baseline with just = c("left", "bottom") and a signed height, so a cell below expectation is a negative height by construction (#266); every association plot would warn.

A rect at ⁠(y, h)⁠ with h < 0 covers the same pixels as one at ⁠(y + h, |h|)⁠, so the drawing is unchanged – only the arithmetic gridSVG does with it. The same holds for x and a negative width.

The svglite export draws negative extents correctly; the repair is kept because it changes nothing drawn.

Value

The same tree with every rect's extent stated positively


Handle HIGH-level Call

Description

Updates state when a HIGH-level plotting function is called.

Usage

on_high_level_call(device_id = grDevices::dev.cur(), call_index)

Arguments

device_id

Graphics device ID

call_index

Index of the call in the calls list

Value

NULL (invisible)


Handle LAYOUT Call

Description

Updates state when a layout function (par, layout) is called.

Usage

on_layout_call(device_id = grDevices::dev.cur(), function_name, args)

Arguments

device_id

Graphics device ID

function_name

Name of layout function

args

Function arguments

Value

NULL (invisible)


Open a temporary device to suppress default graphics window

Description

Called by wrappers when no device is open to prevent R from opening the default interactive graphics device.

Usage

open_maidr_temp_device()

Value

The device ID of the temp device


Open the svglite page a chart is drawn on

Description

Open the svglite page a chart is drawn on

Usage

open_svg_device(width, height)

Arguments

width, height

Page size in inches.

Value

A function returning the page's SVG once the device is closed.


Organize subplots into 2D grid structure

Description

Organize subplots into 2D grid structure

Usage

organize_facet_grid(subplots, panel_layout)

Arguments

subplots

List of processed subplot data

panel_layout

Panel layout information

Value

2D grid structure


Is a package attached ahead of maidr on the search path?

Description

library(quantmod) after library(maidr) puts package:quantmod in front of package:maidr, so an unqualified chartSeries() binds to quantmod's own function and maidr's recording wrapper is never entered. library(vioplot) and library(wordcloud) after maidr do the same to vioplot() and wordcloud(): the "No Base R plots detected" error that follows a bare call to either was measured with maidr 0.5.0 (#320).

Usage

package_masks_maidr(package)

Arguments

package

Name of the package, as in WRAPPED_SUGGESTS.

Details

maidr deliberately does not reach into another package's namespace to win this race: overwriting a foreign package's binding would also redirect the package's internal calls through maidr's ...-forwarding wrapper, which for quantmod corrupts the match.call(expand.dots = TRUE) it relies on. maidr reports the condition instead.

Value

TRUE when both packages are attached and package comes first.


The wrapped Suggests packages attached ahead of maidr right now

Description

The wrapped Suggests packages attached ahead of maidr right now

Usage

packages_masking_maidr()

Value

Package names, in WRAPPED_SUGGESTS order; empty when none masks.


Does a panel contain a layer of the given type?

Description

Does a panel contain a layer of the given type?

Usage

panel_has_layer_of_type(panel, type)

Arguments

panel

A processed patchwork panel (with ⁠$layers⁠)

type

Layer type string ("candlestick", "bar", "line", ...)

Value

Logical


Return the (first) layer in panel whose type matches type

Description

Return the (first) layer in panel whose type matches type

Usage

panel_layer_of_type(panel, type)

Convert a Panel Slot Number to its (row, column) Grid Positions

Description

A layout() matrix may name the same panel in several cells; R draws that panel once, spanning all of them. Returning every matching cell (in reading order) lets the caller advertise the panel in each cell it actually covers, so a spanned region is not mistaken for empty space. An mfrow/mfcol grid cannot span, so it always yields exactly one cell.

Usage

panel_slot_positions(slot, panel_config)

Arguments

slot

Panel slot number (1-based)

panel_config

Panel configuration from detect_panel_configuration()

Value

List of integer vectors c(row, col); empty list if the slot occupies no cell


The transformation applied to one axis of a built plot

Description

The transformation applied to one axis of a built plot

Usage

panel_transformation(built, axis = "x", panel_id = NULL)

Arguments

built

Built plot from ggplot2::ggplot_build()

axis

"x" or "y"

panel_id

Panel index for faceted plots, or NULL for the first

Value

The transformation object, or NULL when there is none to undo


The arguments a periodogram call was made with

Description

Both entry points take the series as their first formal with nothing ahead of it, and a recorded call keeps evaluated arguments, so the series is the value itself. A call whose series is not numeric is declined rather than coerced: the arguments of a call that stopped are recorded all the same, and coercing would announce a curve computed from NAs.

Usage

periodogram_args(layer_info)

Arguments

layer_info

Layer information with the recorded call.

Value

The recorded arguments, or NULL when there is no series to read


One point per frequency

Description

One point per frequency

Usage

periodogram_points(x, y)

Arguments

x

Frequencies, in the order they were drawn.

y

The value at each.

Value

A list of list(x =, y =) points, empty when nothing lines up


The grob a periodogram's curve was drawn as, in its own panel

Description

gridGraphics numbers panels in draw order, so under par(mfrow = ) the second chart's curve is graphics-plot-2-lines-1. The panel index is the one the orchestrator assigned the layer; a layer without one is the first panel.

Usage

periodogram_selector(layer_info, grob)

Arguments

layer_info

Layer information carrying group_index or index

grob

The grob name gridGraphics wrote: "lines" or "step"

Value

A CSS selector for the curve's g element


Perpendicular distance from points to a line segment

Description

Perpendicular distance from points to a line segment

Usage

perpendicular_distance(points, start, end)

Arguments

points

Nx2 numeric matrix of (x, y) points

start

Numeric vector of length 2 (line start)

end

Numeric vector of length 2 (line end)

Value

Numeric vector of perpendicular distances


Address one grob that a chart draws a datum into as a polygon

Description

A pie's wedges and a mosaic's tiles are each their own grob, so each needs its own selector – unlike a barplot(), whose bars all live inside one rect grob. gridSVG appends .1 to the grob name and wraps the shape in a group of that id, and the . has to be escaped for CSS.

Usage

polygon_cell_selector(grob_name)

Arguments

grob_name

A grob name, as find_graphics_plot_grobs() returns it

Value

A CSS selector for that grob's polygon


Position (1-based) of a layer among the polyline-producing layers of a plot

Description

layer_polyline_grobs() returns every polyline in the panel that no geom-named grob tree claims, so the index used to pick one out has to be counted over the same population. geom_line() / geom_path() / tidyquant::geom_ma() (detected as "line"), geom_step() (detected as "step") and geom_contour() / geom_density_2d() (detected as "contour") each render one auto-named polyline grob per layer, so all three types count. Counting only "line" layers would index the wrong polyline for every layer of a plot that combines them – and both charts would read correctly while outlining each other's curves, which is the highlight-only failure xability/maidr#814 names.

Usage

polyline_layer_position(plot, layer_index)

Arguments

plot

The ggplot2 object.

layer_index

Index of the layer of interest in plot$layers.

Value

The 1-based position, or NULL when the layer produces no polyline or registry-based detection fails.


Every auto-named polyline inside a grob, in draw order

Description

Scoped to the grob handed in, so a caller that has already established which tree belongs to its layer cannot pick up a sibling layer's curve.

Usage

polylines_within(grob)

Arguments

grob

A grob to walk.

Value

List of polyline grobs, in the order they are drawn.


Resolve the printed label for a positional axis

Description

The name ggplot2 prints beside the x or y axis, which is the name a reader needs in order to know what the numbers are. A labs() override wins, then the layer's own mapping, then the plot's – the same chain resolve_legend_label() walks for a legend title, because it is the same chain ggplot2 walks.

Usage

positional_axis_label(plot, built = NULL, aes_name = "x", layer_index = NULL)

Arguments

plot

The ggplot object

built

Built plot from ggplot2::ggplot_build(), or NULL to build one on demand

aes_name

"x" or "y"

layer_index

Index of the layer whose mapping takes precedence, or NULL to consult only the plot-level mapping

Details

The difference from the legend case is only what to do when none of them answers. A legend that has no title should have none; a positional axis always has one printed on the chart, so the aesthetic name is emitted rather than nothing. That is a poor label, but it is a label, and the alternative is a number announced with no name at all.

resolve_legend_label()'s documented caution – that labs() records a title even for an unmapped aesthetic, so only ask about one the layer is grouped by – does not apply here. A layer with no x or y mapping has no positions to announce and does not reach a processor that would ask.

Value

Character scalar, never NULL


Convert Currency Prefix to ISO 4217 Code

Description

Maps common currency symbols to their ISO 4217 codes for use in JavaScript's Intl.NumberFormat.

Usage

prefix_to_currency_code(prefix)

Arguments

prefix

Currency symbol (e.g., "$", the Euro sign, the Pound sign)

Value

ISO 4217 currency code (e.g., "USD", "EUR", "GBP")


Description

Print a ggplot with the original (non-MAIDR) print method

Usage

print_ggplot_natively(x)

Arguments

x

A ggplot object

Value

NULL (invisible)


Process a single facet panel

Description

Process a single facet panel

Usage

process_facet_panel(
  plot,
  panel_info,
  panel_data,
  facet_groups,
  gtable_panel_name,
  built,
  layout,
  gtable,
  format_config = NULL
)

Arguments

plot

The original plot

panel_info

Panel information

panel_data

Panel-specific data

facet_groups

Facet group information

gtable_panel_name

Gtable panel name

built

Built plot data

layout

Layout information

gtable

Gtable object

format_config

Optional format configuration from maidr label functions

Value

Processed panel data


Process a faceted plot and return organized subplot data

Description

Process a faceted plot and return organized subplot data

Usage

process_faceted_plot_data(plot, layout, built, gtable, format_config = NULL)

Arguments

plot

The faceted ggplot2 object

layout

Layout information

built

Built plot data

gtable

Gtable object

format_config

Optional format configuration from maidr label functions

Value

List with organized subplot data in 2D grid format


Process a single patchwork panel

Description

Process a single patchwork panel

Usage

process_patchwork_panel(
  leaf_plot,
  panel_name,
  panel_index,
  row,
  col,
  layout,
  gtable,
  n_original_layers = NULL
)

Arguments

leaf_plot

The leaf ggplot object

panel_name

Panel name from gtable

panel_index

Panel index

row

Panel row

col

Panel column

layout

Layout information

gtable

Gtable object

n_original_layers

Number of layers the user actually wrote. Defaults to every layer of leaf_plot; pass the un-augmented count so injected geoms (violin's boxplot) do not emit a maidr layer of their own.

Value

Processed panel data


Process a patchwork plot and return organized subplot data

Description

Process a patchwork plot and return organized subplot data

Usage

process_patchwork_plot_data(plot, layout, gtable, original_plot = NULL)

Arguments

plot

The patchwork plot object, with leaves already augmented

layout

Layout information

gtable

Gtable object

original_plot

The un-augmented composition. Supplied so each leaf is processed for the layers the user wrote rather than for the extra geoms a processor injected to render its selectors.

Value

List with organized subplot data in 2D grid format


Whether a name refers to a processor class this package ships

Description

The check used to be exists(name, mode = "function"), and a processor is an R6 generator rather than a function, so it matched nothing – is.function(Ggplot2BarLayerProcessor) is FALSE and class(...) is "R6ClassGenerator". Every entry a factory offered was filtered out by it, so both factories reported an empty registry for every processor they ship (#200).

Usage

processor_class_exists(processor_class_name)

Arguments

processor_class_name

Name of the processor class.

Value

TRUE when the name is an R6 generator that is reachable.


Advice shown when quantmod masks maidr's chartSeries() wrapper

Description

mask_advice() for quantmod.

Usage

quantmod_mask_advice()

Value

A single advice string.


Is quantmod attached ahead of maidr on the search path?

Description

package_masks_maidr() for quantmod, the first package this was noticed with (#97).

Usage

quantmod_masks_maidr()

Value

TRUE when both packages are attached and quantmod comes first.


Convert R Date Format to Intl.DateTimeFormat Options

Description

Converts R strftime format strings to JavaScript Intl.DateTimeFormat options.

Usage

r_date_format_to_intl_options(format)

Arguments

format

R date format string (e.g., "%Y-%m-%d")

Value

List of Intl.DateTimeFormat options


Convert R Date Format to JavaScript Function

Description

Creates a JavaScript function string that formats dates according to an R strftime format string. Used for complex date formats that cannot be represented by Intl.DateTimeFormat options alone.

Usage

r_date_format_to_js_function(format, tz = "UTC")

Arguments

format

R date format string

tz

Timezone

Value

JavaScript function body string


Ramer-Douglas-Peucker algorithm for 2D polylines

Description

Iterative stack-based implementation to avoid R recursion limits.

Usage

rdp(points, epsilon)

Arguments

points

Nx2 numeric matrix of ordered (x, y) points

epsilon

Maximum allowed perpendicular distance. Larger values yield fewer retained points.

Value

Logical vector of length N (TRUE = keep this point)


Keep the title chartSeries() would have given the call it was made from

Description

Without a name, quantmod::chartSeries() titles the chart with the expression its x was written as (as.character(match.call()["x"])), so chartSeries(AAPL) is titled "AAPL". The call is replayed later with the recorded value in place of that expression, and the title became the series' numbers printed end to end. The name is taken from the call as written, the way quantmod takes it, and recorded as an explicit name.

Usage

record_chartseries_name(args, call_expr)

Arguments

args

The recorded arguments of the chartSeries() call

call_expr

The call as written

Value

args, with name added when the caller gave none


Resolve one axis title from a recorded Base R call

Description

The author's own ⁠xlab=⁠/⁠ylab=⁠ always wins. An empty string counts as unsupplied: Base R draws no title for it, so falling through to the chart type's default announces more than the blank would, and the renderer would otherwise substitute its generic "X"/"Y" anyway. This is how the candlestick processor has always read these arguments.

Usage

recorded_axis_label(args, name, default = NULL)

Arguments

args

Recorded argument list, or NULL

name

Argument to read: "xlab" or "ylab"

default

What this chart type can honestly say when the author said nothing. Pass NULL when it can say nothing: an absent label leaves the generic to the renderer, which is where that decision belongs.

Value

Character scalar, or default


The height a recorded barplot() call draws

Description

barplot() reads its data from height, which is its first formal. The recorder names positional dots but leaves the dispatch argument as the caller wrote it, so height arrives unnamed when it was passed by position and named when it was not – and barplot(beside = TRUE, height = m) put beside in the first slot, where every reader used to look.

Usage

recorded_barplot_height(args)

Arguments

args

Recorded argument list

Value

The height vector or matrix, or NULL


Read a recorded logical argument the way its drawing function does

Description

Every base R reader asked isTRUE() of a recorded flag, and the base R drawing functions ask ⁠if (x)⁠. The two agree on TRUE, on FALSE and on absent, and disagree on every other truthy value R accepts in an if – so a chart written stripchart(x, vertical = 1) was drawn vertically and announced horizontally, with the values on the group axis and the group positions on the value axis, silently, on a chart that renders as an interactive one rather than as a fallback (#256).

Usage

recorded_flag(args, name, default = FALSE)

Arguments

args

Recorded argument list

name

The formal's name

default

What an absent, NA or unreadable argument means

Details

Measured, by reading each drawing function's own body:

function asks
barplot.default ⁠if (beside)⁠, (logx && horiz)
bxp ⁠if (horizontal)⁠
hist.default ⁠if (freq1)⁠
stripchart.default ⁠if (vertical)⁠
qqnorm.default, qqline ⁠if (datax)⁠
vioplot.default `if (horizontal

All seven ask R's own truthiness, so all seven are read through this.

NA and an uncoercible value give the caller's default rather than an error: ⁠if (NA)⁠ stops in R, but a reader that stops takes the whole figure with it, and a chart read under its default is better than no chart at all. A value of any length but one does the same, since if on one of those errors too.

Value

TRUE or FALSE


The formula a recorded call carries, resolved

Description

The formula is the formula argument, the x argument, or the first positional one, whichever is found first. On the NSE path it arrives as the unevaluated call to ~, or – ⁠fmla <- y ~ x; plot(fmla, ...)⁠ – as the name it was bound to, and either is resolved in the snapshot the call was recorded with. Only a name or a ~ call is evaluated: an arbitrary expression in the data slot (plot(rnorm(10))) is not a formula and is not run again to find out.

Usage

recorded_formula(args, call_env = NULL)

Arguments

args

Recorded argument list

call_env

The environment snapshot a deferred call was recorded with, or NULL when every argument is a plain value.

Value

The formula object, or NULL when the call carries none


Resolve a recorded formula into the frame the chart was drawn from

Description

Base R calls are recorded and read later, at show()/save_html() time, and for every argument but one that is harmless: the wrapper records evaluated values, so a vector recorded is a vector and rebinding the name it came from afterwards changes nothing.

Usage

recorded_formula_frame(
  args,
  call_env = NULL,
  formula = recorded_formula(args, call_env)
)

Arguments

args

Recorded argument list

call_env

The environment snapshot a deferred call was recorded with, or NULL when every argument is a plain value.

formula

The formula the call carries, as recorded_formula() resolves it; passed in when the recorder has already resolved it.

Details

A formula is the exception. It is a reference rather than a value – it carries the environment it was written in – and a processor that calls stats::model.frame() on it at render time resolves the variables then. Measured (#254):

len  <- c(1, 2, 3, 10, 11, 12); supp <- rep(c("OJ", "VC"), each = 3)
stripchart(len ~ supp)              # draws 1,2,3 under OJ
len  <- c(99, 98, 97, 96, 95, 94)   # the user carries on working
supp <- rep(c("XX", "YY"), each = 3)
save_html(file = f)                 # announced 99,98,97 under XX

Every value and both group names belonged to bindings made after the drawing, and it was silent: the figure rendered as an interactive chart rather than as a fallback, so nothing said the numbers had moved.

So the frame is built here, while the call is being recorded and the bindings are still the ones the chart was drawn from. stripchart.formula and boxplot.formula build the same stats::model.frame(formula, data) as they draw, so this is the frame they used rather than a reconstruction of it.

Fixed at the recording layer rather than per processor because anything that reads a formula later inherits the same defect.

Value

The model frame, or NULL when the call carries no formula or the frame cannot be built – in which case the reader falls back to resolving it itself, exactly as before.


The main title a recorded call wrote, as text

Description

main = expression(alpha^2) is an ordinary way to put a Greek letter on a chart, and a dozen readers passed the recorded value straight into the layer's title. jsonlite::toJSON() has no method for an expression, so the whole save failed on a title. A title that is not text is announced as empty rather than failing the chart it sits on; the drawing keeps it.

Usage

recorded_main_title(args)

Arguments

args

Recorded argument list

Details

Exact-name lookup, since args$main would partial-match nothing today but is the same shape as the args$x / xlab collision that emptied monthplot() (#292).

Value

Character scalar, empty when there is no usable title


The two-way contingency table a recorded call was handed, when it is one

Description

mosaicplot() is given the table itself, so the recorded call carries every number a mosaic layer wants – the counts, the margins they imply, and the level names from dimnames(). Nothing is inferred from the drawing.

Usage

recorded_two_way_table(args)

Arguments

args

Recorded argument list, or NULL

Details

Only a two-dimensional table is returned. mosaicplot() accepts three and more, splitting recursively, and a mosaic layer has one category axis and one fill – so a deeper table has nowhere to put its later dimensions and is declined rather than flattened into a cross-classification the chart does not claim. A table with unnamed margins is declined too: the levels are what a reader navigates by, and positions are not levels.

mosaicplot()'s other calling style hands it a formula and a data argument instead, and mosaicplot.formula() builds the table from the two. That is recovered by formula_two_way_table() rather than declined, for the reason the direct call is read at all: the table is the one the chart draws, arrived at by the same code, not a plausible reconstruction (#248).

Shared by the adapter's dispatch and the processor's extraction so the two cannot disagree about which calls are readable.

Value

A 2-D table with named margins, or NULL


Hand jsonlite each run of flat records as a data frame

Description

A layer's data is usually one small named list per point, and jsonlite::toJSON() serializes a list element by element through S4 dispatch: 10,000 points took 2 s of a 5 s ggplot2 render, more than the SVG export. A data frame with the same columns is serialized row-wise in one vectorised pass and yields byte-identical JSON under the options set_maidr_data_attr() uses (auto_unbox, na = "null", digits = NA), in about a hundredth of the time.

Usage

records_as_frames(node)

Arguments

node

A maidr-data node (list, or a leaf)

Details

Only a run that is certain to serialize identically is converted: an unnamed list of named lists that all have the same field names in the same order, each field one attribute-free logical, integer, double or character value of the same type in every record. Anything else – a nested value, a ragged or mixed-type field, a factor or date, a zero-length value (which serializes as ⁠[]⁠, not null) – is left as a list and recursed into, so the output never changes, only its cost.

Value

The node with record runs replaced by data frames


Where a rect grob is anchored on one axis, as a fraction

Description

A rectGrob() keeps its justification in just and leaves hjust / vjust NULL unless the caller wrote them, so neither field alone answers the question. just may be a keyword, a number, or a length-two vector of either; absent, grid's own default is "centre".

Usage

rect_anchor(grob, axis)

Arguments

grob

A rect grob

axis

"horizontal" or "vertical"

Details

Anything unrecognised answers 0.5, the default – an anchor this cannot read is one it should not move.

Value

The anchor as a fraction: 0 is the low edge, 1 the high one


Address one tile of a grob that draws a whole panel of them

Description

gridSVG exports a rect grob holding several rectangles as one group of ⁠<rect>⁠ elements, each with its own id: ⁠<grob>.1.<n>⁠, counted from one in draw order. A spine plot's panel is the case that needs it.

Usage

rect_cell_selector(grob_name, drawn_at)

Arguments

grob_name

The grob holding the tiles

drawn_at

Which tile, counted in draw order from one

Value

A CSS selector matching exactly that tile


Read a rectangle layer's bounds as the spans and lanes they draw

Description

A declared maidr_gantt() layer builds xmin, xmax, ymin and ymax, and none of the four columns segment_lane_axis() is keyed on. Measured, today's processor fed a rect frame unchanged answers data lanes: 0 | lanes: NULL | selectors: 0 – a confident empty schedule, which is a false claim of a different kind from the one #197 is about. So the frame is renamed into the segment spelling here and every landed function downstream runs unchanged.

Usage

rect_gantt_frame(built_data, lane_axis = "y")

Arguments

built_data

A layer's computed data, carrying xmin, xmax, ymin and ymax, one row per drawn rectangle

lane_axis

"y" when the lanes run up y and the spans along x, "x" for the mirror image, as the author declared it

Details

The lane is the band's midpoint rather than either edge, so a lane sits where a reader sees it and a band drawn upside down (ymin > ymax) lands in the same place. The span keeps both bounds; segment_lanes() already sorts a span written backwards.

Measured on the repository's own four-interval schedule (ymin = 0.6, 1.6, 2.6, 1.6), the normalised frame gives segment_lane_axis() = "y", lane sizes 1, 2, 1 and emission order 1, 2, 4, 3; the processed layer's data, lanes, orientation and axes come back identical() to the geom_segment() spelling of the same schedule, and only the grob the selectors name differs.

The degenerate guard falls out of the renaming rather than being a rule: rectangles of zero width normalise to level on both axes, which segment_lane_axis() already refuses.

Value

The frame with x, xend, y and yend added, or NULL when it is not a rectangle layer's frame


Register MAIDR's custom print.ggplot method

Description

Stores the original ggplot2 print method and registers MAIDR's version. Called during .onLoad().

Usage

register_ggplot2_print_method()

Render MAIDR Plot in Shiny Server

Description

Creates a Shiny render function for MAIDR widgets using htmlwidgets. This provides automatic dependency injection and robust JavaScript initialization.

Usage

render_maidr(expr, env = parent.frame(), quoted = FALSE)

Arguments

expr

An expression that draws a plot. Either a ggplot object, or Base R plotting calls – their return values differ (plot() returns NULL, barplot() returns bar midpoints) and are ignored; what counts is whether the expression drew. An expression that draws nothing and returns NULL renders nothing, per Shiny convention.

env

The environment in which to evaluate expr

quoted

Is expr a quoted expression

Value

A Shiny render function for use in server

Examples

if (interactive()) {
  library(shiny)
  library(ggplot2)
  server <- function(input, output) {
    output$myplot <- render_maidr({
      ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
        geom_bar(stat = "identity")
    })
  }
}

Render MAIDR Widget in Shiny Server (Internal Alternative)

Description

Internal alternative Shiny server function. This provides the same functionality as render_maidr() but is no longer recommended for direct use. Use maidr_output() and render_maidr() instead for better consistency.

Usage

render_maidr_widget(expr, env = parent.frame(), quoted = FALSE)

Arguments

expr

An expression that returns a ggplot object

env

The environment in which to evaluate expr

quoted

Is expr a quoted expression

Value

A Shiny render function for use in server


Reorder one leaf's data by every layer processor that asks for it

Description

Reorder one leaf's data by every layer processor that asks for it

Usage

reorder_leaf_plot(leaf_plot)

Arguments

leaf_plot

A ggplot object

Value

The plot, its data reordered


Put every leaf's data in the order its processors will read the drawing in

Description

The single-plot and facet paths both reorder the plot data before drawing (Ggplot2PlotOrchestrator$process_layers()), because a segmented bar's DOM order is whatever order its rows arrive in and the processor declares one order – category by category, fills descending – to the frontend. A patchwork leaf was drawn from its rows as given, so its declared order matched the drawing only when the rows happened to be sorted that way, and a dodged leaf in a composition outlined another cell's bar for the value announced (#316). Walks the composition the way augment_patchwork_leaves() does.

Usage

reorder_patchwork_leaves(node)

Arguments

node

A patchwork, a ggplot, or anything else (returned as is)

Value

The node with each leaf's data reordered


Repair NA text-grob justification so gridSVG can export the tree

Description

gridGraphics::grid.echo() translates some base graphics text into grid text grobs that leave vjust (and, in principle, hjust) as NA and defer to the grob's just field instead. gridSVG 1.7.7 passes the raw value to gridSVG:::justTovjust(), which branches on it directly and fails with "missing value where TRUE/FALSE needed", aborting gridSVG::grid.export() from devGrob.text. graphics::pie() is the case that bites: it labels every wedge, so before this repair no base R pie chart could be exported at all – pie(..., labels = NA), which draws no text, exported fine, which is what pins the failure on these grobs. barplot() and friends are unaffected because their text grobs already carry a numeric justification.

Usage

repair_na_text_justification(grob)

Arguments

grob

A grob, gTree, gList, or gtable (or NULL)

Details

Only NA components of text grobs are rewritten, so a grob that already has a usable justification passes through untouched. 0.5 is exactly what grid resolves NA to for the just = "centre" these grobs declare, so the drawn output is byte-identical.

This was an upstream gridSVG/gridGraphics incompatibility rather than anything maidr introduced. The svglite export leaves justification to grid and does not need it; it is kept because it changes nothing grid draws and costs one pass over the tree.

Value

The same tree with NA hjust/vjust on text grobs set to 0.5


Replay Base R Plot from Device Storage

Description

Re-executes the recorded Base R plot calls to render the plot.

Usage

replay_base_r_plot(device_id)

Arguments

device_id

The device ID to get calls from


Replay a recorded plot call with the original (unwrapped) function

Description

Strips maidr-internal arguments and re-executes the call. When the recorded args contain unevaluated expressions (from non-standard evaluation, e.g. curve(sin(x)) or plot(y ~ x, subset = g == 1)), the call is rebuilt and evaluated in the environment captured at record time so those expressions resolve exactly as they did originally.

Usage

replay_plot_call(function_name, args, call_env = NULL)

Arguments

function_name

Name of the recorded function

args

Recorded argument list (values and/or expressions)

call_env

Environment captured when NSE arguments could not be forced at record time, or NULL when all args are plain values

Value

The result of the replayed call (invisibly)


Replay Base R plot to native graphics device

Description

For unsupported plots, close the temp device and replay the plot calls to the native graphics device.

Usage

replay_to_native_device(device_id = grDevices::dev.cur())

Arguments

device_id

The device ID to get plot calls from

Value

NULL (invisible)


Reset Device State

Description

Resets the state for a device (called when storage is cleared).

Usage

reset_device_state(device_id = grDevices::dev.cur())

Arguments

device_id

Graphics device ID

Value

NULL (invisible)


Reset the global registry (mainly for testing)

Description

Reset the global registry (mainly for testing)

Usage

reset_global_registry()

Forget every rebuilt frame

Description

Called when a plot starts being processed, so an entry can only ever be answered to the run that computed it. See Ggplot2PlotOrchestrator$initialize for why a layer alone cannot identify a plot.

Usage

reset_jitter_cache()

Value

Invisibly NULL.


Resolve the legend title for a grouping aesthetic

Description

Returns the title ggplot2 prints above the legend for a grouping aesthetic, which is what the MAIDR payload emits as the z axis label. A labs() override wins (ggplot2 stores it on the built plot's labels, normalising color to colour); otherwise the mapped expression is used, with the layer's own mapping taking precedence over the plot-level one. Returns NULL when the aesthetic carries neither.

Usage

resolve_legend_label(
  plot,
  built = NULL,
  aes_names = "fill",
  layer_index = NULL
)

Arguments

plot

The ggplot object

built

Built plot from ggplot2::ggplot_build(), or NULL to build one on demand

aes_names

Aesthetic names to try, in order. Pass spelling variants of one aesthetic (for example c("colour", "color")), never unrelated aesthetics.

layer_index

Index of the layer whose mapping takes precedence, or NULL to consult only the plot-level mapping

Details

Callers are responsible for only asking about an aesthetic the layer is actually grouped by: labs() records a title even for an unmapped aesthetic, so an unguarded lookup would invent a legend that the plot does not draw.

Value

Character scalar, or NULL when the aesthetic has no title


A recorded argument as a value

Description

A recorded argument as a value

Usage

resolve_recorded_value(value, call_env = NULL)

Arguments

value

A recorded argument, a plain value or an expression

call_env

The snapshot to evaluate an expression in, or NULL

Value

The value, or NULL when an expression has nowhere to be evaluated or fails there


Resolve the aesthetic that splits a layer into series

Description

Mirrors ggplot2's precedence: the layer's own mapping wins over the plot-level one. ggplot2 normalises color to colour, but both spellings are probed defensively.

Usage

resolve_series_group_mapping(
  plot,
  layer_index = NULL,
  aes_groups = list(c("colour", "color"))
)

Arguments

plot

The ggplot2 object

layer_index

Index of the layer whose mapping takes precedence, or NULL to consult only the plot-level mapping

aes_groups

List of aesthetic-name vectors, probed in order. Each element must hold spelling variants of ONE aesthetic (for example c("colour", "color")), never unrelated aesthetics: the winning element is handed to resolve_legend_label(), which documents that contract.

Value

list with aes (the winning spelling variants, or NULL when nothing is mapped) and column (the mapped column name, or "group" as a fallback)


Name each series after the category its built group id stands for

Description

ggplot_build() replaces the grouping column with integer group ids, so the user-facing name has to be recovered from the plot's own data. The ids are assigned in the sorted order of the grouping column's values, which is the order this function relies on. Falls back to "Series <id>" when the mapped column is not present on the plot data (for example an expression such as aes(colour = paste(a, b))).

Usage

resolve_series_group_names(plot, group_ids, column = "group")

Arguments

plot

The ggplot2 object

group_ids

The layer's built group column

column

Name of the mapped grouping column

Value

Character vector, one name per distinct group id in ascending order


Resolve x/y data arguments from a recorded call's argument list

Description

Mirrors how plot()/points()/lines() match their arguments: named x/y win, then the first two UNNAMED arguments in order. Blind positional access (args[[2]]) would grab graphical parameters instead (plot(x, type = "l") -> y = "l") or error for single-argument calls (plot(v), lines(v)).

Usage

resolve_xy_args(args)

Arguments

args

Recorded argument list

Value

List with x and y (either may be NULL)


Restore the original print.ggplot method

Description

Restore the original print.ggplot method

Usage

restore_ggplot2_print_method()

Restore original functions

Description

Deactivates patching by flipping the active flag. Wrappers remain in the namespace but act as pass-through (calling the original function directly). This avoids modifying the locked namespace or the search path.

Usage

restore_original_functions()

Value

NULL (invisible)


Retry a failed plot call from the caller's own frame

Description

The formula methods resolve non-standard arguments relative to parent.frame(): plot.formula() evaluates ⁠subset =⁠ there, and boxplot.formula() reaches into the caller's .... A wrapper puts its own frame in that position, so calls that work in plain R fail through maidr:

Usage

retry_call_in_caller_frame(
  original_function,
  recorded_call,
  caller_env,
  original_error
)

Arguments

original_function

The unwrapped plotting function

recorded_call

match.call() captured by the wrapper

caller_env

The wrapper's calling frame

original_error

The error condition the direct call raised

Details

plot(y ~ x, data = d, subset = g == 1)   # object 'g' not found
boxplot(y ~ g, data = d, subset = x > 5) # ..3 used in an incorrect context

Rebuilding the call and evaluating it in the caller's frame gives those methods the frame they expect. This runs only after the direct call has already failed, so working calls keep the single-evaluation fast path and a genuinely invalid call still reports its original error.

Known trade-off: on this retry path an argument can be evaluated more than once. The failed first attempt already forced some promises, the rebuilt call evaluates the argument expressions afresh, and the argument recording that follows forces the interrupted promise again. An argument carrying a side effect therefore runs it more than once here. The alternative is the pre-existing behaviour, where the whole call simply errored, so the retry is the better trade – but it is a trade.

Value

Result of the retried call


Turn a horizontal box-family layer round for the frontend

Description

BoxTrace, ViolinBoxTrace and ViolinTrace each reverse a horizontal layer on the way in – "reverse points to match visual order (lower-left start)", as src/model/box.ts puts it. The reversal is unconditional, so the producer has to hand them the opposite order for the result to come out lower-left first, and a layer emitted in its own natural bottom-to-top order is read from the top down instead.

Usage

reverse_horizontal_box_layer(layer)

Arguments

layer

A layer list carrying data, selectors and orientation

Details

Both halves move together. The frontend reverses the resolved highlight alongside the points (BoxTrace explicitly, ViolinTrace by reversing the selectors before it resolves them), so a layer that reversed only its data would trade a correct outline for a wrong one.

Only the emission order changes. Each entry keeps its own category name and its own statistics, and orientation still says which axis the values are drawn along.

Worth stating what this does not settle: the frontend's reversal may exist because py-maidr reverses, in which case the cleaner fix is to drop it there and let every producer emit in its own natural order. That is a three-repository change and a released-behaviour change for py-maidr readers, so it is left to the maintainer. Until then the two bindings agree, which is the part a reader can feel.

Value

The layer, with both halves reversed when it is horizontal


The x and y a layer draws, its own mapping first and the plot's beneath

Description

The x and y a layer draws, its own mapping first and the plot's beneath

Usage

roc_layer_rates(layer, plot_object)

Arguments

layer

A ggplot2 layer

plot_object

The plot the layer belongs to

Value

list(x = , y = ) of normalised names, either NULL when unmapped


The name an aesthetic is mapped to, as the ROC vocabulary would spell it

Description

rlang::as_label() renders aes(x = specificity) as specificity, aes(x = 1 - specificity) as 1 - specificity, and the .data[["1-specificity"]] that pROC::ggroc() writes as .data[["1-specificity"]]. Stripping the pronoun and the whitespace makes the three spellings of one rate compare equal.

Usage

roc_mapped_name(mapping, aesthetic)

Arguments

mapping

An aesthetic mapping, or NULL

aesthetic

Which aesthetic to read

Value

The normalised name, or NULL when nothing is mapped


Whether the bundled maidr.js can build a roc trace

Description

The roc trace first shipped in maidr.js 4.9.0. Emitted to an older bundle it is not declined but fatal: the core's factory throws on a trace type it does not know, so the page renders nothing (#214). Until MAIDR_VERSION reaches 4.9.0 a ROC curve therefore keeps the line reading it had – pROC::ggroc() and yardstick's autoplot() in particular, which are detected without any change on the author's side and must not lose a chart they rendered yesterday. The moment the bundle moves, the reading switches with no other change.

Usage

roc_trace_available()

Value

TRUE when the pinned bundle carries the trace


Whether a ROC layer's x is specificity, to be announced as 1 - x

Description

Whether a ROC layer's x is specificity, to be announced as 1 - x

Usage

roc_x_is_specificity(layer, plot_object)

Arguments

layer

A ggplot2 layer

plot_object

The plot the layer belongs to

Value

TRUE only for a detected layer whose x is specificity itself


Run MAIDR Example Plots

Description

Launches example plots demonstrating MAIDR's accessible visualization capabilities. Each example creates an interactive plot using show().

Usage

run_example(example = NULL, type = c("ggplot2", "base_r"))

Arguments

example

Character string specifying which example to run. If NULL (the default), lists all available examples.

type

Character string specifying the plot system to use. Either "ggplot2" (default) or "base_r".

Details

Available examples include various plot types such as bar charts, histograms, scatter plots, line plots, boxplots, heatmaps, and more.

Each example script creates a plot and calls show() to display it in your default web browser with full MAIDR accessibility features including keyboard navigation and screen reader support.

Value

Invisibly returns NULL. Called for its side effect of displaying an interactive plot in the browser or listing available examples.

See Also

show() for displaying plots, save_html() for saving to file

Examples

# List all available examples
run_example()

if (interactive()) {
  # Run ggplot2 bar chart example
  run_example("bar")

  # Run Base R histogram example
  run_example("histogram", type = "base_r")
}


Save Interactive Plot as HTML File

Description

Save a ggplot2 or Base R plot as an HTML file with interactive MAIDR accessibility features.

Usage

save_html(plot = NULL, file = "plot.html", use_cdn = NULL, ...)

Arguments

plot

A ggplot2 object or NULL for Base R auto-detection

file

File path where to save the HTML file (e.g., "plot.html")

use_cdn

Logical. Controls where MAIDR.js is loaded from:

  • TRUE: Use CDN. The file is self-contained but needs internet access when it is viewed. It names the latest published MAIDR.js by version (looked up once per R session, or the bundled version when the lookup cannot be made); pin a version with options(maidr.cdn_version = ...), see ?"maidr-options".

  • FALSE or NULL (default): Use the bundled files. The MAIDR.js library is written to a lib/ folder beside file, which has to travel with it.

...

Additional arguments passed to internal functions

Details

By default the MAIDR.js library is written to a lib/ folder beside file, and the two have to be shared together: zip the folder that holds both, or copy both. An .html sent on its own loads no MAIDR.js and shows a plain, inaccessible chart. use_cdn = TRUE writes one self-contained file instead, which needs internet access whenever it is viewed and loads the latest published MAIDR.js from jsDelivr rather than the copy bundled with this package.

Value

The file path where the HTML was saved (invisibly)

Examples

# ggplot2 bar chart
library(ggplot2)
p <- ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_bar(stat = "identity")

maidr::save_html(p, tempfile(fileext = ".html"))


# ggplot2 violin plot
p_violin <- ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_violin(fill = "lightblue", alpha = 0.7) +
  labs(title = "MPG by Cylinders", x = "Cylinders", y = "MPG")

maidr::save_html(p_violin, tempfile(fileext = ".html"))


# Base R example (requires interactive session for function patching)
if (interactive()) {
  barplot(c(10, 20, 30), names.arg = c("A", "B", "C"))
  maidr::save_html(file = tempfile(fileext = ".html"))
}

Save HTML document to file

Description

Save HTML document to file

Usage

save_html_document(html_doc, file)

Arguments

html_doc

An htmltools HTML document object

file

Output file path


Read a ggplot2 scale's transformation

Description

ggplot2 3.5 renamed the accessor; the field it reads is the same one. Returns NULL when the scale carries no transformation at all, which is the case for a discrete scale.

Usage

scale_transformation(scale)

Arguments

scale

A panel scale from built$layout$panel_scales_x or panel_scales_y

Value

The transformation object, or NULL


Schedule auto-show after the current top-level expression completes

Description

Uses R's task callback mechanism. When a HIGH-level plot function is called, this schedules show() to run after the expression finishes. If another HIGH-level function is called in the same expression, the previous callback is replaced (only one auto-show fires per expression).

Usage

schedule_auto_show()

Which axis a layer's segments lay their lanes on

Description

A segment whose two ends share a coordinate is a span along the other axis, at one position on this one – an interval in a lane, which is a gantt. A segment whose ends share nothing is an edge in a node-link diagram: it has no lane to sit in and no interval to announce.

Usage

segment_lane_axis(built_data)

Arguments

built_data

A layer's computed data, carrying x, xend, y and yend, one row per drawn segment

Details

The question is asked of the whole layer rather than of each row, which is the rule xability/maidr#1100 settled for the same reading in the Observable adapter. One geom_segment() call can hold spans and edges together, and reading three spans out of four segments would announce a gantt quietly missing a quarter of its chart.

A layer whose segments are level on both axes is every span reduced to a point. That is not a schedule with milestones in it – a milestone sits in a lane beside intervals that have length – so it is refused rather than announced as a chart of zero-length work.

Value

"y" when the lanes run up the y axis and the spans along x, "x" for the mirror image, or NULL when the layer is not a gantt


Group a layer's segments into the lanes they were drawn in

Description

Group a layer's segments into the lanes they were drawn in

Usage

segment_lanes(built_data, lane_axis, lane_names = NULL)

Arguments

built_data

A layer's computed data, one row per drawn segment

lane_axis

"y" or "x", as segment_lane_axis() returns

lane_names

The lane names in drawn order, or NULL on a continuous lane axis. Position i on the axis is lane_names[[i]]

Value

A list with data (lanes, each a list of x/start/end intervals), lanes (the names of every lane in drawn order, or NULL) and order (the built-data row behind each interval, in emission order)


Series-Group Helpers

Description

A layer whose grouping aesthetic is mapped (for example aes(colour = g)) draws one curve per group. MAIDR describes that as one series per group, each point carrying the group's name as z, and names those values with the legend title as the z axis label. These helpers are shared by the ggplot2 line and smooth layer processors so that a grouped geom_line() and a grouped geom_smooth() are described the same way.


Set Internal Guard Flag

Description

Guards against recursive tracing by setting an internal flag.

Usage

set_internal_guard(value)

Arguments

value

TRUE to set guard, FALSE to clear

Value

NULL (invisible)


Serialize maidr_data and set it as the SVG root's maidr-data attribute

Description

Mutates svg_doc in place.

Usage

set_maidr_data_attr(svg_doc, maidr_data)

Arguments

svg_doc

Parsed SVG document (xml2)

maidr_data

The maidr-data structure

Value

NULL (invisible)


Write a height back into a recorded barplot() call

Description

The counterpart of recorded_barplot_height(): the value goes back into the slot it was read from, named or positional.

Usage

set_recorded_barplot_height(args, height)

Arguments

args

Recorded argument list

height

The replacement height

Value

The argument list with height replaced


Display Interactive MAIDR Plot

Description

Display a ggplot2 or Base R plot as an interactive, accessible visualization using the MAIDR (Multimodal Access and Interactive Data Representation) system.

Usage

show(plot = NULL, use_cdn = NULL, shiny = FALSE, as_widget = FALSE, ...)

Arguments

plot

A ggplot2 object or NULL for Base R auto-detection

use_cdn

Logical. Controls where MAIDR.js is loaded from:

  • TRUE: Use the jsDelivr CDN (requires internet), which loads the latest published MAIDR.js rather than the bundled copy. The version is looked up once per R session; pin one with options(maidr.cdn_version = ...), see ?"maidr-options".

  • FALSE: Use local bundled files (works offline, and makes no network request)

  • NULL (default): Use the bundled files, so the viewer works offline. With as_widget = TRUE the widget instead auto-detects internet availability and uses the CDN when online, as the knitr and Shiny paths do.

shiny

If TRUE, returns just the SVG content instead of full HTML document

as_widget

If TRUE, returns an htmlwidget object instead of opening in browser

...

Additional arguments passed to internal functions

Details

Attaching maidr masks methods::show(). An object that is not a plot maidr renders – an S4 object, a vector, a data frame – is handed to methods::show(), so it prints as it did before maidr was attached. In a script or a package, call maidr::show() and methods::show() by name; ?"base-r-wrappers" lists everything else attaching maidr masks.

Value

Invisible NULL. The plot is displayed in RStudio Viewer or browser as a side effect.

Examples

# ggplot2 bar chart
library(ggplot2)
p <- ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_bar(stat = "identity")

maidr::show(p)


# ggplot2 violin plot
p_violin <- ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_violin(fill = "lightblue", alpha = 0.7) +
  labs(title = "MPG by Cylinders", x = "Cylinders", y = "MPG")

maidr::show(p_violin)


# Base R example (requires interactive session for function patching)
if (interactive()) {
  barplot(c(10, 20, 30), names.arg = c("A", "B", "C"))
  maidr::show()
}

Adaptively simplify a 2D curve to a target number of points

Description

Uses binary search on epsilon to find the smallest tolerance that yields at most target retained points.

Usage

simplify_curve(points, target, min_epsilon = 0, max_iterations = 50L)

Arguments

points

Nx2 numeric matrix of ordered (x, y) points

target

Desired maximum number of retained points

min_epsilon

Lower bound for epsilon search (default 0)

max_iterations

Maximum binary-search iterations (default 50)

Value

Logical vector of length N (TRUE = keep this point)


Whether the smooth processor can read a layer drawn with this geom

Description

The list this processor works from, stated once so that the classifier can consult it. Ggplot2Adapter$detect_layer_type() decides a layer is a "smooth" partly on its stat – StatFunction and StatDensity both claim one – and a stat can name a geom this list does not. When it did, resolve_target_layer() rejected the layer's own index, found nothing in the fallback search and stop()ped:

Usage

smooth_reads_geom(geom)

Arguments

geom

A layer's geom

Details

stat_function(fun = sin, geom = "point")  Error: No smooth curve layers found in plot
stat_function(fun = sin, geom = "step")   Error: No smooth curve layers found in plot
stat_function(fun = sin)                  interactive

Not a fallback to a picture – an error out of save_html(), so the caller's script stopped, and which geom the author passed decided whether the call returned at all (#230). A decline is a reading decision; an exception is a broken call.

inherits() rather than class(geom)[1] here, unlike the dispatch: this asks whether the processor can read the artist, which a subclass of a readable geom can. GeomFunction and GeomQuantile are named all the same, because both are GeomPath subclasses and a plain geom_path() is typed "line" and never arrives here – widening to the parent would claim nothing extra and would blur what this list is for, the geoms that draw a computed curve (#202, #229).

Value

TRUE when this processor can read a layer drawn with it


Snapshot the bindings a recorded NSE call will need at replay time

Description

Recorded expressions are re-evaluated when the figure is rendered, long after the caller has moved on – and R reuses ONE frame for every iteration of a for loop. Storing that frame therefore makes every iteration replay with the LAST iteration's values:

Usage

snapshot_call_env(args, caller_env)

Arguments

args

Recorded argument list holding the unevaluated expressions

caller_env

The frame the recorded call was made from

Details

par(mfrow = c(1, 2))
for (g in c("a", "b")) plot(y ~ x, data = d, subset = grp == g)

Both panels drew grp == "b", silently and without an error. Copying the whole frame would fix that but brings its own problems (large objects, active bindings, unforced promises, frames that are shared and mutated), so only the names the recorded expressions actually mention are copied, into a CHILD of the caller's frame. Everything else – including anything reached through the enclosing scopes – still resolves exactly as before, and the copies are references, so R's copy-on-write keeps them free.

Active bindings are deliberately left behind: reading one is a side effect, and re-reading it at replay time is the whole point of declaring it active. Names that cannot be read are skipped for the same reason – the fall-through to the caller's frame preserves today's behaviour.

Value

An environment whose parent is caller_env


Name a replayed spineplot(x, y)'s axes the way the caller's call did

Description

spineplot.default() titles its axes deparse1(substitute(x)) and deparse1(substitute(y)) when no xlab/ylab is given. Replayed through do.call() on the recorded values, the substitute is the data itself, and a reader was told the axis was called c(1, 2, 3, ...) for as many characters as the vector took to write. The names the caller wrote are in the recorded call text, so they are matched against the default method's formals and passed as the titles. A table or a formula names its own axes and is left alone.

Usage

spineplot_written_axis_names(args, call_expr)

Arguments

args

Recorded argument list

call_expr

The recorded call, deparsed

Value

args, with xlab/ylab filled in where the call named them


One curve grob per row of a vectorised one

Description

Recycling is by position, which is grid's own rule for a gpar shorter than the positions it styles, so a layer given one colour for four rows keeps that colour on all four rather than losing three of them.

Usage

split_one_curve(curve)

Arguments

curve

A curve grob

Details

A single-row curve is returned untouched: it already satisfies gridSVG, and wrapping it would change the element id its selector is built from.

Value

A gTree of one curve per row, or the grob itself when it has one row


Split a vectorised curve grob into one curve per row

Description

geom_curve() draws every row of its layer as a single curve grob whose positions and whose gp are vectors – measured on ggplot2 3.4.4, a four-row layer arrives as GRID.curve.1 with x1, y1, x2, y2 and gp$col, gp$fill, gp$lwd, gp$lty all of length 4. gridSVG's svgStyleAttributes() rejects that outright – "All SVG style attribute values must have length 1" – so gridSVG::grid.export() aborts on the whole plot and no curve chart could be read at all (#195).

Usage

split_vectorised_curve_grobs(grob)

Arguments

grob

A grob, gTree, gList, or gtable (or NULL)

Details

A segments grob is equally vectorised and exports fine, because gridSVG has a method that splits it into one element per segment. There is no such method for curve, so the split is done here instead: each row becomes its own curve grob carrying its own slice of the gpar, gathered under a gTree keeping the original's name. The drawing is unchanged – every row is drawn with exactly the styling it had – and the export gains one element per row, which is what a gantt's selectors address.

Slicing rather than scalarising is the point. Taking gp[[1]] would also satisfy gridSVG and is visibly wrong: measured on a layer coloured by a third column, the four rows export as rgb(248,118,109), rgb(0,186,56), rgb(97,156,255) and rgb(0,186,56), so collapsing to the first would paint the whole layer red.

This was an upstream gridSVG gap rather than anything maidr introduced. The svglite export draws a vectorised curve as it is, but the split stays: the element per row it gives is what a gantt's selectors address.

Value

The same tree with every multi-row curve grob split row-wise


Strip the bottom axis line and tick marks from chartSeries candlestick SVG

Description

quantmod::chartSeries() emits a bottom date axis (axis line, tick marks, and "Jan 12 2024" labels). The axis line and tick marks are drawn slightly off-center from the candles (chartSeries places ticks at evenly-spaced positions that do not always coincide with the candle centers), which reads as a visual misalignment. This helper removes the axis line and tick marks but preserves the date labels themselves so the chart still communicates which date each candle represents visually. Per-row date info is also encoded in the maidr-data JSON for the screen reader.

Usage

strip_chartseries_date_axis(svg_content, maidr_data)

Arguments

svg_content

Character vector of SVG lines

maidr_data

The maidr-data structure (read-only; used to detect candlestick layers)

Details

The relevant groups have IDs of the form graphics-plot-N-bottom-axis-(line|ticks)-...; we match by substring with ⁠contains(@id, 'bottom-axis-(line|ticks)-')⁠ and explicitly leave ⁠bottom-axis-labels-⁠ untouched.

Safety: no-op when maidr_data contains no candlestick layers (ggplot candlestick / non-candlestick plots use different SVG IDs and are unaffected), when xml2 is unavailable, when SVG parsing fails, or when no matching groups are found.

Value

Modified SVG content (character vector). If any guard fails, returns svg_content unchanged.


Document-level implementation of strip_chartseries_date_axis()

Description

Mutates svg_doc in place.

Usage

strip_chartseries_date_axis_doc(svg_doc)

Arguments

svg_doc

Parsed SVG document (xml2)

Value

TRUE if the document was modified


Strip the right y-axis line and ticks from chartSeries candlestick SVG

Description

quantmod::chartSeries() draws a right-hand y-axis (axis(4)) with a vertical axis line, tick marks, and numeric price labels (e.g. 101..106). In the tree gridGraphics::grid.echo() rebuilds from it, the line and the ticks land inside the plot region, well left of its right border, reading like a stray "axis through the middle" of the chart, while the labels stay where R draws them. This helper removes the ⁠right-axis-line-*⁠ and ⁠right-axis-ticks-*⁠ groups; the price labels are preserved so the chart still communicates the y-axis scale visually.

Usage

strip_chartseries_right_axis(svg_content, maidr_data)

Arguments

svg_content

Character vector of SVG lines

maidr_data

The maidr-data structure (read-only; used to detect candlestick layers)

Details

The matched groups have IDs of the form graphics-plot-N-right-axis-line-... and graphics-plot-N-right-axis-ticks-..., matched by substring.

Safety: no-op when maidr_data contains no candlestick layers (ggplot candlestick / non-candlestick plots use different SVG IDs and are unaffected), when xml2 is unavailable, when SVG parsing fails, or when no matching groups are found.

Value

Modified SVG content (character vector). If any guard fails, returns svg_content unchanged.


Document-level implementation of strip_chartseries_right_axis()

Description

Mutates svg_doc in place.

Usage

strip_chartseries_right_axis_doc(svg_doc)

Arguments

svg_doc

Parsed SVG document (xml2)

Value

TRUE if the document was modified


Strip internal violin_kde metadata without coordinate injection

Description

Removes the temporary fields (.panel_x_range, .panel_y_range, .is_horizontal, .panel_index, .panel_name, data_left_x, data_right_x, data_y) that must never appear in the serialized maidr-data JSON. Called unconditionally after coordinate injection, so a layer whose panel viewport could not be navigated still comes out clean.

Usage

strip_violin_kde_metadata(maidr_data)

Arguments

maidr_data

The maidr-data structure

Value

Cleaned maidr_data


Rewrite svglite shapes into the exported document's shapes

Description

Rewrite svglite shapes into the exported document's shapes

Usage

svg_convert_shapes(
  lines,
  ids,
  clips,
  h,
  as_path = logical(length(lines)),
  own = rep(NA_character_, length(lines)),
  blank = logical(length(lines))
)

Arguments

lines

svglite element lines.

ids

Element ids (NA for none).

clips

Clip-path ids (NA for none).

h

Page height in px.

as_path

Shapes of a path grob, written as ⁠<path>⁠.

own

Each shape's own gpar names (see svg_style_attrs()).

blank

Shapes drawn with a blank line type.

Value

Character vector of rewritten element lines.


Draw one grob in the walk's context, under its ancestors' gpar

Description

Draw one grob in the walk's context, under its ancestors' gpar

Usage

svg_draw(g, st)

Close the pending batch, drawing its marker when shapes follow

Description

Close the pending batch, drawing its marker when shapes follow

Usage

svg_flush(st, draw = TRUE)

Families svglite resolved R's generic font families to

Description

Cached per session: svglite writes the concrete family it measured with ("Liberation Sans"), which a reader's browser may not have. Each is written back as the generic stack gridSVG used, ending in a CSS generic.

Usage

svg_font_aliases()

gridSVG's element id: ⁠<name>.<k>⁠, ⁠<k>⁠ one more than the name's uses

Description

gridSVG's element id: ⁠<name>.<k>⁠, ⁠<k>⁠ one more than the name's uses

Usage

svg_get_id(name, st)

Presentation attributes for a group, as gridSVG gave them

Description

gridSVG wrote a group's own gpar as attributes its shapes inherit, and a shape's own gpar on the shape. Shapes here keep that split (see svg_convert_shapes()), so the groups carry their share: each setting the group's gpar names, at the value the drawing inherits there. Alpha is folded into the opacities rather than written as opacity, the way the device folds it into every shape.

Usage

svg_gp_attrs(eff, set)

Arguments

eff

The effective gpar at the group.

set

Names of the gpar settings the group itself makes.

Value

A string of attributes with a leading space, or "".


Which of an arrowed line's shapes are its pieces

Description

The device writes each piece of a line and then that piece's arrow heads, so pieces and heads interleave. A shape is the next expected piece when it is a line with that piece's point count starting at its first point (to svglite's two decimals); everything else is a head.

Usage

svg_match_pieces(lines, pieces)

Arguments

lines

svglite lines for the element's shapes, in order.

pieces

Data frame of expected pieces (n, x0, y0).

Value

Logical vector: the shape is a piece.


Parse svglite's page into shapes, markers and clip regions

Description

Parse svglite's page into shapes, markers and clip regions

Usage

svg_parse_svglite(svg)

svglite's style declarations as presentation attributes

Description

svglite resolves every setting a shape draws with. gridSVG wrote on a shape only what the shape's own gpar set and let the rest come from its groups, and that split is visible to maidr.js: a highlight clone of a group repaints the shapes inside it only where they inherit. So a shape keeps just the attributes its own gpar accounts for (NA: all of them), with the value svglite resolved, which is the value it was drawn with.

Usage

svg_style_attrs(
  style,
  text,
  own = rep(NA_character_, length(style)),
  line = logical(length(style))
)

Arguments

style

Character vector of style values (NA for none).

text

Logical vector: the element is text.

own

Comma-separated names of each shape's own gpar settings, NA for a shape drawn outside any grob.

line

Logical vector: the element is an open line, which gridSVG always wrote unfilled.

Value

Character vector of attribute strings, each with a leading space.


Stop on svglite output the rewrite cannot vouch for

Description

The rewrite reads svglite's text output, one element per line, and attributes each shape to the grob drawn before it. A later svglite that wrote otherwise would not fail on its own: its shapes would be numbered onto the wrong grobs, and a reader would be announced one mark while another is outlined. So anything the rewrite does not recognise stops the export, and the chart falls back to a static image with a warning naming this, instead of shipping selectors that point at the wrong marks.

Usage

svg_unreadable(what)

Arguments

what

What was found.


The x offset of an SVG translate() transform

Description

The x offset of an SVG translate() transform

Usage

translate_x(transform)

Arguments

transform

The transform attribute value

Value

The first translate()'s x, or 0 when there is none


Put a layer's built points back where its data puts them

Description

Put a layer's built points back where its data puts them

Usage

undisplace_layer(plot, layer_data, layer_index)

Arguments

plot

The ggplot2 object.

layer_data

The layer's built data, as read from built$data.

layer_index

Index of the layer within the plot.

Value

layer_data with x and y restored when the layer was jittered and the rebuild lined up, and unchanged otherwise.


The positions a layer's points would have without its jitter

Description

Rebuilds plot with the one layer's position adjustment replaced by position_identity() and returns that layer's built data.

Usage

undisplaced_layer_data(plot, layer_index)

Arguments

plot

The ggplot2 object.

layer_index

Index of the layer to undisplace.

Details

The replacement is a fresh ggproto child rather than an assignment to the layer's own position field. ggplot2 Layer objects are ggproto and have reference semantics, so plot$layers[[i]]$position <- ... would alter the caller's plot – the object they still hold and may draw again. A child shadows the field instead, leaving the parent untouched; asserted in the tests rather than left as a claim.

Value

A data frame of the layer's undisplaced built data, or NULL when the rebuild fails or does not line up row for row with the original.


Put built positions back into the space the reader sees

Description

ggplot2 applies a scale transformation before the stat runs, so ggplot_build()'s data is in transformed space. Read straight through, a scale_x_log10() chart announces log10 coordinates under the original axis label: a scatter of prices from $5.50 to $9,403 reads as 0.744 to 3.973 under "Price (USD)" (#158).

Usage

untransform_positions(values, built, axis = "x", panel_id = NULL)

Arguments

values

Positions read from the built data

built

Built plot from ggplot2::ggplot_build()

axis

"x" or "y"

panel_id

Panel index for faceted plots, or NULL for the first

Details

Nothing is missing from such a chart and nothing errors. The structure is right, the point count is right, the label is right, and the numbers are false – with no signal a reader could catch, since "these look small" is not checkable without the chart you cannot see.

coord_trans() needs no special case and deliberately gets none. It transforms at draw time, after the stat, so its built data is already in data space and its scale reports identity – the same comparison that skips an untransformed chart skips it too. Testing for "is there a log axis" would have inverted it wrongly.

Applied at the point a value is emitted rather than to the frame as a whole, and that placement is load-bearing: scale_*_reverse() negates, so a frame inverted before a sort would order rows opposite to the way they were drawn, and selectors indexed by that order would land on the wrong element. Ordering follows the drawn scale; only the announced number is put back.

Value

The values in data space, unchanged when there is nothing to undo


Update Device State

Description

Updates the state for a specific graphics device.

Usage

update_device_state(device_id = grDevices::dev.cur(), state)

Arguments

device_id

Graphics device ID

state

Updated state list

Value

NULL (invisible)


Is a set of resolved coordinates usable for an axis grid?

Description

grDevices::xy.coords() coerces whatever it is given, so categorical coordinates come back as all-NA numerics rather than as an error. Those are worse than the raw input: range() on them yields infinities and a warning, where the raw character vector is simply rejected as non-numeric.

Usage

usable_xy_coords(coords)

Arguments

coords

Value returned by grDevices::xy.coords(), or NULL

Value

TRUE when both axes carry at least one finite value


Validate a canonical axes object (strict)

Description

Enforces the canonical schema. On any violation, throws an error with a descriptive message.

Usage

validate_axes(axes, context = "")

Arguments

axes

Axes list to validate (or NULL)

context

Optional string describing the call site (for errors)

Details

Rules:

Value

Invisibly returns axes if valid


Build the anchored grob-name pattern for one kind

Description

Build the anchored grob-name pattern for one kind

Usage

vioplot_grob_pattern(plot_index, kind)

Turn a grob name into the selector gridSVG exports it under

Description

gridSVG appends .1 to each grob id, and the existing base R processors address elements with the ⁠[id^=...]⁠ prefix form.

Usage

vioplot_grob_selector(element, id)

Walk a grabbed scene, redrawing it with batch markers

Description

Walk a grabbed scene, redrawing it with batch markers

Usage

walk_svg_scene(scene, one_at_a_time = FALSE)

Arguments

scene

The gTree grid.grab() returned.

one_at_a_time

Draw rect and circle grobs one element at a time.

Value

The walk state (events per marker, symbol use).


Emit a one-time warning when quantmod::chartSeries() is called with a technical-analysis indicator other than the volume panel (e.g. TA = "addSMA()").

Description

maidr reads the candlesticks and the volume panel addVo() draws, but no other indicator, so it falls back to native (non-accessible) rendering for these calls and surfaces a one-time advisory pointing users to the ggplot2 + tidyquant + patchwork alternative, which maidr's ggplot2 path reads in full.

Usage

warn_chartseries_ta_unsupported()

Value

Invisibly NULL.


Say why a fourfoldplot() call is falling back to a picture

Description

The generic "Plot contains unsupported elements" the fallback already emits is true and uninformative: it does not tell an author that the same chart with std = "ind.max" would have been read. This says which of the three measured refusals applied.

Usage

warn_fourfoldplot_declined(reason)

Arguments

reason

One of "std", "strata" or "table".

Details

The three reasons, all measured on R 4.3.3:

Once per reason per session, not once per plot: detect_layer_type() is called up to five times for one accepted layer (base_r_plot_orchestrator.R lines 145, 163, 195, 499, 653) and once per declined one, so an unguarded warning would repeat. The cost is that an author who draws two default-std charts hears the explanation once – the same trade warn_chartseries_ta_unsupported() makes above.

The message deliberately avoids the substring "unsupported elements": tests/testthat/test-base-r-unrecorded-calls.R greps for exactly that to decide whether the fallback warning arrived, and a second warning carrying it would make that assertion pass for the wrong reason.

Value

Invisibly NULL.


Warn About Panels That Lost Their Accessible Data

Description

Emitted from the single place every render path funnels through, so a figure is described once no matter which entry point produced it.

Usage

warn_panel_fallback(orchestrator)

Arguments

orchestrator

The orchestrator about to render the figure

Value

Invisibly NULL


Wrap a single function

Description

This is only called during .onLoad when the namespace is still open. The wrapper checks is_patching_enabled() at runtime to decide whether to record calls or pass through.

Usage

wrap_function(function_name)

Arguments

function_name

Name of the function to wrap

Value

TRUE if successful, FALSE otherwise


Wrap S3 generic functions (lines and points)

Description

Special handling for S3 generics that can't be traced normally. Only called once during .onLoad when namespace is still open.

Usage

wrap_s3_generics()