From 68bfe936896bfc7c4c97ac239085a003a1280020 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 2 Jul 2026 11:06:42 -0700 Subject: [PATCH 1/6] use rtemis.core logging throughout --- r/R/draw.R | 66 ++++-- r/R/draw_a3.R | 37 ++-- r/R/draw_gantt.R | 42 +++- r/R/draw_spectrogram.R | 271 ++++++++++++++--------- r/R/map.R | 47 +++- r/R/network.R | 43 ++-- r/R/option.R | 5 +- r/R/plot.SupervisedSession.R | 5 +- r/R/utils.R | 49 ++-- r/tests/testthat/test-draw_a3.R | 6 +- r/tests/testthat/test-draw_spectrogram.R | 43 ++-- r/tests/testthat/test-map.R | 2 +- r/tests/testthat/test-network.R | 2 +- 13 files changed, 393 insertions(+), 225 deletions(-) diff --git a/r/R/draw.R b/r/R/draw.R index f5a0895..d6ef925 100644 --- a/r/R/draw.R +++ b/r/R/draw.R @@ -453,7 +453,10 @@ draw_line <- function( # `xlim` only makes sense on a numeric (value) x-axis. if (!is.null(xlim) && x_type != "value") { - cli::cli_abort("{.arg xlim} only applies when {.arg x} is numeric.") + abort( + "`xlim` only applies when `x` is numeric.", + class = c("rtemis_value_error", "rtemis_input_error") + ) } # Resolve axis limits. Defaults: exact data range (no padding) so bands and @@ -481,9 +484,10 @@ draw_line <- function( !is.character(line_style) || any(!line_style %in% valid_line_styles) ) { - cli::cli_abort( - "{.arg line_style} must be {.val NULL} or a character vector of \\ - {.val solid}, {.val dashed}, or {.val dotted}." + abort( + "`line_style` must be NULL or a character vector of ", + "'solid', 'dashed', or 'dotted'.", + class = c("rtemis_value_error", "rtemis_input_error") ) } } @@ -662,18 +666,25 @@ axis_line_for_orthogonal <- function(ortho_lim) { #' @noRd build_block_mark_area <- function(x, blocks, block_color, block_opacity) { if (length(blocks) != length(x)) { - cli::cli_abort( - "{.arg blocks} must have the same length as {.arg x} ({length(x)}); got {length(blocks)}." + abort( + "`blocks` must have the same length as `x` (", + length(x), + "); got ", + length(blocks), + ".", + class = c("rtemis_dim_error", "rtemis_input_error") ) } if (!is.atomic(blocks)) { - cli::cli_abort( - "{.arg blocks} must be an atomic vector (factor, integer, character, or logical)." + abort( + "`blocks` must be an atomic vector (factor, integer, character, or logical).", + class = c("rtemis_type_error", "rtemis_input_error") ) } if (is.null(block_color)) { - cli::cli_abort( - "{.arg block_color} must be provided when {.arg blocks} is set." + abort( + "`block_color` must be provided when `blocks` is set.", + class = c("rtemis_value_error", "rtemis_input_error") ) } check_prob_scalar(block_opacity) @@ -694,15 +705,21 @@ build_block_mark_area <- function(x, blocks, block_color, block_opacity) { if (!is.null(bc_names) && !any(bc_names == "")) { missing_levels <- setdiff(level_keys, bc_names) if (length(missing_levels) > 0L) { - cli::cli_abort( - "{.arg block_color} is missing entries for {.val {missing_levels}}." + abort( + "`block_color` is missing entries for: ", + paste(missing_levels, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } color_for <- function(lvl) block_color[[as_key(lvl)]] } else { if (length(block_color) != k) { - cli::cli_abort( - "{.arg block_color} must have length {k} (one per level of {.arg blocks})." + abort( + "`block_color` must have length ", + k, + " (one per level of `blocks`).", + class = c("rtemis_dim_error", "rtemis_input_error") ) } color_for <- function(lvl) { @@ -818,8 +835,11 @@ resolve_zoom <- function(zoom, axis = "x") { if (is.list(zoom)) { return(zoom) } - cli::cli_abort( - "{.arg zoom} must be {.code TRUE}, {.code FALSE}, a {.cls DataZoom} object, or a list; got {.cls {class(zoom)[1]}}." + abort( + "`zoom` must be TRUE, FALSE, a DataZoom object, or a list; got ", + class(zoom)[1], + ".", + class = c("rtemis_type_error", "rtemis_input_error") ) } @@ -2021,7 +2041,10 @@ draw_heatmap <- function( x <- as.matrix(x) } if (!is.numeric(x)) { - cli::cli_abort("{.arg x} must be a numeric matrix.") + abort( + "`x` must be a numeric matrix.", + class = c("rtemis_type_error", "rtemis_input_error") + ) } if (!is.null(triangle)) { triangle <- match.arg(triangle, c("upper", "lower")) @@ -2710,8 +2733,13 @@ draw_sankey <- function( required_cols <- c("source", "target", "value") missing_cols <- setdiff(required_cols, names(links)) if (length(missing_cols) > 0L) { - cli::cli_abort( - "{.arg links} must have columns {.val {required_cols}}; missing: {.val {missing_cols}}." + abort( + "`links` must have columns ", + paste(required_cols, collapse = ", "), + "; missing: ", + paste(missing_cols, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } diff --git a/r/R/draw_a3.R b/r/R/draw_a3.R index 70dd34f..a559cc7 100644 --- a/r/R/draw_a3.R +++ b/r/R/draw_a3.R @@ -327,16 +327,19 @@ draw_a3 <- function( ) { # ── Input validation ──────────────────────────────────────────────────────── if (!S7::S7_inherits(x)) { - cli::cli_abort( - "{.arg x} must be an {.cls A3} object. \\ - Create one with {.fn rtemis.a3::create_A3}." + abort( + "`x` must be an A3 object. ", + "Create one with `rtemis.a3::create_A3()`.", + class = c("rtemis_type_error", "rtemis_input_error") ) } cls_name <- S7::S7_class(x)@name if (!grepl("::A3$|^A3$", cls_name)) { - cli::cli_abort( - "{.arg x} must be an {.cls A3} object, not {.cls {cls_name}}. \\ - Create one with {.fn rtemis.a3::create_A3}." + abort( + "`x` must be an A3 object, not ", + cls_name, + ". Create one with `rtemis.a3::create_A3()`.", + class = c("rtemis_type_error", "rtemis_input_error") ) } ptm_placement <- match.arg( @@ -345,10 +348,16 @@ draw_a3 <- function( ) n_per_row <- as.integer(n_per_row) if (n_per_row <= 1L) { - cli::cli_abort("{.arg n_per_row} must be an integer > 1.") + abort( + "`n_per_row` must be an integer > 1.", + class = c("rtemis_range_error", "rtemis_input_error") + ) } if (!is.numeric(residue_spacing) || residue_spacing <= 0) { - cli::cli_abort("{.arg residue_spacing} must be a positive number.") + abort( + "`residue_spacing` must be a positive number.", + class = c("rtemis_range_error", "rtemis_input_error") + ) } if (!is.null(position_every)) { position_every <- as.integer(position_every) @@ -357,15 +366,17 @@ draw_a3 <- function( is.na(position_every) || position_every < 1L ) { - cli::cli_abort( - "{.arg position_every} must be a positive integer or {.val NULL}." + abort( + "`position_every` must be a positive integer or NULL.", + class = c("rtemis_range_error", "rtemis_input_error") ) } } if (!is.null(grid) && !S7::S7_inherits(grid, Grid)) { - cli::cli_abort( - "{.arg grid} must be a {.cls Grid} object or {.val NULL}. \\ - Use {.fn Grid} to create one, e.g. {.code Grid(left = 8, top = 8)}." + abort( + "`grid` must be a Grid object or NULL. ", + "Use `Grid()` to create one, e.g. `Grid(left = 8, top = 8)`.", + class = c("rtemis_type_error", "rtemis_input_error") ) } diff --git a/r/R/draw_gantt.R b/r/R/draw_gantt.R index c395261..7edf2fd 100644 --- a/r/R/draw_gantt.R +++ b/r/R/draw_gantt.R @@ -155,28 +155,45 @@ draw_gantt <- function( required_cols <- c("label", "start", "end") missing_cols <- setdiff(required_cols, names(tasks)) if (length(missing_cols) > 0L) { - cli::cli_abort( - "{.arg tasks} must have columns {.val {required_cols}}; missing: {.val {missing_cols}}." + abort( + "`tasks` must have columns ", + paste(required_cols, collapse = ", "), + "; missing: ", + paste(missing_cols, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } if (!axis_type %in% c("value", "time")) { - cli::cli_abort( - "{.arg axis_type} must be one of {.val {c('value', 'time')}}, not {.val {axis_type}}." + abort( + "`axis_type` must be one of 'value', 'time', not '", + axis_type, + "'.", + class = c("rtemis_value_error", "rtemis_input_error") ) } if (!is.null(group) && !group %in% names(tasks)) { - cli::cli_abort( - "{.arg group} column {.val {group}} not found in {.arg tasks}." + abort( + "`group` column '", + group, + "' not found in `tasks`.", + class = c("rtemis_value_error", "rtemis_input_error") ) } if (!is.null(tooltip) && !tooltip %in% names(tasks)) { - cli::cli_abort( - "{.arg tooltip} column {.val {tooltip}} not found in {.arg tasks}." + abort( + "`tooltip` column '", + tooltip, + "' not found in `tasks`.", + class = c("rtemis_value_error", "rtemis_input_error") ) } if (!is.null(border) && !border %in% names(tasks)) { - cli::cli_abort( - "{.arg border} column {.val {border}} not found in {.arg tasks}." + abort( + "`border` column '", + border, + "' not found in `tasks`.", + class = c("rtemis_value_error", "rtemis_input_error") ) } @@ -191,8 +208,9 @@ draw_gantt <- function( # NA start/end serialize as JSON null and break the custom-series renderer; # fail early with a corrective message instead. if (anyNA(starts) || anyNA(ends)) { - cli::cli_abort( - "Columns {.val start} and {.val end} in {.arg tasks} must not contain missing values ({.val NA})." + abort( + "Columns 'start' and 'end' in `tasks` must not contain missing values (NA).", + class = c("rtemis_value_error", "rtemis_input_error") ) } diff --git a/r/R/draw_spectrogram.R b/r/R/draw_spectrogram.R index 04d5c68..aeb07a5 100644 --- a/r/R/draw_spectrogram.R +++ b/r/R/draw_spectrogram.R @@ -9,10 +9,13 @@ .specgram_window <- function(window, n) { if (is.numeric(window)) { if (length(window) != n) { - cli::cli_abort( - "Custom {.arg window} vector has length {length(window)}, \\ - expected {n} (= {.arg n_fft}).", - call = NULL + abort( + "Custom `window` vector has length ", + length(window), + ", expected ", + n, + " (= `n_fft`).", + class = c("rtemis_dim_error", "rtemis_input_error") ) } return(window) @@ -26,13 +29,13 @@ ) fn <- fns[[window]] if (is.null(fn)) { - cli::cli_abort( - c( - "{.arg window} {.val {window}} is not a recognized window type.", - "i" = "Use one of {.val {paste(names(fns), collapse = ', ')}} \\ - or supply a numeric vector of length {.arg n_fft}." - ), - call = NULL + abort( + "`window` '", + window, + "' is not a recognized window type. Use one of ", + paste(names(fns), collapse = ", "), + " or supply a numeric vector of length `n_fft`.", + class = c("rtemis_value_error", "rtemis_input_error") ) } fn(n) @@ -106,17 +109,13 @@ return(cols_light) } - cli::cli_abort( - c( - "{.arg palette} {.val {palette}} is not recognized.", - "i" = paste0( - "Named options: {.val ", - paste(names(viridis_opts), collapse = "}, {.val "), - "}}, {.val diverging}.", - " Or supply a character vector of >= 2 hex colours." - ) - ), - call = NULL + abort( + "`palette` '", + palette, + "' is not recognized. Named options: ", + paste(names(viridis_opts), collapse = ", "), + ", diverging. Or supply a character vector of >= 2 hex colours.", + class = c("rtemis_value_error", "rtemis_input_error") ) } @@ -129,12 +128,10 @@ return(cols) } - cli::cli_abort( - paste0( - "{.arg palette} must be a named palette string or a character vector \\ - of >= 2 hex colours." - ), - call = NULL + abort( + "`palette` must be a named palette string or a character vector ", + "of >= 2 hex colours.", + class = c("rtemis_type_error", "rtemis_input_error") ) } @@ -253,19 +250,19 @@ draw_spectrogram <- function( ) { # -- 1. Validate scalar arguments --------------------------------------------- if (!is.numeric(x) && !is.complex(x)) { - cli::cli_abort( - "{.arg x} must be a numeric or complex matrix (freq x time) \\ - or a numeric vector (raw signal)." + abort( + "`x` must be a numeric or complex matrix (freq x time) ", + "or a numeric vector (raw signal).", + class = c("rtemis_type_error", "rtemis_input_error") ) } if (is.complex(x) && !is.matrix(x)) { - cli::cli_abort( - c( - "{.arg x} is a complex vector, which is not a valid raw signal.", - "i" = "Pass the STFT output as a complex matrix (freq x time), \\ - e.g. {.code signal::specgram(x)$S}." - ) + abort( + "`x` is a complex vector, which is not a valid raw signal. ", + "Pass the STFT output as a complex matrix (freq x time), ", + "e.g. `signal::specgram(x)$S`.", + class = c("rtemis_type_error", "rtemis_input_error") ) } @@ -276,21 +273,27 @@ draw_spectrogram <- function( time_unit <- match.arg(time_unit, c("s", "ms")) if (!is.logical(power) || length(power) != 1L) { - cli::cli_abort( - "{.arg power} must be a single logical value (TRUE or FALSE)." + abort( + "`power` must be a single logical value (TRUE or FALSE).", + class = c("rtemis_type_error", "rtemis_input_error") ) } if (!is.logical(db) || length(db) != 1L) { - cli::cli_abort("{.arg db} must be a single logical value (TRUE or FALSE).") + abort( + "`db` must be a single logical value (TRUE or FALSE).", + class = c("rtemis_type_error", "rtemis_input_error") + ) } if (!is.logical(palette_reverse) || length(palette_reverse) != 1L) { - cli::cli_abort( - "{.arg palette_reverse} must be a single logical value (TRUE or FALSE)." + abort( + "`palette_reverse` must be a single logical value (TRUE or FALSE).", + class = c("rtemis_type_error", "rtemis_input_error") ) } if (!is.logical(show_colorbar) || length(show_colorbar) != 1L) { - cli::cli_abort( - "{.arg show_colorbar} must be a single logical value (TRUE or FALSE)." + abort( + "`show_colorbar` must be a single logical value (TRUE or FALSE).", + class = c("rtemis_type_error", "rtemis_input_error") ) } if ( @@ -299,9 +302,11 @@ draw_spectrogram <- function( is.na(db_range) || db_range <= 0 ) { - cli::cli_abort( - "{.arg db_range} must be a single positive number. \\ - Got {.val {db_range}}." + abort( + "`db_range` must be a single positive number. Got ", + paste(db_range, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } if ( @@ -310,7 +315,10 @@ draw_spectrogram <- function( is.na(n_colors) || n_colors < 2L ) { - cli::cli_abort("{.arg n_colors} must be an integer >= 2.") + abort( + "`n_colors` must be an integer >= 2.", + class = c("rtemis_value_error", "rtemis_input_error") + ) } n_colors <- as.integer(n_colors) @@ -320,9 +328,17 @@ draw_spectrogram <- function( return(invisible(NULL)) } if (!is.numeric(r) || length(r) != 2L || anyNA(r) || r[[1L]] >= r[[2L]]) { - cli::cli_abort( - "{.arg {arg}} must be a length-2 numeric vector with {arg}[1] < {arg}[2]. \\ - Got {.val {r}}." + abort( + "`", + arg, + "` must be a length-2 numeric vector with ", + arg, + "[1] < ", + arg, + "[2]. Got ", + paste(r, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } } @@ -342,11 +358,10 @@ draw_spectrogram <- function( # -- 2. STFT (raw signal) or matrix input ------------------------------------ if (is_raw_signal) { if (is.null(sample_rate)) { - cli::cli_abort( - c( - "{.arg sample_rate} is required when {.arg x} is a raw signal vector.", - "i" = "Provide the sampling rate in Hz, e.g. {.code sample_rate = 44100}." - ) + abort( + "`sample_rate` is required when `x` is a raw signal vector. ", + "Provide the sampling rate in Hz, e.g. `sample_rate = 44100`.", + class = c("rtemis_value_error", "rtemis_input_error") ) } if ( @@ -355,24 +370,38 @@ draw_spectrogram <- function( is.na(sample_rate) || sample_rate <= 0 ) { - cli::cli_abort( - "{.arg sample_rate} must be a single positive number (Hz). \\ - Got {.val {sample_rate}}." + abort( + "`sample_rate` must be a single positive number (Hz). Got ", + paste(sample_rate, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } if (!is.numeric(n_fft) || length(n_fft) != 1L || is.na(n_fft)) { - cli::cli_abort( - "{.arg n_fft} must be a single integer >= 2. Got {.val {n_fft}}." + abort( + "`n_fft` must be a single integer >= 2. Got ", + paste(n_fft, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } n_fft <- as.integer(n_fft) if (n_fft < 2L) { - cli::cli_abort("{.arg n_fft} must be >= 2. Got {n_fft}.") + abort( + "`n_fft` must be >= 2. Got ", + n_fft, + ".", + class = c("rtemis_range_error", "rtemis_input_error") + ) } if (n_fft > length(x)) { - cli::cli_abort( - "{.arg n_fft} ({n_fft}) exceeds the signal length ({length(x)}). \\ - Reduce {.arg n_fft} or supply a longer signal." + abort( + "`n_fft` (", + n_fft, + ") exceeds the signal length (", + length(x), + "). Reduce `n_fft` or supply a longer signal.", + class = c("rtemis_range_error", "rtemis_input_error") ) } @@ -381,16 +410,22 @@ draw_spectrogram <- function( !is.null(overlap) && (!is.numeric(overlap) || length(overlap) != 1L || is.na(overlap)) ) { - cli::cli_abort( - "{.arg overlap} must be a single non-negative integer or NULL. \\ - Got {.val {overlap}}." + abort( + "`overlap` must be a single non-negative integer or NULL. Got ", + paste(overlap, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } ovlp <- as.integer(overlap %||% ceiling(n_fft / 2L)) if (ovlp < 0L || ovlp >= n_fft) { - cli::cli_abort( - "{.arg overlap} must be in [0, n_fft - 1] = [0, {n_fft - 1L}]. \\ - Got {ovlp}." + abort( + "`overlap` must be in [0, n_fft - 1] = [0, ", + n_fft - 1L, + "]. Got ", + ovlp, + ".", + class = c("rtemis_range_error", "rtemis_input_error") ) } @@ -407,9 +442,10 @@ draw_spectrogram <- function( } else { # Pre-computed matrix (real or complex) if (!is.matrix(x)) { - cli::cli_abort( - "{.arg x} must be a numeric or complex matrix (freq x time) \\ - or a numeric vector (raw signal)." + abort( + "`x` must be a numeric or complex matrix (freq x time) ", + "or a numeric vector (raw signal).", + class = c("rtemis_type_error", "rtemis_input_error") ) } S <- x @@ -418,9 +454,11 @@ draw_spectrogram <- function( if (!is.null(time)) { if (!is.numeric(time) || length(time) != n_time || anyNA(time)) { - cli::cli_abort( - "{.arg time} must be a numeric vector of length {n_time} \\ - (= ncol(x)) with no NAs." + abort( + "`time` must be a numeric vector of length ", + n_time, + " (= ncol(x)) with no NAs.", + class = c("rtemis_value_error", "rtemis_input_error") ) } time_s <- time @@ -434,9 +472,11 @@ draw_spectrogram <- function( length(frequency) != n_freq || anyNA(frequency) ) { - cli::cli_abort( - "{.arg frequency} must be a numeric vector of length {n_freq} \\ - (= nrow(x)) with no NAs." + abort( + "`frequency` must be a numeric vector of length ", + n_freq, + " (= nrow(x)) with no NAs.", + class = c("rtemis_value_error", "rtemis_input_error") ) } freq_hz <- frequency @@ -458,13 +498,11 @@ draw_spectrogram <- function( # -- 4. dB conversion -------------------------------------------------------- if (db) { if (!is.complex(S) && any(spec < 0, na.rm = TRUE)) { - cli::cli_warn( - c( - "Pre-computed matrix contains negative values; {.code db = TRUE} \\ - will produce {.val NaN} for those entries.", - "i" = "Pass {.code db = FALSE} if the matrix is already in dB, \\ - or ensure values are non-negative before dB conversion." - ) + warn( + "Pre-computed matrix contains negative values; `db = TRUE` ", + "will produce NaN for those entries. ", + "Pass `db = FALSE` if the matrix is already in dB, ", + "or ensure values are non-negative before dB conversion." ) } eps <- .Machine[["double.eps"]] @@ -479,9 +517,10 @@ draw_spectrogram <- function( if (freq_scale == "log") { keep_f <- freq_hz > 0 if (!any(keep_f)) { - cli::cli_abort( - "No positive-frequency bins found for {.code freq_scale = 'log'}. \\ - This should not happen; please check the signal or frequency vector." + abort( + "No positive-frequency bins found for `freq_scale = 'log'`. ", + "This should not happen; please check the signal or frequency vector.", + class = c("rtemis_value_error", "rtemis_input_error") ) } spec <- spec[keep_f, , drop = FALSE] @@ -492,11 +531,15 @@ draw_spectrogram <- function( if (!is.null(freq_range)) { keep_f <- freq_hz >= freq_range[[1L]] & freq_hz <= freq_range[[2L]] if (!any(keep_f)) { - cli::cli_abort( - "No frequency bins fall within {.arg freq_range} \\ - [{freq_range[1]}, {freq_range[2]}] Hz. \\ - Check {.arg freq_range} against the signal's Nyquist limit \\ - ({max(freq_hz)} Hz)." + abort( + "No frequency bins fall within `freq_range` [", + freq_range[[1L]], + ", ", + freq_range[[2L]], + "] Hz. Check `freq_range` against the signal's Nyquist limit (", + max(freq_hz), + " Hz).", + class = c("rtemis_range_error", "rtemis_input_error") ) } spec <- spec[keep_f, , drop = FALSE] @@ -505,11 +548,15 @@ draw_spectrogram <- function( if (!is.null(time_range)) { keep_t <- time_s >= time_range[[1L]] & time_s <= time_range[[2L]] if (!any(keep_t)) { - cli::cli_abort( - "No time frames fall within {.arg time_range} \\ - [{time_range[1]}, {time_range[2]}] s. \\ - Check {.arg time_range} against the signal duration \\ - ({max(time_s)} s)." + abort( + "No time frames fall within `time_range` [", + time_range[[1L]], + ", ", + time_range[[2L]], + "] s. Check `time_range` against the signal duration (", + max(time_s), + " s).", + class = c("rtemis_range_error", "rtemis_input_error") ) } spec <- spec[, keep_t, drop = FALSE] @@ -551,11 +598,10 @@ draw_spectrogram <- function( # -- 9. Colour-scale limits -------------------------------------------------- if (is.null(zlim)) { if (!any(is.finite(spec))) { - cli::cli_abort( - c( - "Cannot determine colour-scale limits: spectrogram contains no finite values.", - "i" = "Check the input signal, or supply {.arg zlim} explicitly." - ) + abort( + "Cannot determine colour-scale limits: spectrogram contains no finite values. ", + "Check the input signal, or supply `zlim` explicitly.", + class = c("rtemis_value_error", "rtemis_input_error") ) } zlim <- range(spec, na.rm = TRUE) @@ -570,11 +616,16 @@ draw_spectrogram <- function( n_time_disp <- ncol(spec) n_cells <- n_freq_disp * n_time_disp if (n_cells > 500000L) { - cli::cli_warn( - c( - "Spectrogram has {n_cells} cells; browser rendering may be slow.", - "i" = "Consider reducing {.arg n_fft} or the signal length." - ) + warn( + "Spectrogram has ", + n_cells, + " cells; browser rendering may be slow." + ) + info( + "Consider reducing 'n_fft' (currently ", + n_fft, + ")", + " or the signal length." ) } diff --git a/r/R/map.R b/r/R/map.R index cc9ca60..8ff5d92 100644 --- a/r/R/map.R +++ b/r/R/map.R @@ -300,7 +300,12 @@ load_map_geometry <- function(resolution) { ) src <- sources[[resolution]] if (is.null(src)) { - cli::cli_abort("Unknown map resolution {.val {resolution}}.") + abort( + "Unknown map resolution '", + resolution, + "'.", + class = c("rtemis_value_error", "rtemis_input_error") + ) } path <- system.file( "htmlwidgets", @@ -310,8 +315,11 @@ load_map_geometry <- function(resolution) { package = "rtemis.draw" ) if (!nzchar(path) || !file.exists(path)) { - cli::cli_abort( - "Vendored geometry {.file {src[['file']]}} not found in the package." + abort( + "Vendored geometry '", + src[["file"]], + "' not found in the package.", + class = "rtemis_io_error" ) } # Read the whole file as one JSON string; file size in bytes is an upper bound @@ -340,8 +348,10 @@ S7::method(draw, MapLibreOption) <- function( ... ) { if (!is.null(filename)) { - cli::cli_warn( - "Static export of map widgets is not yet supported; ignoring {.arg filename}." + warn( + "Static export of map widgets is not yet supported; ignoring ", + filename, + "." ) } @@ -384,20 +394,33 @@ map_from_data_frame <- function( value_label = NULL ) { if (!is.data.frame(data)) { - cli::cli_abort("{.arg data} must be a data frame.") + abort( + "`data` must be a data frame.", + class = c("rtemis_type_error", "rtemis_input_error") + ) } if (!is.character(location) || length(location) != 1L) { - cli::cli_abort("{.arg location} must be a single column name.") + abort( + "`location` must be a single column name.", + class = c("rtemis_type_error", "rtemis_input_error") + ) } if (!is.character(value) || length(value) != 1L) { - cli::cli_abort("{.arg value} must be a single column name.") + abort( + "`value` must be a single column name.", + class = c("rtemis_type_error", "rtemis_input_error") + ) } missing_cols <- setdiff(c(location, value, tooltip), names(data)) if (length(missing_cols) > 0L) { - cli::cli_abort(c( - "Column{?s} {.val {missing_cols}} not found in {.arg data}.", - "i" = "Available columns: {.val {names(data)}}." - )) + abort( + "Columns not found in `data`: ", + paste(missing_cols, collapse = ", "), + ". Available columns: ", + paste(names(data), collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") + ) } loc <- as.character(data[[location]]) diff --git a/r/R/network.R b/r/R/network.R index 52dba01..79bc83d 100644 --- a/r/R/network.R +++ b/r/R/network.R @@ -292,8 +292,8 @@ S7::method(draw, SigmaOption) <- function( ... ) { if (!is.null(filename)) { - cli::cli_warn( - "Static export of network widgets is not yet supported; ignoring {.arg filename}." + warn( + "Static export of network widgets is not yet supported; ignoring `filename`." ) } render_widget( @@ -334,12 +334,20 @@ graph_from_matrix <- function( threshold = NULL ) { if (!is.matrix(x) || nrow(x) != ncol(x)) { - cli::cli_abort( - "Matrix input to {.fn draw_network} must be square; got {nrow(x)}x{ncol(x)}." + abort( + "Matrix input to `draw_network()` must be square; got ", + nrow(x), + "x", + ncol(x), + ".", + class = c("rtemis_dim_error", "rtemis_input_error") ) } if (!is.numeric(x)) { - cli::cli_abort("Matrix input to {.fn draw_network} must be numeric.") + abort( + "Matrix input to `draw_network()` must be numeric.", + class = c("rtemis_type_error", "rtemis_input_error") + ) } n <- nrow(x) @@ -400,13 +408,15 @@ graph_from_matrix <- function( #' @noRd graph_from_edge_list <- function(edges, nodes = NULL, directed = FALSE) { if (!is.data.frame(edges)) { - cli::cli_abort( - "Edge-list input to {.fn draw_network} must be a data frame." + abort( + "Edge-list input to `draw_network()` must be a data frame.", + class = c("rtemis_type_error", "rtemis_input_error") ) } if (ncol(edges) < 2L) { - cli::cli_abort( - "Edge-list data frame must have at least 2 columns (source, target)." + abort( + "Edge-list data frame must have at least 2 columns (source, target).", + class = c("rtemis_dim_error", "rtemis_input_error") ) } @@ -455,7 +465,10 @@ graph_from_edge_list <- function(edges, nodes = NULL, directed = FALSE) { }) } else { if (!is.data.frame(nodes)) { - cli::cli_abort("`nodes` must be a data frame or NULL.") + abort( + "`nodes` must be a data frame or NULL.", + class = c("rtemis_type_error", "rtemis_input_error") + ) } id_col <- if ("id" %in% names(nodes)) { "id" @@ -678,10 +691,12 @@ draw_network <- function( } else if (is.data.frame(x)) { model <- graph_from_edge_list(x, nodes = nodes, directed = directed) } else { - cli::cli_abort(c( - "{.arg x} must be a square numeric matrix or an edge-list data frame.", - "i" = "Got {.cls {class(x)}}." - )) + abort( + "`x` must be a square numeric matrix or an edge-list data frame; got ", + paste(class(x), collapse = "/"), + ".", + class = c("rtemis_type_error", "rtemis_input_error") + ) } draw_graph( diff --git a/r/R/option.R b/r/R/option.R index 81dede5..cb9e6c0 100644 --- a/r/R/option.R +++ b/r/R/option.R @@ -153,8 +153,9 @@ S7::method(to_list, EChartsOption) <- function(x, ...) { if (S7::S7_inherits(dz)) to_list(dz) else dz })) } else { - cli::cli_abort( - "{.arg data_zoom} must be a {.cls DataZoom} object or a list of them." + abort( + "`data_zoom` must be a DataZoom object or a list of them.", + class = c("rtemis_type_error", "rtemis_input_error") ) } } diff --git a/r/R/plot.SupervisedSession.R b/r/R/plot.SupervisedSession.R index a766869..f82182f 100644 --- a/r/R/plot.SupervisedSession.R +++ b/r/R/plot.SupervisedSession.R @@ -40,8 +40,9 @@ S7::method(plot, SupervisedSession) <- function( ) { events <- x@events if (length(events) == 0L) { - cli::cli_abort( - "This {.cls SupervisedSession} has no recorded events to plot." + abort( + "This SupervisedSession has no recorded events to plot.", + class = c("rtemis_value_error", "rtemis_input_error") ) } started <- x@started diff --git a/r/R/utils.R b/r/R/utils.R index 0d9526a..f24a331 100644 --- a/r/R/utils.R +++ b/r/R/utils.R @@ -292,29 +292,36 @@ parse_margins <- function(margins) { } # Accept a named atomic vector (numeric / character) or a named list. if (!is.list(margins) && !is.atomic(margins)) { - cli::cli_abort( - "{.arg margins} must be a named numeric vector or named list." + abort( + "`margins` must be a named numeric vector or named list.", + class = c("rtemis_type_error", "rtemis_input_error") ) } nms <- names(margins) if (is.null(nms) || any(!nzchar(nms))) { - cli::cli_abort( - "{.arg margins} must be named with any of {.val top}, {.val right}, {.val bottom}, {.val left}." + abort( + "`margins` must be named with any of 'top', 'right', 'bottom', 'left'.", + class = c("rtemis_value_error", "rtemis_input_error") ) } valid_sides <- c("top", "right", "bottom", "left") bad <- setdiff(nms, valid_sides) if (length(bad) > 0L) { - cli::cli_abort( - c( - "{.arg margins} has unrecognised name{?s}: {.val {bad}}.", - "i" = "Valid names are {.val {valid_sides}}." - ) + abort( + "`margins` has unrecognised names: ", + paste(bad, collapse = ", "), + ". Valid names are: ", + paste(valid_sides, collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } if (any(duplicated(nms))) { - cli::cli_abort( - "{.arg margins} has duplicate name{?s}: {.val {nms[duplicated(nms)]}}." + abort( + "`margins` has duplicate names: ", + paste(unique(nms[duplicated(nms)]), collapse = ", "), + ".", + class = c("rtemis_value_error", "rtemis_input_error") ) } for (side in valid_sides) { @@ -329,8 +336,15 @@ parse_margins <- function(margins) { !((is.numeric(val) && length(val) == 1L) || (is.character(val) && length(val) == 1L)) ) { - cli::cli_abort( - "{.arg margins} element {.val {side}} must be a single number or string (e.g. {.val 10%}); got {.cls {class(val)[1]}} of length {length(val)}." + abort( + "`margins` element '", + side, + "' must be a single number or string (e.g. '10%'); got ", + class(val)[1], + " of length ", + length(val), + ".", + class = c("rtemis_type_error", "rtemis_input_error") ) } empty[[side]] <- val @@ -368,7 +382,7 @@ resolve_margins <- function(margins) { #' Validate an axis-limits argument #' #' Checks that a user-supplied `xlim`/`ylim`/`zlim` value is either `NULL` or a -#' length-2 numeric vector. Errors with a corrective [cli::cli_abort()] message +#' length-2 numeric vector. Errors with a corrective [rtemis.core::abort()] message #' otherwise. Returns the value invisibly so callers can chain if desired. #' #' @param value Any: User-supplied limits value. @@ -382,8 +396,11 @@ validate_axis_lim <- function(value, arg) { return(invisible(value)) } if (!is.numeric(value) || length(value) != 2L || !all(is.finite(value))) { - cli::cli_abort( - "{.arg {arg}} must be a length-2 finite numeric vector or {.code NULL}." + abort( + "`", + arg, + "` must be a length-2 finite numeric vector or `NULL`.", + class = c("rtemis_type_error", "rtemis_input_error") ) } invisible(value) diff --git a/r/tests/testthat/test-draw_a3.R b/r/tests/testthat/test-draw_a3.R index 31e06d5..2e347d2 100644 --- a/r/tests/testthat/test-draw_a3.R +++ b/r/tests/testthat/test-draw_a3.R @@ -68,8 +68,8 @@ test_that("a3_circular_offset returns x and y", { # ── draw_a3 ─────────────────────────────────────────────────────────────────── test_that("draw_a3 rejects non-A3 input", { - expect_error(draw_a3("MAEPR"), class = "rlang_error") - expect_error(draw_a3(list(sequence = "MAEPR")), class = "rlang_error") + expect_error(draw_a3("MAEPR"), class = "rtemis_error") + expect_error(draw_a3(list(sequence = "MAEPR")), class = "rtemis_error") }) test_that("draw_a3 creates htmlwidget from minimal A3 object", { @@ -222,5 +222,5 @@ test_that("draw_a3 dataZoom absent when enable_zoom = FALSE", { test_that("draw_a3 n_per_row validation works", { skip_if_not_installed("rtemis.a3") a <- rtemis.a3::create_A3("MAEPR") - expect_error(draw_a3(a, n_per_row = 1L), class = "rlang_error") + expect_error(draw_a3(a, n_per_row = 1L), class = "rtemis_error") }) diff --git a/r/tests/testthat/test-draw_spectrogram.R b/r/tests/testthat/test-draw_spectrogram.R index 161cacb..d0ed16c 100644 --- a/r/tests/testthat/test-draw_spectrogram.R +++ b/r/tests/testthat/test-draw_spectrogram.R @@ -14,14 +14,14 @@ test_that(".specgram_window accepts a pre-built numeric vector", { test_that(".specgram_window errors on unknown name", { expect_error( rtemis.draw:::.specgram_window("bogus", 64L), - class = "rlang_error" + class = "rtemis_error" ) }) test_that(".specgram_window errors when custom vector length mismatches n", { expect_error( rtemis.draw:::.specgram_window(rep(1, 10L), 32L), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -93,7 +93,7 @@ test_that(".spectrogram_palette accepts custom hex vector", { test_that(".spectrogram_palette errors on unknown name", { expect_error( rtemis.draw:::.spectrogram_palette("unknown_pal", 64L, FALSE, c(0, 1)), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -102,32 +102,35 @@ test_that(".spectrogram_palette errors on unknown name", { # --------------------------------------------------------------------------- test_that("draw_spectrogram errors when x is not numeric", { - expect_error(draw_spectrogram("hello"), class = "rlang_error") + expect_error(draw_spectrogram("hello"), class = "rtemis_error") }) test_that("draw_spectrogram errors when x is a complex vector (not matrix)", { expect_error( draw_spectrogram(complex(real = 1:10, imaginary = 1:10)), - class = "rlang_error" + class = "rtemis_error" ) }) test_that("draw_spectrogram errors when raw signal missing sample_rate", { sig <- rnorm(1000) - expect_error(draw_spectrogram(sig), class = "rlang_error") + expect_error(draw_spectrogram(sig), class = "rtemis_error") }) test_that("draw_spectrogram errors when sample_rate is non-positive", { sig <- rnorm(1000) - expect_error(draw_spectrogram(sig, sample_rate = 0), class = "rlang_error") - expect_error(draw_spectrogram(sig, sample_rate = -100), class = "rlang_error") + expect_error(draw_spectrogram(sig, sample_rate = 0), class = "rtemis_error") + expect_error( + draw_spectrogram(sig, sample_rate = -100), + class = "rtemis_error" + ) }) test_that("draw_spectrogram errors when n_fft exceeds signal length", { sig <- rnorm(100) expect_error( draw_spectrogram(sig, sample_rate = 1000, n_fft = 200L), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -135,11 +138,11 @@ test_that("draw_spectrogram errors on invalid overlap", { sig <- rnorm(1000) expect_error( draw_spectrogram(sig, sample_rate = 1000, n_fft = 64L, overlap = 64L), - class = "rlang_error" + class = "rtemis_error" ) expect_error( draw_spectrogram(sig, sample_rate = 1000, n_fft = 64L, overlap = -1L), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -147,11 +150,11 @@ test_that("draw_spectrogram errors on bad db_range", { sig <- rnorm(1000) expect_error( draw_spectrogram(sig, sample_rate = 1000, db_range = -10), - class = "rlang_error" + class = "rtemis_error" ) expect_error( draw_spectrogram(sig, sample_rate = 1000, db_range = 0), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -159,11 +162,11 @@ test_that("draw_spectrogram errors on bad freq_range", { sig <- rnorm(1000) expect_error( draw_spectrogram(sig, sample_rate = 1000, freq_range = c(500, 200)), - class = "rlang_error" + class = "rtemis_error" ) expect_error( draw_spectrogram(sig, sample_rate = 1000, freq_range = c(1, 2, 3)), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -172,7 +175,7 @@ test_that("draw_spectrogram errors when freq_range excludes all bins", { # 1000 Hz sample rate → Nyquist at 500 Hz; requesting above that expect_error( draw_spectrogram(sig, sample_rate = 1000, freq_range = c(600, 800)), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -180,7 +183,7 @@ test_that("draw_spectrogram errors on bad time_range", { sig <- rnorm(1000) expect_error( draw_spectrogram(sig, sample_rate = 1000, time_range = c(2, 1)), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -188,7 +191,7 @@ test_that("draw_spectrogram errors on bad zlim", { sig <- rnorm(1000) expect_error( draw_spectrogram(sig, sample_rate = 1000, zlim = c(10, 5)), - class = "rlang_error" + class = "rtemis_error" ) }) @@ -196,7 +199,7 @@ test_that("draw_spectrogram errors on mismatched time vector for matrix input", mat <- matrix(abs(rnorm(128 * 50)), nrow = 128, ncol = 50) expect_error( draw_spectrogram(mat, time = seq_len(30)), # wrong length - class = "rlang_error" + class = "rtemis_error" ) }) @@ -204,7 +207,7 @@ test_that("draw_spectrogram errors on mismatched frequency vector for matrix inp mat <- matrix(abs(rnorm(128 * 50)), nrow = 128, ncol = 50) expect_error( draw_spectrogram(mat, frequency = seq_len(10)), # wrong length - class = "rlang_error" + class = "rtemis_error" ) }) diff --git a/r/tests/testthat/test-map.R b/r/tests/testthat/test-map.R index 121fba9..672e00e 100644 --- a/r/tests/testthat/test-map.R +++ b/r/tests/testthat/test-map.R @@ -227,7 +227,7 @@ test_that("draw_choropleth honours an explicit theme and NA (no theme)", { test_that("draw_choropleth warns and ignores filename (no static export yet)", { df <- data.frame(iso = "USA", gdp = 1) - expect_warning( + expect_message( draw_choropleth(df, "iso", "gdp", filename = "map.png"), "not yet supported" ) diff --git a/r/tests/testthat/test-network.R b/r/tests/testthat/test-network.R index 345de24..d717349 100644 --- a/r/tests/testthat/test-network.R +++ b/r/tests/testthat/test-network.R @@ -277,7 +277,7 @@ test_that("draw(SigmaOption) warns and ignores filename (no static export yet)", edges = list(), directed = FALSE ) - expect_warning( + expect_message( draw(SigmaOption(model = m), filename = "net.svg"), "not yet supported" ) From 9d13454029ebe98b7f7d7944afaef1648dc10a5d Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 2 Jul 2026 11:06:54 -0700 Subject: [PATCH 2/6] => v0.4.1 --- r/DESCRIPTION | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/r/DESCRIPTION b/r/DESCRIPTION index 37042cf..fbede1f 100644 --- a/r/DESCRIPTION +++ b/r/DESCRIPTION @@ -1,7 +1,7 @@ Package: rtemis.draw Title: Interactive Visualization -Version: 0.4.0 -Date: 2026-06-29 +Version: 0.4.1 +Date: 2026-07-02 Authors@R: person(given = "E.D.", family = "Gennatas", role = c("aut", "cre", "cph"), email = "gennatas@gmail.com", @@ -17,7 +17,6 @@ License: GPL (>= 3) Encoding: UTF-8 Roxygen: list(markdown = TRUE) Imports: - cli, htmlwidgets, jsonlite, rtemis (>= 1.2.4), From 265bbf020c676fccbf8caa94791a03f3b7534c8a Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 2 Jul 2026 21:48:15 -0700 Subject: [PATCH 3/6] add lighten --- r/R/utils.R | 49 +++++++++++++++++++++++++++++++++++ r/tests/testthat/test-utils.R | 31 ++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/r/R/utils.R b/r/R/utils.R index f24a331..4d95368 100644 --- a/r/R/utils.R +++ b/r/R/utils.R @@ -445,3 +445,52 @@ class_or_null_property <- function(s7_class) { } ) } + + +# %% lighten() ---- +#' Lighten colors +#' +#' Lightens colors by linear interpolation toward white in RGB space +#' (a "tint"): each RGB channel moves `amount` of its remaining distance +#' toward 255. The alpha channel is preserved unchanged. +#' +#' @param x Character: R color specification(s) (hex string or named color). +#' @param amount Numeric \[0, 1]: Fraction of the distance toward white to +#' move each channel. `0` returns the input unchanged, `1` returns white. +#' @return Character: Hex color string(s), same length as `x`. Opaque colors +#' are returned as `"#RRGGBB"`; colors with alpha as `"#RRGGBBAA"`. +#' @author EDG +#' @keywords internal +#' @noRd +lighten <- function(x, amount = 0.1) { + if ( + !is.numeric(amount) || + length(amount) != 1L || + is.na(amount) || + amount < 0 || + amount > 1 + ) { + abort( + "`amount` must be a single numeric value in [0, 1].", + class = c("rtemis_value_error", "rtemis_input_error") + ) + } + # Work in [0, 1] + rgba <- grDevices::col2rgb(x, alpha = TRUE) / 255 + # Move each RGB channel `amount` of the way toward 1; leave alpha as is. + rgba[1:3, ] <- rgba[1:3, ] + (1 - rgba[1:3, ]) * amount + out <- character(ncol(rgba)) + opaque <- rgba[4L, ] == 1 + out[opaque] <- grDevices::rgb( + rgba[1L, opaque], + rgba[2L, opaque], + rgba[3L, opaque] + ) + out[!opaque] <- grDevices::rgb( + rgba[1L, !opaque], + rgba[2L, !opaque], + rgba[3L, !opaque], + alpha = rgba[4L, !opaque] + ) + out +} diff --git a/r/tests/testthat/test-utils.R b/r/tests/testthat/test-utils.R index 45902a8..98b0d71 100644 --- a/r/tests/testthat/test-utils.R +++ b/r/tests/testthat/test-utils.R @@ -98,3 +98,34 @@ test_that("resolve_margins returns a Grid with only specified sides", { expect_null(g@right) expect_null(g@bottom) }) + +test_that("lighten interpolates toward white in RGB space", { + # black lightened 10% => 10% of 255 per channel + expect_equal(rtemis.draw:::lighten("#000000", 0.1), "#1A1A1A") + # amount = 0 is identity, amount = 1 is white + expect_equal(rtemis.draw:::lighten("#3366CC", 0), "#3366CC") + expect_equal(rtemis.draw:::lighten("#3366CC", 1), "#FFFFFF") + # white is a fixed point + expect_equal(rtemis.draw:::lighten("#FFFFFF", 0.5), "#FFFFFF") +}) + +test_that("lighten accepts named colors and is vectorized", { + expect_equal( + rtemis.draw:::lighten(c("red", "#000000"), 0.5), + c("#FF8080", "#808080") + ) +}) + +test_that("lighten preserves the alpha channel", { + # opaque input => 6-digit hex, no alpha suffix + expect_equal(rtemis.draw:::lighten("#000000", 0.5), "#808080") + # input with alpha keeps its alpha unchanged + expect_equal(rtemis.draw:::lighten("#00000080", 0.5), "#80808080") +}) + +test_that("lighten rejects invalid amount", { + expect_error(rtemis.draw:::lighten("#000000", -0.1), "amount") + expect_error(rtemis.draw:::lighten("#000000", 1.5), "amount") + expect_error(rtemis.draw:::lighten("#000000", "a"), "amount") + expect_error(rtemis.draw:::lighten("#000000", c(0.1, 0.2)), "amount") +}) From 0d18f0d4a4473031d319ef2238293d97bc88f898 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 2 Jul 2026 21:51:16 -0700 Subject: [PATCH 4/6] match session plot with rtemis colors --- r/R/plot.SupervisedSession.R | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/r/R/plot.SupervisedSession.R b/r/R/plot.SupervisedSession.R index f82182f..b5d61fb 100644 --- a/r/R/plot.SupervisedSession.R +++ b/r/R/plot.SupervisedSession.R @@ -189,14 +189,16 @@ S7::method(plot, SupervisedSession) <- function( # kind's color stable across runs; draw_gantt() zips groups in first-seen (DFS) # order, so index the map by that order. kind_colors <- c( - train = "#607D8B", - outer_fold = "#8E63CE", - tune = "#BE2E5F", - grid_cell = "#466D96", - train_alg = "#4F662A", - predict = "#6CA3A0", - varimp = "#F08904", - metrics = "#FDB808" + train = "#808080", + outer_fold = getFromNamespace("col_outer", "rtemis"), + tune = getFromNamespace("col_tuner", "rtemis"), + grid_cell = lighten(getFromNamespace("col_tuner", "rtemis"), 0.5), + preprocess = getFromNamespace("col_preprocessor", "rtemis"), + decompose = getFromNamespace("col_decom", "rtemis"), + train_alg = getFromNamespace("highlight_col", "rtemis"), + predict = rtemis.core::rtemis_colors[["blue"]], + varimp = rtemis.core::rtemis_colors[["light_blue"]], + metrics = rtemis.core::rtemis_colors[["orange"]] ) present <- unique(tasks[["kind"]]) # Fall back to the default palette for any unmapped kind. From 61611225cb5e6bd6faad9f85ba0d216cde14fb40 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 2 Jul 2026 21:53:25 -0700 Subject: [PATCH 5/6] require core 0.4.1 --- r/DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r/DESCRIPTION b/r/DESCRIPTION index fbede1f..8671d4f 100644 --- a/r/DESCRIPTION +++ b/r/DESCRIPTION @@ -20,7 +20,7 @@ Imports: htmlwidgets, jsonlite, rtemis (>= 1.2.4), - rtemis.core (>= 0.2.0), + rtemis.core (>= 0.4.1), S7, signal, viridisLite From 511a72dab141484e29ce4a6d123e82aefe29d18c Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Fri, 3 Jul 2026 04:19:44 -0700 Subject: [PATCH 6/6] address PR comments --- r/R/draw_spectrogram.R | 20 ++++++++++++++------ r/R/map.R | 4 +--- r/R/utils.R | 7 +++++-- r/tests/testthat/test-utils.R | 9 +++++++++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/r/R/draw_spectrogram.R b/r/R/draw_spectrogram.R index aeb07a5..f8d0aba 100644 --- a/r/R/draw_spectrogram.R +++ b/r/R/draw_spectrogram.R @@ -621,12 +621,20 @@ draw_spectrogram <- function( n_cells, " cells; browser rendering may be slow." ) - info( - "Consider reducing 'n_fft' (currently ", - n_fft, - ")", - " or the signal length." - ) + # `n_fft`/signal length only apply when we computed the STFT ourselves; a + # pre-computed matrix must be shrunk by the caller before plotting. + if (is_raw_signal) { + info( + "Consider reducing `n_fft` (currently ", + n_fft, + ") or the signal length." + ) + } else { + info( + "Consider reducing the matrix resolution before plotting, ", + "e.g. by aggregating neighboring frequency bins or time frames." + ) + } } # -- 12. Axis label defaults and formatted label vectors --------------------- diff --git a/r/R/map.R b/r/R/map.R index 8ff5d92..ba651f1 100644 --- a/r/R/map.R +++ b/r/R/map.R @@ -349,9 +349,7 @@ S7::method(draw, MapLibreOption) <- function( ) { if (!is.null(filename)) { warn( - "Static export of map widgets is not yet supported; ignoring ", - filename, - "." + "Static export of map widgets is not yet supported; ignoring `filename`." ) } diff --git a/r/R/utils.R b/r/R/utils.R index 4d95368..4a7af64 100644 --- a/r/R/utils.R +++ b/r/R/utils.R @@ -457,8 +457,9 @@ class_or_null_property <- function(s7_class) { #' @param x Character: R color specification(s) (hex string or named color). #' @param amount Numeric \[0, 1]: Fraction of the distance toward white to #' move each channel. `0` returns the input unchanged, `1` returns white. -#' @return Character: Hex color string(s), same length as `x`. Opaque colors -#' are returned as `"#RRGGBB"`; colors with alpha as `"#RRGGBBAA"`. +#' @return Character: Hex color string(s), same length as `x`, with names +#' preserved. Opaque colors are returned as `"#RRGGBB"`; colors with alpha +#' as `"#RRGGBBAA"`. #' @author EDG #' @keywords internal #' @noRd @@ -492,5 +493,7 @@ lighten <- function(x, amount = 0.1) { rgba[3L, !opaque], alpha = rgba[4L, !opaque] ) + # grDevices::rgb() drops names; restore them so named palettes survive. + names(out) <- names(x) out } diff --git a/r/tests/testthat/test-utils.R b/r/tests/testthat/test-utils.R index 98b0d71..2189cd4 100644 --- a/r/tests/testthat/test-utils.R +++ b/r/tests/testthat/test-utils.R @@ -116,6 +116,15 @@ test_that("lighten accepts named colors and is vectorized", { ) }) +test_that("lighten preserves names of the input vector", { + expect_equal( + rtemis.draw:::lighten(c(a = "#000000", b = "#FFFFFF"), 0.5), + c(a = "#808080", b = "#FFFFFF") + ) + # unnamed input stays unnamed + expect_null(names(rtemis.draw:::lighten("#000000", 0.5))) +}) + test_that("lighten preserves the alpha channel", { # opaque input => 6-digit hex, no alpha suffix expect_equal(rtemis.draw:::lighten("#000000", 0.5), "#808080")