Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions r/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -17,11 +17,10 @@ License: GPL (>= 3)
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
Imports:
cli,
htmlwidgets,
jsonlite,
rtemis (>= 1.2.4),
rtemis.core (>= 0.2.0),
rtemis.core (>= 0.4.1),
S7,
signal,
viridisLite
Expand Down
66 changes: 47 additions & 19 deletions r/R/draw.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
)
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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")
)
}

Expand Down Expand Up @@ -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"))
Expand Down Expand Up @@ -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")
)
}

Expand Down
37 changes: 24 additions & 13 deletions r/R/draw_a3.R
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
Expand All @@ -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")
)
}

Expand Down
42 changes: 30 additions & 12 deletions r/R/draw_gantt.R
Original file line number Diff line number Diff line change
Expand Up @@ -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")
)
}

Expand All @@ -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")
)
}

Expand Down
Loading